• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/fileapi/external_mount_points.h"
6 
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"
12 
13 namespace {
14 
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/").
NormalizeFilePath(const base::FilePath & path)22 base::FilePath NormalizeFilePath(const base::FilePath& path) {
23   if (path.empty())
24     return path;
25 
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("/"));
29 
30   return base::FilePath(path_str).NormalizePathSeparators();
31 }
32 
IsOverlappingMountPathForbidden(storage::FileSystemType type)33 bool IsOverlappingMountPathForbidden(storage::FileSystemType type) {
34   return type != storage::kFileSystemTypeNativeMedia &&
35          type != storage::kFileSystemTypeDeviceMedia;
36 }
37 
38 // Wrapper around ref-counted ExternalMountPoints that will be used to lazily
39 // create and initialize LazyInstance system ExternalMountPoints.
40 class SystemMountPointsLazyWrapper {
41  public:
SystemMountPointsLazyWrapper()42   SystemMountPointsLazyWrapper()
43       : system_mount_points_(storage::ExternalMountPoints::CreateRefCounted()) {
44   }
45 
~SystemMountPointsLazyWrapper()46   ~SystemMountPointsLazyWrapper() {}
47 
get()48   storage::ExternalMountPoints* get() { return system_mount_points_.get(); }
49 
50  private:
51   scoped_refptr<storage::ExternalMountPoints> system_mount_points_;
52 };
53 
54 base::LazyInstance<SystemMountPointsLazyWrapper>::Leaky
55     g_external_mount_points = LAZY_INSTANCE_INITIALIZER;
56 
57 }  // namespace
58 
59 namespace storage {
60 
61 class ExternalMountPoints::Instance {
62  public:
Instance(FileSystemType type,const base::FilePath & path,const FileSystemMountOption & mount_option)63   Instance(FileSystemType type,
64            const base::FilePath& path,
65            const FileSystemMountOption& mount_option)
66       : type_(type),
67         path_(path.StripTrailingSeparators()),
68         mount_option_(mount_option) {}
~Instance()69   ~Instance() {}
70 
type() const71   FileSystemType type() const { return type_; }
path() const72   const base::FilePath& path() const { return path_; }
mount_option() const73   const FileSystemMountOption& mount_option() const { return mount_option_; }
74 
75  private:
76   const FileSystemType type_;
77   const base::FilePath path_;
78   const FileSystemMountOption mount_option_;
79 
80   DISALLOW_COPY_AND_ASSIGN(Instance);
81 };
82 
83 //--------------------------------------------------------------------------
84 
85 // static
GetSystemInstance()86 ExternalMountPoints* ExternalMountPoints::GetSystemInstance() {
87   return g_external_mount_points.Pointer()->get();
88 }
89 
90 // static
CreateRefCounted()91 scoped_refptr<ExternalMountPoints> ExternalMountPoints::CreateRefCounted() {
92   return new ExternalMountPoints();
93 }
94 
RegisterFileSystem(const std::string & mount_name,FileSystemType type,const FileSystemMountOption & mount_option,const base::FilePath & path_in)95 bool ExternalMountPoints::RegisterFileSystem(
96     const std::string& mount_name,
97     FileSystemType type,
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);
103 
104   base::AutoLock locker(lock_);
105 
106   base::FilePath path = NormalizeFilePath(path_in);
107   if (!ValidateNewMountPoint(mount_name, type, path))
108     return false;
109 
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));
113   return true;
114 }
115 
HandlesFileSystemMountType(FileSystemType type) const116 bool ExternalMountPoints::HandlesFileSystemMountType(
117     FileSystemType type) const {
118   return type == kFileSystemTypeExternal ||
119          type == kFileSystemTypeNativeForPlatformApp;
120 }
121 
RevokeFileSystem(const std::string & mount_name)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())
126     return false;
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);
132   return true;
133 }
134 
GetRegisteredPath(const std::string & filesystem_id,base::FilePath * path) const135 bool ExternalMountPoints::GetRegisteredPath(
136     const std::string& filesystem_id, base::FilePath* path) const {
137   DCHECK(path);
138   base::AutoLock locker(lock_);
139   NameToInstance::const_iterator found = instance_map_.find(filesystem_id);
140   if (found == instance_map_.end())
141     return false;
142   *path = found->second->path();
143   return true;
144 }
145 
CrackVirtualPath(const base::FilePath & virtual_path,std::string * mount_name,FileSystemType * type,std::string * cracked_id,base::FilePath * path,FileSystemMountOption * mount_option) const146 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 {
153   DCHECK(mount_name);
154   DCHECK(path);
155 
156   // The path should not contain any '..' references.
157   if (virtual_path.ReferencesParent())
158     return false;
159 
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)
164     return false;
165 
166   std::vector<base::FilePath::StringType>::iterator component_iter =
167       components.begin();
168   std::string maybe_mount_name =
169       base::FilePath(*component_iter++).MaybeAsASCII();
170   if (maybe_mount_name.empty())
171     return false;
172 
173   base::FilePath cracked_path;
174   {
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())
179       return false;
180 
181     *mount_name = maybe_mount_name;
182     const Instance* instance = found_instance->second;
183     if (type)
184       *type = instance->type();
185     cracked_path = instance->path();
186     *mount_option = instance->mount_option();
187   }
188 
189   for (; component_iter != components.end(); ++component_iter)
190     cracked_path = cracked_path.Append(*component_iter);
191   *path = cracked_path;
192   return true;
193 }
194 
CrackURL(const GURL & url) const195 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);
200 }
201 
CreateCrackedFileSystemURL(const GURL & origin,FileSystemType type,const base::FilePath & path) const202 FileSystemURL ExternalMountPoints::CreateCrackedFileSystemURL(
203     const GURL& origin,
204     FileSystemType type,
205     const base::FilePath& path) const {
206   return CrackFileSystemURL(FileSystemURL(origin, type, path));
207 }
208 
AddMountPointInfosTo(std::vector<MountPointInfo> * mount_points) const209 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()));
216   }
217 }
218 
GetVirtualPath(const base::FilePath & path_in,base::FilePath * virtual_path) const219 bool ExternalMountPoints::GetVirtualPath(const base::FilePath& path_in,
220                                          base::FilePath* virtual_path) const {
221   DCHECK(virtual_path);
222 
223   base::AutoLock locker(lock_);
224 
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())
229     return false;
230 
231   *virtual_path = CreateVirtualRootPath(iter->second);
232   if (iter->first == path)
233     return true;
234   return iter->first.AppendRelativePath(path, virtual_path);
235 }
236 
CreateVirtualRootPath(const std::string & mount_name) const237 base::FilePath ExternalMountPoints::CreateVirtualRootPath(
238     const std::string& mount_name) const {
239   return base::FilePath().AppendASCII(mount_name);
240 }
241 
CreateExternalFileSystemURL(const GURL & origin,const std::string & mount_name,const base::FilePath & path) const242 FileSystemURL ExternalMountPoints::CreateExternalFileSystemURL(
243     const GURL& origin,
244     const std::string& mount_name,
245     const base::FilePath& path) const {
246   return CreateCrackedFileSystemURL(
247       origin,
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()));
252 }
253 
RevokeAllFileSystems()254 void ExternalMountPoints::RevokeAllFileSystems() {
255   NameToInstance instance_map_copy;
256   {
257     base::AutoLock locker(lock_);
258     instance_map_copy = instance_map_;
259     instance_map_.clear();
260     path_to_name_map_.clear();
261   }
262   STLDeleteContainerPairSecondPointers(instance_map_copy.begin(),
263                                        instance_map_copy.end());
264 }
265 
ExternalMountPoints()266 ExternalMountPoints::ExternalMountPoints() {}
267 
~ExternalMountPoints()268 ExternalMountPoints::~ExternalMountPoints() {
269   STLDeleteContainerPairSecondPointers(instance_map_.begin(),
270                                        instance_map_.end());
271 }
272 
CrackFileSystemURL(const FileSystemURL & url) const273 FileSystemURL ExternalMountPoints::CrackFileSystemURL(
274     const FileSystemURL& url) const {
275   if (!HandlesFileSystemMountType(url.type()))
276     return FileSystemURL();
277 
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();
284 #else
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());
290 #endif
291   }
292 
293   std::string mount_name;
294   FileSystemType cracked_type;
295   std::string cracked_id;
296   base::FilePath cracked_path;
297   FileSystemMountOption cracked_mount_option;
298 
299   if (!CrackVirtualPath(virtual_path, &mount_name, &cracked_type,
300                         &cracked_id, &cracked_path, &cracked_mount_option)) {
301     return FileSystemURL();
302   }
303 
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);
309 }
310 
ValidateNewMountPoint(const std::string & mount_name,FileSystemType type,const base::FilePath & path)311 bool ExternalMountPoints::ValidateNewMountPoint(const std::string& mount_name,
312                                                 FileSystemType type,
313                                                 const base::FilePath& path) {
314   lock_.AssertAcquired();
315 
316   // Mount name must not be empty.
317   if (mount_name.empty())
318     return false;
319 
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())
323     return false;
324 
325   // Allow empty paths.
326   if (path.empty())
327     return true;
328 
329   // Verify path is legal.
330   if (path.ReferencesParent() || !path.IsAbsolute())
331     return false;
332 
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)) {
340         return false;
341       }
342     }
343 
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)) {
349         return false;
350       }
351     }
352   }
353 
354   return true;
355 }
356 
357 }  // namespace storage
358