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