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 = 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 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 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 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 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 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 176 Html5Fs::Html5Fs() 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 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 262 void Html5Fs::Destroy() { 263 ppapi_->ReleaseResource(filesystem_resource_); 264 pthread_cond_destroy(&filesystem_open_cond_); 265 } 266 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 276 void Html5Fs::FilesystemOpenCallbackThunk(void* user_data, int32_t result) { 277 Html5Fs* self = static_cast<Html5Fs*>(user_data); 278 self->FilesystemOpenCallback(result); 279 } 280 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