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