Home | History | Annotate | Download | only in fileapi
      1 // Copyright (c) 2013 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/browser/fileapi/external_mount_points.h"
      6 
      7 #include "base/files/file_path.h"
      8 #include "base/lazy_instance.h"
      9 #include "base/path_service.h"
     10 #include "base/stl_util.h"
     11 #include "webkit/browser/fileapi/file_system_url.h"
     12 
     13 namespace {
     14 
     15 // Normalizes file path so it has normalized separators and ends with exactly
     16 // one separator. Paths have to be normalized this way for use in
     17 // GetVirtualPath method. Separators cannot be completely stripped, or
     18 // GetVirtualPath could not working in some edge cases.
     19 // For example, /a/b/c(1)/d would be erroneously resolved as c/d if the
     20 // following mount points were registered: "/a/b/c", "/a/b/c(1)". (Note:
     21 // "/a/b/c" < "/a/b/c(1)" < "/a/b/c/").
     22 base::FilePath NormalizeFilePath(const base::FilePath& path) {
     23   if (path.empty())
     24     return path;
     25 
     26   base::FilePath::StringType path_str = path.StripTrailingSeparators().value();
     27   if (!base::FilePath::IsSeparator(path_str[path_str.length() - 1]))
     28     path_str.append(FILE_PATH_LITERAL("/"));
     29 
     30   return base::FilePath(path_str).NormalizePathSeparators();
     31 }
     32 
     33 bool IsOverlappingMountPathForbidden(fileapi::FileSystemType type) {
     34   return type != fileapi::kFileSystemTypeNativeMedia &&
     35       type != fileapi::kFileSystemTypeDeviceMedia;
     36 }
     37 
     38 // Wrapper around ref-counted ExternalMountPoints that will be used to lazily
     39 // create and initialize LazyInstance system ExternalMountPoints.
     40 class SystemMountPointsLazyWrapper {
     41  public:
     42   SystemMountPointsLazyWrapper()
     43       : system_mount_points_(fileapi::ExternalMountPoints::CreateRefCounted()) {
     44   }
     45 
     46   ~SystemMountPointsLazyWrapper() {}
     47 
     48   fileapi::ExternalMountPoints* get() {
     49     return system_mount_points_.get();
     50   }
     51 
     52  private:
     53   scoped_refptr<fileapi::ExternalMountPoints> system_mount_points_;
     54 };
     55 
     56 base::LazyInstance<SystemMountPointsLazyWrapper>::Leaky
     57     g_external_mount_points = LAZY_INSTANCE_INITIALIZER;
     58 
     59 }  // namespace
     60 
     61 namespace fileapi {
     62 
     63 class ExternalMountPoints::Instance {
     64  public:
     65   Instance(FileSystemType type,
     66            const base::FilePath& path,
     67            const FileSystemMountOption& mount_option)
     68       : type_(type),
     69         path_(path.StripTrailingSeparators()),
     70         mount_option_(mount_option) {}
     71   ~Instance() {}
     72 
     73   FileSystemType type() const { return type_; }
     74   const base::FilePath& path() const { return path_; }
     75   const FileSystemMountOption& mount_option() const { return mount_option_; }
     76 
     77  private:
     78   const FileSystemType type_;
     79   const base::FilePath path_;
     80   const FileSystemMountOption mount_option_;
     81 
     82   DISALLOW_COPY_AND_ASSIGN(Instance);
     83 };
     84 
     85 //--------------------------------------------------------------------------
     86 
     87 // static
     88 ExternalMountPoints* ExternalMountPoints::GetSystemInstance() {
     89   return g_external_mount_points.Pointer()->get();
     90 }
     91 
     92 // static
     93 scoped_refptr<ExternalMountPoints> ExternalMountPoints::CreateRefCounted() {
     94   return new ExternalMountPoints();
     95 }
     96 
     97 bool ExternalMountPoints::RegisterFileSystem(
     98     const std::string& mount_name,
     99     FileSystemType type,
    100     const FileSystemMountOption& mount_option,
    101     const base::FilePath& path_in) {
    102   // COPY_SYNC_OPTION_SYNC is only applicable to native local file system.
    103   DCHECK(type == kFileSystemTypeNativeLocal ||
    104          mount_option.copy_sync_option() != COPY_SYNC_OPTION_SYNC);
    105 
    106   base::AutoLock locker(lock_);
    107 
    108   base::FilePath path = NormalizeFilePath(path_in);
    109   if (!ValidateNewMountPoint(mount_name, type, path))
    110     return false;
    111 
    112   instance_map_[mount_name] = new Instance(type, path, mount_option);
    113   if (!path.empty() && IsOverlappingMountPathForbidden(type))
    114     path_to_name_map_.insert(std::make_pair(path, mount_name));
    115   return true;
    116 }
    117 
    118 bool ExternalMountPoints::HandlesFileSystemMountType(
    119     FileSystemType type) const {
    120   return type == kFileSystemTypeExternal ||
    121          type == kFileSystemTypeNativeForPlatformApp;
    122 }
    123 
    124 bool ExternalMountPoints::RevokeFileSystem(const std::string& mount_name) {
    125   base::AutoLock locker(lock_);
    126   NameToInstance::iterator found = instance_map_.find(mount_name);
    127   if (found == instance_map_.end())
    128     return false;
    129   Instance* instance = found->second;
    130   if (IsOverlappingMountPathForbidden(instance->type()))
    131     path_to_name_map_.erase(NormalizeFilePath(instance->path()));
    132   delete found->second;
    133   instance_map_.erase(found);
    134   return true;
    135 }
    136 
    137 bool ExternalMountPoints::GetRegisteredPath(
    138     const std::string& filesystem_id, base::FilePath* path) const {
    139   DCHECK(path);
    140   base::AutoLock locker(lock_);
    141   NameToInstance::const_iterator found = instance_map_.find(filesystem_id);
    142   if (found == instance_map_.end())
    143     return false;
    144   *path = found->second->path();
    145   return true;
    146 }
    147 
    148 bool ExternalMountPoints::CrackVirtualPath(
    149     const base::FilePath& virtual_path,
    150     std::string* mount_name,
    151     FileSystemType* type,
    152     std::string* cracked_id,
    153     base::FilePath* path,
    154     FileSystemMountOption* mount_option) const {
    155   DCHECK(mount_name);
    156   DCHECK(path);
    157 
    158   // The path should not contain any '..' references.
    159   if (virtual_path.ReferencesParent())
    160     return false;
    161 
    162   // The virtual_path should comprise of <mount_name> and <relative_path> parts.
    163   std::vector<base::FilePath::StringType> components;
    164   virtual_path.GetComponents(&components);
    165   if (components.size() < 1)
    166     return false;
    167 
    168   std::vector<base::FilePath::StringType>::iterator component_iter =
    169       components.begin();
    170   std::string maybe_mount_name =
    171       base::FilePath(*component_iter++).MaybeAsASCII();
    172   if (maybe_mount_name.empty())
    173     return false;
    174 
    175   base::FilePath cracked_path;
    176   {
    177     base::AutoLock locker(lock_);
    178     NameToInstance::const_iterator found_instance =
    179         instance_map_.find(maybe_mount_name);
    180     if (found_instance == instance_map_.end())
    181       return false;
    182 
    183     *mount_name = maybe_mount_name;
    184     const Instance* instance = found_instance->second;
    185     if (type)
    186       *type = instance->type();
    187     cracked_path = instance->path();
    188     *mount_option = instance->mount_option();
    189   }
    190 
    191   for (; component_iter != components.end(); ++component_iter)
    192     cracked_path = cracked_path.Append(*component_iter);
    193   *path = cracked_path;
    194   return true;
    195 }
    196 
    197 FileSystemURL ExternalMountPoints::CrackURL(const GURL& url) const {
    198   FileSystemURL filesystem_url = FileSystemURL(url);
    199   if (!filesystem_url.is_valid())
    200     return FileSystemURL();
    201   return CrackFileSystemURL(filesystem_url);
    202 }
    203 
    204 FileSystemURL ExternalMountPoints::CreateCrackedFileSystemURL(
    205     const GURL& origin,
    206     FileSystemType type,
    207     const base::FilePath& path) const {
    208   return CrackFileSystemURL(FileSystemURL(origin, type, path));
    209 }
    210 
    211 void ExternalMountPoints::AddMountPointInfosTo(
    212     std::vector<MountPointInfo>* mount_points) const {
    213   base::AutoLock locker(lock_);
    214   DCHECK(mount_points);
    215   for (NameToInstance::const_iterator iter = instance_map_.begin();
    216        iter != instance_map_.end(); ++iter) {
    217     mount_points->push_back(MountPointInfo(iter->first, iter->second->path()));
    218   }
    219 }
    220 
    221 bool ExternalMountPoints::GetVirtualPath(const base::FilePath& path_in,
    222                                          base::FilePath* virtual_path) const {
    223   DCHECK(virtual_path);
    224 
    225   base::AutoLock locker(lock_);
    226 
    227   base::FilePath path = NormalizeFilePath(path_in);
    228   std::map<base::FilePath, std::string>::const_reverse_iterator iter(
    229       path_to_name_map_.upper_bound(path));
    230   if (iter == path_to_name_map_.rend())
    231     return false;
    232 
    233   *virtual_path = CreateVirtualRootPath(iter->second);
    234   if (iter->first == path)
    235     return true;
    236   return iter->first.AppendRelativePath(path, virtual_path);
    237 }
    238 
    239 base::FilePath ExternalMountPoints::CreateVirtualRootPath(
    240     const std::string& mount_name) const {
    241   return base::FilePath().AppendASCII(mount_name);
    242 }
    243 
    244 FileSystemURL ExternalMountPoints::CreateExternalFileSystemURL(
    245     const GURL& origin,
    246     const std::string& mount_name,
    247     const base::FilePath& path) const {
    248   return CreateCrackedFileSystemURL(
    249       origin,
    250       fileapi::kFileSystemTypeExternal,
    251       // Avoid using FilePath::Append as path may be an absolute path.
    252       base::FilePath(
    253           CreateVirtualRootPath(mount_name).value() +
    254           base::FilePath::kSeparators[0] + path.value()));
    255 }
    256 
    257 void ExternalMountPoints::RevokeAllFileSystems() {
    258   NameToInstance instance_map_copy;
    259   {
    260     base::AutoLock locker(lock_);
    261     instance_map_copy = instance_map_;
    262     instance_map_.clear();
    263     path_to_name_map_.clear();
    264   }
    265   STLDeleteContainerPairSecondPointers(instance_map_copy.begin(),
    266                                        instance_map_copy.end());
    267 }
    268 
    269 ExternalMountPoints::ExternalMountPoints() {}
    270 
    271 ExternalMountPoints::~ExternalMountPoints() {
    272   STLDeleteContainerPairSecondPointers(instance_map_.begin(),
    273                                        instance_map_.end());
    274 }
    275 
    276 FileSystemURL ExternalMountPoints::CrackFileSystemURL(
    277     const FileSystemURL& url) const {
    278   if (!HandlesFileSystemMountType(url.type()))
    279     return FileSystemURL();
    280 
    281   base::FilePath virtual_path = url.path();
    282   if (url.type() == kFileSystemTypeNativeForPlatformApp) {
    283 #if defined(OS_CHROMEOS)
    284     // On Chrome OS, find a mount point and virtual path for the external fs.
    285     if (!GetVirtualPath(url.path(), &virtual_path))
    286       return FileSystemURL();
    287 #else
    288     // On other OS, it is simply a native local path.
    289     return FileSystemURL(
    290         url.origin(), url.mount_type(), url.virtual_path(),
    291         url.mount_filesystem_id(), kFileSystemTypeNativeLocal,
    292         url.path(), url.filesystem_id(), url.mount_option());
    293 #endif
    294   }
    295 
    296   std::string mount_name;
    297   FileSystemType cracked_type;
    298   std::string cracked_id;
    299   base::FilePath cracked_path;
    300   FileSystemMountOption cracked_mount_option;
    301 
    302   if (!CrackVirtualPath(virtual_path, &mount_name, &cracked_type,
    303                         &cracked_id, &cracked_path, &cracked_mount_option)) {
    304     return FileSystemURL();
    305   }
    306 
    307   return FileSystemURL(
    308       url.origin(), url.mount_type(), url.virtual_path(),
    309       !url.filesystem_id().empty() ? url.filesystem_id() : mount_name,
    310       cracked_type, cracked_path,
    311       cracked_id.empty() ? mount_name : cracked_id, cracked_mount_option);
    312 }
    313 
    314 bool ExternalMountPoints::ValidateNewMountPoint(const std::string& mount_name,
    315                                                 FileSystemType type,
    316                                                 const base::FilePath& path) {
    317   lock_.AssertAcquired();
    318 
    319   // Mount name must not be empty.
    320   if (mount_name.empty())
    321     return false;
    322 
    323   // Verify there is no registered mount point with the same name.
    324   NameToInstance::iterator found = instance_map_.find(mount_name);
    325   if (found != instance_map_.end())
    326     return false;
    327 
    328   // Allow empty paths.
    329   if (path.empty())
    330     return true;
    331 
    332   // Verify path is legal.
    333   if (path.ReferencesParent() || !path.IsAbsolute())
    334     return false;
    335 
    336   if (IsOverlappingMountPathForbidden(type)) {
    337     // Check there the new path does not overlap with one of the existing ones.
    338     std::map<base::FilePath, std::string>::reverse_iterator potential_parent(
    339         path_to_name_map_.upper_bound(path));
    340     if (potential_parent != path_to_name_map_.rend()) {
    341       if (potential_parent->first == path ||
    342           potential_parent->first.IsParent(path)) {
    343         return false;
    344       }
    345     }
    346 
    347     std::map<base::FilePath, std::string>::iterator potential_child =
    348         path_to_name_map_.upper_bound(path);
    349     if (potential_child != path_to_name_map_.end()) {
    350       if (potential_child->first == path ||
    351           path.IsParent(potential_child->first)) {
    352         return false;
    353       }
    354     }
    355   }
    356 
    357   return true;
    358 }
    359 
    360 }  // namespace fileapi
    361