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