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"
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"
24 : messaging_iface_(NULL
),
32 Error
JsFs::Init(const FsInitArgs
& args
) {
33 Error error
= Filesystem::Init(args
);
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_
||
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 ");
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
);
71 PP_Bool success
= dict_iface_
->Set(dict
, key_var
, value
);
73 LOG_ERROR("Unable to set \"%s\" key of dictionary.", key
);
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
) {
93 case PP_VARTYPE_INT32
:
94 *out_value
= var
.value
.as_int
;
97 case PP_VARTYPE_DOUBLE
:
98 *out_value
= static_cast<int32_t>(var
.value
.as_double
);
106 bool JsFs::GetVarUint32(PP_Var var
, uint32_t* out_value
) {
108 case PP_VARTYPE_INT32
:
109 *out_value
= static_cast<uint32_t>(var
.value
.as_int
);
112 case PP_VARTYPE_DOUBLE
:
113 *out_value
= static_cast<uint32_t>(var
.value
.as_double
);
121 bool JsFs::GetVarInt64(PP_Var var
, int64_t* out_value
) {
123 case PP_VARTYPE_INT32
:
124 *out_value
= var
.value
.as_int
;
127 case PP_VARTYPE_DOUBLE
:
128 *out_value
= static_cast<int64_t>(var
.value
.as_double
);
131 case PP_VARTYPE_ARRAY
: {
132 uint32_t len
= array_iface_
->GetLength(var
);
134 LOG_ERROR("Expected int64 array type to have 2 elements, not %d", len
);
138 PP_Var high_int_var
= array_iface_
->Get(var
, 0);
139 ScopedVar
scoped_high_int_var(ppapi_
, high_int_var
);
141 if (!GetVarUint32(high_int_var
, &high_int
))
144 PP_Var low_int_var
= array_iface_
->Get(var
, 1);
145 ScopedVar
scoped_low_int_var(ppapi_
, low_int_var
);
147 if (!GetVarUint32(low_int_var
, &low_int
))
150 *out_value
= static_cast<int64_t>(
151 (static_cast<uint64_t>(high_int
) << 32) | low_int
);
160 PP_Var
JsFs::VMakeRequest(RequestId request_id
,
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
;
174 const char* key
= va_arg(args
, const char*);
175 PP_Var value_var
= PP_MakeUndefined();
179 value_var
= PP_MakeInt32(va_arg(args
, int32_t));
182 value_var
= PP_MakeInt32(va_arg(args
, uint32_t));
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();
194 value_var
= *va_arg(args
, const PP_Var
*);
195 var_iface_
->AddRef(value_var
);
198 // Only '%lld' is supported.
204 int64_t value
= va_arg(args
, int64_t);
205 if (value
>= INT_MIN
&& value
<= INT_MAX
) {
207 value_var
= PP_MakeInt32(static_cast<int32_t>(value
));
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();
231 LOG_ERROR("Unknown format specifier %%\"%s\"", p
);
233 return PP_MakeNull();
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
) {
251 RequestId id
= ++request_id_
;
252 // Skip 0 (the invalid request id) in the very unlikely case that the request
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
)
262 messaging_iface_
->PostMessage(ppapi_
->GetInstance(), dict_var
);
266 bool JsFs::VSendRequestAndWait(ScopedVar
* out_response
,
269 RequestId id
= VSendRequest(format
, args
);
273 out_response
->Reset(WaitForResponse(id
));
277 bool JsFs::SendRequestAndWait(ScopedVar
* out_response
,
281 va_start(args
, format
);
282 bool result
= VSendRequestAndWait(out_response
, format
, args
);
287 Error
JsFs::ErrorFromResponse(const ScopedVar
& response
) {
289 if (ScanVar(response
.pp_var(), "%d", "error", &error
) != 1) {
290 LOG_ERROR("Expected \"error\" field in response.");
297 int JsFs::ScanVar(PP_Var var
, const char* format
, ...) {
299 va_start(args
, format
);
300 int result
= VScanVar(var
, format
, args
);
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
);
313 const char* p
= format
;
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
)
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
,
338 // Only '%hd' is supported.
341 // Read 32-bit value from Pepper and truncate to 16-bits.
343 if (!GetVarInt32(value_var
, &value
)) {
344 LOG_ERROR("Expected int32_t value for key \"%s\" (got %d)", key
,
348 int16_t* short_value
= va_arg(args
, int16_t*);
349 *short_value
= (int16_t)value
;
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
);
361 // Only '%lld' is supported.
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
);
375 PP_Var
* value
= va_arg(args
, PP_Var
*);
376 *value
= scoped_value_var
.Release();
380 LOG_ERROR("Unknown format specifier %%\"%s\"", p
);
396 PP_Var
JsFs::WaitForResponse(RequestId request_id
) {
399 ResponseMap_t::iterator iter
= responses_
.find(request_id
);
400 if (iter
!= responses_
.end()) {
401 PP_Var response
= iter
->second
;
402 responses_
.erase(iter
);
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",
416 "path", path
.Join().c_str(),
417 "oflag", open_flags
)) {
418 LOG_ERROR("Failed to send request.");
424 int result
= ScanVar(response
.pp_var(), "%d%d", "error", &error
, "fd", &fd
);
425 if (result
>= 1 && error
)
429 LOG_ERROR("Expected \"error\" and \"fd\" fields in response.");
433 out_node
->reset(new JsFsNode(this, fd
));
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.");
445 return ErrorFromResponse(response
);
448 Error
JsFs::Mkdir(const Path
& path
, int perm
) {
449 ScopedVar
response(ppapi_
);
450 if (!SendRequestAndWait(&response
, "%s%s%d",
452 "path", path
.Join().c_str(),
454 LOG_ERROR("Failed to send request.");
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.");
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.");
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",
487 "old", path
.Join().c_str(),
488 "new", newpath
.Join().c_str())) {
489 LOG_ERROR("Failed to send request.");
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
);
502 PP_Var response
= *va_arg(args
, PP_Var
*);
506 RequestId response_id
;
507 if (ScanVar(response
, "%d", "id", &response_id
) != 1) {
508 LOG_TRACE("ioctl with no \"id\", ignoring.\n");
512 responses_
.insert(ResponseMap_t::value_type(response_id
, response
));
513 pthread_cond_broadcast(&response_cond_
);
517 } // namespace nacl_io