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