Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / libraries / nacl_io / jsfs / js_fs_node.cc
blob2c45b62c60202cc1dcfa54ef0319504355f45a82
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.
5 #include "nacl_io/jsfs/js_fs_node.h"
7 #include <assert.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <limits.h>
11 #include <string.h>
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"
20 namespace nacl_io {
22 JsFsNode::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) {
31 void JsFsNode::Destroy() {
32 // TODO(binji): implement
35 bool 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;
45 int 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;
53 bool JsFsNode::CanOpen(int open_flags) {
54 struct stat statbuf;
55 Error error = GetStat(&statbuf);
56 if (error)
57 return false;
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);
64 Error JsFsNode::GetStat(struct stat* stat) {
65 AUTO_LOCK(node_lock_);
67 ScopedVar response(ppapi_);
68 if (!SendRequestAndWait(&response, "%s%d", "cmd", "fstat", "fildes", fd_)) {
69 LOG_ERROR("Failed to send request.");
70 return EINVAL;
73 // TODO(binji): find out the size of bionic stat fields.
74 #if defined(__native_client__) && !defined(__BIONIC__)
75 #if defined(__GLIBC__)
76 const char* format = "%d%lld%d%d%d%d%lld%lld%lld%lld%lld%lld%lld";
77 #else
78 const char* format = "%d%lld%d%d%d%d%lld%lld%d%d%lld%lld%lld";
79 #endif
80 #else
81 #define FIELD(x) \
82 if (sizeof(stat->x) == sizeof(int64_t)) \
83 strcat(format, "%lld"); \
84 else if (sizeof(stat->x) == sizeof(int16_t)) \
85 strcat(format, "%hd"); \
86 else \
87 strcat(format, "%d");
89 // For host builds, we'll build up the format string at runtime.
90 char format[100] = "%d"; // First field is "error".
91 FIELD(st_ino);
92 FIELD(st_mode);
93 FIELD(st_nlink);
94 FIELD(st_uid);
95 FIELD(st_gid);
96 FIELD(st_rdev);
97 FIELD(st_size);
98 FIELD(st_blksize);
99 FIELD(st_blocks);
100 FIELD(st_atime);
101 FIELD(st_mtime);
102 FIELD(st_ctime);
104 #undef FIELD
105 #endif
107 int32_t error;
108 int result = ScanVar(response.pp_var(),
109 format,
110 "error", &error,
111 "st_ino", &stat->st_ino,
112 "st_mode", &stat->st_mode,
113 "st_nlink", &stat->st_nlink,
114 "st_uid", &stat->st_uid,
115 "st_gid", &stat->st_gid,
116 "st_rdev", &stat->st_rdev,
117 "st_size", &stat->st_size,
118 "st_blksize", &stat->st_blksize,
119 "st_blocks", &stat->st_blocks,
120 "st_atime", &stat->st_atime,
121 "st_mtime", &stat->st_mtime,
122 "st_ctime", &stat->st_ctime);
124 if (result >= 1 && error)
125 return error;
127 if (result != 13) {
128 LOG_ERROR(
129 "Expected \"st_*\" and \"error\" fields in response (should be 13 "
130 "total, got %d).", result);
131 return EINVAL;
134 stat->st_dev = filesystem()->dev();
136 return 0;
139 Error JsFsNode::GetSize(off_t* out_size) {
140 *out_size = 0;
142 struct stat statbuf;
143 Error error = GetStat(&statbuf);
144 if (error)
145 return error;
147 *out_size = stat_.st_size;
148 return 0;
151 Error JsFsNode::FSync() {
152 AUTO_LOCK(node_lock_);
154 ScopedVar response(ppapi_);
155 if (!SendRequestAndWait(&response, "%s%d", "cmd", "fsync", "fildes", fd_)) {
156 LOG_ERROR("Failed to send request.");
157 return EINVAL;
160 return filesystem()->ErrorFromResponse(response);
163 Error JsFsNode::FTruncate(off_t length) {
164 AUTO_LOCK(node_lock_);
166 ScopedVar response(ppapi_);
167 if (!SendRequestAndWait(&response,
168 "%s%d%lld", "cmd", "ftruncate", "fildes", fd_, "length", length)) {
169 LOG_ERROR("Failed to send request.");
170 return EINVAL;
173 return filesystem()->ErrorFromResponse(response);
176 Error JsFsNode::Read(const HandleAttr& attr,
177 void* buf,
178 size_t count,
179 int* out_bytes) {
180 AUTO_LOCK(node_lock_);
182 *out_bytes = 0;
184 ScopedVar response(ppapi_);
185 if (!SendRequestAndWait(&response, "%s%d%u%lld",
186 "cmd", "pread",
187 "fildes", fd_,
188 "nbyte", count,
189 "offset", attr.offs)) {
190 LOG_ERROR("Failed to send request.");
191 return EINVAL;
194 int32_t error;
196 PP_Var buf_var;
197 int result =
198 ScanVar(response.pp_var(), "%d%p", "error", &error, "buf", &buf_var);
199 ScopedVar scoped_buf_var(ppapi_, buf_var);
201 if (result >= 1 && error)
202 return error;
204 if (result != 2) {
205 LOG_ERROR("Expected \"error\" and \"buf\" fields in response.");
206 return EINVAL;
209 if (buf_var.type != PP_VARTYPE_ARRAY_BUFFER) {
210 LOG_ERROR("Expected \"buf\" to be an ArrayBuffer.");
211 return EINVAL;
214 uint32_t src_buf_len;
215 if (!buffer_iface_->ByteLength(buf_var, &src_buf_len)) {
216 LOG_ERROR("Unable to get byteLength of \"buf\".");
217 return EINVAL;
220 if (src_buf_len > count)
221 src_buf_len = count;
223 void* src_buf = buffer_iface_->Map(buf_var);
224 if (src_buf == NULL) {
225 LOG_ERROR("Unable to map \"buf\".");
226 return EINVAL;
229 memcpy(buf, src_buf, src_buf_len);
230 *out_bytes = src_buf_len;
232 buffer_iface_->Unmap(buf_var);
234 return 0;
237 Error JsFsNode::Write(const HandleAttr& attr,
238 const void* buf,
239 size_t count,
240 int* out_bytes) {
241 AUTO_LOCK(node_lock_);
243 *out_bytes = 0;
245 PP_Var buf_var = buffer_iface_->Create(count);
246 ScopedVar scoped_buf_var(ppapi_, buf_var);
248 if (buf_var.type != PP_VARTYPE_ARRAY_BUFFER) {
249 LOG_ERROR("Unable to create \"buf\" var.");
250 return EINVAL;
253 void* dst_buf = buffer_iface_->Map(buf_var);
254 if (dst_buf == NULL) {
255 LOG_ERROR("Unable to map \"buf\".");
256 return EINVAL;
259 memcpy(dst_buf, buf, count);
261 buffer_iface_->Unmap(buf_var);
263 ScopedVar response(ppapi_);
264 if (!SendRequestAndWait(&response, "%s%d%p%u%lld",
265 "cmd", "pwrite",
266 "fildes", fd_,
267 "buf", &buf_var,
268 "nbyte", count,
269 "offset", attr.offs)) {
270 LOG_ERROR("Failed to send request.");
271 return EINVAL;
274 int error;
275 uint32_t nwrote;
276 int result =
277 ScanVar(response.pp_var(), "%d%u", "error", &error, "nwrote", &nwrote);
279 if (result >= 1 && error)
280 return error;
282 if (result != 2) {
283 LOG_ERROR("Expected \"error\" and \"nwrote\" fields in response.");
284 return EINVAL;
287 *out_bytes = nwrote;
288 return 0;
291 Error JsFsNode::GetDents(size_t offs,
292 struct dirent* pdir,
293 size_t count,
294 int* out_bytes) {
295 AUTO_LOCK(node_lock_);
297 *out_bytes = 0;
299 // Round to the nearest sizeof(dirent) and ask for that.
300 size_t first = offs / sizeof(dirent);
301 size_t last = (offs + count + sizeof(dirent) - 1) / sizeof(dirent);
303 ScopedVar response(ppapi_);
304 if (!SendRequestAndWait(&response, "%s%d%u%u",
305 "cmd", "getdents",
306 "fildes", fd_,
307 "offs", first,
308 "count", last - first)) {
309 LOG_ERROR("Failed to send request.");
310 return EINVAL;
313 int error;
314 PP_Var dirents_var;
315 int result = ScanVar(
316 response.pp_var(), "%d%p", "error", &error, "dirents", &dirents_var);
318 ScopedVar scoped_dirents_var(ppapi_, dirents_var);
320 if (result >= 1 && error)
321 return error;
323 if (result != 2) {
324 LOG_ERROR("Expected \"error\" and \"dirents\" fields in response.");
325 return EINVAL;
328 if (dirents_var.type != PP_VARTYPE_ARRAY) {
329 LOG_ERROR("Expected \"dirents\" to be an Array.");
330 return EINVAL;
333 uint32_t dirents_len = array_iface_->GetLength(dirents_var);
334 uint32_t dirents_byte_len = dirents_len * sizeof(dirent);
336 // Allocate enough full dirents to copy from. This makes it easier if, for
337 // some reason, we are reading unaligned dirents.
338 dirent* dirents = static_cast<dirent*>(malloc(dirents_byte_len));
340 for (uint32_t i = 0; i < dirents_len; ++i) {
341 PP_Var dirent_var = array_iface_->Get(dirents_var, i);
342 PP_Var d_name_var;
343 result = ScanVar(dirent_var,
344 "%lld%p",
345 "d_ino", &dirents[i].d_ino,
346 "d_name", &d_name_var);
347 ScopedVar scoped_dirent_var(ppapi_, dirent_var);
348 ScopedVar scoped_d_name_var(ppapi_, d_name_var);
350 if (result != 2) {
351 LOG_ERROR("Expected dirent[%d] to have \"d_ino\" and \"d_name\".", i);
352 free(dirents);
353 return EINVAL;
356 uint32_t d_name_len;
357 const char* d_name = var_iface_->VarToUtf8(d_name_var, &d_name_len);
359 dirents[i].d_reclen = sizeof(dirent);
360 strncpy(dirents[i].d_name, d_name, sizeof(dirents[i].d_name));
363 size_t dirents_offs = offs - first * sizeof(dirent);
364 if (dirents_offs + count > dirents_byte_len)
365 count = dirents_byte_len - dirents_offs;
367 memcpy(pdir, reinterpret_cast<const char*>(dirents) + dirents_offs, count);
368 *out_bytes = count;
370 free(dirents);
371 return 0;
374 } // namespace nacl_io