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/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"
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
) {
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
;
50 static base::LazyInstance
<IsolatedContext
>::Leaky g_isolated_context
=
51 LAZY_INSTANCE_INITIALIZER
;
55 IsolatedContext::FileInfoSet::FileInfoSet() {}
56 IsolatedContext::FileInfoSet::~FileInfoSet() {}
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())
63 base::FilePath::StringType name
= GetRegisterNameForPath(path
);
64 std::string utf8name
= base::FilePath(name
).AsUTF8Unsafe();
65 base::FilePath normalized_path
= path
.NormalizePathSeparators();
67 fileset_
.insert(MountPointInfo(utf8name
, normalized_path
)).second
;
70 std::string basepart
=
71 base::FilePath(name
).RemoveExtension().AsUTF8Unsafe();
73 base::FilePath(base::FilePath(name
).Extension()).AsUTF8Unsafe();
75 utf8name
= base::StringPrintf("%s (%d)", basepart
.c_str(), suffix
++);
79 fileset_
.insert(MountPointInfo(utf8name
, normalized_path
)).second
;
83 *registered_name
= utf8name
;
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())
92 return fileset_
.insert(
93 MountPointInfo(name
, path
.NormalizePathSeparators())).second
;
96 //--------------------------------------------------------------------------
98 class IsolatedContext::Instance
{
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
,
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
);
121 FileSystemType
type() const { return type_
; }
122 const std::string
& filesystem_id() const { return filesystem_id_
; }
123 const MountPointInfo
& file_info() const { return file_info_
; }
124 const std::set
<MountPointInfo
>& files() const { return files_
; }
125 int ref_counts() const { return ref_counts_
; }
127 void AddRef() { ++ref_counts_
; }
128 void RemoveRef() { --ref_counts_
; }
130 bool ResolvePathForName(const std::string
& name
, base::FilePath
* path
) const;
132 // Returns true if the instance is a single-path instance.
133 bool IsSinglePathInstance() const;
136 const FileSystemType type_
;
137 const std::string filesystem_id_
;
139 // For single-path instance.
140 const MountPointInfo file_info_
;
141 const PathType path_type_
;
143 // For multiple-path instance (e.g. dragged file system).
144 const std::set
<MountPointInfo
> files_
;
146 // Reference counts. Note that an isolated filesystem is created with ref==0
147 // and will get deleted when the ref count reaches <=0.
150 DISALLOW_COPY_AND_ASSIGN(Instance
);
153 IsolatedContext::Instance::Instance(FileSystemType type
,
154 const std::string
& filesystem_id
,
155 const MountPointInfo
& file_info
,
156 Instance::PathType path_type
)
158 filesystem_id_(filesystem_id
),
159 file_info_(file_info
),
160 path_type_(path_type
),
162 DCHECK(IsSinglePathIsolatedFileSystem(type_
));
165 IsolatedContext::Instance::Instance(FileSystemType type
,
166 const std::set
<MountPointInfo
>& files
)
168 path_type_(PLATFORM_PATH
),
171 DCHECK(!IsSinglePathIsolatedFileSystem(type_
));
174 IsolatedContext::Instance::~Instance() {}
176 bool IsolatedContext::Instance::ResolvePathForName(const std::string
& name
,
177 base::FilePath
* path
) const {
178 if (IsSinglePathIsolatedFileSystem(type_
)) {
179 switch (path_type_
) {
181 *path
= file_info_
.path
;
184 *path
= base::FilePath();
190 return file_info_
.name
== name
;
192 std::set
<MountPointInfo
>::const_iterator found
= files_
.find(
193 MountPointInfo(name
, base::FilePath()));
194 if (found
== files_
.end())
200 bool IsolatedContext::Instance::IsSinglePathInstance() const {
201 return IsSinglePathIsolatedFileSystem(type_
);
204 //--------------------------------------------------------------------------
207 IsolatedContext
* IsolatedContext::GetInstance() {
208 return g_isolated_context
.Pointer();
212 bool IsolatedContext::IsIsolatedType(FileSystemType type
) {
213 return type
== kFileSystemTypeIsolated
|| type
== kFileSystemTypeExternal
;
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
;
225 std::string
IsolatedContext::RegisterFileSystemForPath(
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();
234 if (register_name
&& !register_name
->empty()) {
235 name
= *register_name
;
237 name
= base::FilePath(GetRegisterNameForPath(path
)).AsUTF8Unsafe();
239 register_name
->assign(name
);
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
);
251 std::string
IsolatedContext::RegisterFileSystemForVirtualPath(
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(
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
;
269 bool IsolatedContext::HandlesFileSystemMountType(FileSystemType type
) const {
270 return type
== kFileSystemTypeIsolated
;
273 bool IsolatedContext::RevokeFileSystem(const std::string
& filesystem_id
) {
274 base::AutoLock
locker(lock_
);
275 return UnregisterFileSystem(filesystem_id
);
278 bool IsolatedContext::GetRegisteredPath(
279 const std::string
& filesystem_id
, base::FilePath
* path
) const {
281 base::AutoLock
locker(lock_
);
282 IDToInstance::const_iterator found
= instance_map_
.find(filesystem_id
);
283 if (found
== instance_map_
.end() || !found
->second
->IsSinglePathInstance())
285 *path
= found
->second
->file_info().path
;
289 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 {
299 // This should not contain any '..' references.
300 if (virtual_path
.ReferencesParent())
303 // Set the default mount option.
304 *mount_option
= FileSystemMountOption();
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)
311 std::vector
<base::FilePath::StringType
>::iterator component_iter
=
313 std::string fsid
= base::FilePath(*component_iter
++).MaybeAsASCII();
317 base::FilePath cracked_path
;
319 base::AutoLock
locker(lock_
);
320 IDToInstance::const_iterator found_instance
= instance_map_
.find(fsid
);
321 if (found_instance
== instance_map_
.end())
324 const Instance
* instance
= found_instance
->second
;
326 *type
= instance
->type();
328 *cracked_id
= instance
->filesystem_id();
330 if (component_iter
== components
.end()) {
331 // The virtual root case.
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
))
342 for (; component_iter
!= components
.end(); ++component_iter
)
343 cracked_path
= cracked_path
.Append(*component_iter
);
344 *path
= cracked_path
;
348 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
);
355 FileSystemURL
IsolatedContext::CreateCrackedFileSystemURL(
358 const base::FilePath
& path
) const {
359 return CrackFileSystemURL(FileSystemURL(origin
, type
, path
));
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())
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
);
377 path_to_id_map_
.erase(ids_iter
);
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();
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())
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
);
402 bool IsolatedContext::GetDraggedFileInfo(
403 const std::string
& filesystem_id
,
404 std::vector
<MountPointInfo
>* files
) const {
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
)
411 files
->assign(found
->second
->files().begin(),
412 found
->second
->files().end());
416 base::FilePath
IsolatedContext::CreateVirtualRootPath(
417 const std::string
& filesystem_id
) const {
418 return base::FilePath().AppendASCII(filesystem_id
);
421 IsolatedContext::IsolatedContext() {
424 IsolatedContext::~IsolatedContext() {
425 STLDeleteContainerPairSecondPointers(instance_map_
.begin(),
426 instance_map_
.end());
429 FileSystemURL
IsolatedContext::CrackFileSystemURL(
430 const FileSystemURL
& url
) const {
431 if (!HandlesFileSystemMountType(url
.type()))
432 return FileSystemURL();
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();
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
);
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())
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
);
467 delete found
->second
;
468 instance_map_
.erase(found
);
472 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];
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());
484 } // namespace storage