- add sources.
[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, const base::FilePath& path)
61       : type_(type), path_(path.StripTrailingSeparators()) {}
62   ~Instance() {}
63
64   FileSystemType type() const { return type_; }
65   const base::FilePath& path() const { return path_; }
66
67  private:
68   const FileSystemType type_;
69   const base::FilePath path_;
70
71   DISALLOW_COPY_AND_ASSIGN(Instance);
72 };
73
74 //--------------------------------------------------------------------------
75
76 // static
77 ExternalMountPoints* ExternalMountPoints::GetSystemInstance() {
78   return g_external_mount_points.Pointer()->get();
79 }
80
81 // static
82 scoped_refptr<ExternalMountPoints> ExternalMountPoints::CreateRefCounted() {
83   return new ExternalMountPoints();
84 }
85
86 bool ExternalMountPoints::RegisterFileSystem(
87     const std::string& mount_name,
88     FileSystemType type,
89     const base::FilePath& path_in) {
90   base::AutoLock locker(lock_);
91
92   base::FilePath path = NormalizeFilePath(path_in);
93   if (!ValidateNewMountPoint(mount_name, path))
94     return false;
95
96   instance_map_[mount_name] = new Instance(type, path);
97   if (!path.empty())
98     path_to_name_map_.insert(std::make_pair(path, mount_name));
99   return true;
100 }
101
102 bool ExternalMountPoints::HandlesFileSystemMountType(
103     FileSystemType type) const {
104   return type == kFileSystemTypeExternal ||
105          type == kFileSystemTypeNativeForPlatformApp;
106 }
107
108 bool ExternalMountPoints::RevokeFileSystem(const std::string& mount_name) {
109   base::AutoLock locker(lock_);
110   NameToInstance::iterator found = instance_map_.find(mount_name);
111   if (found == instance_map_.end())
112     return false;
113   Instance* instance = found->second;
114   path_to_name_map_.erase(NormalizeFilePath(instance->path()));
115   delete found->second;
116   instance_map_.erase(found);
117   return true;
118 }
119
120 bool ExternalMountPoints::GetRegisteredPath(
121     const std::string& filesystem_id, base::FilePath* path) const {
122   DCHECK(path);
123   base::AutoLock locker(lock_);
124   NameToInstance::const_iterator found = instance_map_.find(filesystem_id);
125   if (found == instance_map_.end())
126     return false;
127   *path = found->second->path();
128   return true;
129 }
130
131 bool ExternalMountPoints::CrackVirtualPath(const base::FilePath& virtual_path,
132                                            std::string* mount_name,
133                                            FileSystemType* type,
134                                            base::FilePath* path) const {
135   DCHECK(mount_name);
136   DCHECK(path);
137
138   // The path should not contain any '..' references.
139   if (virtual_path.ReferencesParent())
140     return false;
141
142   // The virtual_path should comprise of <mount_name> and <relative_path> parts.
143   std::vector<base::FilePath::StringType> components;
144   virtual_path.GetComponents(&components);
145   if (components.size() < 1)
146     return false;
147
148   std::vector<base::FilePath::StringType>::iterator component_iter =
149       components.begin();
150   std::string maybe_mount_name =
151       base::FilePath(*component_iter++).MaybeAsASCII();
152   if (maybe_mount_name.empty())
153     return false;
154
155   base::FilePath cracked_path;
156   {
157     base::AutoLock locker(lock_);
158     NameToInstance::const_iterator found_instance =
159         instance_map_.find(maybe_mount_name);
160     if (found_instance == instance_map_.end())
161       return false;
162
163     *mount_name = maybe_mount_name;
164     const Instance* instance = found_instance->second;
165     if (type)
166       *type = instance->type();
167     cracked_path = instance->path();
168   }
169
170   for (; component_iter != components.end(); ++component_iter)
171     cracked_path = cracked_path.Append(*component_iter);
172   *path = cracked_path;
173   return true;
174 }
175
176 FileSystemURL ExternalMountPoints::CrackURL(const GURL& url) const {
177   FileSystemURL filesystem_url = FileSystemURL(url);
178   if (!filesystem_url.is_valid())
179     return FileSystemURL();
180   return CrackFileSystemURL(filesystem_url);
181 }
182
183 FileSystemURL ExternalMountPoints::CreateCrackedFileSystemURL(
184     const GURL& origin,
185     FileSystemType type,
186     const base::FilePath& path) const {
187   return CrackFileSystemURL(FileSystemURL(origin, type, path));
188 }
189
190 void ExternalMountPoints::AddMountPointInfosTo(
191     std::vector<MountPointInfo>* mount_points) const {
192   base::AutoLock locker(lock_);
193   DCHECK(mount_points);
194   for (NameToInstance::const_iterator iter = instance_map_.begin();
195        iter != instance_map_.end(); ++iter) {
196     mount_points->push_back(MountPointInfo(iter->first, iter->second->path()));
197   }
198 }
199
200 bool ExternalMountPoints::GetVirtualPath(const base::FilePath& path_in,
201                                          base::FilePath* virtual_path) const {
202   DCHECK(virtual_path);
203
204   base::AutoLock locker(lock_);
205
206   base::FilePath path = NormalizeFilePath(path_in);
207   std::map<base::FilePath, std::string>::const_reverse_iterator iter(
208       path_to_name_map_.upper_bound(path));
209   if (iter == path_to_name_map_.rend())
210     return false;
211
212   *virtual_path = CreateVirtualRootPath(iter->second);
213   if (iter->first == path)
214     return true;
215   return iter->first.AppendRelativePath(path, virtual_path);
216 }
217
218 base::FilePath ExternalMountPoints::CreateVirtualRootPath(
219     const std::string& mount_name) const {
220   return base::FilePath().AppendASCII(mount_name);
221 }
222
223 FileSystemURL ExternalMountPoints::CreateExternalFileSystemURL(
224     const GURL& origin,
225     const std::string& mount_name,
226     const base::FilePath& path) const {
227   return CreateCrackedFileSystemURL(
228       origin,
229       fileapi::kFileSystemTypeExternal,
230       // Avoid using FilePath::Append as path may be an absolute path.
231       base::FilePath(
232           CreateVirtualRootPath(mount_name).value() +
233           base::FilePath::kSeparators[0] + path.value()));
234 }
235
236 ExternalMountPoints::ExternalMountPoints() {}
237
238 ExternalMountPoints::~ExternalMountPoints() {
239   STLDeleteContainerPairSecondPointers(instance_map_.begin(),
240                                        instance_map_.end());
241 }
242
243 FileSystemURL ExternalMountPoints::CrackFileSystemURL(
244     const FileSystemURL& url) const {
245   if (!HandlesFileSystemMountType(url.type()))
246     return FileSystemURL();
247
248   base::FilePath virtual_path = url.path();
249   if (url.type() == kFileSystemTypeNativeForPlatformApp) {
250 #if defined(OS_CHROMEOS)
251     // On Chrome OS, find a mount point and virtual path for the external fs.
252     if (!GetVirtualPath(url.path(), &virtual_path))
253       return FileSystemURL();
254 #else
255     // On other OS, it is simply a native local path.
256     return FileSystemURL(
257         url.origin(), url.mount_type(), url.virtual_path(),
258         url.mount_filesystem_id(), kFileSystemTypeNativeLocal,
259         url.path(), url.filesystem_id());
260 #endif
261   }
262
263   std::string mount_name;
264   FileSystemType cracked_type;
265   base::FilePath cracked_path;
266
267   if (!CrackVirtualPath(virtual_path, &mount_name, &cracked_type,
268                         &cracked_path)) {
269     return FileSystemURL();
270   }
271
272   return FileSystemURL(
273       url.origin(), url.mount_type(), url.virtual_path(),
274       !url.filesystem_id().empty() ? url.filesystem_id() : mount_name,
275       cracked_type, cracked_path, mount_name);
276 }
277
278 bool ExternalMountPoints::ValidateNewMountPoint(const std::string& mount_name,
279                                                 const base::FilePath& path) {
280   lock_.AssertAcquired();
281
282   // Mount name must not be empty.
283   if (mount_name.empty())
284     return false;
285
286   // Verify there is no registered mount point with the same name.
287   NameToInstance::iterator found = instance_map_.find(mount_name);
288   if (found != instance_map_.end())
289     return false;
290
291   // Allow empty paths.
292   if (path.empty())
293     return true;
294
295   // Verify path is legal.
296   if (path.ReferencesParent() || !path.IsAbsolute())
297     return false;
298
299   // Check there the new path does not overlap with one of the existing ones.
300   std::map<base::FilePath, std::string>::reverse_iterator potential_parent(
301       path_to_name_map_.upper_bound(path));
302   if (potential_parent != path_to_name_map_.rend()) {
303     if (potential_parent->first == path ||
304         potential_parent->first.IsParent(path)) {
305       return false;
306     }
307   }
308
309   std::map<base::FilePath, std::string>::iterator potential_child =
310       path_to_name_map_.upper_bound(path);
311   if (potential_child == path_to_name_map_.end())
312     return true;
313   return !(potential_child->first == path) &&
314          !path.IsParent(potential_child->first);
315 }
316
317 }  // namespace fileapi