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/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 "webkit/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 // Wrapper around ref-counted ExternalMountPoints that will be used to lazily
34 // create and initialize LazyInstance system ExternalMountPoints.
35 class SystemMountPointsLazyWrapper {
37 SystemMountPointsLazyWrapper()
38 : system_mount_points_(fileapi::ExternalMountPoints::CreateRefCounted()) {
41 ~SystemMountPointsLazyWrapper() {}
43 fileapi::ExternalMountPoints* get() {
44 return system_mount_points_.get();
48 scoped_refptr<fileapi::ExternalMountPoints> system_mount_points_;
51 base::LazyInstance<SystemMountPointsLazyWrapper>::Leaky
52 g_external_mount_points = LAZY_INSTANCE_INITIALIZER;
58 class ExternalMountPoints::Instance {
60 Instance(FileSystemType type, const base::FilePath& path)
61 : type_(type), path_(path.StripTrailingSeparators()) {}
64 FileSystemType type() const { return type_; }
65 const base::FilePath& path() const { return path_; }
68 const FileSystemType type_;
69 const base::FilePath path_;
71 DISALLOW_COPY_AND_ASSIGN(Instance);
74 //--------------------------------------------------------------------------
77 ExternalMountPoints* ExternalMountPoints::GetSystemInstance() {
78 return g_external_mount_points.Pointer()->get();
82 scoped_refptr<ExternalMountPoints> ExternalMountPoints::CreateRefCounted() {
83 return new ExternalMountPoints();
86 bool ExternalMountPoints::RegisterFileSystem(
87 const std::string& mount_name,
89 const base::FilePath& path_in) {
90 base::AutoLock locker(lock_);
92 base::FilePath path = NormalizeFilePath(path_in);
93 if (!ValidateNewMountPoint(mount_name, path))
96 instance_map_[mount_name] = new Instance(type, path);
98 path_to_name_map_.insert(std::make_pair(path, mount_name));
102 bool ExternalMountPoints::HandlesFileSystemMountType(
103 FileSystemType type) const {
104 return type == kFileSystemTypeExternal ||
105 type == kFileSystemTypeNativeForPlatformApp;
108 bool ExternalMountPoints::RevokeFileSystem(const std::string& mount_name) {
109 base::AutoLock locker(lock_);
110 NameToInstance::iterator found = instance_map_.find(mount_name);
111 if (found == instance_map_.end())
113 Instance* instance = found->second;
114 path_to_name_map_.erase(NormalizeFilePath(instance->path()));
115 delete found->second;
116 instance_map_.erase(found);
120 bool ExternalMountPoints::GetRegisteredPath(
121 const std::string& filesystem_id, base::FilePath* path) const {
123 base::AutoLock locker(lock_);
124 NameToInstance::const_iterator found = instance_map_.find(filesystem_id);
125 if (found == instance_map_.end())
127 *path = found->second->path();
131 bool ExternalMountPoints::CrackVirtualPath(const base::FilePath& virtual_path,
132 std::string* mount_name,
133 FileSystemType* type,
134 base::FilePath* path) const {
138 // The path should not contain any '..' references.
139 if (virtual_path.ReferencesParent())
142 // The virtual_path should comprise of <mount_name> and <relative_path> parts.
143 std::vector<base::FilePath::StringType> components;
144 virtual_path.GetComponents(&components);
145 if (components.size() < 1)
148 std::vector<base::FilePath::StringType>::iterator component_iter =
150 std::string maybe_mount_name =
151 base::FilePath(*component_iter++).MaybeAsASCII();
152 if (maybe_mount_name.empty())
155 base::FilePath cracked_path;
157 base::AutoLock locker(lock_);
158 NameToInstance::const_iterator found_instance =
159 instance_map_.find(maybe_mount_name);
160 if (found_instance == instance_map_.end())
163 *mount_name = maybe_mount_name;
164 const Instance* instance = found_instance->second;
166 *type = instance->type();
167 cracked_path = instance->path();
170 for (; component_iter != components.end(); ++component_iter)
171 cracked_path = cracked_path.Append(*component_iter);
172 *path = cracked_path;
176 FileSystemURL ExternalMountPoints::CrackURL(const GURL& url) const {
177 FileSystemURL filesystem_url = FileSystemURL(url);
178 if (!filesystem_url.is_valid())
179 return FileSystemURL();
180 return CrackFileSystemURL(filesystem_url);
183 FileSystemURL ExternalMountPoints::CreateCrackedFileSystemURL(
186 const base::FilePath& path) const {
187 return CrackFileSystemURL(FileSystemURL(origin, type, path));
190 void ExternalMountPoints::AddMountPointInfosTo(
191 std::vector<MountPointInfo>* mount_points) const {
192 base::AutoLock locker(lock_);
193 DCHECK(mount_points);
194 for (NameToInstance::const_iterator iter = instance_map_.begin();
195 iter != instance_map_.end(); ++iter) {
196 mount_points->push_back(MountPointInfo(iter->first, iter->second->path()));
200 bool ExternalMountPoints::GetVirtualPath(const base::FilePath& path_in,
201 base::FilePath* virtual_path) const {
202 DCHECK(virtual_path);
204 base::AutoLock locker(lock_);
206 base::FilePath path = NormalizeFilePath(path_in);
207 std::map<base::FilePath, std::string>::const_reverse_iterator iter(
208 path_to_name_map_.upper_bound(path));
209 if (iter == path_to_name_map_.rend())
212 *virtual_path = CreateVirtualRootPath(iter->second);
213 if (iter->first == path)
215 return iter->first.AppendRelativePath(path, virtual_path);
218 base::FilePath ExternalMountPoints::CreateVirtualRootPath(
219 const std::string& mount_name) const {
220 return base::FilePath().AppendASCII(mount_name);
223 FileSystemURL ExternalMountPoints::CreateExternalFileSystemURL(
225 const std::string& mount_name,
226 const base::FilePath& path) const {
227 return CreateCrackedFileSystemURL(
229 fileapi::kFileSystemTypeExternal,
230 // Avoid using FilePath::Append as path may be an absolute path.
232 CreateVirtualRootPath(mount_name).value() +
233 base::FilePath::kSeparators[0] + path.value()));
236 ExternalMountPoints::ExternalMountPoints() {}
238 ExternalMountPoints::~ExternalMountPoints() {
239 STLDeleteContainerPairSecondPointers(instance_map_.begin(),
240 instance_map_.end());
243 FileSystemURL ExternalMountPoints::CrackFileSystemURL(
244 const FileSystemURL& url) const {
245 if (!HandlesFileSystemMountType(url.type()))
246 return FileSystemURL();
248 base::FilePath virtual_path = url.path();
249 if (url.type() == kFileSystemTypeNativeForPlatformApp) {
250 #if defined(OS_CHROMEOS)
251 // On Chrome OS, find a mount point and virtual path for the external fs.
252 if (!GetVirtualPath(url.path(), &virtual_path))
253 return FileSystemURL();
255 // On other OS, it is simply a native local path.
256 return FileSystemURL(
257 url.origin(), url.mount_type(), url.virtual_path(),
258 url.mount_filesystem_id(), kFileSystemTypeNativeLocal,
259 url.path(), url.filesystem_id());
263 std::string mount_name;
264 FileSystemType cracked_type;
265 base::FilePath cracked_path;
267 if (!CrackVirtualPath(virtual_path, &mount_name, &cracked_type,
269 return FileSystemURL();
272 return FileSystemURL(
273 url.origin(), url.mount_type(), url.virtual_path(),
274 !url.filesystem_id().empty() ? url.filesystem_id() : mount_name,
275 cracked_type, cracked_path, mount_name);
278 bool ExternalMountPoints::ValidateNewMountPoint(const std::string& mount_name,
279 const base::FilePath& path) {
280 lock_.AssertAcquired();
282 // Mount name must not be empty.
283 if (mount_name.empty())
286 // Verify there is no registered mount point with the same name.
287 NameToInstance::iterator found = instance_map_.find(mount_name);
288 if (found != instance_map_.end())
291 // Allow empty paths.
295 // Verify path is legal.
296 if (path.ReferencesParent() || !path.IsAbsolute())
299 // Check there the new path does not overlap with one of the existing ones.
300 std::map<base::FilePath, std::string>::reverse_iterator potential_parent(
301 path_to_name_map_.upper_bound(path));
302 if (potential_parent != path_to_name_map_.rend()) {
303 if (potential_parent->first == path ||
304 potential_parent->first.IsParent(path)) {
309 std::map<base::FilePath, std::string>::iterator potential_child =
310 path_to_name_map_.upper_bound(path);
311 if (potential_child == path_to_name_map_.end())
313 return !(potential_child->first == path) &&
314 !path.IsParent(potential_child->first);
317 } // namespace fileapi