Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / libraries / nacl_io / jsfs / js_fs.cc
blob5f0f7c281a6bc67bc6bb8f1bbdfa57bc0ecddfe8
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.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/ioctl.h"
14 #include "nacl_io/jsfs/js_fs_node.h"
15 #include "nacl_io/kernel_handle.h"
16 #include "nacl_io/log.h"
17 #include "nacl_io/osdirent.h"
18 #include "nacl_io/pepper_interface.h"
19 #include "sdk_util/macros.h"
21 namespace nacl_io {
23 JsFs::JsFs()
24 : messaging_iface_(NULL),
25 array_iface_(NULL),
26 buffer_iface_(NULL),
27 dict_iface_(NULL),
28 var_iface_(NULL),
29 request_id_(0) {
32 Error JsFs::Init(const FsInitArgs& args) {
33 Error error = Filesystem::Init(args);
34 if (error)
35 return error;
37 pthread_cond_init(&response_cond_, NULL);
39 messaging_iface_ = ppapi_->GetMessagingInterface();
40 array_iface_ = ppapi_->GetVarArrayInterface();
41 buffer_iface_ = ppapi_->GetVarArrayBufferInterface();
42 dict_iface_ = ppapi_->GetVarDictionaryInterface();
43 var_iface_ = ppapi_->GetVarInterface();
45 if (!messaging_iface_ || !array_iface_ || !buffer_iface_ || !dict_iface_ ||
46 !var_iface_) {
47 LOG_ERROR("Got 1+ NULL interface(s): %s%s%s%s%s",
48 messaging_iface_ ? "" : "Messaging ",
49 array_iface_ ? "" : "VarArray ",
50 buffer_iface_ ? "" : "VarArrayBuffer ",
51 dict_iface_ ? "" : "VarDictionary ",
52 var_iface_ ? "" : "Var ");
53 return ENOSYS;
56 return 0;
59 void JsFs::Destroy() {
60 pthread_cond_destroy(&response_cond_);
63 bool JsFs::SetDictVar(PP_Var dict, const char* key, PP_Var value) {
64 PP_Var key_var = var_iface_->VarFromUtf8(key, strlen(key));
65 ScopedVar scoped_key(ppapi_, key_var);
66 if (key_var.type != PP_VARTYPE_STRING) {
67 LOG_ERROR("Unable to create string key \"%s\".", key);
68 return false;
71 PP_Bool success = dict_iface_->Set(dict, key_var, value);
72 if (!success) {
73 LOG_ERROR("Unable to set \"%s\" key of dictionary.", key);
74 return false;
77 return true;
80 PP_Var JsFs::GetDictVar(PP_Var dict, const char* key) {
81 PP_Var key_var = var_iface_->VarFromUtf8(key, strlen(key));
82 ScopedVar scoped_key(ppapi_, key_var);
83 if (key_var.type != PP_VARTYPE_STRING) {
84 LOG_ERROR("Unable to create string key \"%s\".", key);
85 return PP_MakeUndefined();
88 return dict_iface_->Get(dict, key_var);
91 bool JsFs::GetVarInt32(PP_Var var, int32_t* out_value) {
92 switch (var.type) {
93 case PP_VARTYPE_INT32:
94 *out_value = var.value.as_int;
95 return true;
97 case PP_VARTYPE_DOUBLE:
98 *out_value = static_cast<int32_t>(var.value.as_double);
99 return true;
101 default:
102 return false;
106 bool JsFs::GetVarUint32(PP_Var var, uint32_t* out_value) {
107 switch (var.type) {
108 case PP_VARTYPE_INT32:
109 *out_value = static_cast<uint32_t>(var.value.as_int);
110 return true;
112 case PP_VARTYPE_DOUBLE:
113 *out_value = static_cast<uint32_t>(var.value.as_double);
114 return true;
116 default:
117 return false;
121 bool JsFs::GetVarInt64(PP_Var var, int64_t* out_value) {
122 switch (var.type) {
123 case PP_VARTYPE_INT32:
124 *out_value = var.value.as_int;
125 return true;
127 case PP_VARTYPE_DOUBLE:
128 *out_value = static_cast<int64_t>(var.value.as_double);
129 return true;
131 case PP_VARTYPE_ARRAY: {
132 uint32_t len = array_iface_->GetLength(var);
133 if (len != 2) {
134 LOG_ERROR("Expected int64 array type to have 2 elements, not %d", len);
135 return false;
138 PP_Var high_int_var = array_iface_->Get(var, 0);
139 ScopedVar scoped_high_int_var(ppapi_, high_int_var);
140 uint32_t high_int;
141 if (!GetVarUint32(high_int_var, &high_int))
142 return false;
144 PP_Var low_int_var = array_iface_->Get(var, 1);
145 ScopedVar scoped_low_int_var(ppapi_, low_int_var);
146 uint32_t low_int;
147 if (!GetVarUint32(low_int_var, &low_int))
148 return false;
150 *out_value = static_cast<int64_t>(
151 (static_cast<uint64_t>(high_int) << 32) | low_int);
152 return true;
155 default:
156 return false;
160 PP_Var JsFs::VMakeRequest(RequestId request_id,
161 const char* format,
162 va_list args) {
163 PP_Var dict = dict_iface_->Create();
164 ScopedVar scoped_dict(ppapi_, dict);
166 if (!SetDictVar(dict, "id", PP_MakeInt32(request_id)))
167 return PP_MakeNull();
169 const char* p = format;
170 while (*p) {
171 assert(*p == '%');
172 ++p;
174 const char* key = va_arg(args, const char*);
175 PP_Var value_var = PP_MakeUndefined();
177 switch(*p) {
178 case 'd':
179 value_var = PP_MakeInt32(va_arg(args, int32_t));
180 break;
181 case 'u':
182 value_var = PP_MakeInt32(va_arg(args, uint32_t));
183 break;
184 case 's': {
185 const char* value = va_arg(args, const char*);
186 value_var = var_iface_->VarFromUtf8(value, strlen(value));
187 if (value_var.type != PP_VARTYPE_STRING) {
188 LOG_ERROR("Unable to create \"%s\" string var.", value);
189 return PP_MakeNull();
191 break;
193 case 'p':
194 value_var = *va_arg(args, const PP_Var*);
195 var_iface_->AddRef(value_var);
196 break;
197 case 'l': {
198 // Only '%lld' is supported.
199 ++p;
200 assert(*p == 'l');
201 ++p;
202 assert(*p == 'd');
204 int64_t value = va_arg(args, int64_t);
205 if (value >= INT_MIN && value <= INT_MAX) {
206 // Send as an int.
207 value_var = PP_MakeInt32(static_cast<int32_t>(value));
208 } else {
209 // Send as an array of two ints: [high int32, low int32].
210 value_var = array_iface_->Create();
211 if (!array_iface_->SetLength(value_var, 2)) {
212 LOG_ERROR("Unable to set length of s64 array.");
213 return PP_MakeNull();
216 if (!array_iface_->Set(value_var, 0, PP_MakeInt32(value >> 32))) {
217 LOG_ERROR("Unable to set of high int32 of s64 array.");
218 return PP_MakeNull();
221 if (!array_iface_->Set(
222 value_var, 1, PP_MakeInt32(value & 0xffffffff))) {
223 LOG_ERROR("Unable to set of low int32 of s64 array.");
224 return PP_MakeNull();
228 break;
230 default:
231 LOG_ERROR("Unknown format specifier %%\"%s\"", p);
232 assert(0);
233 return PP_MakeNull();
236 ++p;
238 if (!SetDictVar(dict, key, value_var))
239 return PP_MakeNull();
241 // Unconditionally release the value var. It is legal to do this even for
242 // non-refcounted types.
243 var_iface_->Release(value_var);
246 return scoped_dict.Release();
249 JsFs::RequestId JsFs::VSendRequest(const char* format, va_list args) {
250 AUTO_LOCK(lock_);
251 RequestId id = ++request_id_;
252 // Skip 0 (the invalid request id) in the very unlikely case that the request
253 // id wraps.
254 if (id == 0)
255 id = ++request_id_;
257 PP_Var dict_var = VMakeRequest(id, format, args);
258 ScopedVar scoped_dict_var(ppapi_, dict_var);
259 if (dict_var.type != PP_VARTYPE_DICTIONARY)
260 return 0;
262 messaging_iface_->PostMessage(ppapi_->GetInstance(), dict_var);
263 return id;
266 bool JsFs::VSendRequestAndWait(ScopedVar* out_response,
267 const char* format,
268 va_list args) {
269 RequestId id = VSendRequest(format, args);
270 if (id == 0)
271 return false;
273 out_response->Reset(WaitForResponse(id));
274 return true;
277 bool JsFs::SendRequestAndWait(ScopedVar* out_response,
278 const char* format,
279 ...) {
280 va_list args;
281 va_start(args, format);
282 bool result = VSendRequestAndWait(out_response, format, args);
283 va_end(args);
284 return result;
287 Error JsFs::ErrorFromResponse(const ScopedVar& response) {
288 int32_t error;
289 if (ScanVar(response.pp_var(), "%d", "error", &error) != 1) {
290 LOG_ERROR("Expected \"error\" field in response.");
291 return EINVAL;
294 return error;
297 int JsFs::ScanVar(PP_Var var, const char* format, ...) {
298 va_list args;
299 va_start(args, format);
300 int result = VScanVar(var, format, args);
301 va_end(args);
302 return result;
305 int JsFs::VScanVar(PP_Var dict_var, const char* format, va_list args) {
306 if (dict_var.type != PP_VARTYPE_DICTIONARY) {
307 LOG_ERROR("Expected var of type dictionary, not %d.", dict_var.type);
308 return 0;
311 int num_values = 0;
313 const char* p = format;
314 while (*p) {
315 assert(*p == '%');
316 ++p;
318 const char* key = va_arg(args, const char*);
319 PP_Var value_var = GetDictVar(dict_var, key);
320 ScopedVar scoped_value_var(ppapi_, value_var);
322 if (value_var.type == PP_VARTYPE_UNDEFINED)
323 break;
325 bool ok = true;
327 switch (*p) {
328 case 'd': {
329 int32_t* value = va_arg(args, int32_t*);
330 if (!GetVarInt32(value_var, value)) {
331 LOG_ERROR("Expected int32_t value for key \"%s\" (got %d)", key,
332 value_var.type);
333 ok = false;
335 break;
337 case 'h': {
338 // Only '%hd' is supported.
339 ++p;
340 assert(*p == 'd');
341 // Read 32-bit value from Pepper and truncate to 16-bits.
342 int32_t value = 0;
343 if (!GetVarInt32(value_var, &value)) {
344 LOG_ERROR("Expected int32_t value for key \"%s\" (got %d)", key,
345 value_var.type);
346 ok = false;
348 int16_t* short_value = va_arg(args, int16_t*);
349 *short_value = (int16_t)value;
350 break;
352 case 'u': {
353 uint32_t* value = va_arg(args, uint32_t*);
354 if (!GetVarUint32(value_var, value)) {
355 LOG_ERROR("Expected uint32_t value for key \"%s\"", key);
356 ok = false;
358 break;
360 case 'l': {
361 // Only '%lld' is supported.
362 ++p;
363 assert(*p == 'l');
364 ++p;
365 assert(*p == 'd');
367 int64_t* value = va_arg(args, int64_t*);
368 if (!GetVarInt64(value_var, value)) {
369 LOG_ERROR("Expected int64_t value for key \"%s\"", key);
370 ok = false;
372 break;
374 case 'p': {
375 PP_Var* value = va_arg(args, PP_Var*);
376 *value = scoped_value_var.Release();
377 break;
379 default:
380 LOG_ERROR("Unknown format specifier %%\"%s\"", p);
381 assert(0);
382 ok = false;
383 break;
386 if (!ok)
387 break;
389 p++;
390 num_values++;
393 return num_values;
396 PP_Var JsFs::WaitForResponse(RequestId request_id) {
397 AUTO_LOCK(lock_);
398 while (1) {
399 ResponseMap_t::iterator iter = responses_.find(request_id);
400 if (iter != responses_.end()) {
401 PP_Var response = iter->second;
402 responses_.erase(iter);
403 return response;
406 pthread_cond_wait(&response_cond_, lock_.mutex());
410 Error JsFs::OpenWithMode(const Path& path, int open_flags, mode_t t,
411 ScopedNode* out_node) {
412 out_node->reset(NULL);
413 ScopedVar response(ppapi_);
414 if (!SendRequestAndWait(&response, "%s%s%d",
415 "cmd", "open",
416 "path", path.Join().c_str(),
417 "oflag", open_flags)) {
418 LOG_ERROR("Failed to send request.");
419 return EINVAL;
422 int32_t error;
423 int32_t fd;
424 int result = ScanVar(response.pp_var(), "%d%d", "error", &error, "fd", &fd);
425 if (result >= 1 && error)
426 return error;
428 if (result != 2) {
429 LOG_ERROR("Expected \"error\" and \"fd\" fields in response.");
430 return EINVAL;
433 out_node->reset(new JsFsNode(this, fd));
434 return 0;
437 Error JsFs::Unlink(const Path& path) {
438 ScopedVar response(ppapi_);
439 if (!SendRequestAndWait(
440 &response, "%s%s", "cmd", "unlink", "path", path.Join().c_str())) {
441 LOG_ERROR("Failed to send request.");
442 return EINVAL;
445 return ErrorFromResponse(response);
448 Error JsFs::Mkdir(const Path& path, int perm) {
449 ScopedVar response(ppapi_);
450 if (!SendRequestAndWait(&response, "%s%s%d",
451 "cmd", "mkdir",
452 "path", path.Join().c_str(),
453 "mode", perm)) {
454 LOG_ERROR("Failed to send request.");
455 return EINVAL;
458 return ErrorFromResponse(response);
461 Error JsFs::Rmdir(const Path& path) {
462 ScopedVar response(ppapi_);
463 if (!SendRequestAndWait(
464 &response, "%s%s", "cmd", "rmdir", "path", path.Join().c_str())) {
465 LOG_ERROR("Failed to send request.");
466 return EINVAL;
469 return ErrorFromResponse(response);
472 Error JsFs::Remove(const Path& path) {
473 ScopedVar response(ppapi_);
474 if (!SendRequestAndWait(
475 &response, "%s%s", "cmd", "remove", "path", path.Join().c_str())) {
476 LOG_ERROR("Failed to send request.");
477 return EINVAL;
480 return ErrorFromResponse(response);
483 Error JsFs::Rename(const Path& path, const Path& newpath) {
484 ScopedVar response(ppapi_);
485 if (!SendRequestAndWait(&response, "%s%s%s",
486 "cmd", "rename",
487 "old", path.Join().c_str(),
488 "new", newpath.Join().c_str())) {
489 LOG_ERROR("Failed to send request.");
490 return EINVAL;
493 return ErrorFromResponse(response);
496 Error JsFs::Filesystem_VIoctl(int request, va_list args) {
497 if (request != NACL_IOC_HANDLEMESSAGE) {
498 LOG_ERROR("Unknown ioctl: %#x", request);
499 return EINVAL;
502 PP_Var response = *va_arg(args, PP_Var*);
504 AUTO_LOCK(lock_);
506 RequestId response_id;
507 if (ScanVar(response, "%d", "id", &response_id) != 1) {
508 LOG_TRACE("ioctl with no \"id\", ignoring.\n");
509 return EINVAL;
512 responses_.insert(ResponseMap_t::value_type(response_id, response));
513 pthread_cond_broadcast(&response_cond_);
514 return 0;
517 } // namespace nacl_io