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.
4
5 #include "nacl_io/html5fs/html5_fs.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include <algorithm>
13
14 #include <ppapi/c/pp_completion_callback.h>
15 #include <ppapi/c/pp_errors.h>
16
17 #include "nacl_io/html5fs/html5_fs_node.h"
18 #include "sdk_util/auto_lock.h"
19
20 namespace nacl_io {
21
22 namespace {
23
24 #if defined(WIN32)
strtoull(const char * nptr,char ** endptr,int base)25 int64_t strtoull(const char* nptr, char** endptr, int base) {
26 return _strtoui64(nptr, endptr, base);
27 }
28 #endif
29
30 } // namespace
31
Access(const Path & path,int a_mode)32 Error Html5Fs::Access(const Path& path, int a_mode) {
33 // a_mode is unused, since all files are readable, writable and executable.
34 ScopedNode node;
35 return Open(path, O_RDONLY, &node);
36 }
37
Open(const Path & path,int open_flags,ScopedNode * out_node)38 Error Html5Fs::Open(const Path& path, int open_flags, ScopedNode* out_node) {
39 out_node->reset(NULL);
40 Error error = BlockUntilFilesystemOpen();
41 if (error)
42 return error;
43
44 PP_Resource fileref = ppapi()->GetFileRefInterface()->Create(
45 filesystem_resource_, GetFullPath(path).Join().c_str());
46 if (!fileref)
47 return ENOENT;
48
49 ScopedNode node(new Html5FsNode(this, fileref));
50 error = node->Init(open_flags);
51 if (error)
52 return error;
53
54 *out_node = node;
55 return 0;
56 }
57
GetFullPath(const Path & path)58 Path Html5Fs::GetFullPath(const Path& path) {
59 Path full_path(path);
60 full_path.Prepend(prefix_);
61 return full_path;
62 }
63
Unlink(const Path & path)64 Error Html5Fs::Unlink(const Path& path) {
65 return RemoveInternal(path, REMOVE_FILE);
66 }
67
Mkdir(const Path & path,int permissions)68 Error Html5Fs::Mkdir(const Path& path, int permissions) {
69 Error error = BlockUntilFilesystemOpen();
70 if (error)
71 return error;
72
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.
75 if (path.IsRoot())
76 return EEXIST;
77
78 ScopedResource fileref_resource(
79 ppapi(),
80 ppapi()->GetFileRefInterface()->Create(filesystem_resource_,
81 GetFullPath(path).Join().c_str()));
82 if (!fileref_resource.pp_resource())
83 return ENOENT;
84
85 int32_t result = ppapi()->GetFileRefInterface()->MakeDirectory(
86 fileref_resource.pp_resource(), PP_FALSE, PP_BlockUntilComplete());
87 if (result != PP_OK)
88 return PPErrorToErrno(result);
89
90 return 0;
91 }
92
Rmdir(const Path & path)93 Error Html5Fs::Rmdir(const Path& path) {
94 return RemoveInternal(path, REMOVE_DIR);
95 }
96
Remove(const Path & path)97 Error Html5Fs::Remove(const Path& path) {
98 return RemoveInternal(path, REMOVE_ALL);
99 }
100
RemoveInternal(const Path & path,int remove_type)101 Error Html5Fs::RemoveInternal(const Path& path, int remove_type) {
102 Error error = BlockUntilFilesystemOpen();
103 if (error)
104 return error;
105
106 ScopedResource fileref_resource(
107 ppapi(),
108 ppapi()->GetFileRefInterface()->Create(filesystem_resource_,
109 GetFullPath(path).Join().c_str()));
110 if (!fileref_resource.pp_resource())
111 return ENOENT;
112
113 // Check file type
114 if (remove_type != REMOVE_ALL) {
115 PP_FileInfo file_info;
116 int32_t query_result = ppapi()->GetFileRefInterface()->Query(
117 fileref_resource.pp_resource(), &file_info, PP_BlockUntilComplete());
118 if (query_result != PP_OK) {
119 LOG_ERROR("Error querying file type");
120 return EINVAL;
121 }
122 switch (file_info.type) {
123 case PP_FILETYPE_DIRECTORY:
124 if (!(remove_type & REMOVE_DIR))
125 return EISDIR;
126 break;
127 case PP_FILETYPE_REGULAR:
128 if (!(remove_type & REMOVE_FILE))
129 return ENOTDIR;
130 break;
131 default:
132 LOG_ERROR("Invalid file type: %d", file_info.type);
133 return EINVAL;
134 }
135 }
136
137 int32_t result = ppapi()->GetFileRefInterface()->Delete(
138 fileref_resource.pp_resource(), PP_BlockUntilComplete());
139 if (result != PP_OK)
140 return PPErrorToErrno(result);
141
142 return 0;
143 }
144
Rename(const Path & path,const Path & newpath)145 Error Html5Fs::Rename(const Path& path, const Path& newpath) {
146 Error error = BlockUntilFilesystemOpen();
147 if (error)
148 return error;
149
150 const char* oldpath_full = GetFullPath(path).Join().c_str();
151 ScopedResource fileref_resource(
152 ppapi(),
153 ppapi()->GetFileRefInterface()->Create(filesystem_resource_,
154 oldpath_full));
155 if (!fileref_resource.pp_resource())
156 return ENOENT;
157
158 const char* newpath_full = GetFullPath(newpath).Join().c_str();
159 ScopedResource new_fileref_resource(
160 ppapi(),
161 ppapi()->GetFileRefInterface()->Create(filesystem_resource_,
162 newpath_full));
163 if (!new_fileref_resource.pp_resource())
164 return ENOENT;
165
166 int32_t result =
167 ppapi()->GetFileRefInterface()->Rename(fileref_resource.pp_resource(),
168 new_fileref_resource.pp_resource(),
169 PP_BlockUntilComplete());
170 if (result != PP_OK)
171 return PPErrorToErrno(result);
172
173 return 0;
174 }
175
Html5Fs()176 Html5Fs::Html5Fs()
177 : filesystem_resource_(0),
178 filesystem_open_has_result_(false),
179 filesystem_open_error_(0) {
180 }
181
Init(const FsInitArgs & args)182 Error Html5Fs::Init(const FsInitArgs& args) {
183 Error error = Filesystem::Init(args);
184 if (error)
185 return error;
186
187 if (!args.ppapi)
188 return ENOSYS;
189
190 pthread_cond_init(&filesystem_open_cond_, NULL);
191
192 // Parse filesystem args.
193 PP_FileSystemType filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
194 int64_t expected_size = 0;
195 for (StringMap_t::const_iterator iter = args.string_map.begin();
196 iter != args.string_map.end();
197 ++iter) {
198 if (iter->first == "type") {
199 if (iter->second == "PERSISTENT") {
200 filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
201 } else if (iter->second == "TEMPORARY") {
202 filesystem_type = PP_FILESYSTEMTYPE_LOCALTEMPORARY;
203 } else if (iter->second == "") {
204 filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
205 } else {
206 LOG_ERROR("html5fs: unknown type: '%s'", iter->second.c_str());
207 return EINVAL;
208 }
209 } else if (iter->first == "expected_size") {
210 expected_size = strtoull(iter->second.c_str(), NULL, 10);
211 } else if (iter->first == "filesystem_resource") {
212 PP_Resource resource = strtoull(iter->second.c_str(), NULL, 10);
213 if (!ppapi_->GetFileSystemInterface()->IsFileSystem(resource))
214 return EINVAL;
215
216 filesystem_resource_ = resource;
217 ppapi_->AddRefResource(filesystem_resource_);
218 } else if (iter->first == "SOURCE") {
219 prefix_ = iter->second;
220 } else {
221 LOG_ERROR("html5fs: bad param: %s", iter->first.c_str());
222 return EINVAL;
223 }
224 }
225
226 if (filesystem_resource_ != 0) {
227 filesystem_open_has_result_ = true;
228 filesystem_open_error_ = PP_OK;
229 return 0;
230 }
231
232 // Initialize filesystem.
233 filesystem_resource_ = ppapi_->GetFileSystemInterface()->Create(
234 ppapi_->GetInstance(), filesystem_type);
235 if (filesystem_resource_ == 0)
236 return ENOSYS;
237
238 // We can't block the main thread, so make an asynchronous call if on main
239 // thread. If we are off-main-thread, then don't make an asynchronous call;
240 // otherwise we require a message loop.
241 bool main_thread = ppapi_->GetCoreInterface()->IsMainThread();
242 PP_CompletionCallback cc =
243 main_thread ? PP_MakeCompletionCallback(
244 &Html5Fs::FilesystemOpenCallbackThunk, this)
245 : PP_BlockUntilComplete();
246
247 int32_t result = ppapi_->GetFileSystemInterface()->Open(
248 filesystem_resource_, expected_size, cc);
249
250 if (!main_thread) {
251 filesystem_open_has_result_ = true;
252 filesystem_open_error_ = PPErrorToErrno(result);
253
254 return filesystem_open_error_;
255 }
256
257 // We have to assume the call to Open will succeed; there is no better
258 // result to return here.
259 return 0;
260 }
261
Destroy()262 void Html5Fs::Destroy() {
263 ppapi_->ReleaseResource(filesystem_resource_);
264 pthread_cond_destroy(&filesystem_open_cond_);
265 }
266
BlockUntilFilesystemOpen()267 Error Html5Fs::BlockUntilFilesystemOpen() {
268 AUTO_LOCK(filesysem_open_lock_);
269 while (!filesystem_open_has_result_) {
270 pthread_cond_wait(&filesystem_open_cond_, filesysem_open_lock_.mutex());
271 }
272 return filesystem_open_error_;
273 }
274
275 // static
FilesystemOpenCallbackThunk(void * user_data,int32_t result)276 void Html5Fs::FilesystemOpenCallbackThunk(void* user_data, int32_t result) {
277 Html5Fs* self = static_cast<Html5Fs*>(user_data);
278 self->FilesystemOpenCallback(result);
279 }
280
FilesystemOpenCallback(int32_t result)281 void Html5Fs::FilesystemOpenCallback(int32_t result) {
282 AUTO_LOCK(filesysem_open_lock_);
283 filesystem_open_has_result_ = true;
284 filesystem_open_error_ = PPErrorToErrno(result);
285 pthread_cond_signal(&filesystem_open_cond_);
286 }
287
288 } // namespace nacl_io
289