- add sources.
[platform/framework/web/crosswalk.git] / src / webkit / browser / fileapi / isolated_context.cc
1 // Copyright (c) 2012 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/isolated_context.h"
6
7 #include "base/basictypes.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/rand_util.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "webkit/browser/fileapi/file_system_url.h"
16
17 namespace fileapi {
18
19 namespace {
20
21 base::FilePath::StringType GetRegisterNameForPath(const base::FilePath& path) {
22   // If it's not a root path simply return a base name.
23   if (path.DirName() != path)
24     return path.BaseName().value();
25
26 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
27   base::FilePath::StringType name;
28   for (size_t i = 0;
29        i < path.value().size() && !base::FilePath::IsSeparator(path.value()[i]);
30        ++i) {
31     if (path.value()[i] == L':') {
32       name.append(L"_drive");
33       break;
34     }
35     name.append(1, path.value()[i]);
36   }
37   return name;
38 #else
39   return FILE_PATH_LITERAL("<root>");
40 #endif
41 }
42
43 bool IsSinglePathIsolatedFileSystem(FileSystemType type) {
44   switch (type) {
45     // As of writing dragged file system is the only filesystem
46     // which could have multiple top-level paths.
47     case kFileSystemTypeDragged:
48       return false;
49
50     case kFileSystemTypeUnknown:
51       NOTREACHED();
52       return true;
53
54     default:
55       return true;
56   }
57   NOTREACHED();
58   return true;
59 }
60
61 static base::LazyInstance<IsolatedContext>::Leaky g_isolated_context =
62     LAZY_INSTANCE_INITIALIZER;
63
64 }  // namespace
65
66 IsolatedContext::FileInfoSet::FileInfoSet() {}
67 IsolatedContext::FileInfoSet::~FileInfoSet() {}
68
69 bool IsolatedContext::FileInfoSet::AddPath(
70     const base::FilePath& path, std::string* registered_name) {
71   // The given path should not contain any '..' and should be absolute.
72   if (path.ReferencesParent() || !path.IsAbsolute())
73     return false;
74   base::FilePath::StringType name = GetRegisterNameForPath(path);
75   std::string utf8name = base::FilePath(name).AsUTF8Unsafe();
76   base::FilePath normalized_path = path.NormalizePathSeparators();
77   bool inserted =
78       fileset_.insert(MountPointInfo(utf8name, normalized_path)).second;
79   if (!inserted) {
80     int suffix = 1;
81     std::string basepart =
82         base::FilePath(name).RemoveExtension().AsUTF8Unsafe();
83     std::string ext =
84         base::FilePath(base::FilePath(name).Extension()).AsUTF8Unsafe();
85     while (!inserted) {
86       utf8name = base::StringPrintf("%s (%d)", basepart.c_str(), suffix++);
87       if (!ext.empty())
88         utf8name.append(ext);
89       inserted =
90           fileset_.insert(MountPointInfo(utf8name, normalized_path)).second;
91     }
92   }
93   if (registered_name)
94     *registered_name = utf8name;
95   return true;
96 }
97
98 bool IsolatedContext::FileInfoSet::AddPathWithName(
99     const base::FilePath& path, const std::string& name) {
100   // The given path should not contain any '..' and should be absolute.
101   if (path.ReferencesParent() || !path.IsAbsolute())
102     return false;
103   return fileset_.insert(
104       MountPointInfo(name, path.NormalizePathSeparators())).second;
105 }
106
107 //--------------------------------------------------------------------------
108
109 class IsolatedContext::Instance {
110  public:
111   enum PathType {
112     PLATFORM_PATH,
113     VIRTUAL_PATH
114   };
115
116   // For a single-path isolated file system, which could be registered by
117   // IsolatedContext::RegisterFileSystemForPath() or
118   // IsolatedContext::RegisterFileSystemForVirtualPath().
119   // Most of isolated file system contexts should be of this type.
120   Instance(FileSystemType type, const MountPointInfo& file_info,
121            PathType path_type);
122
123   // For a multi-paths isolated file system.  As of writing only file system
124   // type which could have multi-paths is Dragged file system, and
125   // could be registered by IsolatedContext::RegisterDraggedFileSystem().
126   Instance(FileSystemType type, const std::set<MountPointInfo>& files);
127
128   ~Instance();
129
130   FileSystemType type() const { return type_; }
131   const MountPointInfo& file_info() const { return file_info_; }
132   const std::set<MountPointInfo>& files() const { return files_; }
133   int ref_counts() const { return ref_counts_; }
134
135   void AddRef() { ++ref_counts_; }
136   void RemoveRef() { --ref_counts_; }
137
138   bool ResolvePathForName(const std::string& name, base::FilePath* path) const;
139
140   // Returns true if the instance is a single-path instance.
141   bool IsSinglePathInstance() const;
142
143  private:
144   const FileSystemType type_;
145
146   // For single-path instance.
147   const MountPointInfo file_info_;
148   const PathType path_type_;
149
150   // For multiple-path instance (e.g. dragged file system).
151   const std::set<MountPointInfo> files_;
152
153   // Reference counts. Note that an isolated filesystem is created with ref==0
154   // and will get deleted when the ref count reaches <=0.
155   int ref_counts_;
156
157   DISALLOW_COPY_AND_ASSIGN(Instance);
158 };
159
160 IsolatedContext::Instance::Instance(FileSystemType type,
161                                     const MountPointInfo& file_info,
162                                     Instance::PathType path_type)
163     : type_(type),
164       file_info_(file_info),
165       path_type_(path_type),
166       ref_counts_(0) {
167   DCHECK(IsSinglePathIsolatedFileSystem(type_));
168 }
169
170 IsolatedContext::Instance::Instance(FileSystemType type,
171                                     const std::set<MountPointInfo>& files)
172     : type_(type),
173       path_type_(PLATFORM_PATH),
174       files_(files),
175       ref_counts_(0) {
176   DCHECK(!IsSinglePathIsolatedFileSystem(type_));
177 }
178
179 IsolatedContext::Instance::~Instance() {}
180
181 bool IsolatedContext::Instance::ResolvePathForName(const std::string& name,
182                                                    base::FilePath* path) const {
183   if (IsSinglePathIsolatedFileSystem(type_)) {
184     switch (path_type_) {
185       case PLATFORM_PATH:
186         *path = file_info_.path;
187         break;
188       case VIRTUAL_PATH:
189         *path = base::FilePath();
190         break;
191       default:
192         NOTREACHED();
193     }
194
195     return file_info_.name == name;
196   }
197   std::set<MountPointInfo>::const_iterator found = files_.find(
198       MountPointInfo(name, base::FilePath()));
199   if (found == files_.end())
200     return false;
201   *path = found->path;
202   return true;
203 }
204
205 bool IsolatedContext::Instance::IsSinglePathInstance() const {
206   return IsSinglePathIsolatedFileSystem(type_);
207 }
208
209 //--------------------------------------------------------------------------
210
211 // static
212 IsolatedContext* IsolatedContext::GetInstance() {
213   return g_isolated_context.Pointer();
214 }
215
216 // static
217 bool IsolatedContext::IsIsolatedType(FileSystemType type) {
218   return type == kFileSystemTypeIsolated || type == kFileSystemTypeExternal;
219 }
220
221 std::string IsolatedContext::RegisterDraggedFileSystem(
222     const FileInfoSet& files) {
223   base::AutoLock locker(lock_);
224   std::string filesystem_id = GetNewFileSystemId();
225   instance_map_[filesystem_id] = new Instance(
226       kFileSystemTypeDragged, files.fileset());
227   return filesystem_id;
228 }
229
230 std::string IsolatedContext::RegisterFileSystemForPath(
231     FileSystemType type,
232     const base::FilePath& path_in,
233     std::string* register_name) {
234   base::FilePath path(path_in.NormalizePathSeparators());
235   if (path.ReferencesParent() || !path.IsAbsolute())
236     return std::string();
237   std::string name;
238   if (register_name && !register_name->empty()) {
239     name = *register_name;
240   } else {
241     name = base::FilePath(GetRegisterNameForPath(path)).AsUTF8Unsafe();
242     if (register_name)
243       register_name->assign(name);
244   }
245
246   base::AutoLock locker(lock_);
247   std::string filesystem_id = GetNewFileSystemId();
248   instance_map_[filesystem_id] = new Instance(type, MountPointInfo(name, path),
249                                               Instance::PLATFORM_PATH);
250   path_to_id_map_[path].insert(filesystem_id);
251   return filesystem_id;
252 }
253
254 std::string IsolatedContext::RegisterFileSystemForVirtualPath(
255     FileSystemType type,
256     const std::string& register_name,
257     const base::FilePath& cracked_path_prefix) {
258   base::AutoLock locker(lock_);
259   base::FilePath path(cracked_path_prefix.NormalizePathSeparators());
260   if (path.ReferencesParent())
261     return std::string();
262   std::string filesystem_id = GetNewFileSystemId();
263   instance_map_[filesystem_id] = new Instance(
264       type,
265       MountPointInfo(register_name, cracked_path_prefix),
266       Instance::VIRTUAL_PATH);
267   path_to_id_map_[path].insert(filesystem_id);
268   return filesystem_id;
269 }
270
271 bool IsolatedContext::HandlesFileSystemMountType(FileSystemType type) const {
272   return type == kFileSystemTypeIsolated;
273 }
274
275 bool IsolatedContext::RevokeFileSystem(const std::string& filesystem_id) {
276   base::AutoLock locker(lock_);
277   return UnregisterFileSystem(filesystem_id);
278 }
279
280 bool IsolatedContext::GetRegisteredPath(
281     const std::string& filesystem_id, base::FilePath* path) const {
282   DCHECK(path);
283   base::AutoLock locker(lock_);
284   IDToInstance::const_iterator found = instance_map_.find(filesystem_id);
285   if (found == instance_map_.end() || !found->second->IsSinglePathInstance())
286     return false;
287   *path = found->second->file_info().path;
288   return true;
289 }
290
291 bool IsolatedContext::CrackVirtualPath(const base::FilePath& virtual_path,
292                                        std::string* id_or_name,
293                                        FileSystemType* type,
294                                        base::FilePath* path) const {
295   DCHECK(id_or_name);
296   DCHECK(path);
297
298   // This should not contain any '..' references.
299   if (virtual_path.ReferencesParent())
300     return false;
301
302   // The virtual_path should comprise <id_or_name> and <relative_path> parts.
303   std::vector<base::FilePath::StringType> components;
304   virtual_path.GetComponents(&components);
305   if (components.size() < 1)
306     return false;
307   std::vector<base::FilePath::StringType>::iterator component_iter =
308       components.begin();
309   std::string fsid = base::FilePath(*component_iter++).MaybeAsASCII();
310   if (fsid.empty())
311     return false;
312
313   base::FilePath cracked_path;
314   {
315     base::AutoLock locker(lock_);
316     IDToInstance::const_iterator found_instance = instance_map_.find(fsid);
317     if (found_instance == instance_map_.end())
318       return false;
319     *id_or_name = fsid;
320     const Instance* instance = found_instance->second;
321     if (type)
322       *type = instance->type();
323
324     if (component_iter == components.end()) {
325       // The virtual root case.
326       path->clear();
327       return true;
328     }
329
330     // *component_iter should be a name of the registered path.
331     std::string name = base::FilePath(*component_iter++).AsUTF8Unsafe();
332     if (!instance->ResolvePathForName(name, &cracked_path))
333       return false;
334   }
335
336   for (; component_iter != components.end(); ++component_iter)
337     cracked_path = cracked_path.Append(*component_iter);
338   *path = cracked_path;
339   return true;
340 }
341
342 FileSystemURL IsolatedContext::CrackURL(const GURL& url) const {
343   FileSystemURL filesystem_url = FileSystemURL(url);
344   if (!filesystem_url.is_valid())
345     return FileSystemURL();
346   return CrackFileSystemURL(filesystem_url);
347 }
348
349 FileSystemURL IsolatedContext::CreateCrackedFileSystemURL(
350     const GURL& origin,
351     FileSystemType type,
352     const base::FilePath& path) const {
353   return CrackFileSystemURL(FileSystemURL(origin, type, path));
354 }
355
356 void IsolatedContext::RevokeFileSystemByPath(const base::FilePath& path_in) {
357   base::AutoLock locker(lock_);
358   base::FilePath path(path_in.NormalizePathSeparators());
359   PathToID::iterator ids_iter = path_to_id_map_.find(path);
360   if (ids_iter == path_to_id_map_.end())
361     return;
362   std::set<std::string>& ids = ids_iter->second;
363   for (std::set<std::string>::iterator iter = ids.begin();
364        iter != ids.end(); ++iter) {
365     IDToInstance::iterator found = instance_map_.find(*iter);
366     if (found != instance_map_.end()) {
367       delete found->second;
368       instance_map_.erase(found);
369     }
370   }
371   path_to_id_map_.erase(ids_iter);
372 }
373
374 void IsolatedContext::AddReference(const std::string& filesystem_id) {
375   base::AutoLock locker(lock_);
376   DCHECK(instance_map_.find(filesystem_id) != instance_map_.end());
377   instance_map_[filesystem_id]->AddRef();
378 }
379
380 void IsolatedContext::RemoveReference(const std::string& filesystem_id) {
381   base::AutoLock locker(lock_);
382   // This could get called for non-existent filesystem if it has been
383   // already deleted by RevokeFileSystemByPath.
384   IDToInstance::iterator found = instance_map_.find(filesystem_id);
385   if (found == instance_map_.end())
386     return;
387   Instance* instance = found->second;
388   DCHECK_GT(instance->ref_counts(), 0);
389   instance->RemoveRef();
390   if (instance->ref_counts() == 0) {
391     bool deleted = UnregisterFileSystem(filesystem_id);
392     DCHECK(deleted);
393   }
394 }
395
396 bool IsolatedContext::GetDraggedFileInfo(
397     const std::string& filesystem_id,
398     std::vector<MountPointInfo>* files) const {
399   DCHECK(files);
400   base::AutoLock locker(lock_);
401   IDToInstance::const_iterator found = instance_map_.find(filesystem_id);
402   if (found == instance_map_.end() ||
403       found->second->type() != kFileSystemTypeDragged)
404     return false;
405   files->assign(found->second->files().begin(),
406                 found->second->files().end());
407   return true;
408 }
409
410 base::FilePath IsolatedContext::CreateVirtualRootPath(
411     const std::string& filesystem_id) const {
412   return base::FilePath().AppendASCII(filesystem_id);
413 }
414
415 IsolatedContext::IsolatedContext() {
416 }
417
418 IsolatedContext::~IsolatedContext() {
419   STLDeleteContainerPairSecondPointers(instance_map_.begin(),
420                                        instance_map_.end());
421 }
422
423 FileSystemURL IsolatedContext::CrackFileSystemURL(
424     const FileSystemURL& url) const {
425   if (!HandlesFileSystemMountType(url.type()))
426     return FileSystemURL();
427
428   std::string mount_name;
429   FileSystemType cracked_type;
430   base::FilePath cracked_path;
431   if (!CrackVirtualPath(url.path(), &mount_name, &cracked_type, &cracked_path))
432     return FileSystemURL();
433
434   return FileSystemURL(
435       url.origin(), url.mount_type(), url.virtual_path(),
436       !url.filesystem_id().empty() ? url.filesystem_id() : mount_name,
437       cracked_type, cracked_path, mount_name);
438 }
439
440 bool IsolatedContext::UnregisterFileSystem(const std::string& filesystem_id) {
441   lock_.AssertAcquired();
442   IDToInstance::iterator found = instance_map_.find(filesystem_id);
443   if (found == instance_map_.end())
444     return false;
445   Instance* instance = found->second;
446   if (instance->IsSinglePathInstance()) {
447     PathToID::iterator ids_iter = path_to_id_map_.find(
448         instance->file_info().path);
449     DCHECK(ids_iter != path_to_id_map_.end());
450     ids_iter->second.erase(filesystem_id);
451     if (ids_iter->second.empty())
452       path_to_id_map_.erase(ids_iter);
453   }
454   delete found->second;
455   instance_map_.erase(found);
456   return true;
457 }
458
459 std::string IsolatedContext::GetNewFileSystemId() const {
460   // Returns an arbitrary random string which must be unique in the map.
461   lock_.AssertAcquired();
462   uint32 random_data[4];
463   std::string id;
464   do {
465     base::RandBytes(random_data, sizeof(random_data));
466     id = base::HexEncode(random_data, sizeof(random_data));
467   } while (instance_map_.find(id) != instance_map_.end());
468   return id;
469 }
470
471 }  // namespace fileapi