Mike Frysinger | 38ae98d | 2012-04-11 12:03:44 -0400 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "cros-disks/platform.h" |
| 6 | |
Ben Chan | 1d5a8e7 | 2011-08-01 15:21:39 -0700 | [diff] [blame] | 7 | #include <grp.h> |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 8 | #include <pwd.h> |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 9 | #include <sys/mount.h> |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 10 | #include <sys/stat.h> |
| 11 | #include <unistd.h> |
| 12 | |
Ben Chan | 5e3ca67 | 2014-08-25 15:53:58 -0700 | [diff] [blame] | 13 | #include <memory> |
| 14 | #include <vector> |
| 15 | |
Ben Chan | cd8fda4 | 2014-09-05 08:21:06 -0700 | [diff] [blame^] | 16 | #include <base/files/file_util.h> |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 17 | #include <base/logging.h> |
Mike Frysinger | 38ae98d | 2012-04-11 12:03:44 -0400 | [diff] [blame] | 18 | #include <base/stl_util.h> |
Ben Chan | 97e20d4 | 2014-02-05 18:38:07 -0800 | [diff] [blame] | 19 | #include <base/strings/string_util.h> |
| 20 | #include <base/strings/stringprintf.h> |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 21 | |
Ben Chan | 2e8aa6f | 2013-02-15 11:40:04 -0800 | [diff] [blame] | 22 | using base::FilePath; |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 23 | using std::string; |
Ben Chan | 5e3ca67 | 2014-08-25 15:53:58 -0700 | [diff] [blame] | 24 | using std::unique_ptr; |
| 25 | using std::vector; |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 26 | |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 27 | namespace { |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 28 | |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 29 | const unsigned kFallbackGroupBufferSize = 16384; |
| 30 | const unsigned kFallbackPasswordBufferSize = 16384; |
| 31 | |
| 32 | } // namespace |
| 33 | |
| 34 | namespace cros_disks { |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 35 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 36 | Platform::Platform() |
Ben Chan | 10510e8 | 2014-08-16 18:07:36 -0700 | [diff] [blame] | 37 | : mount_group_id_(0), |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 38 | mount_user_id_(0), |
| 39 | mount_user_("root") { |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 40 | } |
| 41 | |
Ben Chan | adc5d00 | 2014-03-12 15:02:26 -0700 | [diff] [blame] | 42 | bool Platform::GetRealPath(const string& path, string* real_path) const { |
| 43 | CHECK(real_path) << "Invalid real_path argument"; |
| 44 | |
Ben Chan | 44e7ea6 | 2014-08-29 18:13:37 -0700 | [diff] [blame] | 45 | unique_ptr<char, base::FreeDeleter> result(realpath(path.c_str(), nullptr)); |
Ben Chan | adc5d00 | 2014-03-12 15:02:26 -0700 | [diff] [blame] | 46 | if (!result) { |
| 47 | PLOG(ERROR) << "Failed to get real path of '" << path << "'"; |
| 48 | return false; |
| 49 | } |
| 50 | |
| 51 | *real_path = result.get(); |
| 52 | return true; |
| 53 | } |
| 54 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 55 | bool Platform::CreateDirectory(const string& path) const { |
Ben Chan | 97e20d4 | 2014-02-05 18:38:07 -0800 | [diff] [blame] | 56 | if (!base::CreateDirectory(FilePath(path))) { |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 57 | LOG(ERROR) << "Failed to create directory '" << path << "'"; |
| 58 | return false; |
| 59 | } |
| 60 | LOG(INFO) << "Created directory '" << path << "'"; |
| 61 | return true; |
| 62 | } |
| 63 | |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 64 | bool Platform::CreateOrReuseEmptyDirectory(const string& path) const { |
| 65 | CHECK(!path.empty()) << "Invalid path argument"; |
| 66 | |
| 67 | // Reuse the target path if it already exists and is empty. |
| 68 | // rmdir handles the cases when the target path exists but |
| 69 | // is not empty, is already mounted or is used by some process. |
| 70 | rmdir(path.c_str()); |
| 71 | if (mkdir(path.c_str(), S_IRWXU) != 0) { |
| 72 | PLOG(ERROR) << "Failed to create directory '" << path << "'"; |
| 73 | return false; |
| 74 | } |
| 75 | return true; |
| 76 | } |
| 77 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 78 | bool Platform::CreateOrReuseEmptyDirectoryWithFallback( |
Ben Chan | f869288 | 2011-08-21 10:15:30 -0700 | [diff] [blame] | 79 | string* path, unsigned max_suffix_to_retry, |
| 80 | const std::set<std::string>& reserved_paths) const { |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 81 | CHECK(path && !path->empty()) << "Invalid path argument"; |
| 82 | |
Ben Chan | f869288 | 2011-08-21 10:15:30 -0700 | [diff] [blame] | 83 | if (!ContainsKey(reserved_paths, *path) && CreateOrReuseEmptyDirectory(*path)) |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 84 | return true; |
| 85 | |
| 86 | for (unsigned suffix = 1; suffix <= max_suffix_to_retry; ++suffix) { |
Ben Chan | ec4eaab | 2012-02-05 23:26:58 -0800 | [diff] [blame] | 87 | string fallback_path = GetDirectoryFallbackName(*path, suffix); |
Ben Chan | f869288 | 2011-08-21 10:15:30 -0700 | [diff] [blame] | 88 | if (!ContainsKey(reserved_paths, fallback_path) && |
| 89 | CreateOrReuseEmptyDirectory(fallback_path)) { |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 90 | *path = fallback_path; |
| 91 | return true; |
| 92 | } |
| 93 | } |
| 94 | return false; |
| 95 | } |
| 96 | |
Ben Chan | ec4eaab | 2012-02-05 23:26:58 -0800 | [diff] [blame] | 97 | string Platform::GetDirectoryFallbackName(const string& path, |
| 98 | unsigned suffix) const { |
| 99 | if (!path.empty() && IsAsciiDigit(path[path.size() - 1])) |
| 100 | return base::StringPrintf("%s (%u)", path.c_str(), suffix); |
| 101 | |
| 102 | return base::StringPrintf("%s %u", path.c_str(), suffix); |
| 103 | } |
| 104 | |
Ben Chan | 1d5a8e7 | 2011-08-01 15:21:39 -0700 | [diff] [blame] | 105 | bool Platform::GetGroupId(const string& group_name, gid_t* group_id) const { |
Ben Chan | 7073508 | 2014-06-16 20:03:37 -0700 | [diff] [blame] | 106 | long buffer_size = sysconf(_SC_GETGR_R_SIZE_MAX); // NOLINT(runtime/int) |
Ben Chan | 1d5a8e7 | 2011-08-01 15:21:39 -0700 | [diff] [blame] | 107 | if (buffer_size <= 0) |
| 108 | buffer_size = kFallbackGroupBufferSize; |
| 109 | |
Ben Chan | 71daeac | 2014-09-08 09:03:36 -0700 | [diff] [blame] | 110 | group group_buffer, *group_buffer_ptr = nullptr; |
Ben Chan | 5e3ca67 | 2014-08-25 15:53:58 -0700 | [diff] [blame] | 111 | vector<char> buffer(buffer_size); |
| 112 | getgrnam_r(group_name.c_str(), &group_buffer, buffer.data(), buffer_size, |
| 113 | &group_buffer_ptr); |
Ben Chan | 44e7ea6 | 2014-08-29 18:13:37 -0700 | [diff] [blame] | 114 | if (group_buffer_ptr == nullptr) { |
Ben Chan | 1d5a8e7 | 2011-08-01 15:21:39 -0700 | [diff] [blame] | 115 | PLOG(WARNING) << "Failed to determine group ID of group '" |
| 116 | << group_name << "'"; |
| 117 | return false; |
| 118 | } |
| 119 | |
| 120 | if (group_id) |
| 121 | *group_id = group_buffer.gr_gid; |
| 122 | return true; |
| 123 | } |
| 124 | |
| 125 | bool Platform::GetUserAndGroupId(const string& user_name, |
| 126 | uid_t* user_id, gid_t* group_id) const { |
Ben Chan | 7073508 | 2014-06-16 20:03:37 -0700 | [diff] [blame] | 127 | long buffer_size = sysconf(_SC_GETPW_R_SIZE_MAX); // NOLINT(runtime/int) |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 128 | if (buffer_size <= 0) |
| 129 | buffer_size = kFallbackPasswordBufferSize; |
| 130 | |
Ben Chan | 71daeac | 2014-09-08 09:03:36 -0700 | [diff] [blame] | 131 | passwd password_buffer, *password_buffer_ptr = nullptr; |
Ben Chan | 5e3ca67 | 2014-08-25 15:53:58 -0700 | [diff] [blame] | 132 | vector<char> buffer(buffer_size); |
| 133 | getpwnam_r(user_name.c_str(), &password_buffer, buffer.data(), buffer_size, |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 134 | &password_buffer_ptr); |
Ben Chan | 44e7ea6 | 2014-08-29 18:13:37 -0700 | [diff] [blame] | 135 | if (password_buffer_ptr == nullptr) { |
Ben Chan | 1d5a8e7 | 2011-08-01 15:21:39 -0700 | [diff] [blame] | 136 | PLOG(WARNING) << "Failed to determine user and group ID of user '" |
| 137 | << user_name << "'"; |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 138 | return false; |
| 139 | } |
| 140 | |
Ben Chan | 1d5a8e7 | 2011-08-01 15:21:39 -0700 | [diff] [blame] | 141 | if (user_id) |
| 142 | *user_id = password_buffer.pw_uid; |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 143 | |
Ben Chan | 1d5a8e7 | 2011-08-01 15:21:39 -0700 | [diff] [blame] | 144 | if (group_id) |
| 145 | *group_id = password_buffer.pw_gid; |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 146 | |
| 147 | return true; |
| 148 | } |
| 149 | |
Ben Chan | b1ac5a8 | 2011-08-02 17:53:55 -0700 | [diff] [blame] | 150 | bool Platform::GetOwnership(const string& path, |
| 151 | uid_t* user_id, gid_t* group_id) const { |
| 152 | struct stat path_status; |
| 153 | if (stat(path.c_str(), &path_status) != 0) { |
| 154 | PLOG(ERROR) << "Failed to get the ownership of '" << path << "'"; |
| 155 | return false; |
| 156 | } |
| 157 | |
| 158 | if (user_id) |
| 159 | *user_id = path_status.st_uid; |
| 160 | |
| 161 | if (group_id) |
| 162 | *group_id = path_status.st_gid; |
| 163 | |
| 164 | return true; |
| 165 | } |
| 166 | |
| 167 | bool Platform::GetPermissions(const string& path, mode_t* mode) const { |
| 168 | CHECK(mode) << "Invalid mode argument"; |
| 169 | |
| 170 | struct stat path_status; |
| 171 | if (stat(path.c_str(), &path_status) != 0) { |
| 172 | PLOG(ERROR) << "Failed to get the permissions of '" << path << "'"; |
| 173 | return false; |
| 174 | } |
| 175 | *mode = path_status.st_mode; |
| 176 | return true; |
| 177 | } |
| 178 | |
| 179 | bool Platform::SetMountUser(const string& user_name) { |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 180 | if (GetUserAndGroupId(user_name, &mount_user_id_, &mount_group_id_)) { |
| 181 | mount_user_ = user_name; |
| 182 | return true; |
| 183 | } |
| 184 | return false; |
Ben Chan | 93dec93 | 2011-08-03 12:57:49 -0700 | [diff] [blame] | 185 | } |
| 186 | |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 187 | bool Platform::RemoveEmptyDirectory(const string& path) const { |
| 188 | if (rmdir(path.c_str()) != 0) { |
| 189 | PLOG(WARNING) << "Failed to remove directory '" << path << "'"; |
| 190 | return false; |
| 191 | } |
| 192 | return true; |
| 193 | } |
| 194 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 195 | bool Platform::SetOwnership(const string& path, |
| 196 | uid_t user_id, gid_t group_id) const { |
| 197 | if (chown(path.c_str(), user_id, group_id)) { |
| 198 | PLOG(ERROR) << "Failed to set ownership of '" << path |
| 199 | << "' to (uid=" << user_id << ", gid=" << group_id << ")"; |
| 200 | return false; |
| 201 | } |
| 202 | return true; |
| 203 | } |
| 204 | |
| 205 | bool Platform::SetPermissions(const string& path, mode_t mode) const { |
| 206 | if (chmod(path.c_str(), mode)) { |
| 207 | PLOG(ERROR) << "Failed to set permissions of '" << path |
| 208 | << "' to " << base::StringPrintf("%04o", mode); |
| 209 | return false; |
| 210 | } |
| 211 | return true; |
| 212 | } |
| 213 | |
| 214 | bool Platform::Unmount(const string& path) const { |
| 215 | if (umount(path.c_str()) != 0) { |
| 216 | PLOG(ERROR) << "Failed to unmount '" << path << "'"; |
| 217 | return false; |
| 218 | } |
| 219 | LOG(INFO) << "Unmount '" << path << "'"; |
| 220 | return true; |
| 221 | } |
| 222 | |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 223 | } // namespace cros_disks |