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