1 // Copyright 2013 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/html5fs/html5_fs.h"
14 #include <ppapi/c/pp_completion_callback.h>
15 #include <ppapi/c/pp_errors.h>
17 #include "nacl_io/html5fs/html5_fs_node.h"
18 #include "sdk_util/auto_lock.h"
25 int64_t strtoull(const char* nptr
, char** endptr
, int base
) {
26 return _strtoui64(nptr
, endptr
, base
);
32 // Continuing DJB2a hash
33 ino_t
Html5Fs::HashPathSegment(ino_t hash
, const char *str
, size_t len
) {
34 // First add the path seperator
35 hash
= (hash
* static_cast<ino_t
>(33)) ^ '/';
37 hash
= (hash
* static_cast<ino_t
>(33)) ^ *str
++;
42 ino_t
Html5Fs::HashPath(const Path
& path
) {
43 // Prime the DJB2a hash
46 // Apply a running DJB2a to each part of the path
47 for (size_t segment
= 0; segment
< path
.Size(); segment
++) {
48 const std::string
& part
= path
.Part(segment
);
49 hash
= HashPathSegment(hash
, part
.c_str(), part
.length());
55 // For HTML5, the INO should be the one used by the system, however PPAPI
56 // does not provide access to the real INO. Instead, since HTML5 does not
57 // suport links, we assume that files are unique based on path to the base
59 void Html5Fs::OnNodeCreated(Node
* node
) {
60 node
->stat_
.st_dev
= dev_
;
63 void Html5Fs::OnNodeDestroyed(Node
* node
) {}
66 Error
Html5Fs::OpenWithMode(const Path
& path
, int open_flags
, mode_t mode
,
67 ScopedNode
* out_node
) {
68 out_node
->reset(NULL
);
69 Error error
= BlockUntilFilesystemOpen();
73 PP_Resource fileref
= file_ref_iface_
->Create(
74 filesystem_resource_
, GetFullPath(path
).Join().c_str());
78 ScopedNode
node(new Html5FsNode(this, fileref
));
79 error
= node
->Init(open_flags
);
81 // Set the INO based on the path
82 node
->stat_
.st_ino
= HashPath(path
);
91 Path
Html5Fs::GetFullPath(const Path
& path
) {
96 rel_path
.MakeRelative();
97 Path
full_path(prefix_
);
98 full_path
.Append(rel_path
);
102 Error
Html5Fs::Unlink(const Path
& path
) {
103 return RemoveInternal(path
, REMOVE_FILE
);
106 Error
Html5Fs::Mkdir(const Path
& path
, int permissions
) {
107 Error error
= BlockUntilFilesystemOpen();
111 // FileRef returns PP_ERROR_NOACCESS which is translated to EACCES if you
112 // try to create the root directory. EEXIST is a better errno here.
116 ScopedResource
fileref_resource(
118 file_ref_iface_
->Create(filesystem_resource_
,
119 GetFullPath(path
).Join().c_str()));
120 if (!fileref_resource
.pp_resource())
123 int32_t result
= file_ref_iface_
->MakeDirectory(
124 fileref_resource
.pp_resource(), PP_FALSE
, PP_BlockUntilComplete());
126 return PPERROR_TO_ERRNO(result
);
131 Error
Html5Fs::Rmdir(const Path
& path
) {
132 return RemoveInternal(path
, REMOVE_DIR
);
135 Error
Html5Fs::Remove(const Path
& path
) {
136 return RemoveInternal(path
, REMOVE_ALL
);
139 Error
Html5Fs::RemoveInternal(const Path
& path
, int remove_type
) {
140 Error error
= BlockUntilFilesystemOpen();
144 ScopedResource
fileref_resource(
146 file_ref_iface_
->Create(filesystem_resource_
,
147 GetFullPath(path
).Join().c_str()));
148 if (!fileref_resource
.pp_resource())
152 if (remove_type
!= REMOVE_ALL
) {
153 PP_FileInfo file_info
;
154 int32_t query_result
= file_ref_iface_
->Query(
155 fileref_resource
.pp_resource(), &file_info
, PP_BlockUntilComplete());
156 if (query_result
!= PP_OK
) {
157 if (query_result
== PP_ERROR_FILENOTFOUND
) {
160 LOG_ERROR("Error querying file type");
163 switch (file_info
.type
) {
164 case PP_FILETYPE_DIRECTORY
:
165 if (!(remove_type
& REMOVE_DIR
))
168 case PP_FILETYPE_REGULAR
:
169 if (!(remove_type
& REMOVE_FILE
))
173 LOG_ERROR("Invalid file type: %d", file_info
.type
);
178 int32_t result
= file_ref_iface_
->Delete(fileref_resource
.pp_resource(),
179 PP_BlockUntilComplete());
181 return PPERROR_TO_ERRNO(result
);
186 Error
Html5Fs::Rename(const Path
& path
, const Path
& newpath
) {
187 Error error
= BlockUntilFilesystemOpen();
191 std::string oldpath_full
= GetFullPath(path
).Join();
192 ScopedResource
fileref_resource(
194 file_ref_iface_
->Create(filesystem_resource_
, oldpath_full
.c_str()));
195 if (!fileref_resource
.pp_resource())
198 std::string newpath_full
= GetFullPath(newpath
).Join();
199 ScopedResource
new_fileref_resource(
201 file_ref_iface_
->Create(filesystem_resource_
, newpath_full
.c_str()));
202 if (!new_fileref_resource
.pp_resource())
205 int32_t result
= file_ref_iface_
->Rename(fileref_resource
.pp_resource(),
206 new_fileref_resource
.pp_resource(),
207 PP_BlockUntilComplete());
209 return PPERROR_TO_ERRNO(result
);
215 : filesystem_iface_(NULL
),
216 file_ref_iface_(NULL
),
217 file_io_iface_(NULL
),
218 filesystem_resource_(0),
219 filesystem_open_has_result_(false),
220 filesystem_open_error_(0) {
223 Error
Html5Fs::Init(const FsInitArgs
& args
) {
224 pthread_cond_init(&filesystem_open_cond_
, NULL
);
226 Error error
= Filesystem::Init(args
);
231 LOG_ERROR("ppapi is NULL.");
235 core_iface_
= ppapi()->GetCoreInterface();
236 filesystem_iface_
= ppapi()->GetFileSystemInterface();
237 file_io_iface_
= ppapi()->GetFileIoInterface();
238 file_ref_iface_
= ppapi()->GetFileRefInterface();
240 if (!(core_iface_
&& filesystem_iface_
&& file_io_iface_
&&
242 LOG_ERROR("Got NULL interface(s): %s%s%s%s",
243 core_iface_
? "" : "Core ",
244 filesystem_iface_
? "" : "FileSystem ",
245 file_ref_iface_
? "" : "FileRef",
246 file_io_iface_
? "" : "FileIo ");
250 // Parse filesystem args.
251 PP_FileSystemType filesystem_type
= PP_FILESYSTEMTYPE_LOCALPERSISTENT
;
252 int64_t expected_size
= 0;
253 for (StringMap_t::const_iterator iter
= args
.string_map
.begin();
254 iter
!= args
.string_map
.end();
256 if (iter
->first
== "type") {
257 if (iter
->second
== "PERSISTENT") {
258 filesystem_type
= PP_FILESYSTEMTYPE_LOCALPERSISTENT
;
259 } else if (iter
->second
== "TEMPORARY") {
260 filesystem_type
= PP_FILESYSTEMTYPE_LOCALTEMPORARY
;
261 } else if (iter
->second
.empty()) {
262 filesystem_type
= PP_FILESYSTEMTYPE_LOCALPERSISTENT
;
264 LOG_ERROR("Unknown filesystem type: '%s'", iter
->second
.c_str());
267 } else if (iter
->first
== "expected_size") {
268 expected_size
= strtoull(iter
->second
.c_str(), NULL
, 10);
269 } else if (iter
->first
== "filesystem_resource") {
270 PP_Resource resource
= strtoull(iter
->second
.c_str(), NULL
, 10);
271 if (!filesystem_iface_
->IsFileSystem(resource
))
274 filesystem_resource_
= resource
;
275 ppapi_
->AddRefResource(filesystem_resource_
);
276 } else if (iter
->first
== "SOURCE") {
277 prefix_
= iter
->second
;
279 LOG_ERROR("Invalid mount param: %s", iter
->first
.c_str());
284 if (filesystem_resource_
!= 0) {
285 filesystem_open_has_result_
= true;
286 filesystem_open_error_
= PP_OK
;
290 // Initialize filesystem.
291 filesystem_resource_
=
292 filesystem_iface_
->Create(ppapi_
->GetInstance(), filesystem_type
);
293 if (filesystem_resource_
== 0)
296 // We can't block the main thread, so make an asynchronous call if on main
297 // thread. If we are off-main-thread, then don't make an asynchronous call;
298 // otherwise we require a message loop.
299 bool main_thread
= core_iface_
->IsMainThread();
300 PP_CompletionCallback cc
=
301 main_thread
? PP_MakeCompletionCallback(
302 &Html5Fs::FilesystemOpenCallbackThunk
, this)
303 : PP_BlockUntilComplete();
306 filesystem_iface_
->Open(filesystem_resource_
, expected_size
, cc
);
309 filesystem_open_has_result_
= true;
310 filesystem_open_error_
= PPERROR_TO_ERRNO(result
);
312 return filesystem_open_error_
;
315 // We have to assume the call to Open will succeed; there is no better
316 // result to return here.
320 void Html5Fs::Destroy() {
321 if (ppapi_
!= NULL
&& filesystem_resource_
!= 0)
322 ppapi_
->ReleaseResource(filesystem_resource_
);
323 pthread_cond_destroy(&filesystem_open_cond_
);
326 Error
Html5Fs::BlockUntilFilesystemOpen() {
327 AUTO_LOCK(filesysem_open_lock_
);
328 while (!filesystem_open_has_result_
) {
329 pthread_cond_wait(&filesystem_open_cond_
, filesysem_open_lock_
.mutex());
331 return filesystem_open_error_
;
335 void Html5Fs::FilesystemOpenCallbackThunk(void* user_data
, int32_t result
) {
336 Html5Fs
* self
= static_cast<Html5Fs
*>(user_data
);
337 self
->FilesystemOpenCallback(result
);
340 void Html5Fs::FilesystemOpenCallback(int32_t result
) {
341 AUTO_LOCK(filesysem_open_lock_
);
342 filesystem_open_has_result_
= true;
343 filesystem_open_error_
= PPERROR_TO_ERRNO(result
);
344 pthread_cond_signal(&filesystem_open_cond_
);
347 } // namespace nacl_io