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