42b6de7754c26627d26b8ba0e1ee66ef817e2780
[platform/framework/web/crosswalk.git] / src / webkit / browser / fileapi / external_mount_points.cc
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/").
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
33 // Wrapper around ref-counted ExternalMountPoints that will be used to lazily
34 // create and initialize LazyInstance system ExternalMountPoints.
35 class SystemMountPointsLazyWrapper {
36  public:
37   SystemMountPointsLazyWrapper()
38       : system_mount_points_(fileapi::ExternalMountPoints::CreateRefCounted()) {
39   }
40
41   ~SystemMountPointsLazyWrapper() {}
42
43   fileapi::ExternalMountPoints* get() {
44     return system_mount_points_.get();
45   }
46
47  private:
48   scoped_refptr<fileapi::ExternalMountPoints> system_mount_points_;
49 };
50
51 base::LazyInstance<SystemMountPointsLazyWrapper>::Leaky
52     g_external_mount_points = LAZY_INSTANCE_INITIALIZER;
53
54 }  // namespace
55
56 namespace fileapi {
57
58 class ExternalMountPoints::Instance {
59  public:
60   Instance(FileSystemType type,
61            const base::FilePath& path,
62            const FileSystemMountOption& mount_option)
63       : type_(type),
64         path_(path.StripTrailingSeparators()),
65         mount_option_(mount_option) {}
66   ~Instance() {}
67
68   FileSystemType type() const { return type_; }
69   const base::FilePath& path() const { return path_; }
70   const FileSystemMountOption& mount_option() const { return mount_option_; }
71
72  private:
73   const FileSystemType type_;
74   const base::FilePath path_;
75   const FileSystemMountOption mount_option_;
76
77   DISALLOW_COPY_AND_ASSIGN(Instance);
78 };
79
80 //--------------------------------------------------------------------------
81
82 // static
83 ExternalMountPoints* ExternalMountPoints::GetSystemInstance() {
84   return g_external_mount_points.Pointer()->get();
85 }
86
87 // static
88 scoped_refptr<ExternalMountPoints> ExternalMountPoints::CreateRefCounted() {
89   return new ExternalMountPoints();
90 }
91
92 bool ExternalMountPoints::RegisterFileSystem(
93     const std::string& mount_name,
94     FileSystemType type,
95     const FileSystemMountOption& mount_option,
96     const base::FilePath& path_in) {
97   // COPY_SYNC_OPTION_SYNC is only applicable to native local file system.
98   DCHECK(type == kFileSystemTypeNativeLocal ||
99          mount_option.copy_sync_option() != COPY_SYNC_OPTION_SYNC);
100
101   base::AutoLock locker(lock_);
102
103   base::FilePath path = NormalizeFilePath(path_in);
104   if (!ValidateNewMountPoint(mount_name, path))
105     return false;
106
107   instance_map_[mount_name] = new Instance(type, path, mount_option);
108   if (!path.empty())
109     path_to_name_map_.insert(std::make_pair(path, mount_name));
110   return true;
111 }
112
113 bool ExternalMountPoints::HandlesFileSystemMountType(
114     FileSystemType type) const {
115   return type == kFileSystemTypeExternal ||
116          type == kFileSystemTypeNativeForPlatformApp;
117 }
118
119 bool ExternalMountPoints::RevokeFileSystem(const std::string& mount_name) {
120   base::AutoLock locker(lock_);
121   NameToInstance::iterator found = instance_map_.find(mount_name);
122   if (found == instance_map_.end())
123     return false;
124   Instance* instance = found->second;
125   path_to_name_map_.erase(NormalizeFilePath(instance->path()));
126   delete found->second;
127   instance_map_.erase(found);
128   return true;
129 }
130
131 bool ExternalMountPoints::GetRegisteredPath(
132     const std::string& filesystem_id, base::FilePath* path) const {
133   DCHECK(path);
134   base::AutoLock locker(lock_);
135   NameToInstance::const_iterator found = instance_map_.find(filesystem_id);
136   if (found == instance_map_.end())
137     return false;
138   *path = found->second->path();
139   return true;
140 }
141
142 bool ExternalMountPoints::CrackVirtualPath(
143     const base::FilePath& virtual_path,
144     std::string* mount_name,
145     FileSystemType* type,
146     base::FilePath* path,
147     FileSystemMountOption* mount_option) const {
148   DCHECK(mount_name);
149   DCHECK(path);
150
151   // The path should not contain any '..' references.
152   if (virtual_path.ReferencesParent())
153     return false;
154
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;
160
161   std::vector<base::FilePath::StringType>::iterator component_iter =
162       components.begin();
163   std::string maybe_mount_name =
164       base::FilePath(*component_iter++).MaybeAsASCII();
165   if (maybe_mount_name.empty())
166     return false;
167
168   base::FilePath cracked_path;
169   {
170     base::AutoLock locker(lock_);
171     NameToInstance::const_iterator found_instance =
172         instance_map_.find(maybe_mount_name);
173     if (found_instance == instance_map_.end())
174       return false;
175
176     *mount_name = maybe_mount_name;
177     const Instance* instance = found_instance->second;
178     if (type)
179       *type = instance->type();
180     cracked_path = instance->path();
181     *mount_option = instance->mount_option();
182   }
183
184   for (; component_iter != components.end(); ++component_iter)
185     cracked_path = cracked_path.Append(*component_iter);
186   *path = cracked_path;
187   return true;
188 }
189
190 FileSystemURL ExternalMountPoints::CrackURL(const GURL& url) const {
191   FileSystemURL filesystem_url = FileSystemURL(url);
192   if (!filesystem_url.is_valid())
193     return FileSystemURL();
194   return CrackFileSystemURL(filesystem_url);
195 }
196
197 FileSystemURL ExternalMountPoints::CreateCrackedFileSystemURL(
198     const GURL& origin,
199     FileSystemType type,
200     const base::FilePath& path) const {
201   return CrackFileSystemURL(FileSystemURL(origin, type, path));
202 }
203
204 void ExternalMountPoints::AddMountPointInfosTo(
205     std::vector<MountPointInfo>* mount_points) const {
206   base::AutoLock locker(lock_);
207   DCHECK(mount_points);
208   for (NameToInstance::const_iterator iter = instance_map_.begin();
209        iter != instance_map_.end(); ++iter) {
210     mount_points->push_back(MountPointInfo(iter->first, iter->second->path()));
211   }
212 }
213
214 bool ExternalMountPoints::GetVirtualPath(const base::FilePath& path_in,
215                                          base::FilePath* virtual_path) const {
216   DCHECK(virtual_path);
217
218   base::AutoLock locker(lock_);
219
220   base::FilePath path = NormalizeFilePath(path_in);
221   std::map<base::FilePath, std::string>::const_reverse_iterator iter(
222       path_to_name_map_.upper_bound(path));
223   if (iter == path_to_name_map_.rend())
224     return false;
225
226   *virtual_path = CreateVirtualRootPath(iter->second);
227   if (iter->first == path)
228     return true;
229   return iter->first.AppendRelativePath(path, virtual_path);
230 }
231
232 base::FilePath ExternalMountPoints::CreateVirtualRootPath(
233     const std::string& mount_name) const {
234   return base::FilePath().AppendASCII(mount_name);
235 }
236
237 FileSystemURL ExternalMountPoints::CreateExternalFileSystemURL(
238     const GURL& origin,
239     const std::string& mount_name,
240     const base::FilePath& path) const {
241   return CreateCrackedFileSystemURL(
242       origin,
243       fileapi::kFileSystemTypeExternal,
244       // Avoid using FilePath::Append as path may be an absolute path.
245       base::FilePath(
246           CreateVirtualRootPath(mount_name).value() +
247           base::FilePath::kSeparators[0] + path.value()));
248 }
249
250 void ExternalMountPoints::RevokeAllFileSystems() {
251   NameToInstance instance_map_copy;
252   {
253     base::AutoLock locker(lock_);
254     instance_map_copy = instance_map_;
255     instance_map_.clear();
256     path_to_name_map_.clear();
257   }
258   STLDeleteContainerPairSecondPointers(instance_map_copy.begin(),
259                                        instance_map_copy.end());
260 }
261
262 ExternalMountPoints::ExternalMountPoints() {}
263
264 ExternalMountPoints::~ExternalMountPoints() {
265   STLDeleteContainerPairSecondPointers(instance_map_.begin(),
266                                        instance_map_.end());
267 }
268
269 FileSystemURL ExternalMountPoints::CrackFileSystemURL(
270     const FileSystemURL& url) const {
271   if (!HandlesFileSystemMountType(url.type()))
272     return FileSystemURL();
273
274   base::FilePath virtual_path = url.path();
275   if (url.type() == kFileSystemTypeNativeForPlatformApp) {
276 #if defined(OS_CHROMEOS)
277     // On Chrome OS, find a mount point and virtual path for the external fs.
278     if (!GetVirtualPath(url.path(), &virtual_path))
279       return FileSystemURL();
280 #else
281     // On other OS, it is simply a native local path.
282     return FileSystemURL(
283         url.origin(), url.mount_type(), url.virtual_path(),
284         url.mount_filesystem_id(), kFileSystemTypeNativeLocal,
285         url.path(), url.filesystem_id(), url.mount_option());
286 #endif
287   }
288
289   std::string mount_name;
290   FileSystemType cracked_type;
291   base::FilePath cracked_path;
292   FileSystemMountOption cracked_mount_option;
293
294   if (!CrackVirtualPath(virtual_path, &mount_name, &cracked_type,
295                         &cracked_path, &cracked_mount_option)) {
296     return FileSystemURL();
297   }
298
299   return FileSystemURL(
300       url.origin(), url.mount_type(), url.virtual_path(),
301       !url.filesystem_id().empty() ? url.filesystem_id() : mount_name,
302       cracked_type, cracked_path, mount_name, cracked_mount_option);
303 }
304
305 bool ExternalMountPoints::ValidateNewMountPoint(const std::string& mount_name,
306                                                 const base::FilePath& path) {
307   lock_.AssertAcquired();
308
309   // Mount name must not be empty.
310   if (mount_name.empty())
311     return false;
312
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;
317
318   // Allow empty paths.
319   if (path.empty())
320     return true;
321
322   // Verify path is legal.
323   if (path.ReferencesParent() || !path.IsAbsolute())
324     return false;
325
326   // Check there the new path does not overlap with one of the existing ones.
327   std::map<base::FilePath, std::string>::reverse_iterator potential_parent(
328       path_to_name_map_.upper_bound(path));
329   if (potential_parent != path_to_name_map_.rend()) {
330     if (potential_parent->first == path ||
331         potential_parent->first.IsParent(path)) {
332       return false;
333     }
334   }
335
336   std::map<base::FilePath, std::string>::iterator potential_child =
337       path_to_name_map_.upper_bound(path);
338   if (potential_child == path_to_name_map_.end())
339     return true;
340   return !(potential_child->first == path) &&
341          !path.IsParent(potential_child->first);
342 }
343
344 }  // namespace fileapi