blob: f74f4170d2925f98027e67897c99ce775b5710c0 [file] [log] [blame]
Mike Frysinger38ae98d2012-04-11 12:03:44 -04001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Ben Chan29be9152011-07-25 14:39:48 -07002// 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 Chan1d5a8e72011-08-01 15:21:39 -07007#include <grp.h>
Ben Chan29be9152011-07-25 14:39:48 -07008#include <pwd.h>
Ben Chan8dcede82011-07-25 20:56:13 -07009#include <sys/mount.h>
Ben Chan29be9152011-07-25 14:39:48 -070010#include <sys/stat.h>
11#include <unistd.h>
12
Ben Chan5e3ca672014-08-25 15:53:58 -070013#include <memory>
14#include <vector>
15
Ben Chancd8fda42014-09-05 08:21:06 -070016#include <base/files/file_util.h>
Ben Chan29be9152011-07-25 14:39:48 -070017#include <base/logging.h>
Mike Frysinger38ae98d2012-04-11 12:03:44 -040018#include <base/stl_util.h>
Ben Chan97e20d42014-02-05 18:38:07 -080019#include <base/strings/string_util.h>
20#include <base/strings/stringprintf.h>
Ben Chan29be9152011-07-25 14:39:48 -070021
Ben Chan2e8aa6f2013-02-15 11:40:04 -080022using base::FilePath;
Ben Chan29be9152011-07-25 14:39:48 -070023using std::string;
Ben Chan5e3ca672014-08-25 15:53:58 -070024using std::unique_ptr;
25using std::vector;
Ben Chan29be9152011-07-25 14:39:48 -070026
Ben Chan460439f2011-09-13 09:16:28 -070027namespace {
Ben Chan29be9152011-07-25 14:39:48 -070028
Ben Chan460439f2011-09-13 09:16:28 -070029const unsigned kFallbackGroupBufferSize = 16384;
30const unsigned kFallbackPasswordBufferSize = 16384;
31
32} // namespace
33
34namespace cros_disks {
Ben Chan29be9152011-07-25 14:39:48 -070035
Ben Chan8dcede82011-07-25 20:56:13 -070036Platform::Platform()
Ben Chan10510e82014-08-16 18:07:36 -070037 : mount_group_id_(0),
Ben Chan98f8ae02011-10-04 16:34:34 -070038 mount_user_id_(0),
39 mount_user_("root") {
Ben Chan29be9152011-07-25 14:39:48 -070040}
41
Ben Chanadc5d002014-03-12 15:02:26 -070042bool Platform::GetRealPath(const string& path, string* real_path) const {
43 CHECK(real_path) << "Invalid real_path argument";
44
Ben Chan44e7ea62014-08-29 18:13:37 -070045 unique_ptr<char, base::FreeDeleter> result(realpath(path.c_str(), nullptr));
Ben Chanadc5d002014-03-12 15:02:26 -070046 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 Chan8dcede82011-07-25 20:56:13 -070055bool Platform::CreateDirectory(const string& path) const {
Ben Chan97e20d42014-02-05 18:38:07 -080056 if (!base::CreateDirectory(FilePath(path))) {
Ben Chan8dcede82011-07-25 20:56:13 -070057 LOG(ERROR) << "Failed to create directory '" << path << "'";
58 return false;
59 }
60 LOG(INFO) << "Created directory '" << path << "'";
61 return true;
62}
63
Ben Chan29be9152011-07-25 14:39:48 -070064bool 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 Chan8dcede82011-07-25 20:56:13 -070078bool Platform::CreateOrReuseEmptyDirectoryWithFallback(
Ben Chanf8692882011-08-21 10:15:30 -070079 string* path, unsigned max_suffix_to_retry,
80 const std::set<std::string>& reserved_paths) const {
Ben Chan29be9152011-07-25 14:39:48 -070081 CHECK(path && !path->empty()) << "Invalid path argument";
82
Ben Chanf8692882011-08-21 10:15:30 -070083 if (!ContainsKey(reserved_paths, *path) && CreateOrReuseEmptyDirectory(*path))
Ben Chan29be9152011-07-25 14:39:48 -070084 return true;
85
86 for (unsigned suffix = 1; suffix <= max_suffix_to_retry; ++suffix) {
Ben Chanec4eaab2012-02-05 23:26:58 -080087 string fallback_path = GetDirectoryFallbackName(*path, suffix);
Ben Chanf8692882011-08-21 10:15:30 -070088 if (!ContainsKey(reserved_paths, fallback_path) &&
89 CreateOrReuseEmptyDirectory(fallback_path)) {
Ben Chan29be9152011-07-25 14:39:48 -070090 *path = fallback_path;
91 return true;
92 }
93 }
94 return false;
95}
96
Ben Chanec4eaab2012-02-05 23:26:58 -080097string 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 Chan1d5a8e72011-08-01 15:21:39 -0700105bool Platform::GetGroupId(const string& group_name, gid_t* group_id) const {
Ben Chan70735082014-06-16 20:03:37 -0700106 long buffer_size = sysconf(_SC_GETGR_R_SIZE_MAX); // NOLINT(runtime/int)
Ben Chan1d5a8e72011-08-01 15:21:39 -0700107 if (buffer_size <= 0)
108 buffer_size = kFallbackGroupBufferSize;
109
Ben Chan71daeac2014-09-08 09:03:36 -0700110 group group_buffer, *group_buffer_ptr = nullptr;
Ben Chan5e3ca672014-08-25 15:53:58 -0700111 vector<char> buffer(buffer_size);
112 getgrnam_r(group_name.c_str(), &group_buffer, buffer.data(), buffer_size,
113 &group_buffer_ptr);
Ben Chan44e7ea62014-08-29 18:13:37 -0700114 if (group_buffer_ptr == nullptr) {
Ben Chan1d5a8e72011-08-01 15:21:39 -0700115 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
125bool Platform::GetUserAndGroupId(const string& user_name,
126 uid_t* user_id, gid_t* group_id) const {
Ben Chan70735082014-06-16 20:03:37 -0700127 long buffer_size = sysconf(_SC_GETPW_R_SIZE_MAX); // NOLINT(runtime/int)
Ben Chan29be9152011-07-25 14:39:48 -0700128 if (buffer_size <= 0)
129 buffer_size = kFallbackPasswordBufferSize;
130
Ben Chan71daeac2014-09-08 09:03:36 -0700131 passwd password_buffer, *password_buffer_ptr = nullptr;
Ben Chan5e3ca672014-08-25 15:53:58 -0700132 vector<char> buffer(buffer_size);
133 getpwnam_r(user_name.c_str(), &password_buffer, buffer.data(), buffer_size,
Ben Chan29be9152011-07-25 14:39:48 -0700134 &password_buffer_ptr);
Ben Chan44e7ea62014-08-29 18:13:37 -0700135 if (password_buffer_ptr == nullptr) {
Ben Chan1d5a8e72011-08-01 15:21:39 -0700136 PLOG(WARNING) << "Failed to determine user and group ID of user '"
137 << user_name << "'";
Ben Chan29be9152011-07-25 14:39:48 -0700138 return false;
139 }
140
Ben Chan1d5a8e72011-08-01 15:21:39 -0700141 if (user_id)
142 *user_id = password_buffer.pw_uid;
Ben Chan29be9152011-07-25 14:39:48 -0700143
Ben Chan1d5a8e72011-08-01 15:21:39 -0700144 if (group_id)
145 *group_id = password_buffer.pw_gid;
Ben Chan29be9152011-07-25 14:39:48 -0700146
147 return true;
148}
149
Ben Chanb1ac5a82011-08-02 17:53:55 -0700150bool 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
167bool 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
179bool Platform::SetMountUser(const string& user_name) {
Ben Chan98f8ae02011-10-04 16:34:34 -0700180 if (GetUserAndGroupId(user_name, &mount_user_id_, &mount_group_id_)) {
181 mount_user_ = user_name;
182 return true;
183 }
184 return false;
Ben Chan93dec932011-08-03 12:57:49 -0700185}
186
Ben Chan29be9152011-07-25 14:39:48 -0700187bool 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 Chan8dcede82011-07-25 20:56:13 -0700195bool 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
205bool 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
214bool 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 Chan29be9152011-07-25 14:39:48 -0700223} // namespace cros_disks