Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / libraries / nacl_io / html5fs / html5_fs.cc
blob954d8e3632e855a24f789463a2853298204f8cbf
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"
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stdlib.h>
10 #include <string.h>
12 #include <algorithm>
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"
20 namespace nacl_io {
22 namespace {
24 #if defined(WIN32)
25 int64_t strtoull(const char* nptr, char** endptr, int base) {
26 return _strtoui64(nptr, endptr, base);
28 #endif
30 } // namespace
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)) ^ '/';
36 while (len--) {
37 hash = (hash * static_cast<ino_t>(33)) ^ *str++;
39 return hash;
42 ino_t Html5Fs::HashPath(const Path& path) {
43 // Prime the DJB2a hash
44 ino_t hash = 5381;
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());
51 return hash;
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
58 // of the mount.
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();
70 if (error)
71 return error;
73 PP_Resource fileref = file_ref_iface_->Create(
74 filesystem_resource_, GetFullPath(path).Join().c_str());
75 if (!fileref)
76 return ENOENT;
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);
84 if (error)
85 return error;
87 *out_node = node;
88 return 0;
91 Path Html5Fs::GetFullPath(const Path& path) {
92 if (prefix_.empty())
93 return path;
95 Path rel_path(path);
96 rel_path.MakeRelative();
97 Path full_path(prefix_);
98 full_path.Append(rel_path);
99 return full_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();
108 if (error)
109 return error;
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.
113 if (path.IsRoot())
114 return EEXIST;
116 ScopedResource fileref_resource(
117 ppapi(),
118 file_ref_iface_->Create(filesystem_resource_,
119 GetFullPath(path).Join().c_str()));
120 if (!fileref_resource.pp_resource())
121 return ENOENT;
123 int32_t result = file_ref_iface_->MakeDirectory(
124 fileref_resource.pp_resource(), PP_FALSE, PP_BlockUntilComplete());
125 if (result != PP_OK)
126 return PPERROR_TO_ERRNO(result);
128 return 0;
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();
141 if (error)
142 return error;
144 ScopedResource fileref_resource(
145 ppapi(),
146 file_ref_iface_->Create(filesystem_resource_,
147 GetFullPath(path).Join().c_str()));
148 if (!fileref_resource.pp_resource())
149 return ENOENT;
151 // Check file type
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) {
158 return ENOENT;
160 LOG_ERROR("Error querying file type");
161 return EINVAL;
163 switch (file_info.type) {
164 case PP_FILETYPE_DIRECTORY:
165 if (!(remove_type & REMOVE_DIR))
166 return EISDIR;
167 break;
168 case PP_FILETYPE_REGULAR:
169 if (!(remove_type & REMOVE_FILE))
170 return ENOTDIR;
171 break;
172 default:
173 LOG_ERROR("Invalid file type: %d", file_info.type);
174 return EINVAL;
178 int32_t result = file_ref_iface_->Delete(fileref_resource.pp_resource(),
179 PP_BlockUntilComplete());
180 if (result != PP_OK)
181 return PPERROR_TO_ERRNO(result);
183 return 0;
186 Error Html5Fs::Rename(const Path& path, const Path& newpath) {
187 Error error = BlockUntilFilesystemOpen();
188 if (error)
189 return error;
191 std::string oldpath_full = GetFullPath(path).Join();
192 ScopedResource fileref_resource(
193 ppapi(),
194 file_ref_iface_->Create(filesystem_resource_, oldpath_full.c_str()));
195 if (!fileref_resource.pp_resource())
196 return ENOENT;
198 std::string newpath_full = GetFullPath(newpath).Join();
199 ScopedResource new_fileref_resource(
200 ppapi(),
201 file_ref_iface_->Create(filesystem_resource_, newpath_full.c_str()));
202 if (!new_fileref_resource.pp_resource())
203 return ENOENT;
205 int32_t result = file_ref_iface_->Rename(fileref_resource.pp_resource(),
206 new_fileref_resource.pp_resource(),
207 PP_BlockUntilComplete());
208 if (result != PP_OK)
209 return PPERROR_TO_ERRNO(result);
211 return 0;
214 Html5Fs::Html5Fs()
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);
227 if (error)
228 return error;
230 if (!args.ppapi) {
231 LOG_ERROR("ppapi is NULL.");
232 return ENOSYS;
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_ &&
241 file_ref_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 ");
247 return ENOSYS;
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();
255 ++iter) {
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;
263 } else {
264 LOG_ERROR("Unknown filesystem type: '%s'", iter->second.c_str());
265 return EINVAL;
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))
272 return EINVAL;
274 filesystem_resource_ = resource;
275 ppapi_->AddRefResource(filesystem_resource_);
276 } else if (iter->first == "SOURCE") {
277 prefix_ = iter->second;
278 } else {
279 LOG_ERROR("Invalid mount param: %s", iter->first.c_str());
280 return EINVAL;
284 if (filesystem_resource_ != 0) {
285 filesystem_open_has_result_ = true;
286 filesystem_open_error_ = PP_OK;
287 return 0;
290 // Initialize filesystem.
291 filesystem_resource_ =
292 filesystem_iface_->Create(ppapi_->GetInstance(), filesystem_type);
293 if (filesystem_resource_ == 0)
294 return ENOSYS;
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();
305 int32_t result =
306 filesystem_iface_->Open(filesystem_resource_, expected_size, cc);
308 if (!main_thread) {
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.
317 return 0;
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_;
334 // static
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