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 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 fileapi 485