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 "storage/browser/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 "storage/browser/fileapi/file_system_url.h"
15 // Normalizes file path so it has normalized separators and ends with exactly
16 // one separator. Paths have to be normalized this way for use in
17 // GetVirtualPath method. Separators cannot be completely stripped, or
18 // GetVirtualPath could not working in some edge cases.
19 // For example, /a/b/c(1)/d would be erroneously resolved as c/d if the
20 // following mount points were registered: "/a/b/c", "/a/b/c(1)". (Note:
21 // "/a/b/c" < "/a/b/c(1)" < "/a/b/c/").
22 base::FilePath
NormalizeFilePath(const base::FilePath
& path
) {
26 base::FilePath::StringType path_str
= path
.StripTrailingSeparators().value();
27 if (!base::FilePath::IsSeparator(path_str
[path_str
.length() - 1]))
28 path_str
.append(FILE_PATH_LITERAL("/"));
30 return base::FilePath(path_str
).NormalizePathSeparators();
33 bool IsOverlappingMountPathForbidden(storage::FileSystemType type
) {
34 return type
!= storage::kFileSystemTypeNativeMedia
&&
35 type
!= storage::kFileSystemTypeDeviceMedia
;
38 // Wrapper around ref-counted ExternalMountPoints that will be used to lazily
39 // create and initialize LazyInstance system ExternalMountPoints.
40 class SystemMountPointsLazyWrapper
{
42 SystemMountPointsLazyWrapper()
43 : system_mount_points_(storage::ExternalMountPoints::CreateRefCounted()) {
46 ~SystemMountPointsLazyWrapper() {}
48 storage::ExternalMountPoints
* get() { return system_mount_points_
.get(); }
51 scoped_refptr
<storage::ExternalMountPoints
> system_mount_points_
;
54 base::LazyInstance
<SystemMountPointsLazyWrapper
>::Leaky
55 g_external_mount_points
= LAZY_INSTANCE_INITIALIZER
;
61 class ExternalMountPoints::Instance
{
63 Instance(FileSystemType type
,
64 const base::FilePath
& path
,
65 const FileSystemMountOption
& mount_option
)
67 path_(path
.StripTrailingSeparators()),
68 mount_option_(mount_option
) {}
71 FileSystemType
type() const { return type_
; }
72 const base::FilePath
& path() const { return path_
; }
73 const FileSystemMountOption
& mount_option() const { return mount_option_
; }
76 const FileSystemType type_
;
77 const base::FilePath path_
;
78 const FileSystemMountOption mount_option_
;
80 DISALLOW_COPY_AND_ASSIGN(Instance
);
83 //--------------------------------------------------------------------------
86 ExternalMountPoints
* ExternalMountPoints::GetSystemInstance() {
87 return g_external_mount_points
.Pointer()->get();
91 scoped_refptr
<ExternalMountPoints
> ExternalMountPoints::CreateRefCounted() {
92 return new ExternalMountPoints();
95 bool ExternalMountPoints::RegisterFileSystem(
96 const std::string
& mount_name
,
98 const FileSystemMountOption
& mount_option
,
99 const base::FilePath
& path_in
) {
100 // COPY_SYNC_OPTION_SYNC is only applicable to native local file system.
101 DCHECK(type
== kFileSystemTypeNativeLocal
||
102 mount_option
.copy_sync_option() != COPY_SYNC_OPTION_SYNC
);
104 base::AutoLock
locker(lock_
);
106 base::FilePath path
= NormalizeFilePath(path_in
);
107 if (!ValidateNewMountPoint(mount_name
, type
, path
))
110 instance_map_
[mount_name
] = new Instance(type
, path
, mount_option
);
111 if (!path
.empty() && IsOverlappingMountPathForbidden(type
))
112 path_to_name_map_
.insert(std::make_pair(path
, mount_name
));
116 bool ExternalMountPoints::HandlesFileSystemMountType(
117 FileSystemType type
) const {
118 return type
== kFileSystemTypeExternal
||
119 type
== kFileSystemTypeNativeForPlatformApp
;
122 bool ExternalMountPoints::RevokeFileSystem(const std::string
& mount_name
) {
123 base::AutoLock
locker(lock_
);
124 NameToInstance::iterator found
= instance_map_
.find(mount_name
);
125 if (found
== instance_map_
.end())
127 Instance
* instance
= found
->second
;
128 if (IsOverlappingMountPathForbidden(instance
->type()))
129 path_to_name_map_
.erase(NormalizeFilePath(instance
->path()));
130 delete found
->second
;
131 instance_map_
.erase(found
);
135 bool ExternalMountPoints::GetRegisteredPath(
136 const std::string
& filesystem_id
, base::FilePath
* path
) const {
138 base::AutoLock
locker(lock_
);
139 NameToInstance::const_iterator found
= instance_map_
.find(filesystem_id
);
140 if (found
== instance_map_
.end())
142 *path
= found
->second
->path();
146 bool ExternalMountPoints::CrackVirtualPath(
147 const base::FilePath
& virtual_path
,
148 std::string
* mount_name
,
149 FileSystemType
* type
,
150 std::string
* cracked_id
,
151 base::FilePath
* path
,
152 FileSystemMountOption
* mount_option
) const {
156 // The path should not contain any '..' references.
157 if (virtual_path
.ReferencesParent())
160 // The virtual_path should comprise of <mount_name> and <relative_path> parts.
161 std::vector
<base::FilePath::StringType
> components
;
162 virtual_path
.GetComponents(&components
);
163 if (components
.size() < 1)
166 std::vector
<base::FilePath::StringType
>::iterator component_iter
=
168 std::string maybe_mount_name
=
169 base::FilePath(*component_iter
++).MaybeAsASCII();
170 if (maybe_mount_name
.empty())
173 base::FilePath cracked_path
;
175 base::AutoLock
locker(lock_
);
176 NameToInstance::const_iterator found_instance
=
177 instance_map_
.find(maybe_mount_name
);
178 if (found_instance
== instance_map_
.end())
181 *mount_name
= maybe_mount_name
;
182 const Instance
* instance
= found_instance
->second
;
184 *type
= instance
->type();
185 cracked_path
= instance
->path();
186 *mount_option
= instance
->mount_option();
189 for (; component_iter
!= components
.end(); ++component_iter
)
190 cracked_path
= cracked_path
.Append(*component_iter
);
191 *path
= cracked_path
;
195 FileSystemURL
ExternalMountPoints::CrackURL(const GURL
& url
) const {
196 FileSystemURL filesystem_url
= FileSystemURL(url
);
197 if (!filesystem_url
.is_valid())
198 return FileSystemURL();
199 return CrackFileSystemURL(filesystem_url
);
202 FileSystemURL
ExternalMountPoints::CreateCrackedFileSystemURL(
205 const base::FilePath
& path
) const {
206 return CrackFileSystemURL(FileSystemURL(origin
, type
, path
));
209 void ExternalMountPoints::AddMountPointInfosTo(
210 std::vector
<MountPointInfo
>* mount_points
) const {
211 base::AutoLock
locker(lock_
);
212 DCHECK(mount_points
);
213 for (NameToInstance::const_iterator iter
= instance_map_
.begin();
214 iter
!= instance_map_
.end(); ++iter
) {
215 mount_points
->push_back(MountPointInfo(iter
->first
, iter
->second
->path()));
219 bool ExternalMountPoints::GetVirtualPath(const base::FilePath
& path_in
,
220 base::FilePath
* virtual_path
) const {
221 DCHECK(virtual_path
);
223 base::AutoLock
locker(lock_
);
225 base::FilePath path
= NormalizeFilePath(path_in
);
226 std::map
<base::FilePath
, std::string
>::const_reverse_iterator
iter(
227 path_to_name_map_
.upper_bound(path
));
228 if (iter
== path_to_name_map_
.rend())
231 *virtual_path
= CreateVirtualRootPath(iter
->second
);
232 if (iter
->first
== path
)
234 return iter
->first
.AppendRelativePath(path
, virtual_path
);
237 base::FilePath
ExternalMountPoints::CreateVirtualRootPath(
238 const std::string
& mount_name
) const {
239 return base::FilePath().AppendASCII(mount_name
);
242 FileSystemURL
ExternalMountPoints::CreateExternalFileSystemURL(
244 const std::string
& mount_name
,
245 const base::FilePath
& path
) const {
246 return CreateCrackedFileSystemURL(
248 storage::kFileSystemTypeExternal
,
249 // Avoid using FilePath::Append as path may be an absolute path.
250 base::FilePath(CreateVirtualRootPath(mount_name
).value() +
251 base::FilePath::kSeparators
[0] + path
.value()));
254 void ExternalMountPoints::RevokeAllFileSystems() {
255 NameToInstance instance_map_copy
;
257 base::AutoLock
locker(lock_
);
258 instance_map_copy
= instance_map_
;
259 instance_map_
.clear();
260 path_to_name_map_
.clear();
262 STLDeleteContainerPairSecondPointers(instance_map_copy
.begin(),
263 instance_map_copy
.end());
266 ExternalMountPoints::ExternalMountPoints() {}
268 ExternalMountPoints::~ExternalMountPoints() {
269 STLDeleteContainerPairSecondPointers(instance_map_
.begin(),
270 instance_map_
.end());
273 FileSystemURL
ExternalMountPoints::CrackFileSystemURL(
274 const FileSystemURL
& url
) const {
275 if (!HandlesFileSystemMountType(url
.type()))
276 return FileSystemURL();
278 base::FilePath virtual_path
= url
.path();
279 if (url
.type() == kFileSystemTypeNativeForPlatformApp
) {
280 #if defined(OS_CHROMEOS)
281 // On Chrome OS, find a mount point and virtual path for the external fs.
282 if (!GetVirtualPath(url
.path(), &virtual_path
))
283 return FileSystemURL();
285 // On other OS, it is simply a native local path.
286 return FileSystemURL(
287 url
.origin(), url
.mount_type(), url
.virtual_path(),
288 url
.mount_filesystem_id(), kFileSystemTypeNativeLocal
,
289 url
.path(), url
.filesystem_id(), url
.mount_option());
293 std::string mount_name
;
294 FileSystemType cracked_type
;
295 std::string cracked_id
;
296 base::FilePath cracked_path
;
297 FileSystemMountOption cracked_mount_option
;
299 if (!CrackVirtualPath(virtual_path
, &mount_name
, &cracked_type
,
300 &cracked_id
, &cracked_path
, &cracked_mount_option
)) {
301 return FileSystemURL();
304 return FileSystemURL(
305 url
.origin(), url
.mount_type(), url
.virtual_path(),
306 !url
.filesystem_id().empty() ? url
.filesystem_id() : mount_name
,
307 cracked_type
, cracked_path
,
308 cracked_id
.empty() ? mount_name
: cracked_id
, cracked_mount_option
);
311 bool ExternalMountPoints::ValidateNewMountPoint(const std::string
& mount_name
,
313 const base::FilePath
& path
) {
314 lock_
.AssertAcquired();
316 // Mount name must not be empty.
317 if (mount_name
.empty())
320 // Verify there is no registered mount point with the same name.
321 NameToInstance::iterator found
= instance_map_
.find(mount_name
);
322 if (found
!= instance_map_
.end())
325 // Allow empty paths.
329 // Verify path is legal.
330 if (path
.ReferencesParent() || !path
.IsAbsolute())
333 if (IsOverlappingMountPathForbidden(type
)) {
334 // Check there the new path does not overlap with one of the existing ones.
335 std::map
<base::FilePath
, std::string
>::reverse_iterator
potential_parent(
336 path_to_name_map_
.upper_bound(path
));
337 if (potential_parent
!= path_to_name_map_
.rend()) {
338 if (potential_parent
->first
== path
||
339 potential_parent
->first
.IsParent(path
)) {
344 std::map
<base::FilePath
, std::string
>::iterator potential_child
=
345 path_to_name_map_
.upper_bound(path
);
346 if (potential_child
!= path_to_name_map_
.end()) {
347 if (potential_child
->first
== path
||
348 path
.IsParent(potential_child
->first
)) {
357 } // namespace storage