blob: c6a8243b410bda2d57e5ebcaf8a49f8250b2432c [file] [log] [blame]
// Copyright 2015 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 "services/files/files_impl.h"
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/posix/eintr_wrapper.h"
#include "base/sha1.h"
#include "base/strings/string_number_conversions.h"
#include "mojo/public/cpp/application/connection_context.h"
#include "services/files/directory_impl.h"
namespace mojo {
namespace files {
namespace {
base::ScopedFD CreateAndOpenTemporaryDirectory(
scoped_ptr<base::ScopedTempDir>* temp_dir) {
(*temp_dir).reset(new base::ScopedTempDir());
CHECK((*temp_dir)->CreateUniqueTempDir());
base::ScopedFD temp_dir_fd(HANDLE_EINTR(
open((*temp_dir)->path().value().c_str(), O_RDONLY | O_DIRECTORY, 0)));
PCHECK(temp_dir_fd.is_valid());
DVLOG(1) << "Made a temporary directory: " << (*temp_dir)->path().value();
return temp_dir_fd;
}
// Gets the path to a directory relative to the home directory. Returns an empty
// path on failure.
base::FilePath GetHomeRelativePath(const char* dir_name) {
const char* home_dir_name = getenv("HOME");
if (!home_dir_name || !home_dir_name[0]) {
LOG(ERROR) << "HOME not set";
return base::FilePath();
}
return base::FilePath(home_dir_name).Append(dir_name);
}
// Ensures that the parent directory for the (per-application) persistent cache
// directory exists and returns a path to it. Returns an empty path on failure.
base::FilePath EnsureAppPersistentCacheParentDirectory() {
base::FilePath parent_dir_name =
GetHomeRelativePath("MojoAppPersistentCaches");
// Just |mkdir()| it (|mkdir()| isn't interruptible). If it already exists,
// assume everything is OK (this isn't entirely right -- e.g., it may not be a
// directory or it may have the wrong permissions -- but it's enough for us to
// proceed to the next step).
if (mkdir(parent_dir_name.value().c_str(), S_IRWXU) != 0 && errno != EEXIST) {
PLOG(ERROR) << "mkdir failed: " << parent_dir_name.value();
return base::FilePath();
}
return parent_dir_name;
}
// Opens the application-specific persistent cache directory for the given |url|
// (creating it if necessary).
base::ScopedFD OpenAppPersistentCacheDirectory(std::string url) {
base::FilePath parent_dir_name = EnsureAppPersistentCacheParentDirectory();
if (parent_dir_name.empty())
return base::ScopedFD();
// TODO(vtl): We should probably use SHA-256 instead of SHA-1.
std::string raw_hash = base::SHA1HashString(url);
std::string hex_hash = base::HexEncode(raw_hash.data(), raw_hash.size());
base::FilePath dir_name = parent_dir_name.Append(hex_hash);
if (mkdir(dir_name.value().c_str(), S_IRWXU) != 0 && errno != EEXIST) {
PLOG(ERROR) << "mkdir failed: " << dir_name.value();
return base::ScopedFD();
}
return base::ScopedFD(
HANDLE_EINTR(open(dir_name.value().c_str(), O_RDONLY | O_DIRECTORY, 0)));
}
#ifndef NDEBUG
base::ScopedFD OpenMojoDebugDirectory() {
base::FilePath mojo_debug_dir_name = GetHomeRelativePath("MojoDebug");
if (mojo_debug_dir_name.empty())
return base::ScopedFD();
return base::ScopedFD(HANDLE_EINTR(
open(mojo_debug_dir_name.value().c_str(), O_RDONLY | O_DIRECTORY, 0)));
}
#endif
} // namespace
FilesImpl::FilesImpl(const ConnectionContext& connection_context,
InterfaceRequest<Files> request)
: client_url_(connection_context.remote_url),
binding_(this, request.Pass()) {}
FilesImpl::~FilesImpl() {}
void FilesImpl::OpenFileSystem(const mojo::String& file_system,
InterfaceRequest<Directory> directory,
const OpenFileSystemCallback& callback) {
base::ScopedFD dir_fd;
// Set only if the |DirectoryImpl| will own a temporary directory.
scoped_ptr<base::ScopedTempDir> temp_dir;
if (file_system.is_null()) {
// TODO(vtl): ScopedGeneric (hence ScopedFD) doesn't have an operator=!
dir_fd.reset(CreateAndOpenTemporaryDirectory(&temp_dir).release());
DCHECK(temp_dir);
} else if (file_system.get() == std::string("app_persistent_cache")) {
// TODO(vtl): ScopedGeneric (hence ScopedFD) doesn't have an operator=!
dir_fd.reset(OpenAppPersistentCacheDirectory(client_url_).release());
if (!dir_fd.is_valid()) {
LOG(ERROR) << "Failed to open app persistent cache directory for "
<< client_url_;
callback.Run(Error::UNKNOWN);
return;
}
} else if (file_system.get() == std::string("debug")) {
#ifdef NDEBUG
LOG(WARNING) << "~/MojoDebug only available in Debug builds";
#else
// TODO(vtl): ScopedGeneric (hence ScopedFD) doesn't have an operator=!
dir_fd.reset(OpenMojoDebugDirectory().release());
#endif
if (!dir_fd.is_valid()) {
LOG(ERROR) << "~/MojoDebug unavailable";
callback.Run(Error::UNAVAILABLE);
return;
}
} else {
LOG(ERROR) << "Unknown file system: " << file_system.get();
callback.Run(Error::UNIMPLEMENTED);
return;
}
new DirectoryImpl(directory.Pass(), dir_fd.Pass(), temp_dir.Pass());
callback.Run(Error::OK);
}
} // namespace files
} // namespace mojo