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.
4
5 #include "storage/browser/file_system/external_mount_points.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/files/file_path.h"
11 #include "base/lazy_instance.h"
12 #include "base/macros.h"
13 #include "build/chromeos_buildflags.h"
14 #include "storage/browser/file_system/file_system_url.h"
15
16 namespace storage {
17
18 namespace {
19
20 // Normalizes file path so it has normalized separators and ends with exactly
21 // one separator. Paths have to be normalized this way for use in
22 // GetVirtualPath method. Separators cannot be completely stripped, or
23 // GetVirtualPath could not working in some edge cases.
24 // For example, /a/b/c(1)/d would be erroneously resolved as c/d if the
25 // following mount points were registered: "/a/b/c", "/a/b/c(1)". (Note:
26 // "/a/b/c" < "/a/b/c(1)" < "/a/b/c/").
NormalizeFilePath(const base::FilePath & path)27 base::FilePath NormalizeFilePath(const base::FilePath& path) {
28 if (path.empty())
29 return path;
30
31 base::FilePath::StringType path_str = path.StripTrailingSeparators().value();
32 if (!base::FilePath::IsSeparator(path_str.back()))
33 path_str.append(FILE_PATH_LITERAL("/"));
34
35 return base::FilePath(path_str).NormalizePathSeparators();
36 }
37
IsOverlappingMountPathForbidden(FileSystemType type)38 bool IsOverlappingMountPathForbidden(FileSystemType type) {
39 return type != kFileSystemTypeNativeMedia &&
40 type != kFileSystemTypeDeviceMedia;
41 }
42
43 // Wrapper around ref-counted ExternalMountPoints that will be used to lazily
44 // create and initialize LazyInstance system ExternalMountPoints.
45 class SystemMountPointsLazyWrapper {
46 public:
SystemMountPointsLazyWrapper()47 SystemMountPointsLazyWrapper()
48 : system_mount_points_(ExternalMountPoints::CreateRefCounted()) {}
49
50 ~SystemMountPointsLazyWrapper() = default;
51
get()52 ExternalMountPoints* get() { return system_mount_points_.get(); }
53
54 private:
55 scoped_refptr<ExternalMountPoints> system_mount_points_;
56 };
57
58 base::LazyInstance<SystemMountPointsLazyWrapper>::Leaky
59 g_external_mount_points = LAZY_INSTANCE_INITIALIZER;
60
61 } // namespace
62
63 class ExternalMountPoints::Instance {
64 public:
Instance(FileSystemType type,const base::FilePath & path,const FileSystemMountOption & mount_option)65 Instance(FileSystemType type,
66 const base::FilePath& path,
67 const FileSystemMountOption& mount_option)
68 : type_(type),
69 path_(path.StripTrailingSeparators()),
70 mount_option_(mount_option) {}
71 ~Instance() = default;
72
type() const73 FileSystemType type() const { return type_; }
path() const74 const base::FilePath& path() const { return path_; }
mount_option() const75 const FileSystemMountOption& mount_option() const { return mount_option_; }
76
77 private:
78 const FileSystemType type_;
79 const base::FilePath path_;
80 const FileSystemMountOption mount_option_;
81
82 DISALLOW_COPY_AND_ASSIGN(Instance);
83 };
84
85 //--------------------------------------------------------------------------
86
87 // static
GetSystemInstance()88 ExternalMountPoints* ExternalMountPoints::GetSystemInstance() {
89 return g_external_mount_points.Pointer()->get();
90 }
91
92 // static
CreateRefCounted()93 scoped_refptr<ExternalMountPoints> ExternalMountPoints::CreateRefCounted() {
94 return new ExternalMountPoints();
95 }
96
RegisterFileSystem(const std::string & mount_name,FileSystemType type,const FileSystemMountOption & mount_option,const base::FilePath & path_in)97 bool ExternalMountPoints::RegisterFileSystem(
98 const std::string& mount_name,
99 FileSystemType type,
100 const FileSystemMountOption& mount_option,
101 const base::FilePath& path_in) {
102 base::AutoLock locker(lock_);
103
104 base::FilePath path = NormalizeFilePath(path_in);
105 if (!ValidateNewMountPoint(mount_name, type, path))
106 return false;
107
108 instance_map_[mount_name] =
109 std::make_unique<Instance>(type, path, mount_option);
110 if (!path.empty() && IsOverlappingMountPathForbidden(type))
111 path_to_name_map_.insert(std::make_pair(path, mount_name));
112 return true;
113 }
114
HandlesFileSystemMountType(FileSystemType type) const115 bool ExternalMountPoints::HandlesFileSystemMountType(
116 FileSystemType type) const {
117 return type == kFileSystemTypeExternal ||
118 type == kFileSystemTypeNativeForPlatformApp;
119 }
120
RevokeFileSystem(const std::string & mount_name)121 bool ExternalMountPoints::RevokeFileSystem(const std::string& mount_name) {
122 base::AutoLock locker(lock_);
123 auto found = instance_map_.find(mount_name);
124 if (found == instance_map_.end())
125 return false;
126 Instance* instance = found->second.get();
127 if (IsOverlappingMountPathForbidden(instance->type()))
128 path_to_name_map_.erase(NormalizeFilePath(instance->path()));
129 instance_map_.erase(found);
130 return true;
131 }
132
GetRegisteredPath(const std::string & filesystem_id,base::FilePath * path) const133 bool ExternalMountPoints::GetRegisteredPath(const std::string& filesystem_id,
134 base::FilePath* path) const {
135 DCHECK(path);
136 base::AutoLock locker(lock_);
137 auto found = instance_map_.find(filesystem_id);
138 if (found == instance_map_.end())
139 return false;
140 *path = found->second->path();
141 return true;
142 }
143
CrackVirtualPath(const base::FilePath & virtual_path,std::string * mount_name,FileSystemType * type,std::string * cracked_id,base::FilePath * path,FileSystemMountOption * mount_option) const144 bool ExternalMountPoints::CrackVirtualPath(
145 const base::FilePath& virtual_path,
146 std::string* mount_name,
147 FileSystemType* type,
148 std::string* cracked_id,
149 base::FilePath* path,
150 FileSystemMountOption* mount_option) const {
151 DCHECK(mount_name);
152 DCHECK(path);
153
154 // The path should not contain any '..' references.
155 if (virtual_path.ReferencesParent())
156 return false;
157
158 // The virtual_path should comprise of <mount_name> and <relative_path> parts.
159 std::vector<base::FilePath::StringType> components;
160 virtual_path.GetComponents(&components);
161 if (components.size() < 1)
162 return false;
163
164 auto component_iter = components.begin();
165 std::string maybe_mount_name =
166 base::FilePath(*component_iter++).AsUTF8Unsafe();
167
168 base::FilePath cracked_path;
169 {
170 base::AutoLock locker(lock_);
171 auto found_instance = instance_map_.find(maybe_mount_name);
172 if (found_instance == instance_map_.end())
173 return false;
174
175 *mount_name = maybe_mount_name;
176 const Instance* instance = found_instance->second.get();
177 if (type)
178 *type = instance->type();
179 cracked_path = instance->path();
180 *mount_option = instance->mount_option();
181 }
182
183 for (; component_iter != components.end(); ++component_iter)
184 cracked_path = cracked_path.Append(*component_iter);
185 *path = cracked_path;
186 return true;
187 }
188
CrackURL(const GURL & url) const189 FileSystemURL ExternalMountPoints::CrackURL(const GURL& url) const {
190 FileSystemURL filesystem_url = FileSystemURL(url);
191 if (!filesystem_url.is_valid())
192 return FileSystemURL();
193 return CrackFileSystemURL(filesystem_url);
194 }
195
CreateCrackedFileSystemURL(const url::Origin & origin,FileSystemType type,const base::FilePath & virtual_path) const196 FileSystemURL ExternalMountPoints::CreateCrackedFileSystemURL(
197 const url::Origin& origin,
198 FileSystemType type,
199 const base::FilePath& virtual_path) const {
200 return CrackFileSystemURL(FileSystemURL(origin, type, virtual_path));
201 }
202
AddMountPointInfosTo(std::vector<MountPointInfo> * mount_points) const203 void ExternalMountPoints::AddMountPointInfosTo(
204 std::vector<MountPointInfo>* mount_points) const {
205 base::AutoLock locker(lock_);
206 DCHECK(mount_points);
207 for (const auto& pair : instance_map_) {
208 mount_points->push_back(MountPointInfo(pair.first, pair.second->path()));
209 }
210 }
211
GetVirtualPath(const base::FilePath & path_in,base::FilePath * virtual_path) const212 bool ExternalMountPoints::GetVirtualPath(const base::FilePath& path_in,
213 base::FilePath* virtual_path) const {
214 DCHECK(virtual_path);
215
216 base::AutoLock locker(lock_);
217
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;
223
224 *virtual_path = CreateVirtualRootPath(iter->second);
225 if (iter->first == path)
226 return true;
227 return iter->first.AppendRelativePath(path, virtual_path);
228 }
229
CreateVirtualRootPath(const std::string & mount_name) const230 base::FilePath ExternalMountPoints::CreateVirtualRootPath(
231 const std::string& mount_name) const {
232 return base::FilePath().Append(base::FilePath::FromUTF8Unsafe(mount_name));
233 }
234
CreateExternalFileSystemURL(const url::Origin & origin,const std::string & mount_name,const base::FilePath & path) const235 FileSystemURL ExternalMountPoints::CreateExternalFileSystemURL(
236 const url::Origin& origin,
237 const std::string& mount_name,
238 const base::FilePath& path) const {
239 return CreateCrackedFileSystemURL(
240 origin, kFileSystemTypeExternal,
241 // Avoid using FilePath::Append as path may be an absolute path.
242 base::FilePath(CreateVirtualRootPath(mount_name).value() +
243 base::FilePath::kSeparators[0] + path.value()));
244 }
245
RevokeAllFileSystems()246 void ExternalMountPoints::RevokeAllFileSystems() {
247 NameToInstance instance_map_copy;
248 {
249 base::AutoLock locker(lock_);
250 // This swap moves the contents of instance_map_ to the local variable so
251 // they can be freed outside the lock.
252 instance_map_copy.swap(instance_map_);
253 path_to_name_map_.clear();
254 }
255 }
256
257 ExternalMountPoints::ExternalMountPoints() = default;
258
259 ExternalMountPoints::~ExternalMountPoints() = default;
260
CrackFileSystemURL(const FileSystemURL & url) const261 FileSystemURL ExternalMountPoints::CrackFileSystemURL(
262 const FileSystemURL& url) const {
263 if (!HandlesFileSystemMountType(url.type()))
264 return FileSystemURL();
265
266 base::FilePath virtual_path = url.path();
267 if (url.type() == kFileSystemTypeNativeForPlatformApp) {
268 #if BUILDFLAG(IS_CHROMEOS_ASH)
269 // On Chrome OS, find a mount point and virtual path for the external fs.
270 if (!GetVirtualPath(url.path(), &virtual_path))
271 return FileSystemURL();
272 #else
273 // On other OS, it is simply a native local path.
274 return FileSystemURL(url.origin(), url.mount_type(), url.virtual_path(),
275 url.mount_filesystem_id(), kFileSystemTypeNativeLocal,
276 url.path(), url.filesystem_id(), url.mount_option());
277 #endif
278 }
279
280 std::string mount_name;
281 FileSystemType cracked_type;
282 std::string cracked_id;
283 base::FilePath cracked_path;
284 FileSystemMountOption cracked_mount_option;
285
286 if (!CrackVirtualPath(virtual_path, &mount_name, &cracked_type, &cracked_id,
287 &cracked_path, &cracked_mount_option)) {
288 return FileSystemURL();
289 }
290
291 return FileSystemURL(
292 url.origin(), url.mount_type(), url.virtual_path(),
293 !url.filesystem_id().empty() ? url.filesystem_id() : mount_name,
294 cracked_type, cracked_path, cracked_id.empty() ? mount_name : cracked_id,
295 cracked_mount_option);
296 }
297
ValidateNewMountPoint(const std::string & mount_name,FileSystemType type,const base::FilePath & path)298 bool ExternalMountPoints::ValidateNewMountPoint(const std::string& mount_name,
299 FileSystemType type,
300 const base::FilePath& path) {
301 lock_.AssertAcquired();
302
303 // Mount name must not be empty.
304 if (mount_name.empty())
305 return false;
306
307 // Verify there is no registered mount point with the same name.
308 auto found = instance_map_.find(mount_name);
309 if (found != instance_map_.end())
310 return false;
311
312 // Allow empty paths.
313 if (path.empty())
314 return true;
315
316 // Verify path is legal.
317 if (path.ReferencesParent() || !path.IsAbsolute())
318 return false;
319
320 if (IsOverlappingMountPathForbidden(type)) {
321 // Check there the new path does not overlap with one of the existing ones.
322 std::map<base::FilePath, std::string>::reverse_iterator potential_parent(
323 path_to_name_map_.upper_bound(path));
324 if (potential_parent != path_to_name_map_.rend()) {
325 if (potential_parent->first == path ||
326 potential_parent->first.IsParent(path)) {
327 return false;
328 }
329 }
330
331 auto potential_child = path_to_name_map_.upper_bound(path);
332 if (potential_child != path_to_name_map_.end()) {
333 if (potential_child->first == path ||
334 path.IsParent(potential_child->first)) {
335 return false;
336 }
337 }
338 }
339
340 return true;
341 }
342
343 } // namespace storage
344