Home | History | Annotate | Download | only in fileapi
      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