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.
5 #include "webkit/browser/fileapi/isolated_context.h"
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"
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();
26 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
27 base::FilePath::StringType name;
29 i < path.value().size() && !base::FilePath::IsSeparator(path.value()[i]);
31 if (path.value()[i] == L':') {
32 name.append(L"_drive");
35 name.append(1, path.value()[i]);
39 return FILE_PATH_LITERAL("<root>");
43 bool IsSinglePathIsolatedFileSystem(FileSystemType type) {
45 // As of writing dragged file system is the only filesystem
46 // which could have multiple top-level paths.
47 case kFileSystemTypeDragged:
50 case kFileSystemTypeUnknown:
61 static base::LazyInstance<IsolatedContext>::Leaky g_isolated_context =
62 LAZY_INSTANCE_INITIALIZER;
66 IsolatedContext::FileInfoSet::FileInfoSet() {}
67 IsolatedContext::FileInfoSet::~FileInfoSet() {}
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())
74 base::FilePath::StringType name = GetRegisterNameForPath(path);
75 std::string utf8name = base::FilePath(name).AsUTF8Unsafe();
76 base::FilePath normalized_path = path.NormalizePathSeparators();
78 fileset_.insert(MountPointInfo(utf8name, normalized_path)).second;
81 std::string basepart =
82 base::FilePath(name).RemoveExtension().AsUTF8Unsafe();
84 base::FilePath(base::FilePath(name).Extension()).AsUTF8Unsafe();
86 utf8name = base::StringPrintf("%s (%d)", basepart.c_str(), suffix++);
90 fileset_.insert(MountPointInfo(utf8name, normalized_path)).second;
94 *registered_name = utf8name;
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())
103 return fileset_.insert(
104 MountPointInfo(name, path.NormalizePathSeparators())).second;
107 //--------------------------------------------------------------------------
109 class IsolatedContext::Instance {
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,
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);
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_; }
135 void AddRef() { ++ref_counts_; }
136 void RemoveRef() { --ref_counts_; }
138 bool ResolvePathForName(const std::string& name, base::FilePath* path) const;
140 // Returns true if the instance is a single-path instance.
141 bool IsSinglePathInstance() const;
144 const FileSystemType type_;
146 // For single-path instance.
147 const MountPointInfo file_info_;
148 const PathType path_type_;
150 // For multiple-path instance (e.g. dragged file system).
151 const std::set<MountPointInfo> files_;
153 // Reference counts. Note that an isolated filesystem is created with ref==0
154 // and will get deleted when the ref count reaches <=0.
157 DISALLOW_COPY_AND_ASSIGN(Instance);
160 IsolatedContext::Instance::Instance(FileSystemType type,
161 const MountPointInfo& file_info,
162 Instance::PathType path_type)
164 file_info_(file_info),
165 path_type_(path_type),
167 DCHECK(IsSinglePathIsolatedFileSystem(type_));
170 IsolatedContext::Instance::Instance(FileSystemType type,
171 const std::set<MountPointInfo>& files)
173 path_type_(PLATFORM_PATH),
176 DCHECK(!IsSinglePathIsolatedFileSystem(type_));
179 IsolatedContext::Instance::~Instance() {}
181 bool IsolatedContext::Instance::ResolvePathForName(const std::string& name,
182 base::FilePath* path) const {
183 if (IsSinglePathIsolatedFileSystem(type_)) {
184 switch (path_type_) {
186 *path = file_info_.path;
189 *path = base::FilePath();
195 return file_info_.name == name;
197 std::set<MountPointInfo>::const_iterator found = files_.find(
198 MountPointInfo(name, base::FilePath()));
199 if (found == files_.end())
205 bool IsolatedContext::Instance::IsSinglePathInstance() const {
206 return IsSinglePathIsolatedFileSystem(type_);
209 //--------------------------------------------------------------------------
212 IsolatedContext* IsolatedContext::GetInstance() {
213 return g_isolated_context.Pointer();
217 bool IsolatedContext::IsIsolatedType(FileSystemType type) {
218 return type == kFileSystemTypeIsolated || type == kFileSystemTypeExternal;
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;
230 std::string IsolatedContext::RegisterFileSystemForPath(
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();
238 if (register_name && !register_name->empty()) {
239 name = *register_name;
241 name = base::FilePath(GetRegisterNameForPath(path)).AsUTF8Unsafe();
243 register_name->assign(name);
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;
254 std::string IsolatedContext::RegisterFileSystemForVirtualPath(
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(
265 MountPointInfo(register_name, cracked_path_prefix),
266 Instance::VIRTUAL_PATH);
267 path_to_id_map_[path].insert(filesystem_id);
268 return filesystem_id;
271 bool IsolatedContext::HandlesFileSystemMountType(FileSystemType type) const {
272 return type == kFileSystemTypeIsolated;
275 bool IsolatedContext::RevokeFileSystem(const std::string& filesystem_id) {
276 base::AutoLock locker(lock_);
277 return UnregisterFileSystem(filesystem_id);
280 bool IsolatedContext::GetRegisteredPath(
281 const std::string& filesystem_id, base::FilePath* path) const {
283 base::AutoLock locker(lock_);
284 IDToInstance::const_iterator found = instance_map_.find(filesystem_id);
285 if (found == instance_map_.end() || !found->second->IsSinglePathInstance())
287 *path = found->second->file_info().path;
291 bool IsolatedContext::CrackVirtualPath(const base::FilePath& virtual_path,
292 std::string* id_or_name,
293 FileSystemType* type,
294 base::FilePath* path) const {
298 // This should not contain any '..' references.
299 if (virtual_path.ReferencesParent())
302 // The virtual_path should comprise <id_or_name> and <relative_path> parts.
303 std::vector<base::FilePath::StringType> components;
304 virtual_path.GetComponents(&components);
305 if (components.size() < 1)
307 std::vector<base::FilePath::StringType>::iterator component_iter =
309 std::string fsid = base::FilePath(*component_iter++).MaybeAsASCII();
313 base::FilePath cracked_path;
315 base::AutoLock locker(lock_);
316 IDToInstance::const_iterator found_instance = instance_map_.find(fsid);
317 if (found_instance == instance_map_.end())
320 const Instance* instance = found_instance->second;
322 *type = instance->type();
324 if (component_iter == components.end()) {
325 // The virtual root case.
330 // *component_iter should be a name of the registered path.
331 std::string name = base::FilePath(*component_iter++).AsUTF8Unsafe();
332 if (!instance->ResolvePathForName(name, &cracked_path))
336 for (; component_iter != components.end(); ++component_iter)
337 cracked_path = cracked_path.Append(*component_iter);
338 *path = cracked_path;
342 FileSystemURL IsolatedContext::CrackURL(const GURL& url) const {
343 FileSystemURL filesystem_url = FileSystemURL(url);
344 if (!filesystem_url.is_valid())
345 return FileSystemURL();
346 return CrackFileSystemURL(filesystem_url);
349 FileSystemURL IsolatedContext::CreateCrackedFileSystemURL(
352 const base::FilePath& path) const {
353 return CrackFileSystemURL(FileSystemURL(origin, type, path));
356 void IsolatedContext::RevokeFileSystemByPath(const base::FilePath& path_in) {
357 base::AutoLock locker(lock_);
358 base::FilePath path(path_in.NormalizePathSeparators());
359 PathToID::iterator ids_iter = path_to_id_map_.find(path);
360 if (ids_iter == path_to_id_map_.end())
362 std::set<std::string>& ids = ids_iter->second;
363 for (std::set<std::string>::iterator iter = ids.begin();
364 iter != ids.end(); ++iter) {
365 IDToInstance::iterator found = instance_map_.find(*iter);
366 if (found != instance_map_.end()) {
367 delete found->second;
368 instance_map_.erase(found);
371 path_to_id_map_.erase(ids_iter);
374 void IsolatedContext::AddReference(const std::string& filesystem_id) {
375 base::AutoLock locker(lock_);
376 DCHECK(instance_map_.find(filesystem_id) != instance_map_.end());
377 instance_map_[filesystem_id]->AddRef();
380 void IsolatedContext::RemoveReference(const std::string& filesystem_id) {
381 base::AutoLock locker(lock_);
382 // This could get called for non-existent filesystem if it has been
383 // already deleted by RevokeFileSystemByPath.
384 IDToInstance::iterator found = instance_map_.find(filesystem_id);
385 if (found == instance_map_.end())
387 Instance* instance = found->second;
388 DCHECK_GT(instance->ref_counts(), 0);
389 instance->RemoveRef();
390 if (instance->ref_counts() == 0) {
391 bool deleted = UnregisterFileSystem(filesystem_id);
396 bool IsolatedContext::GetDraggedFileInfo(
397 const std::string& filesystem_id,
398 std::vector<MountPointInfo>* files) const {
400 base::AutoLock locker(lock_);
401 IDToInstance::const_iterator found = instance_map_.find(filesystem_id);
402 if (found == instance_map_.end() ||
403 found->second->type() != kFileSystemTypeDragged)
405 files->assign(found->second->files().begin(),
406 found->second->files().end());
410 base::FilePath IsolatedContext::CreateVirtualRootPath(
411 const std::string& filesystem_id) const {
412 return base::FilePath().AppendASCII(filesystem_id);
415 IsolatedContext::IsolatedContext() {
418 IsolatedContext::~IsolatedContext() {
419 STLDeleteContainerPairSecondPointers(instance_map_.begin(),
420 instance_map_.end());
423 FileSystemURL IsolatedContext::CrackFileSystemURL(
424 const FileSystemURL& url) const {
425 if (!HandlesFileSystemMountType(url.type()))
426 return FileSystemURL();
428 std::string mount_name;
429 FileSystemType cracked_type;
430 base::FilePath cracked_path;
431 if (!CrackVirtualPath(url.path(), &mount_name, &cracked_type, &cracked_path))
432 return FileSystemURL();
434 return FileSystemURL(
435 url.origin(), url.mount_type(), url.virtual_path(),
436 !url.filesystem_id().empty() ? url.filesystem_id() : mount_name,
437 cracked_type, cracked_path, mount_name);
440 bool IsolatedContext::UnregisterFileSystem(const std::string& filesystem_id) {
441 lock_.AssertAcquired();
442 IDToInstance::iterator found = instance_map_.find(filesystem_id);
443 if (found == instance_map_.end())
445 Instance* instance = found->second;
446 if (instance->IsSinglePathInstance()) {
447 PathToID::iterator ids_iter = path_to_id_map_.find(
448 instance->file_info().path);
449 DCHECK(ids_iter != path_to_id_map_.end());
450 ids_iter->second.erase(filesystem_id);
451 if (ids_iter->second.empty())
452 path_to_id_map_.erase(ids_iter);
454 delete found->second;
455 instance_map_.erase(found);
459 std::string IsolatedContext::GetNewFileSystemId() const {
460 // Returns an arbitrary random string which must be unique in the map.
461 lock_.AssertAcquired();
462 uint32 random_data[4];
465 base::RandBytes(random_data, sizeof(random_data));
466 id = base::HexEncode(random_data, sizeof(random_data));
467 } while (instance_map_.find(id) != instance_map_.end());
471 } // namespace fileapi