blob: f74f4170d2925f98027e67897c99ce775b5710c0 [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS 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 "cros-disks/platform.h"
#include <grp.h>
#include <pwd.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <unistd.h>
#include <memory>
#include <vector>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/stl_util.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
using base::FilePath;
using std::string;
using std::unique_ptr;
using std::vector;
namespace {
const unsigned kFallbackGroupBufferSize = 16384;
const unsigned kFallbackPasswordBufferSize = 16384;
} // namespace
namespace cros_disks {
Platform::Platform()
: mount_group_id_(0),
mount_user_id_(0),
mount_user_("root") {
}
bool Platform::GetRealPath(const string& path, string* real_path) const {
CHECK(real_path) << "Invalid real_path argument";
unique_ptr<char, base::FreeDeleter> result(realpath(path.c_str(), nullptr));
if (!result) {
PLOG(ERROR) << "Failed to get real path of '" << path << "'";
return false;
}
*real_path = result.get();
return true;
}
bool Platform::CreateDirectory(const string& path) const {
if (!base::CreateDirectory(FilePath(path))) {
LOG(ERROR) << "Failed to create directory '" << path << "'";
return false;
}
LOG(INFO) << "Created directory '" << path << "'";
return true;
}
bool Platform::CreateOrReuseEmptyDirectory(const string& path) const {
CHECK(!path.empty()) << "Invalid path argument";
// Reuse the target path if it already exists and is empty.
// rmdir handles the cases when the target path exists but
// is not empty, is already mounted or is used by some process.
rmdir(path.c_str());
if (mkdir(path.c_str(), S_IRWXU) != 0) {
PLOG(ERROR) << "Failed to create directory '" << path << "'";
return false;
}
return true;
}
bool Platform::CreateOrReuseEmptyDirectoryWithFallback(
string* path, unsigned max_suffix_to_retry,
const std::set<std::string>& reserved_paths) const {
CHECK(path && !path->empty()) << "Invalid path argument";
if (!ContainsKey(reserved_paths, *path) && CreateOrReuseEmptyDirectory(*path))
return true;
for (unsigned suffix = 1; suffix <= max_suffix_to_retry; ++suffix) {
string fallback_path = GetDirectoryFallbackName(*path, suffix);
if (!ContainsKey(reserved_paths, fallback_path) &&
CreateOrReuseEmptyDirectory(fallback_path)) {
*path = fallback_path;
return true;
}
}
return false;
}
string Platform::GetDirectoryFallbackName(const string& path,
unsigned suffix) const {
if (!path.empty() && IsAsciiDigit(path[path.size() - 1]))
return base::StringPrintf("%s (%u)", path.c_str(), suffix);
return base::StringPrintf("%s %u", path.c_str(), suffix);
}
bool Platform::GetGroupId(const string& group_name, gid_t* group_id) const {
long buffer_size = sysconf(_SC_GETGR_R_SIZE_MAX); // NOLINT(runtime/int)
if (buffer_size <= 0)
buffer_size = kFallbackGroupBufferSize;
group group_buffer, *group_buffer_ptr = nullptr;
vector<char> buffer(buffer_size);
getgrnam_r(group_name.c_str(), &group_buffer, buffer.data(), buffer_size,
&group_buffer_ptr);
if (group_buffer_ptr == nullptr) {
PLOG(WARNING) << "Failed to determine group ID of group '"
<< group_name << "'";
return false;
}
if (group_id)
*group_id = group_buffer.gr_gid;
return true;
}
bool Platform::GetUserAndGroupId(const string& user_name,
uid_t* user_id, gid_t* group_id) const {
long buffer_size = sysconf(_SC_GETPW_R_SIZE_MAX); // NOLINT(runtime/int)
if (buffer_size <= 0)
buffer_size = kFallbackPasswordBufferSize;
passwd password_buffer, *password_buffer_ptr = nullptr;
vector<char> buffer(buffer_size);
getpwnam_r(user_name.c_str(), &password_buffer, buffer.data(), buffer_size,
&password_buffer_ptr);
if (password_buffer_ptr == nullptr) {
PLOG(WARNING) << "Failed to determine user and group ID of user '"
<< user_name << "'";
return false;
}
if (user_id)
*user_id = password_buffer.pw_uid;
if (group_id)
*group_id = password_buffer.pw_gid;
return true;
}
bool Platform::GetOwnership(const string& path,
uid_t* user_id, gid_t* group_id) const {
struct stat path_status;
if (stat(path.c_str(), &path_status) != 0) {
PLOG(ERROR) << "Failed to get the ownership of '" << path << "'";
return false;
}
if (user_id)
*user_id = path_status.st_uid;
if (group_id)
*group_id = path_status.st_gid;
return true;
}
bool Platform::GetPermissions(const string& path, mode_t* mode) const {
CHECK(mode) << "Invalid mode argument";
struct stat path_status;
if (stat(path.c_str(), &path_status) != 0) {
PLOG(ERROR) << "Failed to get the permissions of '" << path << "'";
return false;
}
*mode = path_status.st_mode;
return true;
}
bool Platform::SetMountUser(const string& user_name) {
if (GetUserAndGroupId(user_name, &mount_user_id_, &mount_group_id_)) {
mount_user_ = user_name;
return true;
}
return false;
}
bool Platform::RemoveEmptyDirectory(const string& path) const {
if (rmdir(path.c_str()) != 0) {
PLOG(WARNING) << "Failed to remove directory '" << path << "'";
return false;
}
return true;
}
bool Platform::SetOwnership(const string& path,
uid_t user_id, gid_t group_id) const {
if (chown(path.c_str(), user_id, group_id)) {
PLOG(ERROR) << "Failed to set ownership of '" << path
<< "' to (uid=" << user_id << ", gid=" << group_id << ")";
return false;
}
return true;
}
bool Platform::SetPermissions(const string& path, mode_t mode) const {
if (chmod(path.c_str(), mode)) {
PLOG(ERROR) << "Failed to set permissions of '" << path
<< "' to " << base::StringPrintf("%04o", mode);
return false;
}
return true;
}
bool Platform::Unmount(const string& path) const {
if (umount(path.c_str()) != 0) {
PLOG(ERROR) << "Failed to unmount '" << path << "'";
return false;
}
LOG(INFO) << "Unmount '" << path << "'";
return true;
}
} // namespace cros_disks