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