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/browser/fileapi/isolated_context.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/files/file_path.h"
      9 #include "base/logging.h"
     10 #include "base/rand_util.h"
     11 #include "base/stl_util.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/stringprintf.h"
     15 #include "webkit/browser/fileapi/file_system_url.h"
     16 
     17 namespace fileapi {
     18 
     19 namespace {
     20 
     21 base::FilePath::StringType GetRegisterNameForPath(const base::FilePath& path) {
     22   // If it's not a root path simply return a base name.
     23   if (path.DirName() != path)
     24     return path.BaseName().value();
     25 
     26 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
     27   base::FilePath::StringType name;
     28   for (size_t i = 0;
     29        i < path.value().size() && !base::FilePath::IsSeparator(path.value()[i]);
     30        ++i) {
     31     if (path.value()[i] == L':') {
     32       name.append(L"_drive");
     33       break;
     34     }
     35     name.append(1, path.value()[i]);
     36   }
     37   return name;
     38 #else
     39   return FILE_PATH_LITERAL("<root>");
     40 #endif
     41 }
     42 
     43 bool IsSinglePathIsolatedFileSystem(FileSystemType type) {
     44   switch (type) {
     45     // As of writing dragged file system is the only filesystem
     46     // which could have multiple top-level paths.
     47     case kFileSystemTypeDragged:
     48       return false;
     49 
     50     case kFileSystemTypeUnknown:
     51       NOTREACHED();
     52       return true;
     53 
     54     default:
     55       return true;
     56   }
     57   NOTREACHED();
     58   return true;
     59 }
     60 
     61 static base::LazyInstance<IsolatedContext>::Leaky g_isolated_context =
     62     LAZY_INSTANCE_INITIALIZER;
     63 
     64 }  // namespace
     65 
     66 IsolatedContext::FileInfoSet::FileInfoSet() {}
     67 IsolatedContext::FileInfoSet::~FileInfoSet() {}
     68 
     69 bool IsolatedContext::FileInfoSet::AddPath(
     70     const base::FilePath& path, std::string* registered_name) {
     71   // The given path should not contain any '..' and should be absolute.
     72   if (path.ReferencesParent() || !path.IsAbsolute())
     73     return false;
     74   base::FilePath::StringType name = GetRegisterNameForPath(path);
     75   std::string utf8name = base::FilePath(name).AsUTF8Unsafe();
     76   base::FilePath normalized_path = path.NormalizePathSeparators();
     77   bool inserted =
     78       fileset_.insert(MountPointInfo(utf8name, normalized_path)).second;
     79   if (!inserted) {
     80     int suffix = 1;
     81     std::string basepart =
     82         base::FilePath(name).RemoveExtension().AsUTF8Unsafe();
     83     std::string ext =
     84         base::FilePath(base::FilePath(name).Extension()).AsUTF8Unsafe();
     85     while (!inserted) {
     86       utf8name = base::StringPrintf("%s (%d)", basepart.c_str(), suffix++);
     87       if (!ext.empty())
     88         utf8name.append(ext);
     89       inserted =
     90           fileset_.insert(MountPointInfo(utf8name, normalized_path)).second;
     91     }
     92   }
     93   if (registered_name)
     94     *registered_name = utf8name;
     95   return true;
     96 }
     97 
     98 bool IsolatedContext::FileInfoSet::AddPathWithName(
     99     const base::FilePath& path, const std::string& name) {
    100   // The given path should not contain any '..' and should be absolute.
    101   if (path.ReferencesParent() || !path.IsAbsolute())
    102     return false;
    103   return fileset_.insert(
    104       MountPointInfo(name, path.NormalizePathSeparators())).second;
    105 }
    106 
    107 //--------------------------------------------------------------------------
    108 
    109 class IsolatedContext::Instance {
    110  public:
    111   enum PathType {
    112     PLATFORM_PATH,
    113     VIRTUAL_PATH
    114   };
    115 
    116   // For a single-path isolated file system, which could be registered by
    117   // IsolatedContext::RegisterFileSystemForPath() or
    118   // IsolatedContext::RegisterFileSystemForVirtualPath().
    119   // Most of isolated file system contexts should be of this type.
    120   Instance(FileSystemType type, const MountPointInfo& file_info,
    121            PathType path_type);
    122 
    123   // For a multi-paths isolated file system.  As of writing only file system
    124   // type which could have multi-paths is Dragged file system, and
    125   // could be registered by IsolatedContext::RegisterDraggedFileSystem().
    126   Instance(FileSystemType type, const std::set<MountPointInfo>& files);
    127 
    128   ~Instance();
    129 
    130   FileSystemType type() const { return type_; }
    131   const MountPointInfo& file_info() const { return file_info_; }
    132   const std::set<MountPointInfo>& files() const { return files_; }
    133   int ref_counts() const { return ref_counts_; }
    134 
    135   void AddRef() { ++ref_counts_; }
    136   void RemoveRef() { --ref_counts_; }
    137 
    138   bool ResolvePathForName(const std::string& name, base::FilePath* path) const;
    139 
    140   // Returns true if the instance is a single-path instance.
    141   bool IsSinglePathInstance() const;
    142 
    143  private:
    144   const FileSystemType type_;
    145 
    146   // For single-path instance.
    147   const MountPointInfo file_info_;
    148   const PathType path_type_;
    149 
    150   // For multiple-path instance (e.g. dragged file system).
    151   const std::set<MountPointInfo> files_;
    152 
    153   // Reference counts. Note that an isolated filesystem is created with ref==0
    154   // and will get deleted when the ref count reaches <=0.
    155   int ref_counts_;
    156 
    157   DISALLOW_COPY_AND_ASSIGN(Instance);
    158 };
    159 
    160 IsolatedContext::Instance::Instance(FileSystemType type,
    161                                     const MountPointInfo& file_info,
    162                                     Instance::PathType path_type)
    163     : type_(type),
    164       file_info_(file_info),
    165       path_type_(path_type),
    166       ref_counts_(0) {
    167   DCHECK(IsSinglePathIsolatedFileSystem(type_));
    168 }
    169 
    170 IsolatedContext::Instance::Instance(FileSystemType type,
    171                                     const std::set<MountPointInfo>& files)
    172     : type_(type),
    173       path_type_(PLATFORM_PATH),
    174       files_(files),
    175       ref_counts_(0) {
    176   DCHECK(!IsSinglePathIsolatedFileSystem(type_));
    177 }
    178 
    179 IsolatedContext::Instance::~Instance() {}
    180 
    181 bool IsolatedContext::Instance::ResolvePathForName(const std::string& name,
    182                                                    base::FilePath* path) const {
    183   if (IsSinglePathIsolatedFileSystem(type_)) {
    184     switch (path_type_) {
    185       case PLATFORM_PATH:
    186         *path = file_info_.path;
    187         break;
    188       case VIRTUAL_PATH:
    189         *path = base::FilePath();
    190         break;
    191       default:
    192         NOTREACHED();
    193     }
    194 
    195     return file_info_.name == name;
    196   }
    197   std::set<MountPointInfo>::const_iterator found = files_.find(
    198       MountPointInfo(name, base::FilePath()));
    199   if (found == files_.end())
    200     return false;
    201   *path = found->path;
    202   return true;
    203 }
    204 
    205 bool IsolatedContext::Instance::IsSinglePathInstance() const {
    206   return IsSinglePathIsolatedFileSystem(type_);
    207 }
    208 
    209 //--------------------------------------------------------------------------
    210 
    211 // static
    212 IsolatedContext* IsolatedContext::GetInstance() {
    213   return g_isolated_context.Pointer();
    214 }
    215 
    216 // static
    217 bool IsolatedContext::IsIsolatedType(FileSystemType type) {
    218   return type == kFileSystemTypeIsolated || type == kFileSystemTypeExternal;
    219 }
    220 
    221 std::string IsolatedContext::RegisterDraggedFileSystem(
    222     const FileInfoSet& files) {
    223   base::AutoLock locker(lock_);
    224   std::string filesystem_id = GetNewFileSystemId();
    225   instance_map_[filesystem_id] = new Instance(
    226       kFileSystemTypeDragged, files.fileset());
    227   return filesystem_id;
    228 }
    229 
    230 std::string IsolatedContext::RegisterFileSystemForPath(
    231     FileSystemType type,
    232     const base::FilePath& path_in,
    233     std::string* register_name) {
    234   base::FilePath path(path_in.NormalizePathSeparators());
    235   if (path.ReferencesParent() || !path.IsAbsolute())
    236     return std::string();
    237   std::string name;
    238   if (register_name && !register_name->empty()) {
    239     name = *register_name;
    240   } else {
    241     name = base::FilePath(GetRegisterNameForPath(path)).AsUTF8Unsafe();
    242     if (register_name)
    243       register_name->assign(name);
    244   }
    245 
    246   base::AutoLock locker(lock_);
    247   std::string filesystem_id = GetNewFileSystemId();
    248   instance_map_[filesystem_id] = new Instance(type, MountPointInfo(name, path),
    249                                               Instance::PLATFORM_PATH);
    250   path_to_id_map_[path].insert(filesystem_id);
    251   return filesystem_id;
    252 }
    253 
    254 std::string IsolatedContext::RegisterFileSystemForVirtualPath(
    255     FileSystemType type,
    256     const std::string& register_name,
    257     const base::FilePath& cracked_path_prefix) {
    258   base::AutoLock locker(lock_);
    259   base::FilePath path(cracked_path_prefix.NormalizePathSeparators());
    260   if (path.ReferencesParent())
    261     return std::string();
    262   std::string filesystem_id = GetNewFileSystemId();
    263   instance_map_[filesystem_id] = new Instance(
    264       type,
    265       MountPointInfo(register_name, cracked_path_prefix),
    266       Instance::VIRTUAL_PATH);
    267   path_to_id_map_[path].insert(filesystem_id);
    268   return filesystem_id;
    269 }
    270 
    271 bool IsolatedContext::HandlesFileSystemMountType(FileSystemType type) const {
    272   return type == kFileSystemTypeIsolated;
    273 }
    274 
    275 bool IsolatedContext::RevokeFileSystem(const std::string& filesystem_id) {
    276   base::AutoLock locker(lock_);
    277   return UnregisterFileSystem(filesystem_id);
    278 }
    279 
    280 bool IsolatedContext::GetRegisteredPath(
    281     const std::string& filesystem_id, base::FilePath* path) const {
    282   DCHECK(path);
    283   base::AutoLock locker(lock_);
    284   IDToInstance::const_iterator found = instance_map_.find(filesystem_id);
    285   if (found == instance_map_.end() || !found->second->IsSinglePathInstance())
    286     return false;
    287   *path = found->second->file_info().path;
    288   return true;
    289 }
    290 
    291 bool IsolatedContext::CrackVirtualPath(
    292     const base::FilePath& virtual_path,
    293     std::string* id_or_name,
    294     FileSystemType* type,
    295     base::FilePath* path,
    296     FileSystemMountOption* mount_option) const {
    297   DCHECK(id_or_name);
    298   DCHECK(path);
    299 
    300   // This should not contain any '..' references.
    301   if (virtual_path.ReferencesParent())
    302     return false;
    303 
    304   // Set the default mount option.
    305   *mount_option = FileSystemMountOption();
    306 
    307   // The virtual_path should comprise <id_or_name> and <relative_path> parts.
    308   std::vector<base::FilePath::StringType> components;
    309   virtual_path.GetComponents(&components);
    310   if (components.size() < 1)
    311     return false;
    312   std::vector<base::FilePath::StringType>::iterator component_iter =
    313       components.begin();
    314   std::string fsid = base::FilePath(*component_iter++).MaybeAsASCII();
    315   if (fsid.empty())
    316     return false;
    317 
    318   base::FilePath cracked_path;
    319   {
    320     base::AutoLock locker(lock_);
    321     IDToInstance::const_iterator found_instance = instance_map_.find(fsid);
    322     if (found_instance == instance_map_.end())
    323       return false;
    324     *id_or_name = fsid;
    325     const Instance* instance = found_instance->second;
    326     if (type)
    327       *type = instance->type();
    328 
    329     if (component_iter == components.end()) {
    330       // The virtual root case.
    331       path->clear();
    332       return true;
    333     }
    334 
    335     // *component_iter should be a name of the registered path.
    336     std::string name = base::FilePath(*component_iter++).AsUTF8Unsafe();
    337     if (!instance->ResolvePathForName(name, &cracked_path))
    338       return false;
    339   }
    340 
    341   for (; component_iter != components.end(); ++component_iter)
    342     cracked_path = cracked_path.Append(*component_iter);
    343   *path = cracked_path;
    344   return true;
    345 }
    346 
    347 FileSystemURL IsolatedContext::CrackURL(const GURL& url) const {
    348   FileSystemURL filesystem_url = FileSystemURL(url);
    349   if (!filesystem_url.is_valid())
    350     return FileSystemURL();
    351   return CrackFileSystemURL(filesystem_url);
    352 }
    353 
    354 FileSystemURL IsolatedContext::CreateCrackedFileSystemURL(
    355     const GURL& origin,
    356     FileSystemType type,
    357     const base::FilePath& path) const {
    358   return CrackFileSystemURL(FileSystemURL(origin, type, path));
    359 }
    360 
    361 void IsolatedContext::RevokeFileSystemByPath(const base::FilePath& path_in) {
    362   base::AutoLock locker(lock_);
    363   base::FilePath path(path_in.NormalizePathSeparators());
    364   PathToID::iterator ids_iter = path_to_id_map_.find(path);
    365   if (ids_iter == path_to_id_map_.end())
    366     return;
    367   std::set<std::string>& ids = ids_iter->second;
    368   for (std::set<std::string>::iterator iter = ids.begin();
    369        iter != ids.end(); ++iter) {
    370     IDToInstance::iterator found = instance_map_.find(*iter);
    371     if (found != instance_map_.end()) {
    372       delete found->second;
    373       instance_map_.erase(found);
    374     }
    375   }
    376   path_to_id_map_.erase(ids_iter);
    377 }
    378 
    379 void IsolatedContext::AddReference(const std::string& filesystem_id) {
    380   base::AutoLock locker(lock_);
    381   DCHECK(instance_map_.find(filesystem_id) != instance_map_.end());
    382   instance_map_[filesystem_id]->AddRef();
    383 }
    384 
    385 void IsolatedContext::RemoveReference(const std::string& filesystem_id) {
    386   base::AutoLock locker(lock_);
    387   // This could get called for non-existent filesystem if it has been
    388   // already deleted by RevokeFileSystemByPath.
    389   IDToInstance::iterator found = instance_map_.find(filesystem_id);
    390   if (found == instance_map_.end())
    391     return;
    392   Instance* instance = found->second;
    393   DCHECK_GT(instance->ref_counts(), 0);
    394   instance->RemoveRef();
    395   if (instance->ref_counts() == 0) {
    396     bool deleted = UnregisterFileSystem(filesystem_id);
    397     DCHECK(deleted);
    398   }
    399 }
    400 
    401 bool IsolatedContext::GetDraggedFileInfo(
    402     const std::string& filesystem_id,
    403     std::vector<MountPointInfo>* files) const {
    404   DCHECK(files);
    405   base::AutoLock locker(lock_);
    406   IDToInstance::const_iterator found = instance_map_.find(filesystem_id);
    407   if (found == instance_map_.end() ||
    408       found->second->type() != kFileSystemTypeDragged)
    409     return false;
    410   files->assign(found->second->files().begin(),
    411                 found->second->files().end());
    412   return true;
    413 }
    414 
    415 base::FilePath IsolatedContext::CreateVirtualRootPath(
    416     const std::string& filesystem_id) const {
    417   return base::FilePath().AppendASCII(filesystem_id);
    418 }
    419 
    420 IsolatedContext::IsolatedContext() {
    421 }
    422 
    423 IsolatedContext::~IsolatedContext() {
    424   STLDeleteContainerPairSecondPointers(instance_map_.begin(),
    425                                        instance_map_.end());
    426 }
    427 
    428 FileSystemURL IsolatedContext::CrackFileSystemURL(
    429     const FileSystemURL& url) const {
    430   if (!HandlesFileSystemMountType(url.type()))
    431     return FileSystemURL();
    432 
    433   std::string mount_name;
    434   FileSystemType cracked_type;
    435   base::FilePath cracked_path;
    436   FileSystemMountOption cracked_mount_option;
    437   if (!CrackVirtualPath(url.path(), &mount_name, &cracked_type,
    438                         &cracked_path, &cracked_mount_option)) {
    439     return FileSystemURL();
    440   }
    441 
    442   return FileSystemURL(
    443       url.origin(), url.mount_type(), url.virtual_path(),
    444       !url.filesystem_id().empty() ? url.filesystem_id() : mount_name,
    445       cracked_type, cracked_path, mount_name, cracked_mount_option);
    446 }
    447 
    448 bool IsolatedContext::UnregisterFileSystem(const std::string& filesystem_id) {
    449   lock_.AssertAcquired();
    450   IDToInstance::iterator found = instance_map_.find(filesystem_id);
    451   if (found == instance_map_.end())
    452     return false;
    453   Instance* instance = found->second;
    454   if (instance->IsSinglePathInstance()) {
    455     PathToID::iterator ids_iter = path_to_id_map_.find(
    456         instance->file_info().path);
    457     DCHECK(ids_iter != path_to_id_map_.end());
    458     ids_iter->second.erase(filesystem_id);
    459     if (ids_iter->second.empty())
    460       path_to_id_map_.erase(ids_iter);
    461   }
    462   delete found->second;
    463   instance_map_.erase(found);
    464   return true;
    465 }
    466 
    467 std::string IsolatedContext::GetNewFileSystemId() const {
    468   // Returns an arbitrary random string which must be unique in the map.
    469   lock_.AssertAcquired();
    470   uint32 random_data[4];
    471   std::string id;
    472   do {
    473     base::RandBytes(random_data, sizeof(random_data));
    474     id = base::HexEncode(random_data, sizeof(random_data));
    475   } while (instance_map_.find(id) != instance_map_.end());
    476   return id;
    477 }
    478 
    479 }  // namespace fileapi
    480