1 // Copyright (c) 2012 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 "webkit/common/fileapi/file_system_util.h" 6 7 #include <algorithm> 8 9 #include "base/files/file_path.h" 10 #include "base/logging.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/sys_string_conversions.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "net/base/net_errors.h" 15 #include "url/gurl.h" 16 #include "webkit/common/database/database_identifier.h" 17 18 namespace fileapi { 19 20 const char kPersistentDir[] = "/persistent"; 21 const char kTemporaryDir[] = "/temporary"; 22 const char kIsolatedDir[] = "/isolated"; 23 const char kExternalDir[] = "/external"; 24 const char kTestDir[] = "/test"; 25 26 const base::FilePath::CharType VirtualPath::kRoot[] = FILE_PATH_LITERAL("/"); 27 const base::FilePath::CharType VirtualPath::kSeparator = FILE_PATH_LITERAL('/'); 28 29 // TODO(ericu): Consider removing support for '\', even on Windows, if possible. 30 // There's a lot of test code that will need reworking, and we may have trouble 31 // with base::FilePath elsewhere [e.g. DirName and other methods may also need 32 // replacement]. 33 base::FilePath VirtualPath::BaseName(const base::FilePath& virtual_path) { 34 base::FilePath::StringType path = virtual_path.value(); 35 36 // Keep everything after the final separator, but if the pathname is only 37 // one character and it's a separator, leave it alone. 38 while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1])) 39 path.resize(path.size() - 1); 40 base::FilePath::StringType::size_type last_separator = 41 path.find_last_of(base::FilePath::kSeparators); 42 if (last_separator != base::FilePath::StringType::npos && 43 last_separator < path.size() - 1) 44 path.erase(0, last_separator + 1); 45 46 return base::FilePath(path); 47 } 48 49 base::FilePath VirtualPath::DirName(const base::FilePath& virtual_path) { 50 typedef base::FilePath::StringType StringType; 51 StringType path = virtual_path.value(); 52 53 // The logic below is taken from that of base::FilePath::DirName, except 54 // that this version never cares about '//' or drive-letters even on win32. 55 56 // Strip trailing separators. 57 while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1])) 58 path.resize(path.size() - 1); 59 60 StringType::size_type last_separator = 61 path.find_last_of(base::FilePath::kSeparators); 62 if (last_separator == StringType::npos) { 63 // path_ is in the current directory. 64 return base::FilePath(base::FilePath::kCurrentDirectory); 65 } 66 if (last_separator == 0) { 67 // path_ is in the root directory. 68 return base::FilePath(path.substr(0, 1)); 69 } 70 // path_ is somewhere else, trim the basename. 71 path.resize(last_separator); 72 73 // Strip trailing separators. 74 while (path.size() > 1 && base::FilePath::IsSeparator(path[path.size() - 1])) 75 path.resize(path.size() - 1); 76 77 if (path.empty()) 78 return base::FilePath(base::FilePath::kCurrentDirectory); 79 80 return base::FilePath(path); 81 } 82 83 void VirtualPath::GetComponents( 84 const base::FilePath& path, 85 std::vector<base::FilePath::StringType>* components) { 86 typedef base::FilePath::StringType StringType; 87 88 DCHECK(components); 89 if (!components) 90 return; 91 components->clear(); 92 if (path.value().empty()) 93 return; 94 95 StringType::size_type begin = 0, end = 0; 96 while (begin < path.value().length() && end != StringType::npos) { 97 end = path.value().find_first_of(base::FilePath::kSeparators, begin); 98 StringType component = path.value().substr( 99 begin, end == StringType::npos ? StringType::npos : end - begin); 100 if (!component.empty() && component != base::FilePath::kCurrentDirectory) 101 components->push_back(component); 102 begin = end + 1; 103 } 104 } 105 106 void VirtualPath::GetComponentsUTF8Unsafe( 107 const base::FilePath& path, 108 std::vector<std::string>* components) { 109 DCHECK(components); 110 if (!components) 111 return; 112 components->clear(); 113 114 std::vector<base::FilePath::StringType> stringtype_components; 115 VirtualPath::GetComponents(path, &stringtype_components); 116 std::vector<base::FilePath::StringType>::const_iterator it; 117 for (it = stringtype_components.begin(); it != stringtype_components.end(); 118 ++it) { 119 components->push_back(base::FilePath(*it).AsUTF8Unsafe()); 120 } 121 } 122 123 base::FilePath::StringType VirtualPath::GetNormalizedFilePath( 124 const base::FilePath& path) { 125 base::FilePath::StringType normalized_path = path.value(); 126 const size_t num_separators = base::FilePath::StringType( 127 base::FilePath::kSeparators).length(); 128 for (size_t i = 0; i < num_separators; ++i) { 129 std::replace(normalized_path.begin(), normalized_path.end(), 130 base::FilePath::kSeparators[i], kSeparator); 131 } 132 133 return (IsAbsolute(normalized_path)) ? 134 normalized_path : base::FilePath::StringType(kRoot) + normalized_path; 135 } 136 137 bool VirtualPath::IsAbsolute(const base::FilePath::StringType& path) { 138 return path.find(kRoot) == 0; 139 } 140 141 bool VirtualPath::IsRootPath(const base::FilePath& path) { 142 std::vector<base::FilePath::StringType> components; 143 VirtualPath::GetComponents(path, &components); 144 return (path.empty() || components.empty() || 145 (components.size() == 1 && 146 components[0] == VirtualPath::kRoot)); 147 } 148 149 GURL GetFileSystemRootURI(const GURL& origin_url, FileSystemType type) { 150 // origin_url is based on a security origin, so http://foo.com or file:/// 151 // instead of the corresponding filesystem URL. 152 DCHECK(!origin_url.SchemeIsFileSystem()); 153 154 std::string url = "filesystem:" + origin_url.GetWithEmptyPath().spec(); 155 switch (type) { 156 case kFileSystemTypeTemporary: 157 url += (kTemporaryDir + 1); // We don't want the leading slash. 158 return GURL(url + "/"); 159 case kFileSystemTypePersistent: 160 url += (kPersistentDir + 1); // We don't want the leading slash. 161 return GURL(url + "/"); 162 case kFileSystemTypeExternal: 163 url += (kExternalDir + 1); // We don't want the leading slash. 164 return GURL(url + "/"); 165 case kFileSystemTypeIsolated: 166 url += (kIsolatedDir + 1); // We don't want the leading slash. 167 return GURL(url + "/"); 168 case kFileSystemTypeTest: 169 url += (kTestDir + 1); // We don't want the leading slash. 170 return GURL(url + "/"); 171 // Internal types are always pointed via isolated or external URLs. 172 default: 173 NOTREACHED(); 174 } 175 NOTREACHED(); 176 return GURL(); 177 } 178 179 std::string GetFileSystemName(const GURL& origin_url, FileSystemType type) { 180 std::string origin_identifier = 181 webkit_database::GetIdentifierFromOrigin(origin_url); 182 std::string type_string = GetFileSystemTypeString(type); 183 DCHECK(!type_string.empty()); 184 return origin_identifier + ":" + type_string; 185 } 186 187 FileSystemType QuotaStorageTypeToFileSystemType( 188 quota::StorageType storage_type) { 189 switch (storage_type) { 190 case quota::kStorageTypeTemporary: 191 return kFileSystemTypeTemporary; 192 case quota::kStorageTypePersistent: 193 return kFileSystemTypePersistent; 194 case quota::kStorageTypeSyncable: 195 return kFileSystemTypeSyncable; 196 case quota::kStorageTypeQuotaNotManaged: 197 case quota::kStorageTypeUnknown: 198 return kFileSystemTypeUnknown; 199 } 200 return kFileSystemTypeUnknown; 201 } 202 203 quota::StorageType FileSystemTypeToQuotaStorageType(FileSystemType type) { 204 switch (type) { 205 case kFileSystemTypeTemporary: 206 return quota::kStorageTypeTemporary; 207 case kFileSystemTypePersistent: 208 return quota::kStorageTypePersistent; 209 case kFileSystemTypeSyncable: 210 case kFileSystemTypeSyncableForInternalSync: 211 return quota::kStorageTypeSyncable; 212 case kFileSystemTypePluginPrivate: 213 return quota::kStorageTypeQuotaNotManaged; 214 default: 215 return quota::kStorageTypeUnknown; 216 } 217 } 218 219 std::string GetFileSystemTypeString(FileSystemType type) { 220 switch (type) { 221 case kFileSystemTypeTemporary: 222 return "Temporary"; 223 case kFileSystemTypePersistent: 224 return "Persistent"; 225 case kFileSystemTypeIsolated: 226 return "Isolated"; 227 case kFileSystemTypeExternal: 228 return "External"; 229 case kFileSystemTypeTest: 230 return "Test"; 231 case kFileSystemTypeNativeLocal: 232 return "NativeLocal"; 233 case kFileSystemTypeRestrictedNativeLocal: 234 return "RestrictedNativeLocal"; 235 case kFileSystemTypeDragged: 236 return "Dragged"; 237 case kFileSystemTypeNativeMedia: 238 return "NativeMedia"; 239 case kFileSystemTypeDeviceMedia: 240 return "DeviceMedia"; 241 case kFileSystemTypePicasa: 242 return "Picasa"; 243 case kFileSystemTypeItunes: 244 return "Itunes"; 245 case kFileSystemTypeIphoto: 246 return "Iphoto"; 247 case kFileSystemTypeDrive: 248 return "Drive"; 249 case kFileSystemTypeSyncable: 250 case kFileSystemTypeSyncableForInternalSync: 251 return "Syncable"; 252 case kFileSystemTypeNativeForPlatformApp: 253 return "NativeForPlatformApp"; 254 case kFileSystemTypeForTransientFile: 255 return "TransientFile"; 256 case kFileSystemTypePluginPrivate: 257 return "PluginPrivate"; 258 case kFileSystemInternalTypeEnumStart: 259 case kFileSystemInternalTypeEnumEnd: 260 NOTREACHED(); 261 // Fall through. 262 case kFileSystemTypeUnknown: 263 return "Unknown"; 264 } 265 NOTREACHED(); 266 return std::string(); 267 } 268 269 std::string FilePathToString(const base::FilePath& file_path) { 270 #if defined(OS_WIN) 271 return UTF16ToUTF8(file_path.value()); 272 #elif defined(OS_POSIX) 273 return file_path.value(); 274 #endif 275 } 276 277 base::FilePath StringToFilePath(const std::string& file_path_string) { 278 #if defined(OS_WIN) 279 return base::FilePath(UTF8ToUTF16(file_path_string)); 280 #elif defined(OS_POSIX) 281 return base::FilePath(file_path_string); 282 #endif 283 } 284 285 blink::WebFileError PlatformFileErrorToWebFileError( 286 base::PlatformFileError error_code) { 287 switch (error_code) { 288 case base::PLATFORM_FILE_ERROR_NOT_FOUND: 289 return blink::WebFileErrorNotFound; 290 case base::PLATFORM_FILE_ERROR_INVALID_OPERATION: 291 case base::PLATFORM_FILE_ERROR_EXISTS: 292 case base::PLATFORM_FILE_ERROR_NOT_EMPTY: 293 return blink::WebFileErrorInvalidModification; 294 case base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY: 295 case base::PLATFORM_FILE_ERROR_NOT_A_FILE: 296 return blink::WebFileErrorTypeMismatch; 297 case base::PLATFORM_FILE_ERROR_ACCESS_DENIED: 298 return blink::WebFileErrorNoModificationAllowed; 299 case base::PLATFORM_FILE_ERROR_FAILED: 300 return blink::WebFileErrorInvalidState; 301 case base::PLATFORM_FILE_ERROR_ABORT: 302 return blink::WebFileErrorAbort; 303 case base::PLATFORM_FILE_ERROR_SECURITY: 304 return blink::WebFileErrorSecurity; 305 case base::PLATFORM_FILE_ERROR_NO_SPACE: 306 return blink::WebFileErrorQuotaExceeded; 307 case base::PLATFORM_FILE_ERROR_INVALID_URL: 308 return blink::WebFileErrorEncoding; 309 default: 310 return blink::WebFileErrorInvalidModification; 311 } 312 } 313 314 bool GetFileSystemPublicType( 315 const std::string type_string, 316 blink::WebFileSystemType* type) { 317 DCHECK(type); 318 if (type_string == "Temporary") { 319 *type = blink::WebFileSystemTypeTemporary; 320 return true; 321 } 322 if (type_string == "Persistent") { 323 *type = blink::WebFileSystemTypePersistent; 324 return true; 325 } 326 if (type_string == "Isolated") { 327 *type = blink::WebFileSystemTypeIsolated; 328 return true; 329 } 330 if (type_string == "External") { 331 *type = blink::WebFileSystemTypeExternal; 332 return true; 333 } 334 NOTREACHED(); 335 return false; 336 } 337 338 std::string GetIsolatedFileSystemName(const GURL& origin_url, 339 const std::string& filesystem_id) { 340 std::string name(fileapi::GetFileSystemName( 341 origin_url, fileapi::kFileSystemTypeIsolated)); 342 name.append("_"); 343 name.append(filesystem_id); 344 return name; 345 } 346 347 bool CrackIsolatedFileSystemName(const std::string& filesystem_name, 348 std::string* filesystem_id) { 349 DCHECK(filesystem_id); 350 351 // |filesystem_name| is of the form {origin}:isolated_{filesystem_id}. 352 std::string start_token(":"); 353 start_token = start_token.append( 354 GetFileSystemTypeString(kFileSystemTypeIsolated)).append("_"); 355 // WebKit uses different case in its constant for isolated file system 356 // names, so we do a case insensitive compare by converting both strings 357 // to uppercase. 358 // TODO(benwells): Remove this when WebKit uses the same constant. 359 start_token = StringToUpperASCII(start_token); 360 std::string filesystem_name_upper = StringToUpperASCII(filesystem_name); 361 size_t pos = filesystem_name_upper.find(start_token); 362 if (pos == std::string::npos) 363 return false; 364 if (pos == 0) 365 return false; 366 367 *filesystem_id = filesystem_name.substr(pos + start_token.length(), 368 std::string::npos); 369 if (filesystem_id->empty()) 370 return false; 371 372 return true; 373 } 374 375 bool ValidateIsolatedFileSystemId(const std::string& filesystem_id) { 376 const size_t kExpectedFileSystemIdSize = 32; 377 if (filesystem_id.size() != kExpectedFileSystemIdSize) 378 return false; 379 const std::string kExpectedChars("ABCDEF0123456789"); 380 return ContainsOnlyChars(filesystem_id, kExpectedChars); 381 } 382 383 std::string GetIsolatedFileSystemRootURIString( 384 const GURL& origin_url, 385 const std::string& filesystem_id, 386 const std::string& optional_root_name) { 387 std::string root = GetFileSystemRootURI(origin_url, 388 kFileSystemTypeIsolated).spec(); 389 if (base::FilePath::FromUTF8Unsafe(filesystem_id).ReferencesParent()) 390 return std::string(); 391 root.append(filesystem_id); 392 root.append("/"); 393 if (!optional_root_name.empty()) { 394 if (base::FilePath::FromUTF8Unsafe(optional_root_name).ReferencesParent()) 395 return std::string(); 396 root.append(optional_root_name); 397 root.append("/"); 398 } 399 return root; 400 } 401 402 std::string GetExternalFileSystemRootURIString( 403 const GURL& origin_url, 404 const std::string& mount_name) { 405 std::string root = GetFileSystemRootURI(origin_url, 406 kFileSystemTypeExternal).spec(); 407 if (base::FilePath::FromUTF8Unsafe(mount_name).ReferencesParent()) 408 return std::string(); 409 root.append(mount_name); 410 root.append("/"); 411 return root; 412 } 413 414 base::PlatformFileError NetErrorToPlatformFileError(int error) { 415 switch (error) { 416 case net::OK: 417 return base::PLATFORM_FILE_OK; 418 case net::ERR_ADDRESS_IN_USE: 419 return base::PLATFORM_FILE_ERROR_IN_USE; 420 case net::ERR_FILE_EXISTS: 421 return base::PLATFORM_FILE_ERROR_EXISTS; 422 case net::ERR_FILE_NOT_FOUND: 423 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 424 case net::ERR_ACCESS_DENIED: 425 return base::PLATFORM_FILE_ERROR_ACCESS_DENIED; 426 case net::ERR_TOO_MANY_SOCKET_STREAMS: 427 return base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED; 428 case net::ERR_OUT_OF_MEMORY: 429 return base::PLATFORM_FILE_ERROR_NO_MEMORY; 430 case net::ERR_FILE_NO_SPACE: 431 return base::PLATFORM_FILE_ERROR_NO_SPACE; 432 case net::ERR_INVALID_ARGUMENT: 433 case net::ERR_INVALID_HANDLE: 434 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; 435 case net::ERR_ABORTED: 436 case net::ERR_CONNECTION_ABORTED: 437 return base::PLATFORM_FILE_ERROR_ABORT; 438 case net::ERR_ADDRESS_INVALID: 439 case net::ERR_INVALID_URL: 440 return base::PLATFORM_FILE_ERROR_INVALID_URL; 441 default: 442 return base::PLATFORM_FILE_ERROR_FAILED; 443 } 444 } 445 446 #if defined(OS_CHROMEOS) 447 FileSystemInfo GetFileSystemInfoForChromeOS(const GURL& origin_url) { 448 FileSystemType mount_type = fileapi::kFileSystemTypeExternal; 449 return FileSystemInfo(fileapi::GetFileSystemName(origin_url, mount_type), 450 fileapi::GetFileSystemRootURI(origin_url, mount_type), 451 mount_type); 452 } 453 #endif 454 455 } // namespace fileapi 456