1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "nacl_io/kernel_handle.h"
6
7#include <errno.h>
8#include <pthread.h>
9
10#include "nacl_io/filesystem.h"
11#include "nacl_io/node.h"
12#include "nacl_io/osunistd.h"
13#include "nacl_io/socket/socket_node.h"
14
15#include "sdk_util/auto_lock.h"
16
17namespace nacl_io {
18
19// It is only legal to construct a handle while the kernel lock is held.
20KernelHandle::KernelHandle() : filesystem_(NULL), node_(NULL) {
21}
22
23KernelHandle::KernelHandle(const ScopedFilesystem& fs, const ScopedNode& node)
24    : filesystem_(fs), node_(node) {
25}
26
27KernelHandle::~KernelHandle() {
28  // Force release order for cases where filesystem_ is not ref'd by mounting.
29  node_.reset(NULL);
30  filesystem_.reset(NULL);
31}
32
33// Returns the SocketNode* if this node is a socket.
34SocketNode* KernelHandle::socket_node() {
35  if (node_.get() && node_->IsaSock())
36    return reinterpret_cast<SocketNode*>(node_.get());
37  return NULL;
38}
39
40Error KernelHandle::Init(int open_flags) {
41  handle_attr_.flags = open_flags;
42
43  if (!node_->CanOpen(open_flags)) {
44    return EACCES;
45  }
46
47  if (open_flags & O_APPEND) {
48    Error error = node_->GetSize(&handle_attr_.offs);
49    if (error)
50      return error;
51  }
52
53  return 0;
54}
55
56Error KernelHandle::Seek(off_t offset, int whence, off_t* out_offset) {
57  // By default, don't move the offset.
58  *out_offset = offset;
59  off_t base;
60  off_t node_size;
61
62  AUTO_LOCK(handle_lock_);
63  Error error = node_->GetSize(&node_size);
64  if (error)
65    return error;
66
67  switch (whence) {
68    case SEEK_SET:
69      base = 0;
70      break;
71    case SEEK_CUR:
72      base = handle_attr_.offs;
73      break;
74    case SEEK_END:
75      base = node_size;
76      break;
77    default:
78      return -1;
79  }
80
81  if (base + offset < 0)
82    return EINVAL;
83
84  off_t new_offset = base + offset;
85
86  // Seeking past the end of the file will zero out the space between the old
87  // end and the new end.
88  if (new_offset > node_size) {
89    error = node_->FTruncate(new_offset);
90    if (error)
91      return EINVAL;
92  }
93
94  *out_offset = handle_attr_.offs = new_offset;
95  return 0;
96}
97
98Error KernelHandle::Read(void* buf, size_t nbytes, int* cnt) {
99  AUTO_LOCK(handle_lock_);
100  if (OpenMode() == O_WRONLY)
101    return EACCES;
102  Error error = node_->Read(handle_attr_, buf, nbytes, cnt);
103  if (0 == error)
104    handle_attr_.offs += *cnt;
105  return error;
106}
107
108Error KernelHandle::Write(const void* buf, size_t nbytes, int* cnt) {
109  AUTO_LOCK(handle_lock_);
110  if (OpenMode() == O_RDONLY)
111    return EACCES;
112  Error error = node_->Write(handle_attr_, buf, nbytes, cnt);
113  if (0 == error)
114    handle_attr_.offs += *cnt;
115  return error;
116}
117
118Error KernelHandle::GetDents(struct dirent* pdir, size_t nbytes, int* cnt) {
119  AUTO_LOCK(handle_lock_);
120  Error error = node_->GetDents(handle_attr_.offs, pdir, nbytes, cnt);
121  if (0 == error)
122    handle_attr_.offs += *cnt;
123  return error;
124}
125
126Error KernelHandle::Fcntl(int request, int* result, ...) {
127  va_list ap;
128  va_start(ap, result);
129  Error rtn = VFcntl(request, result, ap);
130  va_end(ap);
131  return rtn;
132}
133
134Error KernelHandle::VFcntl(int request, int* result, va_list args) {
135  switch (request) {
136    case F_GETFL: {
137      *result = handle_attr_.flags;
138      return 0;
139    }
140    case F_SETFL: {
141      AUTO_LOCK(handle_lock_);
142      int flags = va_arg(args, int);
143      if (!(flags & O_APPEND) && (handle_attr_.flags & O_APPEND)) {
144        // Attempt to clear O_APPEND.
145        return EPERM;
146      }
147      // Only certain flags are mutable
148      const int mutable_flags = O_ASYNC | O_NONBLOCK;
149      flags &= mutable_flags;
150      handle_attr_.flags &= ~mutable_flags;
151      handle_attr_.flags |= flags;
152      return 0;
153    }
154    default:
155      LOG_ERROR("Unsupported fcntl: %#x", request);
156      break;
157  }
158  return ENOSYS;
159}
160
161Error KernelHandle::Accept(PP_Resource* new_sock,
162                           struct sockaddr* addr,
163                           socklen_t* len) {
164  SocketNode* sock = socket_node();
165  if (!sock)
166    return ENOTSOCK;
167
168  AUTO_LOCK(handle_lock_);
169  return sock->Accept(handle_attr_, new_sock, addr, len);
170}
171
172Error KernelHandle::Connect(const struct sockaddr* addr, socklen_t len) {
173  SocketNode* sock = socket_node();
174  if (!sock)
175    return ENOTSOCK;
176
177  AUTO_LOCK(handle_lock_);
178  return sock->Connect(handle_attr_, addr, len);
179}
180
181Error KernelHandle::Recv(void* buf, size_t len, int flags, int* out_len) {
182  SocketNode* sock = socket_node();
183  if (!sock)
184    return ENOTSOCK;
185  if (OpenMode() == O_WRONLY)
186    return EACCES;
187
188  AUTO_LOCK(handle_lock_);
189  return sock->Recv(handle_attr_, buf, len, flags, out_len);
190}
191
192Error KernelHandle::RecvFrom(void* buf,
193                             size_t len,
194                             int flags,
195                             struct sockaddr* src_addr,
196                             socklen_t* addrlen,
197                             int* out_len) {
198  SocketNode* sock = socket_node();
199  if (!sock)
200    return ENOTSOCK;
201  if (OpenMode() == O_WRONLY)
202    return EACCES;
203
204  AUTO_LOCK(handle_lock_);
205  return sock->RecvFrom(handle_attr_, buf, len, flags, src_addr, addrlen,
206                        out_len);
207}
208
209Error KernelHandle::Send(const void* buf, size_t len, int flags, int* out_len) {
210  SocketNode* sock = socket_node();
211  if (!sock)
212    return ENOTSOCK;
213  if (OpenMode() == O_RDONLY)
214    return EACCES;
215
216  AUTO_LOCK(handle_lock_);
217  return sock->Send(handle_attr_, buf, len, flags, out_len);
218}
219
220Error KernelHandle::SendTo(const void* buf,
221                           size_t len,
222                           int flags,
223                           const struct sockaddr* dest_addr,
224                           socklen_t addrlen,
225                           int* out_len) {
226  SocketNode* sock = socket_node();
227  if (!sock)
228    return ENOTSOCK;
229  if (OpenMode() == O_RDONLY)
230    return EACCES;
231
232  AUTO_LOCK(handle_lock_);
233  return sock->SendTo(handle_attr_, buf, len, flags, dest_addr, addrlen,
234                      out_len);
235}
236
237}  // namespace nacl_io
238