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"
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
;
27 i
< path
.value().size() && !base::FilePath::IsSeparator(path
.value()[i
]);
29 if (path
.value()[i
] == L
':') {
30 name
.append(L
"_drive");
33 name
.append(1, path
.value()[i
]);
37 return FILE_PATH_LITERAL("<root>");
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
;
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())
61 base::FilePath::StringType name
= GetRegisterNameForPath(path
);
62 std::string utf8name
= base::FilePath(name
).AsUTF8Unsafe();
63 base::FilePath normalized_path
= path
.NormalizePathSeparators();
65 fileset_
.insert(MountPointInfo(utf8name
, normalized_path
)).second
;
68 std::string basepart
=
69 base::FilePath(name
).RemoveExtension().AsUTF8Unsafe();
71 base::FilePath(base::FilePath(name
).Extension()).AsUTF8Unsafe();
73 utf8name
= base::StringPrintf("%s (%d)", basepart
.c_str(), suffix
++);
77 fileset_
.insert(MountPointInfo(utf8name
, normalized_path
)).second
;
81 *registered_name
= utf8name
;
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())
90 return fileset_
.insert(
91 MountPointInfo(name
, path
.NormalizePathSeparators())).second
;
94 //--------------------------------------------------------------------------
96 class IsolatedContext::Instance
{
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
,
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
);
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;
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.
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
)
156 filesystem_id_(filesystem_id
),
157 file_info_(file_info
),
158 path_type_(path_type
),
160 DCHECK(IsSinglePathIsolatedFileSystem(type_
));
163 IsolatedContext::Instance::Instance(FileSystemType type
,
164 const std::set
<MountPointInfo
>& files
)
166 path_type_(PLATFORM_PATH
),
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_
) {
179 *path
= file_info_
.path
;
182 *path
= base::FilePath();
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())
198 bool IsolatedContext::Instance::IsSinglePathInstance() const {
199 return IsSinglePathIsolatedFileSystem(type_
);
202 //--------------------------------------------------------------------------
205 IsolatedContext
* IsolatedContext::GetInstance() {
206 return g_isolated_context
.Pointer();
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(
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();
232 if (register_name
&& !register_name
->empty()) {
233 name
= *register_name
;
235 name
= base::FilePath(GetRegisterNameForPath(path
)).AsUTF8Unsafe();
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
);
249 std::string
IsolatedContext::RegisterFileSystemForVirtualPath(
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(
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 {
279 base::AutoLock
locker(lock_
);
280 IDToInstance::const_iterator found
= instance_map_
.find(filesystem_id
);
281 if (found
== instance_map_
.end() || !found
->second
->IsSinglePathInstance())
283 *path
= found
->second
->file_info().path
;
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 {
297 // This should not contain any '..' references.
298 if (virtual_path
.ReferencesParent())
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)
309 std::vector
<base::FilePath::StringType
>::iterator component_iter
=
311 std::string fsid
= base::FilePath(*component_iter
++).MaybeAsASCII();
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())
322 const Instance
* instance
= found_instance
->second
;
324 *type
= instance
->type();
326 *cracked_id
= instance
->filesystem_id();
328 if (component_iter
== components
.end()) {
329 // The virtual root case.
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
))
340 for (; component_iter
!= components
.end(); ++component_iter
)
341 cracked_path
= cracked_path
.Append(*component_iter
);
342 *path
= cracked_path
;
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(
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())
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())
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
);
400 bool IsolatedContext::GetDraggedFileInfo(
401 const std::string
& filesystem_id
,
402 std::vector
<MountPointInfo
>* files
) const {
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
)
409 files
->assign(found
->second
->files().begin(),
410 found
->second
->files().end());
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())
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
);
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];
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());
482 } // namespace storage