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/escape.h"
     15 #include "net/base/net_errors.h"
     16 #include "url/gurl.h"
     17 #include "webkit/common/database/database_identifier.h"
     18 
     19 namespace fileapi {
     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 =
    241       webkit_database::GetIdentifierFromOrigin(origin_url);
    242   std::string type_string = GetFileSystemTypeString(type);
    243   DCHECK(!type_string.empty());
    244   return origin_identifier + ":" + type_string;
    245 }
    246 
    247 FileSystemType QuotaStorageTypeToFileSystemType(
    248     quota::StorageType storage_type) {
    249   switch (storage_type) {
    250     case quota::kStorageTypeTemporary:
    251       return kFileSystemTypeTemporary;
    252     case quota::kStorageTypePersistent:
    253       return kFileSystemTypePersistent;
    254     case quota::kStorageTypeSyncable:
    255       return kFileSystemTypeSyncable;
    256     case quota::kStorageTypeQuotaNotManaged:
    257     case quota::kStorageTypeUnknown:
    258       return kFileSystemTypeUnknown;
    259   }
    260   return kFileSystemTypeUnknown;
    261 }
    262 
    263 quota::StorageType FileSystemTypeToQuotaStorageType(FileSystemType type) {
    264   switch (type) {
    265     case kFileSystemTypeTemporary:
    266       return quota::kStorageTypeTemporary;
    267     case kFileSystemTypePersistent:
    268       return quota::kStorageTypePersistent;
    269     case kFileSystemTypeSyncable:
    270     case kFileSystemTypeSyncableForInternalSync:
    271       return quota::kStorageTypeSyncable;
    272     case kFileSystemTypePluginPrivate:
    273       return quota::kStorageTypeQuotaNotManaged;
    274     default:
    275       return quota::kStorageTypeUnknown;
    276   }
    277 }
    278 
    279 std::string GetFileSystemTypeString(FileSystemType type) {
    280   switch (type) {
    281     case kFileSystemTypeTemporary:
    282       return "Temporary";
    283     case kFileSystemTypePersistent:
    284       return "Persistent";
    285     case kFileSystemTypeIsolated:
    286       return "Isolated";
    287     case kFileSystemTypeExternal:
    288       return "External";
    289     case kFileSystemTypeTest:
    290       return "Test";
    291     case kFileSystemTypeNativeLocal:
    292       return "NativeLocal";
    293     case kFileSystemTypeRestrictedNativeLocal:
    294       return "RestrictedNativeLocal";
    295     case kFileSystemTypeDragged:
    296       return "Dragged";
    297     case kFileSystemTypeNativeMedia:
    298       return "NativeMedia";
    299     case kFileSystemTypeDeviceMedia:
    300       return "DeviceMedia";
    301     case kFileSystemTypePicasa:
    302       return "Picasa";
    303     case kFileSystemTypeItunes:
    304       return "Itunes";
    305     case kFileSystemTypeIphoto:
    306       return "Iphoto";
    307     case kFileSystemTypeDrive:
    308       return "Drive";
    309     case kFileSystemTypeSyncable:
    310     case kFileSystemTypeSyncableForInternalSync:
    311       return "Syncable";
    312     case kFileSystemTypeNativeForPlatformApp:
    313       return "NativeForPlatformApp";
    314     case kFileSystemTypeForTransientFile:
    315       return "TransientFile";
    316     case kFileSystemTypePluginPrivate:
    317       return "PluginPrivate";
    318     case kFileSystemTypeCloudDevice:
    319       return "CloudDevice";
    320     case kFileSystemTypeProvided:
    321       return "Provided";
    322     case kFileSystemTypeDeviceMediaAsFileStorage:
    323       return "DeviceMediaStorage";
    324     case kFileSystemInternalTypeEnumStart:
    325     case kFileSystemInternalTypeEnumEnd:
    326       NOTREACHED();
    327       // Fall through.
    328     case kFileSystemTypeUnknown:
    329       return "Unknown";
    330   }
    331   NOTREACHED();
    332   return std::string();
    333 }
    334 
    335 std::string FilePathToString(const base::FilePath& file_path) {
    336 #if defined(OS_WIN)
    337   return base::UTF16ToUTF8(file_path.value());
    338 #elif defined(OS_POSIX)
    339   return file_path.value();
    340 #endif
    341 }
    342 
    343 base::FilePath StringToFilePath(const std::string& file_path_string) {
    344 #if defined(OS_WIN)
    345   return base::FilePath(base::UTF8ToUTF16(file_path_string));
    346 #elif defined(OS_POSIX)
    347   return base::FilePath(file_path_string);
    348 #endif
    349 }
    350 
    351 blink::WebFileError FileErrorToWebFileError(
    352     base::File::Error error_code) {
    353   switch (error_code) {
    354     case base::File::FILE_ERROR_NOT_FOUND:
    355       return blink::WebFileErrorNotFound;
    356     case base::File::FILE_ERROR_INVALID_OPERATION:
    357     case base::File::FILE_ERROR_EXISTS:
    358     case base::File::FILE_ERROR_NOT_EMPTY:
    359       return blink::WebFileErrorInvalidModification;
    360     case base::File::FILE_ERROR_NOT_A_DIRECTORY:
    361     case base::File::FILE_ERROR_NOT_A_FILE:
    362       return blink::WebFileErrorTypeMismatch;
    363     case base::File::FILE_ERROR_ACCESS_DENIED:
    364       return blink::WebFileErrorNoModificationAllowed;
    365     case base::File::FILE_ERROR_FAILED:
    366       return blink::WebFileErrorInvalidState;
    367     case base::File::FILE_ERROR_ABORT:
    368       return blink::WebFileErrorAbort;
    369     case base::File::FILE_ERROR_SECURITY:
    370       return blink::WebFileErrorSecurity;
    371     case base::File::FILE_ERROR_NO_SPACE:
    372       return blink::WebFileErrorQuotaExceeded;
    373     case base::File::FILE_ERROR_INVALID_URL:
    374       return blink::WebFileErrorEncoding;
    375     default:
    376       return blink::WebFileErrorInvalidModification;
    377   }
    378 }
    379 
    380 bool GetFileSystemPublicType(
    381     const std::string type_string,
    382     blink::WebFileSystemType* type) {
    383   DCHECK(type);
    384   if (type_string == "Temporary") {
    385     *type = blink::WebFileSystemTypeTemporary;
    386     return true;
    387   }
    388   if (type_string == "Persistent") {
    389     *type = blink::WebFileSystemTypePersistent;
    390     return true;
    391   }
    392   if (type_string == "Isolated") {
    393     *type = blink::WebFileSystemTypeIsolated;
    394     return true;
    395   }
    396   if (type_string == "External") {
    397     *type = blink::WebFileSystemTypeExternal;
    398     return true;
    399   }
    400   NOTREACHED();
    401   return false;
    402 }
    403 
    404 std::string GetIsolatedFileSystemName(const GURL& origin_url,
    405                                       const std::string& filesystem_id) {
    406   std::string name(fileapi::GetFileSystemName(
    407       origin_url, fileapi::kFileSystemTypeIsolated));
    408   name.append("_");
    409   name.append(filesystem_id);
    410   return name;
    411 }
    412 
    413 bool CrackIsolatedFileSystemName(const std::string& filesystem_name,
    414                                  std::string* filesystem_id) {
    415   DCHECK(filesystem_id);
    416 
    417   // |filesystem_name| is of the form {origin}:isolated_{filesystem_id}.
    418   std::string start_token(":");
    419   start_token = start_token.append(
    420       GetFileSystemTypeString(kFileSystemTypeIsolated)).append("_");
    421   // WebKit uses different case in its constant for isolated file system
    422   // names, so we do a case insensitive compare by converting both strings
    423   // to uppercase.
    424   // TODO(benwells): Remove this when WebKit uses the same constant.
    425   start_token = StringToUpperASCII(start_token);
    426   std::string filesystem_name_upper = StringToUpperASCII(filesystem_name);
    427   size_t pos = filesystem_name_upper.find(start_token);
    428   if (pos == std::string::npos)
    429     return false;
    430   if (pos == 0)
    431     return false;
    432 
    433   *filesystem_id = filesystem_name.substr(pos + start_token.length(),
    434                                           std::string::npos);
    435   if (filesystem_id->empty())
    436     return false;
    437 
    438   return true;
    439 }
    440 
    441 bool ValidateIsolatedFileSystemId(const std::string& filesystem_id) {
    442   const size_t kExpectedFileSystemIdSize = 32;
    443   if (filesystem_id.size() != kExpectedFileSystemIdSize)
    444     return false;
    445   const std::string kExpectedChars("ABCDEF0123456789");
    446   return base::ContainsOnlyChars(filesystem_id, kExpectedChars);
    447 }
    448 
    449 std::string GetIsolatedFileSystemRootURIString(
    450     const GURL& origin_url,
    451     const std::string& filesystem_id,
    452     const std::string& optional_root_name) {
    453   std::string root = GetFileSystemRootURI(origin_url,
    454                                           kFileSystemTypeIsolated).spec();
    455   if (base::FilePath::FromUTF8Unsafe(filesystem_id).ReferencesParent())
    456     return std::string();
    457   root.append(net::EscapePath(filesystem_id));
    458   root.append("/");
    459   if (!optional_root_name.empty()) {
    460     if (base::FilePath::FromUTF8Unsafe(optional_root_name).ReferencesParent())
    461       return std::string();
    462     root.append(net::EscapePath(optional_root_name));
    463     root.append("/");
    464   }
    465   return root;
    466 }
    467 
    468 std::string GetExternalFileSystemRootURIString(
    469     const GURL& origin_url,
    470     const std::string& mount_name) {
    471   std::string root = GetFileSystemRootURI(origin_url,
    472                                           kFileSystemTypeExternal).spec();
    473   if (base::FilePath::FromUTF8Unsafe(mount_name).ReferencesParent())
    474     return std::string();
    475   root.append(net::EscapePath(mount_name));
    476   root.append("/");
    477   return root;
    478 }
    479 
    480 base::File::Error NetErrorToFileError(int error) {
    481   switch (error) {
    482     case net::OK:
    483       return base::File::FILE_OK;
    484     case net::ERR_ADDRESS_IN_USE:
    485       return base::File::FILE_ERROR_IN_USE;
    486     case net::ERR_FILE_EXISTS:
    487       return base::File::FILE_ERROR_EXISTS;
    488     case net::ERR_FILE_NOT_FOUND:
    489       return base::File::FILE_ERROR_NOT_FOUND;
    490     case net::ERR_ACCESS_DENIED:
    491       return base::File::FILE_ERROR_ACCESS_DENIED;
    492     case net::ERR_TOO_MANY_SOCKET_STREAMS:
    493       return base::File::FILE_ERROR_TOO_MANY_OPENED;
    494     case net::ERR_OUT_OF_MEMORY:
    495       return base::File::FILE_ERROR_NO_MEMORY;
    496     case net::ERR_FILE_NO_SPACE:
    497       return base::File::FILE_ERROR_NO_SPACE;
    498     case net::ERR_INVALID_ARGUMENT:
    499     case net::ERR_INVALID_HANDLE:
    500       return base::File::FILE_ERROR_INVALID_OPERATION;
    501     case net::ERR_ABORTED:
    502     case net::ERR_CONNECTION_ABORTED:
    503       return base::File::FILE_ERROR_ABORT;
    504     case net::ERR_ADDRESS_INVALID:
    505     case net::ERR_INVALID_URL:
    506       return base::File::FILE_ERROR_INVALID_URL;
    507     default:
    508       return base::File::FILE_ERROR_FAILED;
    509   }
    510 }
    511 
    512 }  // namespace fileapi
    513