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