| // 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/memory/free_deleter.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() && base::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 |