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 Error Html5Fs::Access(const Path& path, int a_mode) {
33 // a_mode is unused, since all files are readable, writable and executable.
35 return Open(path, O_RDONLY, &node);
38 Error Html5Fs::Open(const Path& path, int open_flags, ScopedNode* out_node) {
39 out_node->reset(NULL);
40 Error error = BlockUntilFilesystemOpen();
44 PP_Resource fileref = file_ref_iface_->Create(
45 filesystem_resource_, GetFullPath(path).Join().c_str());
49 ScopedNode node(new Html5FsNode(this, fileref));
50 error = node->Init(open_flags);
58 Path Html5Fs::GetFullPath(const Path& path) {
60 full_path.Prepend(prefix_);
64 Error Html5Fs::Unlink(const Path& path) {
65 return RemoveInternal(path, REMOVE_FILE);
68 Error Html5Fs::Mkdir(const Path& path, int permissions) {
69 Error error = BlockUntilFilesystemOpen();
73 // FileRef returns PP_ERROR_NOACCESS which is translated to EACCES if you
74 // try to create the root directory. EEXIST is a better errno here.
78 ScopedResource fileref_resource(
80 file_ref_iface_->Create(filesystem_resource_,
81 GetFullPath(path).Join().c_str()));
82 if (!fileref_resource.pp_resource())
85 int32_t result = file_ref_iface_->MakeDirectory(
86 fileref_resource.pp_resource(), PP_FALSE, PP_BlockUntilComplete());
88 return PPErrorToErrno(result);
93 Error Html5Fs::Rmdir(const Path& path) {
94 return RemoveInternal(path, REMOVE_DIR);
97 Error Html5Fs::Remove(const Path& path) {
98 return RemoveInternal(path, REMOVE_ALL);
101 Error Html5Fs::RemoveInternal(const Path& path, int remove_type) {
102 Error error = BlockUntilFilesystemOpen();
106 ScopedResource fileref_resource(
108 file_ref_iface_->Create(filesystem_resource_,
109 GetFullPath(path).Join().c_str()));
110 if (!fileref_resource.pp_resource())
114 if (remove_type != REMOVE_ALL) {
115 PP_FileInfo file_info;
116 int32_t query_result = file_ref_iface_->Query(
117 fileref_resource.pp_resource(), &file_info, PP_BlockUntilComplete());
118 if (query_result != PP_OK) {
119 LOG_ERROR("Error querying file type");
122 switch (file_info.type) {
123 case PP_FILETYPE_DIRECTORY:
124 if (!(remove_type & REMOVE_DIR))
127 case PP_FILETYPE_REGULAR:
128 if (!(remove_type & REMOVE_FILE))
132 LOG_ERROR("Invalid file type: %d", file_info.type);
137 int32_t result = file_ref_iface_->Delete(fileref_resource.pp_resource(),
138 PP_BlockUntilComplete());
140 return PPErrorToErrno(result);
145 Error Html5Fs::Rename(const Path& path, const Path& newpath) {
146 Error error = BlockUntilFilesystemOpen();
150 std::string oldpath_full = GetFullPath(path).Join();
151 ScopedResource fileref_resource(
153 file_ref_iface_->Create(filesystem_resource_, oldpath_full.c_str()));
154 if (!fileref_resource.pp_resource())
157 std::string newpath_full = GetFullPath(newpath).Join();
158 ScopedResource new_fileref_resource(
160 file_ref_iface_->Create(filesystem_resource_, newpath_full.c_str()));
161 if (!new_fileref_resource.pp_resource())
164 int32_t result = file_ref_iface_->Rename(fileref_resource.pp_resource(),
165 new_fileref_resource.pp_resource(),
166 PP_BlockUntilComplete());
168 return PPErrorToErrno(result);
174 : filesystem_iface_(NULL),
175 file_ref_iface_(NULL),
176 file_io_iface_(NULL),
177 filesystem_resource_(0),
178 filesystem_open_has_result_(false),
179 filesystem_open_error_(0) {
182 Error Html5Fs::Init(const FsInitArgs& args) {
183 Error error = Filesystem::Init(args);
188 LOG_ERROR("ppapi is NULL.");
192 core_iface_ = ppapi()->GetCoreInterface();
193 filesystem_iface_ = ppapi()->GetFileSystemInterface();
194 file_io_iface_ = ppapi()->GetFileIoInterface();
195 file_ref_iface_ = ppapi()->GetFileRefInterface();
197 if (!(core_iface_ && filesystem_iface_ && file_io_iface_ &&
199 LOG_ERROR("Got NULL interface(s): %s%s%s%s",
200 core_iface_ ? "" : "Core ",
201 filesystem_iface_ ? "" : "FileSystem ",
202 file_ref_iface_ ? "" : "FileRef",
203 file_io_iface_ ? "" : "FileIo ");
207 pthread_cond_init(&filesystem_open_cond_, NULL);
209 // Parse filesystem args.
210 PP_FileSystemType filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
211 int64_t expected_size = 0;
212 for (StringMap_t::const_iterator iter = args.string_map.begin();
213 iter != args.string_map.end();
215 if (iter->first == "type") {
216 if (iter->second == "PERSISTENT") {
217 filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
218 } else if (iter->second == "TEMPORARY") {
219 filesystem_type = PP_FILESYSTEMTYPE_LOCALTEMPORARY;
220 } else if (iter->second == "") {
221 filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
223 LOG_ERROR("Unknown filesystem type: '%s'", iter->second.c_str());
226 } else if (iter->first == "expected_size") {
227 expected_size = strtoull(iter->second.c_str(), NULL, 10);
228 } else if (iter->first == "filesystem_resource") {
229 PP_Resource resource = strtoull(iter->second.c_str(), NULL, 10);
230 if (!filesystem_iface_->IsFileSystem(resource))
233 filesystem_resource_ = resource;
234 ppapi_->AddRefResource(filesystem_resource_);
235 } else if (iter->first == "SOURCE") {
236 prefix_ = iter->second;
238 LOG_ERROR("Invalid mount param: %s", iter->first.c_str());
243 if (filesystem_resource_ != 0) {
244 filesystem_open_has_result_ = true;
245 filesystem_open_error_ = PP_OK;
249 // Initialize filesystem.
250 filesystem_resource_ =
251 filesystem_iface_->Create(ppapi_->GetInstance(), filesystem_type);
252 if (filesystem_resource_ == 0)
255 // We can't block the main thread, so make an asynchronous call if on main
256 // thread. If we are off-main-thread, then don't make an asynchronous call;
257 // otherwise we require a message loop.
258 bool main_thread = core_iface_->IsMainThread();
259 PP_CompletionCallback cc =
260 main_thread ? PP_MakeCompletionCallback(
261 &Html5Fs::FilesystemOpenCallbackThunk, this)
262 : PP_BlockUntilComplete();
265 filesystem_iface_->Open(filesystem_resource_, expected_size, cc);
268 filesystem_open_has_result_ = true;
269 filesystem_open_error_ = PPErrorToErrno(result);
271 return filesystem_open_error_;
274 // We have to assume the call to Open will succeed; there is no better
275 // result to return here.
279 void Html5Fs::Destroy() {
280 ppapi_->ReleaseResource(filesystem_resource_);
281 pthread_cond_destroy(&filesystem_open_cond_);
284 Error Html5Fs::BlockUntilFilesystemOpen() {
285 AUTO_LOCK(filesysem_open_lock_);
286 while (!filesystem_open_has_result_) {
287 pthread_cond_wait(&filesystem_open_cond_, filesysem_open_lock_.mutex());
289 return filesystem_open_error_;
293 void Html5Fs::FilesystemOpenCallbackThunk(void* user_data, int32_t result) {
294 Html5Fs* self = static_cast<Html5Fs*>(user_data);
295 self->FilesystemOpenCallback(result);
298 void Html5Fs::FilesystemOpenCallback(int32_t result) {
299 AUTO_LOCK(filesysem_open_lock_);
300 filesystem_open_has_result_ = true;
301 filesystem_open_error_ = PPErrorToErrno(result);
302 pthread_cond_signal(&filesystem_open_cond_);
305 } // namespace nacl_io