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