Include all dupe types (event when value is zero) in scan stats.
[chromium-blink-merge.git] / storage / browser / fileapi / external_mount_points.cc
blobd497b4d77c307828610c7899b47de23dabb63d6a
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"
12 namespace {
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) {
22 if (path.empty())
23 return 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 {
40 public:
41 SystemMountPointsLazyWrapper()
42 : system_mount_points_(storage::ExternalMountPoints::CreateRefCounted()) {
45 ~SystemMountPointsLazyWrapper() {}
47 storage::ExternalMountPoints* get() { return system_mount_points_.get(); }
49 private:
50 scoped_refptr<storage::ExternalMountPoints> system_mount_points_;
53 base::LazyInstance<SystemMountPointsLazyWrapper>::Leaky
54 g_external_mount_points = LAZY_INSTANCE_INITIALIZER;
56 } // namespace
58 namespace storage {
60 class ExternalMountPoints::Instance {
61 public:
62 Instance(FileSystemType type,
63 const base::FilePath& path,
64 const FileSystemMountOption& mount_option)
65 : type_(type),
66 path_(path.StripTrailingSeparators()),
67 mount_option_(mount_option) {}
68 ~Instance() {}
70 FileSystemType type() const { return type_; }
71 const base::FilePath& path() const { return path_; }
72 const FileSystemMountOption& mount_option() const { return mount_option_; }
74 private:
75 const FileSystemType type_;
76 const base::FilePath path_;
77 const FileSystemMountOption mount_option_;
79 DISALLOW_COPY_AND_ASSIGN(Instance);
82 //--------------------------------------------------------------------------
84 // static
85 ExternalMountPoints* ExternalMountPoints::GetSystemInstance() {
86 return g_external_mount_points.Pointer()->get();
89 // static
90 scoped_refptr<ExternalMountPoints> ExternalMountPoints::CreateRefCounted() {
91 return new ExternalMountPoints();
94 bool ExternalMountPoints::RegisterFileSystem(
95 const std::string& mount_name,
96 FileSystemType type,
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))
103 return false;
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));
108 return true;
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())
121 return false;
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);
127 return true;
130 bool ExternalMountPoints::GetRegisteredPath(
131 const std::string& filesystem_id, base::FilePath* path) const {
132 DCHECK(path);
133 base::AutoLock locker(lock_);
134 NameToInstance::const_iterator found = instance_map_.find(filesystem_id);
135 if (found == instance_map_.end())
136 return false;
137 *path = found->second->path();
138 return true;
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 {
148 DCHECK(mount_name);
149 DCHECK(path);
151 // The path should not contain any '..' references.
152 if (virtual_path.ReferencesParent())
153 return false;
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)
159 return false;
161 std::vector<base::FilePath::StringType>::iterator component_iter =
162 components.begin();
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())
172 return false;
174 *mount_name = maybe_mount_name;
175 const Instance* instance = found_instance->second;
176 if (type)
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;
185 return true;
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(
196 const GURL& origin,
197 FileSystemType type,
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())
222 return false;
224 *virtual_path = CreateVirtualRootPath(iter->second);
225 if (iter->first == path)
226 return true;
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(
236 const GURL& origin,
237 const std::string& mount_name,
238 const base::FilePath& path) const {
239 return CreateCrackedFileSystemURL(
240 origin,
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();
277 #else
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());
283 #endif
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,
305 FileSystemType type,
306 const base::FilePath& path) {
307 lock_.AssertAcquired();
309 // Mount name must not be empty.
310 if (mount_name.empty())
311 return false;
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())
316 return false;
318 // Allow empty paths.
319 if (path.empty())
320 return true;
322 // Verify path is legal.
323 if (path.ReferencesParent() || !path.IsAbsolute())
324 return false;
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)) {
333 return false;
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)) {
342 return false;
347 return true;
350 } // namespace storage