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 "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