1// Copyright 2014 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/jsfs/js_fs_node.h"
6
7#include <assert.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <limits.h>
11#include <string.h>
12
13#include "nacl_io/jsfs/js_fs.h"
14#include "nacl_io/kernel_handle.h"
15#include "nacl_io/log.h"
16#include "nacl_io/osdirent.h"
17#include "nacl_io/pepper_interface.h"
18#include "sdk_util/macros.h"
19
20namespace nacl_io {
21
22JsFsNode::JsFsNode(Filesystem* filesystem, int32_t fd)
23    : Node(filesystem),
24      ppapi_(filesystem->ppapi()),
25      array_iface_(ppapi_->GetVarArrayInterface()),
26      buffer_iface_(ppapi_->GetVarArrayBufferInterface()),
27      var_iface_(ppapi_->GetVarInterface()),
28      fd_(fd) {
29}
30
31void JsFsNode::Destroy() {
32  // TODO(binji): implement
33}
34
35bool JsFsNode::SendRequestAndWait(ScopedVar* out_response,
36                                  const char* format,
37                                  ...) {
38  va_list args;
39  va_start(args, format);
40  bool result = filesystem()->VSendRequestAndWait(out_response, format, args);
41  va_end(args);
42  return result;
43}
44
45int JsFsNode::ScanVar(PP_Var var, const char* format, ...) {
46  va_list args;
47  va_start(args, format);
48  int result = filesystem()->VScanVar(var, format, args);
49  va_end(args);
50  return result;
51}
52
53bool JsFsNode::CanOpen(int open_flags) {
54  struct stat statbuf;
55  Error error = GetStat(&statbuf);
56  if (error)
57    return false;
58
59  // GetStat cached the mode in stat_.st_mode. Forward to Node::CanOpen,
60  // which will check this mode against open_flags.
61  return Node::CanOpen(open_flags);
62}
63
64Error JsFsNode::GetStat(struct stat* stat) {
65  AUTO_LOCK(node_lock_);
66
67  ScopedVar response(ppapi_);
68  if (!SendRequestAndWait(&response, "%s%d", "cmd", "fstat", "fildes", fd_)) {
69    LOG_ERROR("Failed to send request.");
70    return EINVAL;
71  }
72
73  // TODO(binji): find out the size of bionic stat fields.
74#if defined(__native_client__) && !defined(__BIONIC__)
75#if defined(__GLIBC__)
76const char* format = "%d%lld%d%d%d%d%lld%lld%lld%lld%lld%lld%lld";
77#else
78const char* format = "%d%lld%d%d%d%d%lld%lld%d%d%lld%lld%lld";
79#endif
80#else
81#define FIELD(x)                              \
82  assert(sizeof(stat->x) >= sizeof(int32_t)); \
83  strcat(format, sizeof(stat->x) == sizeof(int64_t) ? "%lld" : "%d");
84
85  // For host builds, we'll build up the format string at runtime.
86  char format[100] = "%d";  // First field is "error".
87  FIELD(st_ino);
88  FIELD(st_mode);
89  FIELD(st_nlink);
90  FIELD(st_uid);
91  FIELD(st_gid);
92  FIELD(st_rdev);
93  FIELD(st_size);
94  FIELD(st_blksize);
95  FIELD(st_blocks);
96  FIELD(st_atime);
97  FIELD(st_mtime);
98  FIELD(st_ctime);
99
100#undef FIELD
101#endif
102
103  int32_t error;
104  int result = ScanVar(response.pp_var(),
105                       format,
106                       "error", &error,
107                       "st_ino", &stat->st_ino,
108                       "st_mode", &stat->st_mode,
109                       "st_nlink", &stat->st_nlink,
110                       "st_uid", &stat->st_uid,
111                       "st_gid", &stat->st_gid,
112                       "st_rdev", &stat->st_rdev,
113                       "st_size", &stat->st_size,
114                       "st_blksize", &stat->st_blksize,
115                       "st_blocks", &stat->st_blocks,
116                       "st_atime", &stat->st_atime,
117                       "st_mtime", &stat->st_mtime,
118                       "st_ctime", &stat->st_ctime);
119
120  if (result >= 1 && error)
121    return error;
122
123  if (result != 13) {
124    LOG_ERROR(
125        "Expected \"st_*\" and \"error\" fields in response (should be 13 "
126        "total).");
127    return EINVAL;
128  }
129
130  stat->st_dev = filesystem()->dev();
131
132  return 0;
133}
134
135Error JsFsNode::GetSize(off_t* out_size) {
136  *out_size = 0;
137
138  struct stat statbuf;
139  Error error = GetStat(&statbuf);
140  if (error)
141    return error;
142
143  *out_size = stat_.st_size;
144  return 0;
145}
146
147Error JsFsNode::FSync() {
148  AUTO_LOCK(node_lock_);
149
150  ScopedVar response(ppapi_);
151  if (!SendRequestAndWait(&response, "%s%d", "cmd", "fsync", "fildes", fd_)) {
152    LOG_ERROR("Failed to send request.");
153    return EINVAL;
154  }
155
156  return filesystem()->ErrorFromResponse(response);
157}
158
159Error JsFsNode::FTruncate(off_t length) {
160  AUTO_LOCK(node_lock_);
161
162  ScopedVar response(ppapi_);
163  if (!SendRequestAndWait(&response,
164      "%s%d%lld", "cmd", "ftruncate", "fildes", fd_, "length", length)) {
165    LOG_ERROR("Failed to send request.");
166    return EINVAL;
167  }
168
169  return filesystem()->ErrorFromResponse(response);
170}
171
172Error JsFsNode::Read(const HandleAttr& attr,
173                     void* buf,
174                     size_t count,
175                     int* out_bytes) {
176  AUTO_LOCK(node_lock_);
177
178  *out_bytes = 0;
179
180  ScopedVar response(ppapi_);
181  if (!SendRequestAndWait(&response, "%s%d%u%lld",
182                          "cmd", "pread",
183                          "fildes", fd_,
184                          "nbyte", count,
185                          "offset", attr.offs)) {
186    LOG_ERROR("Failed to send request.");
187    return EINVAL;
188  }
189
190  int32_t error;
191
192  PP_Var buf_var;
193  int result =
194      ScanVar(response.pp_var(), "%d%p", "error", &error, "buf", &buf_var);
195  ScopedVar scoped_buf_var(ppapi_, buf_var);
196
197  if (result >= 1 && error)
198    return error;
199
200  if (result != 2) {
201    LOG_ERROR("Expected \"error\" and \"buf\" fields in response.");
202    return EINVAL;
203  }
204
205  if (buf_var.type != PP_VARTYPE_ARRAY_BUFFER) {
206    LOG_ERROR("Expected \"buf\" to be an ArrayBuffer.");
207    return EINVAL;
208  }
209
210  uint32_t src_buf_len;
211  if (!buffer_iface_->ByteLength(buf_var, &src_buf_len)) {
212    LOG_ERROR("Unable to get byteLength of \"buf\".");
213    return EINVAL;
214  }
215
216  if (src_buf_len > count)
217    src_buf_len = count;
218
219  void* src_buf = buffer_iface_->Map(buf_var);
220  if (src_buf == NULL) {
221    LOG_ERROR("Unable to map \"buf\".");
222    return EINVAL;
223  }
224
225  memcpy(buf, src_buf, src_buf_len);
226  *out_bytes = src_buf_len;
227
228  buffer_iface_->Unmap(buf_var);
229
230  return 0;
231}
232
233Error JsFsNode::Write(const HandleAttr& attr,
234                      const void* buf,
235                      size_t count,
236                      int* out_bytes) {
237  AUTO_LOCK(node_lock_);
238
239  *out_bytes = 0;
240
241  PP_Var buf_var = buffer_iface_->Create(count);
242  ScopedVar scoped_buf_var(ppapi_, buf_var);
243
244  if (buf_var.type != PP_VARTYPE_ARRAY_BUFFER) {
245    LOG_ERROR("Unable to create \"buf\" var.");
246    return EINVAL;
247  }
248
249  void* dst_buf = buffer_iface_->Map(buf_var);
250  if (dst_buf == NULL) {
251    LOG_ERROR("Unable to map \"buf\".");
252    return EINVAL;
253  }
254
255  memcpy(dst_buf, buf, count);
256
257  buffer_iface_->Unmap(buf_var);
258
259  ScopedVar response(ppapi_);
260  if (!SendRequestAndWait(&response, "%s%d%p%u%lld",
261                          "cmd", "pwrite",
262                          "fildes", fd_,
263                          "buf", &buf_var,
264                          "nbyte", count,
265                          "offset", attr.offs)) {
266    LOG_ERROR("Failed to send request.");
267    return EINVAL;
268  }
269
270  int error;
271  uint32_t nwrote;
272  int result =
273      ScanVar(response.pp_var(), "%d%u", "error", &error, "nwrote", &nwrote);
274
275  if (result >= 1 && error)
276    return error;
277
278  if (result != 2) {
279    LOG_ERROR("Expected \"error\" and \"nwrote\" fields in response.");
280    return EINVAL;
281  }
282
283  *out_bytes = nwrote;
284  return 0;
285}
286
287Error JsFsNode::GetDents(size_t offs,
288                         struct dirent* pdir,
289                         size_t count,
290                         int* out_bytes) {
291  AUTO_LOCK(node_lock_);
292
293  *out_bytes = 0;
294
295  // Round to the nearest sizeof(dirent) and ask for that.
296  size_t first = offs / sizeof(dirent);
297  size_t last = (offs + count + sizeof(dirent) - 1) / sizeof(dirent);
298
299  ScopedVar response(ppapi_);
300  if (!SendRequestAndWait(&response, "%s%d%u%u",
301                          "cmd", "getdents",
302                          "fildes", fd_,
303                          "offs", first,
304                          "count", last - first)) {
305    LOG_ERROR("Failed to send request.");
306    return EINVAL;
307  }
308
309  int error;
310  PP_Var dirents_var;
311  int result = ScanVar(
312      response.pp_var(), "%d%p", "error", &error, "dirents", &dirents_var);
313
314  ScopedVar scoped_dirents_var(ppapi_, dirents_var);
315
316  if (result >= 1 && error)
317    return error;
318
319  if (result != 2) {
320    LOG_ERROR("Expected \"error\" and \"dirents\" fields in response.");
321    return EINVAL;
322  }
323
324  if (dirents_var.type != PP_VARTYPE_ARRAY) {
325    LOG_ERROR("Expected \"dirents\" to be an Array.");
326    return EINVAL;
327  }
328
329  uint32_t dirents_len = array_iface_->GetLength(dirents_var);
330  uint32_t dirents_byte_len = dirents_len * sizeof(dirent);
331
332  // Allocate enough full dirents to copy from. This makes it easier if, for
333  // some reason, we are reading unaligned dirents.
334  dirent* dirents = static_cast<dirent*>(malloc(dirents_byte_len));
335
336  for (uint32_t i = 0; i < dirents_len; ++i) {
337    PP_Var dirent_var = array_iface_->Get(dirents_var, i);
338    PP_Var d_name_var;
339    result = ScanVar(dirent_var,
340                     "%lld%p",
341                     "d_ino", &dirents[i].d_ino,
342                     "d_name", &d_name_var);
343    ScopedVar scoped_dirent_var(ppapi_, dirent_var);
344    ScopedVar scoped_d_name_var(ppapi_, d_name_var);
345
346    if (result != 2) {
347      LOG_ERROR("Expected dirent[%d] to have \"d_ino\" and \"d_name\".", i);
348      free(dirents);
349      return EINVAL;
350    }
351
352    uint32_t d_name_len;
353    const char* d_name = var_iface_->VarToUtf8(d_name_var, &d_name_len);
354
355    dirents[i].d_off = sizeof(dirent);
356    dirents[i].d_reclen = sizeof(dirent);
357    strncpy(dirents[i].d_name, d_name, sizeof(dirents[i].d_name));
358  }
359
360  size_t dirents_offs = offs - first * sizeof(dirent);
361  if (dirents_offs + count > dirents_byte_len)
362    count = dirents_byte_len - dirents_offs;
363
364  memcpy(pdir, reinterpret_cast<const char*>(dirents) + dirents_offs, count);
365  *out_bytes = count;
366
367  free(dirents);
368  return 0;
369}
370
371}  // namespace nacl_io
372