root/webkit/browser/fileapi/external_mount_points.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. NormalizeFilePath
  2. get
  3. mount_option_
  4. Instance
  5. type
  6. path
  7. mount_option
  8. GetSystemInstance
  9. CreateRefCounted
  10. RegisterFileSystem
  11. HandlesFileSystemMountType
  12. RevokeFileSystem
  13. GetRegisteredPath
  14. CrackVirtualPath
  15. CrackURL
  16. CreateCrackedFileSystemURL
  17. AddMountPointInfosTo
  18. GetVirtualPath
  19. CreateVirtualRootPath
  20. CreateExternalFileSystemURL
  21. RevokeAllFileSystems
  22. CrackFileSystemURL
  23. ValidateNewMountPoint

// 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/browser/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/browser/fileapi/file_system_url.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()) {
  }

  ~SystemMountPointsLazyWrapper() {}

  fileapi::ExternalMountPoints* get() {
    return system_mount_points_.get();
  }

 private:
  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,
           const FileSystemMountOption& mount_option)
      : type_(type),
        path_(path.StripTrailingSeparators()),
        mount_option_(mount_option) {}
  ~Instance() {}

  FileSystemType type() const { return type_; }
  const base::FilePath& path() const { return path_; }
  const FileSystemMountOption& mount_option() const { return mount_option_; }

 private:
  const FileSystemType type_;
  const base::FilePath path_;
  const FileSystemMountOption mount_option_;

  DISALLOW_COPY_AND_ASSIGN(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 FileSystemMountOption& mount_option,
    const base::FilePath& path_in) {
  // COPY_SYNC_OPTION_SYNC is only applicable to native local file system.
  DCHECK(type == kFileSystemTypeNativeLocal ||
         mount_option.copy_sync_option() != COPY_SYNC_OPTION_SYNC);

  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, mount_option);
  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 ||
         type == kFileSystemTypeNativeForPlatformApp;
}

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,
    FileSystemMountOption* mount_option) 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();
    *mount_option = instance->mount_option();
  }

  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 CrackFileSystemURL(filesystem_url);
}

FileSystemURL ExternalMountPoints::CreateCrackedFileSystemURL(
    const GURL& origin,
    FileSystemType type,
    const base::FilePath& path) const {
  return CrackFileSystemURL(FileSystemURL(origin, type, path));
}

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) const {
  DCHECK(virtual_path);

  base::AutoLock locker(lock_);

  base::FilePath path = NormalizeFilePath(path_in);
  std::map<base::FilePath, std::string>::const_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);
}

FileSystemURL ExternalMountPoints::CreateExternalFileSystemURL(
    const GURL& origin,
    const std::string& mount_name,
    const base::FilePath& path) const {
  return CreateCrackedFileSystemURL(
      origin,
      fileapi::kFileSystemTypeExternal,
      // Avoid using FilePath::Append as path may be an absolute path.
      base::FilePath(
          CreateVirtualRootPath(mount_name).value() +
          base::FilePath::kSeparators[0] + path.value()));
}

void ExternalMountPoints::RevokeAllFileSystems() {
  NameToInstance instance_map_copy;
  {
    base::AutoLock locker(lock_);
    instance_map_copy = instance_map_;
    instance_map_.clear();
    path_to_name_map_.clear();
  }
  STLDeleteContainerPairSecondPointers(instance_map_copy.begin(),
                                       instance_map_copy.end());
}

ExternalMountPoints::ExternalMountPoints() {}

ExternalMountPoints::~ExternalMountPoints() {
  STLDeleteContainerPairSecondPointers(instance_map_.begin(),
                                       instance_map_.end());
}

FileSystemURL ExternalMountPoints::CrackFileSystemURL(
    const FileSystemURL& url) const {
  if (!HandlesFileSystemMountType(url.type()))
    return FileSystemURL();

  base::FilePath virtual_path = url.path();
  if (url.type() == kFileSystemTypeNativeForPlatformApp) {
#if defined(OS_CHROMEOS)
    // On Chrome OS, find a mount point and virtual path for the external fs.
    if (!GetVirtualPath(url.path(), &virtual_path))
      return FileSystemURL();
#else
    // On other OS, it is simply a native local path.
    return FileSystemURL(
        url.origin(), url.mount_type(), url.virtual_path(),
        url.mount_filesystem_id(), kFileSystemTypeNativeLocal,
        url.path(), url.filesystem_id(), url.mount_option());
#endif
  }

  std::string mount_name;
  FileSystemType cracked_type;
  base::FilePath cracked_path;
  FileSystemMountOption cracked_mount_option;

  if (!CrackVirtualPath(virtual_path, &mount_name, &cracked_type,
                        &cracked_path, &cracked_mount_option)) {
    return FileSystemURL();
  }

  return FileSystemURL(
      url.origin(), url.mount_type(), url.virtual_path(),
      !url.filesystem_id().empty() ? url.filesystem_id() : mount_name,
      cracked_type, cracked_path, mount_name, cracked_mount_option);
}

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);
}

}  // namespace fileapi

/* [<][>][^][v][top][bottom][index][help] */