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