// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "webkit/fileapi/external_mount_points.h" #include "base/file_path.h" #include "base/lazy_instance.h" #include "base/path_service.h" #include "base/stl_util.h" #include "webkit/fileapi/file_system_url.h" #include "webkit/fileapi/remote_file_system_proxy.h" namespace { // Normalizes file path so it has normalized separators and ends with exactly // one separator. Paths have to be normalized this way for use in // GetVirtualPath method. Separators cannot be completely stripped, or // GetVirtualPath could not working in some edge cases. // For example, /a/b/c(1)/d would be erroneously resolved as c/d if the // following mount points were registered: "/a/b/c", "/a/b/c(1)". (Note: // "/a/b/c" < "/a/b/c(1)" < "/a/b/c/"). base::FilePath NormalizeFilePath(const base::FilePath& path) { if (path.empty()) return path; base::FilePath::StringType path_str = path.StripTrailingSeparators().value(); if (!base::FilePath::IsSeparator(path_str[path_str.length() - 1])) path_str.append(FILE_PATH_LITERAL("/")); return base::FilePath(path_str).NormalizePathSeparators(); } // Wrapper around ref-counted ExternalMountPoints that will be used to lazily // create and initialize LazyInstance system ExternalMountPoints. class SystemMountPointsLazyWrapper { public: SystemMountPointsLazyWrapper() : system_mount_points_(fileapi::ExternalMountPoints::CreateRefCounted()) { RegisterDefaultMountPoints(); } ~SystemMountPointsLazyWrapper() {} fileapi::ExternalMountPoints* get() { return system_mount_points_.get(); } private: void RegisterDefaultMountPoints() { #if defined(OS_CHROMEOS) // Add default system mount points. system_mount_points_->RegisterFileSystem( "archive", fileapi::kFileSystemTypeNativeLocal, base::FilePath(FILE_PATH_LITERAL("/media/archive"))); system_mount_points_->RegisterFileSystem( "removable", fileapi::kFileSystemTypeNativeLocal, base::FilePath(FILE_PATH_LITERAL("/media/removable"))); system_mount_points_->RegisterFileSystem( "oem", fileapi::kFileSystemTypeRestrictedNativeLocal, base::FilePath(FILE_PATH_LITERAL("/usr/share/oem"))); #endif // defined(OS_CHROMEOS) } scoped_refptr system_mount_points_; }; base::LazyInstance::Leaky g_external_mount_points = LAZY_INSTANCE_INITIALIZER; } // namespace namespace fileapi { class ExternalMountPoints::Instance { public: Instance(FileSystemType type, const base::FilePath& path, RemoteFileSystemProxyInterface* remote_proxy); ~Instance(); FileSystemType type() const { return type_; } const base::FilePath& path() const { return path_; } RemoteFileSystemProxyInterface* remote_proxy() const { return remote_proxy_.get(); } private: const FileSystemType type_; const base::FilePath path_; // For file systems that have a remote file system proxy. scoped_refptr remote_proxy_; DISALLOW_COPY_AND_ASSIGN(Instance); }; ExternalMountPoints::Instance::Instance(FileSystemType type, const base::FilePath& path, RemoteFileSystemProxyInterface* proxy) : type_(type), path_(path.StripTrailingSeparators()), remote_proxy_(proxy) { DCHECK(!proxy || (kFileSystemTypeDrive == type_)); } ExternalMountPoints::Instance::~Instance() {} //-------------------------------------------------------------------------- // static ExternalMountPoints* ExternalMountPoints::GetSystemInstance() { return g_external_mount_points.Pointer()->get(); } // static scoped_refptr ExternalMountPoints::CreateRefCounted() { return new ExternalMountPoints(); } bool ExternalMountPoints::RegisterFileSystem( const std::string& mount_name, FileSystemType type, const base::FilePath& path) { return RegisterRemoteFileSystem(mount_name, type, NULL, path); } bool ExternalMountPoints::RegisterRemoteFileSystem( const std::string& mount_name, FileSystemType type, RemoteFileSystemProxyInterface* remote_proxy, const base::FilePath& path_in) { base::AutoLock locker(lock_); base::FilePath path = NormalizeFilePath(path_in); if (!ValidateNewMountPoint(mount_name, path)) return false; instance_map_[mount_name] = new Instance(type, path, remote_proxy); if (!path.empty()) path_to_name_map_.insert(std::make_pair(path, mount_name)); return true; } bool ExternalMountPoints::HandlesFileSystemMountType( FileSystemType type) const { return type == kFileSystemTypeExternal; } bool ExternalMountPoints::RevokeFileSystem(const std::string& mount_name) { base::AutoLock locker(lock_); NameToInstance::iterator found = instance_map_.find(mount_name); if (found == instance_map_.end()) return false; Instance* instance = found->second; path_to_name_map_.erase(NormalizeFilePath(instance->path())); delete found->second; instance_map_.erase(found); return true; } bool ExternalMountPoints::GetRegisteredPath( const std::string& filesystem_id, base::FilePath* path) const { DCHECK(path); base::AutoLock locker(lock_); NameToInstance::const_iterator found = instance_map_.find(filesystem_id); if (found == instance_map_.end()) return false; *path = found->second->path(); return true; } bool ExternalMountPoints::CrackVirtualPath(const base::FilePath& virtual_path, std::string* mount_name, FileSystemType* type, base::FilePath* path) const { DCHECK(mount_name); DCHECK(path); // The path should not contain any '..' references. if (virtual_path.ReferencesParent()) return false; // The virtual_path should comprise of and parts. std::vector components; virtual_path.GetComponents(&components); if (components.size() < 1) return false; std::vector::iterator component_iter = components.begin(); std::string maybe_mount_name = base::FilePath(*component_iter++).MaybeAsASCII(); if (maybe_mount_name.empty()) return false; base::FilePath cracked_path; { base::AutoLock locker(lock_); NameToInstance::const_iterator found_instance = instance_map_.find(maybe_mount_name); if (found_instance == instance_map_.end()) return false; *mount_name = maybe_mount_name; const Instance* instance = found_instance->second; if (type) *type = instance->type(); cracked_path = instance->path(); } for (; component_iter != components.end(); ++component_iter) cracked_path = cracked_path.Append(*component_iter); *path = cracked_path; return true; } FileSystemURL ExternalMountPoints::CrackURL(const GURL& url) const { FileSystemURL filesystem_url = FileSystemURL(url); if (!filesystem_url.is_valid()) return FileSystemURL(); return CreateCrackedFileSystemURL(filesystem_url.origin(), filesystem_url.mount_type(), filesystem_url.path()); } FileSystemURL ExternalMountPoints::CreateCrackedFileSystemURL( const GURL& origin, FileSystemType type, const base::FilePath& path) const { if (!HandlesFileSystemMountType(type)) return FileSystemURL(); std::string mount_name; FileSystemType cracked_type; base::FilePath cracked_path; if (!CrackVirtualPath(path, &mount_name, &cracked_type, &cracked_path)) return FileSystemURL(); return FileSystemURL(origin, type, path, mount_name, cracked_type, cracked_path); } RemoteFileSystemProxyInterface* ExternalMountPoints::GetRemoteFileSystemProxy( const std::string& mount_name) const { base::AutoLock locker(lock_); NameToInstance::const_iterator found = instance_map_.find(mount_name); if (found == instance_map_.end()) return NULL; return found->second->remote_proxy(); } void ExternalMountPoints::AddMountPointInfosTo( std::vector* mount_points) const { base::AutoLock locker(lock_); DCHECK(mount_points); for (NameToInstance::const_iterator iter = instance_map_.begin(); iter != instance_map_.end(); ++iter) { mount_points->push_back(MountPointInfo(iter->first, iter->second->path())); } } bool ExternalMountPoints::GetVirtualPath(const base::FilePath& path_in, base::FilePath* virtual_path) { DCHECK(virtual_path); base::AutoLock locker(lock_); base::FilePath path = NormalizeFilePath(path_in); std::map::reverse_iterator iter( path_to_name_map_.upper_bound(path)); if (iter == path_to_name_map_.rend()) return false; *virtual_path = CreateVirtualRootPath(iter->second); if (iter->first == path) return true; return iter->first.AppendRelativePath(path, virtual_path); } base::FilePath ExternalMountPoints::CreateVirtualRootPath( const std::string& mount_name) const { return base::FilePath().AppendASCII(mount_name); } ExternalMountPoints::ExternalMountPoints() {} ExternalMountPoints::~ExternalMountPoints() { STLDeleteContainerPairSecondPointers(instance_map_.begin(), instance_map_.end()); } bool ExternalMountPoints::ValidateNewMountPoint(const std::string& mount_name, const base::FilePath& path) { lock_.AssertAcquired(); // Mount name must not be empty. if (mount_name.empty()) return false; // Verify there is no registered mount point with the same name. NameToInstance::iterator found = instance_map_.find(mount_name); if (found != instance_map_.end()) return false; // Allow empty paths. if (path.empty()) return true; // Verify path is legal. if (path.ReferencesParent() || !path.IsAbsolute()) return false; // Check there the new path does not overlap with one of the existing ones. std::map::reverse_iterator potential_parent( path_to_name_map_.upper_bound(path)); if (potential_parent != path_to_name_map_.rend()) { if (potential_parent->first == path || potential_parent->first.IsParent(path)) { return false; } } std::map::iterator potential_child = path_to_name_map_.upper_bound(path); if (potential_child == path_to_name_map_.end()) return true; return !(potential_child->first == path) && !path.IsParent(potential_child->first); } ScopedExternalFileSystem::ScopedExternalFileSystem( const std::string& mount_name, FileSystemType type, const base::FilePath& path) : mount_name_(mount_name) { ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( mount_name, type, path); } base::FilePath ScopedExternalFileSystem::GetVirtualRootPath() const { return ExternalMountPoints::GetSystemInstance()-> CreateVirtualRootPath(mount_name_); } ScopedExternalFileSystem::~ScopedExternalFileSystem() { ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(mount_name_); } } // namespace fileapi