1 // Copyright (c) 2013 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/fileapi/external_mount_points.h"
7 #include "base/files/file_path.h"
8 #include "base/lazy_instance.h"
9 #include "base/path_service.h"
10 #include "base/stl_util.h"
11 #include "webkit/fileapi/file_system_url.h"
12 #include "webkit/fileapi/remote_file_system_proxy.h"
16 // Normalizes file path so it has normalized separators and ends with exactly
17 // one separator. Paths have to be normalized this way for use in
18 // GetVirtualPath method. Separators cannot be completely stripped, or
19 // GetVirtualPath could not working in some edge cases.
20 // For example, /a/b/c(1)/d would be erroneously resolved as c/d if the
21 // following mount points were registered: "/a/b/c", "/a/b/c(1)". (Note:
22 // "/a/b/c" < "/a/b/c(1)" < "/a/b/c/").
23 base::FilePath
NormalizeFilePath(const base::FilePath
& path
) {
27 base::FilePath::StringType path_str
= path
.StripTrailingSeparators().value();
28 if (!base::FilePath::IsSeparator(path_str
[path_str
.length() - 1]))
29 path_str
.append(FILE_PATH_LITERAL("/"));
31 return base::FilePath(path_str
).NormalizePathSeparators();
34 // Wrapper around ref-counted ExternalMountPoints that will be used to lazily
35 // create and initialize LazyInstance system ExternalMountPoints.
36 class SystemMountPointsLazyWrapper
{
38 SystemMountPointsLazyWrapper()
39 : system_mount_points_(fileapi::ExternalMountPoints::CreateRefCounted()) {
42 ~SystemMountPointsLazyWrapper() {}
44 fileapi::ExternalMountPoints
* get() {
45 return system_mount_points_
.get();
49 scoped_refptr
<fileapi::ExternalMountPoints
> system_mount_points_
;
52 base::LazyInstance
<SystemMountPointsLazyWrapper
>::Leaky
53 g_external_mount_points
= LAZY_INSTANCE_INITIALIZER
;
59 class ExternalMountPoints::Instance
{
61 Instance(FileSystemType type
,
62 const base::FilePath
& path
,
63 RemoteFileSystemProxyInterface
* remote_proxy
);
67 FileSystemType
type() const { return type_
; }
68 const base::FilePath
& path() const { return path_
; }
69 RemoteFileSystemProxyInterface
* remote_proxy() const {
70 return remote_proxy_
.get();
74 const FileSystemType type_
;
75 const base::FilePath path_
;
77 // For file systems that have a remote file system proxy.
78 scoped_refptr
<RemoteFileSystemProxyInterface
> remote_proxy_
;
80 DISALLOW_COPY_AND_ASSIGN(Instance
);
83 ExternalMountPoints::Instance::Instance(FileSystemType type
,
84 const base::FilePath
& path
,
85 RemoteFileSystemProxyInterface
* proxy
)
87 path_(path
.StripTrailingSeparators()),
88 remote_proxy_(proxy
) {
89 DCHECK(!proxy
|| (kFileSystemTypeDrive
== type_
));
92 ExternalMountPoints::Instance::~Instance() {}
94 //--------------------------------------------------------------------------
97 ExternalMountPoints
* ExternalMountPoints::GetSystemInstance() {
98 return g_external_mount_points
.Pointer()->get();
102 scoped_refptr
<ExternalMountPoints
> ExternalMountPoints::CreateRefCounted() {
103 return new ExternalMountPoints();
106 bool ExternalMountPoints::RegisterFileSystem(
107 const std::string
& mount_name
,
109 const base::FilePath
& path
) {
110 return RegisterRemoteFileSystem(mount_name
, type
, NULL
, path
);
113 bool ExternalMountPoints::RegisterRemoteFileSystem(
114 const std::string
& mount_name
,
116 RemoteFileSystemProxyInterface
* remote_proxy
,
117 const base::FilePath
& path_in
) {
118 base::AutoLock
locker(lock_
);
120 base::FilePath path
= NormalizeFilePath(path_in
);
121 if (!ValidateNewMountPoint(mount_name
, path
))
124 instance_map_
[mount_name
] = new Instance(type
, path
, remote_proxy
);
126 path_to_name_map_
.insert(std::make_pair(path
, mount_name
));
130 bool ExternalMountPoints::HandlesFileSystemMountType(
131 FileSystemType type
) const {
132 return type
== kFileSystemTypeExternal
||
133 type
== kFileSystemTypeNativeForPlatformApp
;
136 bool ExternalMountPoints::RevokeFileSystem(const std::string
& mount_name
) {
137 base::AutoLock
locker(lock_
);
138 NameToInstance::iterator found
= instance_map_
.find(mount_name
);
139 if (found
== instance_map_
.end())
141 Instance
* instance
= found
->second
;
142 path_to_name_map_
.erase(NormalizeFilePath(instance
->path()));
143 delete found
->second
;
144 instance_map_
.erase(found
);
148 bool ExternalMountPoints::GetRegisteredPath(
149 const std::string
& filesystem_id
, base::FilePath
* path
) const {
151 base::AutoLock
locker(lock_
);
152 NameToInstance::const_iterator found
= instance_map_
.find(filesystem_id
);
153 if (found
== instance_map_
.end())
155 *path
= found
->second
->path();
159 bool ExternalMountPoints::CrackVirtualPath(const base::FilePath
& virtual_path
,
160 std::string
* mount_name
,
161 FileSystemType
* type
,
162 base::FilePath
* path
) const {
166 // The path should not contain any '..' references.
167 if (virtual_path
.ReferencesParent())
170 // The virtual_path should comprise of <mount_name> and <relative_path> parts.
171 std::vector
<base::FilePath::StringType
> components
;
172 virtual_path
.GetComponents(&components
);
173 if (components
.size() < 1)
176 std::vector
<base::FilePath::StringType
>::iterator component_iter
=
178 std::string maybe_mount_name
= base::FilePath(*component_iter
++).MaybeAsASCII();
179 if (maybe_mount_name
.empty())
182 base::FilePath cracked_path
;
184 base::AutoLock
locker(lock_
);
185 NameToInstance::const_iterator found_instance
=
186 instance_map_
.find(maybe_mount_name
);
187 if (found_instance
== instance_map_
.end())
190 *mount_name
= maybe_mount_name
;
191 const Instance
* instance
= found_instance
->second
;
193 *type
= instance
->type();
194 cracked_path
= instance
->path();
197 for (; component_iter
!= components
.end(); ++component_iter
)
198 cracked_path
= cracked_path
.Append(*component_iter
);
199 *path
= cracked_path
;
203 FileSystemURL
ExternalMountPoints::CrackURL(const GURL
& url
) const {
204 FileSystemURL filesystem_url
= FileSystemURL(url
);
205 if (!filesystem_url
.is_valid())
206 return FileSystemURL();
207 return CrackFileSystemURL(filesystem_url
);
210 FileSystemURL
ExternalMountPoints::CreateCrackedFileSystemURL(
213 const base::FilePath
& path
) const {
214 return CrackFileSystemURL(FileSystemURL(origin
, type
, path
));
217 RemoteFileSystemProxyInterface
* ExternalMountPoints::GetRemoteFileSystemProxy(
218 const std::string
& mount_name
) const {
219 base::AutoLock
locker(lock_
);
220 NameToInstance::const_iterator found
= instance_map_
.find(mount_name
);
221 if (found
== instance_map_
.end())
223 return found
->second
->remote_proxy();
226 void ExternalMountPoints::AddMountPointInfosTo(
227 std::vector
<MountPointInfo
>* mount_points
) const {
228 base::AutoLock
locker(lock_
);
229 DCHECK(mount_points
);
230 for (NameToInstance::const_iterator iter
= instance_map_
.begin();
231 iter
!= instance_map_
.end(); ++iter
) {
232 mount_points
->push_back(MountPointInfo(iter
->first
, iter
->second
->path()));
236 bool ExternalMountPoints::GetVirtualPath(const base::FilePath
& path_in
,
237 base::FilePath
* virtual_path
) const {
238 DCHECK(virtual_path
);
240 base::AutoLock
locker(lock_
);
242 base::FilePath path
= NormalizeFilePath(path_in
);
243 std::map
<base::FilePath
, std::string
>::const_reverse_iterator
iter(
244 path_to_name_map_
.upper_bound(path
));
245 if (iter
== path_to_name_map_
.rend())
248 *virtual_path
= CreateVirtualRootPath(iter
->second
);
249 if (iter
->first
== path
)
251 return iter
->first
.AppendRelativePath(path
, virtual_path
);
254 base::FilePath
ExternalMountPoints::CreateVirtualRootPath(
255 const std::string
& mount_name
) const {
256 return base::FilePath().AppendASCII(mount_name
);
259 ExternalMountPoints::ExternalMountPoints() {}
261 ExternalMountPoints::~ExternalMountPoints() {
262 STLDeleteContainerPairSecondPointers(instance_map_
.begin(),
263 instance_map_
.end());
266 FileSystemURL
ExternalMountPoints::CrackFileSystemURL(
267 const FileSystemURL
& url
) const {
268 if (!HandlesFileSystemMountType(url
.type()))
269 return FileSystemURL();
271 base::FilePath virtual_path
= url
.path();
272 if (url
.type() == kFileSystemTypeNativeForPlatformApp
) {
273 #if defined(OS_CHROMEOS)
274 // On Chrome OS, find a mount point and virtual path for the external fs.
275 if (!GetVirtualPath(url
.path(), &virtual_path
))
276 return FileSystemURL();
278 // On other OS, it is simply a native local path.
279 return FileSystemURL(
280 url
.origin(), url
.mount_type(), url
.virtual_path(),
281 url
.mount_filesystem_id(), kFileSystemTypeNativeLocal
,
282 url
.path(), url
.filesystem_id());
286 std::string mount_name
;
287 FileSystemType cracked_type
;
288 base::FilePath cracked_path
;
290 if (!CrackVirtualPath(virtual_path
, &mount_name
, &cracked_type
,
292 return FileSystemURL();
295 return FileSystemURL(
296 url
.origin(), url
.mount_type(), url
.virtual_path(),
297 !url
.filesystem_id().empty() ? url
.filesystem_id() : mount_name
,
298 cracked_type
, cracked_path
, mount_name
);
301 bool ExternalMountPoints::ValidateNewMountPoint(const std::string
& mount_name
,
302 const base::FilePath
& path
) {
303 lock_
.AssertAcquired();
305 // Mount name must not be empty.
306 if (mount_name
.empty())
309 // Verify there is no registered mount point with the same name.
310 NameToInstance::iterator found
= instance_map_
.find(mount_name
);
311 if (found
!= instance_map_
.end())
314 // Allow empty paths.
318 // Verify path is legal.
319 if (path
.ReferencesParent() || !path
.IsAbsolute())
322 // Check there the new path does not overlap with one of the existing ones.
323 std::map
<base::FilePath
, std::string
>::reverse_iterator
potential_parent(
324 path_to_name_map_
.upper_bound(path
));
325 if (potential_parent
!= path_to_name_map_
.rend()) {
326 if (potential_parent
->first
== path
||
327 potential_parent
->first
.IsParent(path
)) {
332 std::map
<base::FilePath
, std::string
>::iterator potential_child
=
333 path_to_name_map_
.upper_bound(path
);
334 if (potential_child
== path_to_name_map_
.end())
336 return !(potential_child
->first
== path
) &&
337 !path
.IsParent(potential_child
->first
);
340 ScopedExternalFileSystem::ScopedExternalFileSystem(
341 const std::string
& mount_name
,
343 const base::FilePath
& path
)
344 : mount_name_(mount_name
) {
345 ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
346 mount_name
, type
, path
);
349 base::FilePath
ScopedExternalFileSystem::GetVirtualRootPath() const {
350 return ExternalMountPoints::GetSystemInstance()->
351 CreateVirtualRootPath(mount_name_
);
354 ScopedExternalFileSystem::~ScopedExternalFileSystem() {
355 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(mount_name_
);
358 } // namespace fileapi