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/stl_util.h"
10 #include "storage/browser/fileapi/file_system_url.h"
14 // Normalizes file path so it has normalized separators and ends with exactly
15 // one separator. Paths have to be normalized this way for use in
16 // GetVirtualPath method. Separators cannot be completely stripped, or
17 // GetVirtualPath could not working in some edge cases.
18 // For example, /a/b/c(1)/d would be erroneously resolved as c/d if the
19 // following mount points were registered: "/a/b/c", "/a/b/c(1)". (Note:
20 // "/a/b/c" < "/a/b/c(1)" < "/a/b/c/").
21 base::FilePath
NormalizeFilePath(const base::FilePath
& path
) {
25 base::FilePath::StringType path_str
= path
.StripTrailingSeparators().value();
26 if (!base::FilePath::IsSeparator(path_str
[path_str
.length() - 1]))
27 path_str
.append(FILE_PATH_LITERAL("/"));
29 return base::FilePath(path_str
).NormalizePathSeparators();
32 bool IsOverlappingMountPathForbidden(storage::FileSystemType type
) {
33 return type
!= storage::kFileSystemTypeNativeMedia
&&
34 type
!= storage::kFileSystemTypeDeviceMedia
;
37 // Wrapper around ref-counted ExternalMountPoints that will be used to lazily
38 // create and initialize LazyInstance system ExternalMountPoints.
39 class SystemMountPointsLazyWrapper
{
41 SystemMountPointsLazyWrapper()
42 : system_mount_points_(storage::ExternalMountPoints::CreateRefCounted()) {
45 ~SystemMountPointsLazyWrapper() {}
47 storage::ExternalMountPoints
* get() { return system_mount_points_
.get(); }
50 scoped_refptr
<storage::ExternalMountPoints
> system_mount_points_
;
53 base::LazyInstance
<SystemMountPointsLazyWrapper
>::Leaky
54 g_external_mount_points
= LAZY_INSTANCE_INITIALIZER
;
60 class ExternalMountPoints::Instance
{
62 Instance(FileSystemType type
,
63 const base::FilePath
& path
,
64 const FileSystemMountOption
& mount_option
)
66 path_(path
.StripTrailingSeparators()),
67 mount_option_(mount_option
) {}
70 FileSystemType
type() const { return type_
; }
71 const base::FilePath
& path() const { return path_
; }
72 const FileSystemMountOption
& mount_option() const { return mount_option_
; }
75 const FileSystemType type_
;
76 const base::FilePath path_
;
77 const FileSystemMountOption mount_option_
;
79 DISALLOW_COPY_AND_ASSIGN(Instance
);
82 //--------------------------------------------------------------------------
85 ExternalMountPoints
* ExternalMountPoints::GetSystemInstance() {
86 return g_external_mount_points
.Pointer()->get();
90 scoped_refptr
<ExternalMountPoints
> ExternalMountPoints::CreateRefCounted() {
91 return new ExternalMountPoints();
94 bool ExternalMountPoints::RegisterFileSystem(
95 const std::string
& mount_name
,
97 const FileSystemMountOption
& mount_option
,
98 const base::FilePath
& path_in
) {
99 base::AutoLock
locker(lock_
);
101 base::FilePath path
= NormalizeFilePath(path_in
);
102 if (!ValidateNewMountPoint(mount_name
, type
, path
))
105 instance_map_
[mount_name
] = new Instance(type
, path
, mount_option
);
106 if (!path
.empty() && IsOverlappingMountPathForbidden(type
))
107 path_to_name_map_
.insert(std::make_pair(path
, mount_name
));
111 bool ExternalMountPoints::HandlesFileSystemMountType(
112 FileSystemType type
) const {
113 return type
== kFileSystemTypeExternal
||
114 type
== kFileSystemTypeNativeForPlatformApp
;
117 bool ExternalMountPoints::RevokeFileSystem(const std::string
& mount_name
) {
118 base::AutoLock
locker(lock_
);
119 NameToInstance::iterator found
= instance_map_
.find(mount_name
);
120 if (found
== instance_map_
.end())
122 Instance
* instance
= found
->second
;
123 if (IsOverlappingMountPathForbidden(instance
->type()))
124 path_to_name_map_
.erase(NormalizeFilePath(instance
->path()));
125 delete found
->second
;
126 instance_map_
.erase(found
);
130 bool ExternalMountPoints::GetRegisteredPath(
131 const std::string
& filesystem_id
, base::FilePath
* path
) const {
133 base::AutoLock
locker(lock_
);
134 NameToInstance::const_iterator found
= instance_map_
.find(filesystem_id
);
135 if (found
== instance_map_
.end())
137 *path
= found
->second
->path();
141 bool ExternalMountPoints::CrackVirtualPath(
142 const base::FilePath
& virtual_path
,
143 std::string
* mount_name
,
144 FileSystemType
* type
,
145 std::string
* cracked_id
,
146 base::FilePath
* path
,
147 FileSystemMountOption
* mount_option
) const {
151 // The path should not contain any '..' references.
152 if (virtual_path
.ReferencesParent())
155 // The virtual_path should comprise of <mount_name> and <relative_path> parts.
156 std::vector
<base::FilePath::StringType
> components
;
157 virtual_path
.GetComponents(&components
);
158 if (components
.size() < 1)
161 std::vector
<base::FilePath::StringType
>::iterator component_iter
=
163 std::string maybe_mount_name
=
164 base::FilePath(*component_iter
++).AsUTF8Unsafe();
166 base::FilePath cracked_path
;
168 base::AutoLock
locker(lock_
);
169 NameToInstance::const_iterator found_instance
=
170 instance_map_
.find(maybe_mount_name
);
171 if (found_instance
== instance_map_
.end())
174 *mount_name
= maybe_mount_name
;
175 const Instance
* instance
= found_instance
->second
;
177 *type
= instance
->type();
178 cracked_path
= instance
->path();
179 *mount_option
= instance
->mount_option();
182 for (; component_iter
!= components
.end(); ++component_iter
)
183 cracked_path
= cracked_path
.Append(*component_iter
);
184 *path
= cracked_path
;
188 FileSystemURL
ExternalMountPoints::CrackURL(const GURL
& url
) const {
189 FileSystemURL filesystem_url
= FileSystemURL(url
);
190 if (!filesystem_url
.is_valid())
191 return FileSystemURL();
192 return CrackFileSystemURL(filesystem_url
);
195 FileSystemURL
ExternalMountPoints::CreateCrackedFileSystemURL(
198 const base::FilePath
& path
) const {
199 return CrackFileSystemURL(FileSystemURL(origin
, type
, path
));
202 void ExternalMountPoints::AddMountPointInfosTo(
203 std::vector
<MountPointInfo
>* mount_points
) const {
204 base::AutoLock
locker(lock_
);
205 DCHECK(mount_points
);
206 for (NameToInstance::const_iterator iter
= instance_map_
.begin();
207 iter
!= instance_map_
.end(); ++iter
) {
208 mount_points
->push_back(MountPointInfo(iter
->first
, iter
->second
->path()));
212 bool ExternalMountPoints::GetVirtualPath(const base::FilePath
& path_in
,
213 base::FilePath
* virtual_path
) const {
214 DCHECK(virtual_path
);
216 base::AutoLock
locker(lock_
);
218 base::FilePath path
= NormalizeFilePath(path_in
);
219 std::map
<base::FilePath
, std::string
>::const_reverse_iterator
iter(
220 path_to_name_map_
.upper_bound(path
));
221 if (iter
== path_to_name_map_
.rend())
224 *virtual_path
= CreateVirtualRootPath(iter
->second
);
225 if (iter
->first
== path
)
227 return iter
->first
.AppendRelativePath(path
, virtual_path
);
230 base::FilePath
ExternalMountPoints::CreateVirtualRootPath(
231 const std::string
& mount_name
) const {
232 return base::FilePath().Append(base::FilePath::FromUTF8Unsafe(mount_name
));
235 FileSystemURL
ExternalMountPoints::CreateExternalFileSystemURL(
237 const std::string
& mount_name
,
238 const base::FilePath
& path
) const {
239 return CreateCrackedFileSystemURL(
241 storage::kFileSystemTypeExternal
,
242 // Avoid using FilePath::Append as path may be an absolute path.
243 base::FilePath(CreateVirtualRootPath(mount_name
).value() +
244 base::FilePath::kSeparators
[0] + path
.value()));
247 void ExternalMountPoints::RevokeAllFileSystems() {
248 NameToInstance instance_map_copy
;
250 base::AutoLock
locker(lock_
);
251 instance_map_copy
= instance_map_
;
252 instance_map_
.clear();
253 path_to_name_map_
.clear();
255 STLDeleteContainerPairSecondPointers(instance_map_copy
.begin(),
256 instance_map_copy
.end());
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(), url
.mount_option());
286 std::string mount_name
;
287 FileSystemType cracked_type
;
288 std::string cracked_id
;
289 base::FilePath cracked_path
;
290 FileSystemMountOption cracked_mount_option
;
292 if (!CrackVirtualPath(virtual_path
, &mount_name
, &cracked_type
,
293 &cracked_id
, &cracked_path
, &cracked_mount_option
)) {
294 return FileSystemURL();
297 return FileSystemURL(
298 url
.origin(), url
.mount_type(), url
.virtual_path(),
299 !url
.filesystem_id().empty() ? url
.filesystem_id() : mount_name
,
300 cracked_type
, cracked_path
,
301 cracked_id
.empty() ? mount_name
: cracked_id
, cracked_mount_option
);
304 bool ExternalMountPoints::ValidateNewMountPoint(const std::string
& mount_name
,
306 const base::FilePath
& path
) {
307 lock_
.AssertAcquired();
309 // Mount name must not be empty.
310 if (mount_name
.empty())
313 // Verify there is no registered mount point with the same name.
314 NameToInstance::iterator found
= instance_map_
.find(mount_name
);
315 if (found
!= instance_map_
.end())
318 // Allow empty paths.
322 // Verify path is legal.
323 if (path
.ReferencesParent() || !path
.IsAbsolute())
326 if (IsOverlappingMountPathForbidden(type
)) {
327 // Check there the new path does not overlap with one of the existing ones.
328 std::map
<base::FilePath
, std::string
>::reverse_iterator
potential_parent(
329 path_to_name_map_
.upper_bound(path
));
330 if (potential_parent
!= path_to_name_map_
.rend()) {
331 if (potential_parent
->first
== path
||
332 potential_parent
->first
.IsParent(path
)) {
337 std::map
<base::FilePath
, std::string
>::iterator potential_child
=
338 path_to_name_map_
.upper_bound(path
);
339 if (potential_child
!= path_to_name_map_
.end()) {
340 if (potential_child
->first
== path
||
341 path
.IsParent(potential_child
->first
)) {
350 } // namespace storage