• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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