• 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 "webkit/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 "webkit/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(fileapi::FileSystemType type)33 bool IsOverlappingMountPathForbidden(fileapi::FileSystemType type) {
34   return type != fileapi::kFileSystemTypeNativeMedia &&
35       type != fileapi::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_(fileapi::ExternalMountPoints::CreateRefCounted()) {
44   }
45 
~SystemMountPointsLazyWrapper()46   ~SystemMountPointsLazyWrapper() {}
47 
get()48   fileapi::ExternalMountPoints* get() {
49     return system_mount_points_.get();
50   }
51 
52  private:
53   scoped_refptr<fileapi::ExternalMountPoints> system_mount_points_;
54 };
55 
56 base::LazyInstance<SystemMountPointsLazyWrapper>::Leaky
57     g_external_mount_points = LAZY_INSTANCE_INITIALIZER;
58 
59 }  // namespace
60 
61 namespace fileapi {
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) {}
~Instance()71   ~Instance() {}
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   // COPY_SYNC_OPTION_SYNC is only applicable to native local file system.
103   DCHECK(type == kFileSystemTypeNativeLocal ||
104          mount_option.copy_sync_option() != COPY_SYNC_OPTION_SYNC);
105 
106   base::AutoLock locker(lock_);
107 
108   base::FilePath path = NormalizeFilePath(path_in);
109   if (!ValidateNewMountPoint(mount_name, type, path))
110     return false;
111 
112   instance_map_[mount_name] = new Instance(type, path, mount_option);
113   if (!path.empty() && IsOverlappingMountPathForbidden(type))
114     path_to_name_map_.insert(std::make_pair(path, mount_name));
115   return true;
116 }
117 
HandlesFileSystemMountType(FileSystemType type) const118 bool ExternalMountPoints::HandlesFileSystemMountType(
119     FileSystemType type) const {
120   return type == kFileSystemTypeExternal ||
121          type == kFileSystemTypeNativeForPlatformApp;
122 }
123 
RevokeFileSystem(const std::string & mount_name)124 bool ExternalMountPoints::RevokeFileSystem(const std::string& mount_name) {
125   base::AutoLock locker(lock_);
126   NameToInstance::iterator found = instance_map_.find(mount_name);
127   if (found == instance_map_.end())
128     return false;
129   Instance* instance = found->second;
130   if (IsOverlappingMountPathForbidden(instance->type()))
131     path_to_name_map_.erase(NormalizeFilePath(instance->path()));
132   delete found->second;
133   instance_map_.erase(found);
134   return true;
135 }
136 
GetRegisteredPath(const std::string & filesystem_id,base::FilePath * path) const137 bool ExternalMountPoints::GetRegisteredPath(
138     const std::string& filesystem_id, base::FilePath* path) const {
139   DCHECK(path);
140   base::AutoLock locker(lock_);
141   NameToInstance::const_iterator found = instance_map_.find(filesystem_id);
142   if (found == instance_map_.end())
143     return false;
144   *path = found->second->path();
145   return true;
146 }
147 
CrackVirtualPath(const base::FilePath & virtual_path,std::string * mount_name,FileSystemType * type,std::string * cracked_id,base::FilePath * path,FileSystemMountOption * mount_option) const148 bool ExternalMountPoints::CrackVirtualPath(
149     const base::FilePath& virtual_path,
150     std::string* mount_name,
151     FileSystemType* type,
152     std::string* cracked_id,
153     base::FilePath* path,
154     FileSystemMountOption* mount_option) const {
155   DCHECK(mount_name);
156   DCHECK(path);
157 
158   // The path should not contain any '..' references.
159   if (virtual_path.ReferencesParent())
160     return false;
161 
162   // The virtual_path should comprise of <mount_name> and <relative_path> parts.
163   std::vector<base::FilePath::StringType> components;
164   virtual_path.GetComponents(&components);
165   if (components.size() < 1)
166     return false;
167 
168   std::vector<base::FilePath::StringType>::iterator component_iter =
169       components.begin();
170   std::string maybe_mount_name =
171       base::FilePath(*component_iter++).MaybeAsASCII();
172   if (maybe_mount_name.empty())
173     return false;
174 
175   base::FilePath cracked_path;
176   {
177     base::AutoLock locker(lock_);
178     NameToInstance::const_iterator found_instance =
179         instance_map_.find(maybe_mount_name);
180     if (found_instance == instance_map_.end())
181       return false;
182 
183     *mount_name = maybe_mount_name;
184     const Instance* instance = found_instance->second;
185     if (type)
186       *type = instance->type();
187     cracked_path = instance->path();
188     *mount_option = instance->mount_option();
189   }
190 
191   for (; component_iter != components.end(); ++component_iter)
192     cracked_path = cracked_path.Append(*component_iter);
193   *path = cracked_path;
194   return true;
195 }
196 
CrackURL(const GURL & url) const197 FileSystemURL ExternalMountPoints::CrackURL(const GURL& url) const {
198   FileSystemURL filesystem_url = FileSystemURL(url);
199   if (!filesystem_url.is_valid())
200     return FileSystemURL();
201   return CrackFileSystemURL(filesystem_url);
202 }
203 
CreateCrackedFileSystemURL(const GURL & origin,FileSystemType type,const base::FilePath & path) const204 FileSystemURL ExternalMountPoints::CreateCrackedFileSystemURL(
205     const GURL& origin,
206     FileSystemType type,
207     const base::FilePath& path) const {
208   return CrackFileSystemURL(FileSystemURL(origin, type, path));
209 }
210 
AddMountPointInfosTo(std::vector<MountPointInfo> * mount_points) const211 void ExternalMountPoints::AddMountPointInfosTo(
212     std::vector<MountPointInfo>* mount_points) const {
213   base::AutoLock locker(lock_);
214   DCHECK(mount_points);
215   for (NameToInstance::const_iterator iter = instance_map_.begin();
216        iter != instance_map_.end(); ++iter) {
217     mount_points->push_back(MountPointInfo(iter->first, iter->second->path()));
218   }
219 }
220 
GetVirtualPath(const base::FilePath & path_in,base::FilePath * virtual_path) const221 bool ExternalMountPoints::GetVirtualPath(const base::FilePath& path_in,
222                                          base::FilePath* virtual_path) const {
223   DCHECK(virtual_path);
224 
225   base::AutoLock locker(lock_);
226 
227   base::FilePath path = NormalizeFilePath(path_in);
228   std::map<base::FilePath, std::string>::const_reverse_iterator iter(
229       path_to_name_map_.upper_bound(path));
230   if (iter == path_to_name_map_.rend())
231     return false;
232 
233   *virtual_path = CreateVirtualRootPath(iter->second);
234   if (iter->first == path)
235     return true;
236   return iter->first.AppendRelativePath(path, virtual_path);
237 }
238 
CreateVirtualRootPath(const std::string & mount_name) const239 base::FilePath ExternalMountPoints::CreateVirtualRootPath(
240     const std::string& mount_name) const {
241   return base::FilePath().AppendASCII(mount_name);
242 }
243 
CreateExternalFileSystemURL(const GURL & origin,const std::string & mount_name,const base::FilePath & path) const244 FileSystemURL ExternalMountPoints::CreateExternalFileSystemURL(
245     const GURL& origin,
246     const std::string& mount_name,
247     const base::FilePath& path) const {
248   return CreateCrackedFileSystemURL(
249       origin,
250       fileapi::kFileSystemTypeExternal,
251       // Avoid using FilePath::Append as path may be an absolute path.
252       base::FilePath(
253           CreateVirtualRootPath(mount_name).value() +
254           base::FilePath::kSeparators[0] + path.value()));
255 }
256 
RevokeAllFileSystems()257 void ExternalMountPoints::RevokeAllFileSystems() {
258   NameToInstance instance_map_copy;
259   {
260     base::AutoLock locker(lock_);
261     instance_map_copy = instance_map_;
262     instance_map_.clear();
263     path_to_name_map_.clear();
264   }
265   STLDeleteContainerPairSecondPointers(instance_map_copy.begin(),
266                                        instance_map_copy.end());
267 }
268 
ExternalMountPoints()269 ExternalMountPoints::ExternalMountPoints() {}
270 
~ExternalMountPoints()271 ExternalMountPoints::~ExternalMountPoints() {
272   STLDeleteContainerPairSecondPointers(instance_map_.begin(),
273                                        instance_map_.end());
274 }
275 
CrackFileSystemURL(const FileSystemURL & url) const276 FileSystemURL ExternalMountPoints::CrackFileSystemURL(
277     const FileSystemURL& url) const {
278   if (!HandlesFileSystemMountType(url.type()))
279     return FileSystemURL();
280 
281   base::FilePath virtual_path = url.path();
282   if (url.type() == kFileSystemTypeNativeForPlatformApp) {
283 #if defined(OS_CHROMEOS)
284     // On Chrome OS, find a mount point and virtual path for the external fs.
285     if (!GetVirtualPath(url.path(), &virtual_path))
286       return FileSystemURL();
287 #else
288     // On other OS, it is simply a native local path.
289     return FileSystemURL(
290         url.origin(), url.mount_type(), url.virtual_path(),
291         url.mount_filesystem_id(), kFileSystemTypeNativeLocal,
292         url.path(), url.filesystem_id(), url.mount_option());
293 #endif
294   }
295 
296   std::string mount_name;
297   FileSystemType cracked_type;
298   std::string cracked_id;
299   base::FilePath cracked_path;
300   FileSystemMountOption cracked_mount_option;
301 
302   if (!CrackVirtualPath(virtual_path, &mount_name, &cracked_type,
303                         &cracked_id, &cracked_path, &cracked_mount_option)) {
304     return FileSystemURL();
305   }
306 
307   return FileSystemURL(
308       url.origin(), url.mount_type(), url.virtual_path(),
309       !url.filesystem_id().empty() ? url.filesystem_id() : mount_name,
310       cracked_type, cracked_path,
311       cracked_id.empty() ? mount_name : cracked_id, cracked_mount_option);
312 }
313 
ValidateNewMountPoint(const std::string & mount_name,FileSystemType type,const base::FilePath & path)314 bool ExternalMountPoints::ValidateNewMountPoint(const std::string& mount_name,
315                                                 FileSystemType type,
316                                                 const base::FilePath& path) {
317   lock_.AssertAcquired();
318 
319   // Mount name must not be empty.
320   if (mount_name.empty())
321     return false;
322 
323   // Verify there is no registered mount point with the same name.
324   NameToInstance::iterator found = instance_map_.find(mount_name);
325   if (found != instance_map_.end())
326     return false;
327 
328   // Allow empty paths.
329   if (path.empty())
330     return true;
331 
332   // Verify path is legal.
333   if (path.ReferencesParent() || !path.IsAbsolute())
334     return false;
335 
336   if (IsOverlappingMountPathForbidden(type)) {
337     // Check there the new path does not overlap with one of the existing ones.
338     std::map<base::FilePath, std::string>::reverse_iterator potential_parent(
339         path_to_name_map_.upper_bound(path));
340     if (potential_parent != path_to_name_map_.rend()) {
341       if (potential_parent->first == path ||
342           potential_parent->first.IsParent(path)) {
343         return false;
344       }
345     }
346 
347     std::map<base::FilePath, std::string>::iterator potential_child =
348         path_to_name_map_.upper_bound(path);
349     if (potential_child != path_to_name_map_.end()) {
350       if (potential_child->first == path ||
351           path.IsParent(potential_child->first)) {
352         return false;
353       }
354     }
355   }
356 
357   return true;
358 }
359 
360 }  // namespace fileapi
361