Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / libraries / nacl_io / html5fs / html5_fs.cc
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)
25 int64_t strtoull(const char* nptr, char** endptr, int base) {
26   return _strtoui64(nptr, endptr, base);
27 }
28 #endif
29
30 }  // namespace
31
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
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 = file_ref_iface_->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
58 Path Html5Fs::GetFullPath(const Path& path) {
59   Path full_path(path);
60   full_path.Prepend(prefix_);
61   return full_path;
62 }
63
64 Error Html5Fs::Unlink(const Path& path) {
65   return RemoveInternal(path, REMOVE_FILE);
66 }
67
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       file_ref_iface_->Create(filesystem_resource_,
81                               GetFullPath(path).Join().c_str()));
82   if (!fileref_resource.pp_resource())
83     return ENOENT;
84
85   int32_t result = file_ref_iface_->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
93 Error Html5Fs::Rmdir(const Path& path) {
94   return RemoveInternal(path, REMOVE_DIR);
95 }
96
97 Error Html5Fs::Remove(const Path& path) {
98   return RemoveInternal(path, REMOVE_ALL);
99 }
100
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       file_ref_iface_->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 = 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");
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 = file_ref_iface_->Delete(fileref_resource.pp_resource(),
138                                            PP_BlockUntilComplete());
139   if (result != PP_OK)
140     return PPErrorToErrno(result);
141
142   return 0;
143 }
144
145 Error Html5Fs::Rename(const Path& path, const Path& newpath) {
146   Error error = BlockUntilFilesystemOpen();
147   if (error)
148     return error;
149
150   std::string oldpath_full = GetFullPath(path).Join();
151   ScopedResource fileref_resource(
152       ppapi(),
153       file_ref_iface_->Create(filesystem_resource_, oldpath_full.c_str()));
154   if (!fileref_resource.pp_resource())
155     return ENOENT;
156
157   std::string newpath_full = GetFullPath(newpath).Join();
158   ScopedResource new_fileref_resource(
159       ppapi(),
160       file_ref_iface_->Create(filesystem_resource_, newpath_full.c_str()));
161   if (!new_fileref_resource.pp_resource())
162     return ENOENT;
163
164   int32_t result = file_ref_iface_->Rename(fileref_resource.pp_resource(),
165                                            new_fileref_resource.pp_resource(),
166                                            PP_BlockUntilComplete());
167   if (result != PP_OK)
168     return PPErrorToErrno(result);
169
170   return 0;
171 }
172
173 Html5Fs::Html5Fs()
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) {
180 }
181
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     LOG_ERROR("ppapi is NULL.");
189     return ENOSYS;
190   }
191
192   core_iface_ = ppapi()->GetCoreInterface();
193   filesystem_iface_ = ppapi()->GetFileSystemInterface();
194   file_io_iface_ = ppapi()->GetFileIoInterface();
195   file_ref_iface_ = ppapi()->GetFileRefInterface();
196
197   if (!(core_iface_ && filesystem_iface_ && file_io_iface_ &&
198         file_ref_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 ");
204     return ENOSYS;
205   }
206
207   pthread_cond_init(&filesystem_open_cond_, NULL);
208
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();
214        ++iter) {
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;
222       } else {
223         LOG_ERROR("Unknown filesystem type: '%s'", iter->second.c_str());
224         return EINVAL;
225       }
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))
231         return EINVAL;
232
233       filesystem_resource_ = resource;
234       ppapi_->AddRefResource(filesystem_resource_);
235     } else if (iter->first == "SOURCE") {
236       prefix_ = iter->second;
237     } else {
238       LOG_ERROR("Invalid mount param: %s", iter->first.c_str());
239       return EINVAL;
240     }
241   }
242
243   if (filesystem_resource_ != 0) {
244     filesystem_open_has_result_ = true;
245     filesystem_open_error_ = PP_OK;
246     return 0;
247   }
248
249   // Initialize filesystem.
250   filesystem_resource_ =
251       filesystem_iface_->Create(ppapi_->GetInstance(), filesystem_type);
252   if (filesystem_resource_ == 0)
253     return ENOSYS;
254
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();
263
264   int32_t result =
265       filesystem_iface_->Open(filesystem_resource_, expected_size, cc);
266
267   if (!main_thread) {
268     filesystem_open_has_result_ = true;
269     filesystem_open_error_ = PPErrorToErrno(result);
270
271     return filesystem_open_error_;
272   }
273
274   // We have to assume the call to Open will succeed; there is no better
275   // result to return here.
276   return 0;
277 }
278
279 void Html5Fs::Destroy() {
280   ppapi_->ReleaseResource(filesystem_resource_);
281   pthread_cond_destroy(&filesystem_open_cond_);
282 }
283
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());
288   }
289   return filesystem_open_error_;
290 }
291
292 // static
293 void Html5Fs::FilesystemOpenCallbackThunk(void* user_data, int32_t result) {
294   Html5Fs* self = static_cast<Html5Fs*>(user_data);
295   self->FilesystemOpenCallback(result);
296 }
297
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_);
303 }
304
305 }  // namespace nacl_io