blob: 5c84c1bb248e49456e94b1d85b2fae022b7e4d97 [file] [log] [blame]
// 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/files/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<fileapi::ExternalMountPoints> system_mount_points_;
};
base::LazyInstance<SystemMountPointsLazyWrapper>::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<RemoteFileSystemProxyInterface> 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> 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 <mount_name> and <relative_path> parts.
std::vector<base::FilePath::StringType> components;
virtual_path.GetComponents(&components);
if (components.size() < 1)
return false;
std::vector<base::FilePath::StringType>::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<MountPointInfo>* 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<base::FilePath, std::string>::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<base::FilePath, std::string>::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<base::FilePath, std::string>::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