blob: b07ef70b893e9d62f89bd3abd746f8d54c9ceab5 [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 Chan35cd7ae2019-05-02 22:42:37 -07007#include <errno.h>
Ben Chan8dcede82011-07-25 20:56:13 -07008#include <sys/mount.h>
Ben Chan29be9152011-07-25 14:39:48 -07009#include <sys/stat.h>
10#include <unistd.h>
11
Ben Chan5e3ca672014-08-25 15:53:58 -070012#include <memory>
13#include <vector>
14
Ben Chancd8fda42014-09-05 08:21:06 -070015#include <base/files/file_util.h>
Ben Chan29be9152011-07-25 14:39:48 -070016#include <base/logging.h>
Heng-Ruey Hsu57595062016-05-24 16:48:58 +080017#include <base/memory/free_deleter.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 Chan950db3d2018-10-31 13:55:02 -070021#include <brillo/userdb_utils.h>
Ben Chan29be9152011-07-25 14:39:48 -070022
François Degros8b4e31e2019-07-29 11:39:19 +100023#include "cros-disks/quote.h"
24
Ben Chan460439f2011-09-13 09:16:28 -070025namespace cros_disks {
Ben Chan29be9152011-07-25 14:39:48 -070026
Ben Chan8dcede82011-07-25 20:56:13 -070027Platform::Platform()
Ben Chande0e3f62017-09-26 06:28:39 -070028 : mount_group_id_(0), mount_user_id_(0), mount_user_("root") {}
Ben Chan29be9152011-07-25 14:39:48 -070029
Ben Chan213c6d92019-04-10 16:21:52 -070030bool Platform::GetRealPath(const std::string& path,
31 std::string* real_path) const {
Ben Chanadc5d002014-03-12 15:02:26 -070032 CHECK(real_path) << "Invalid real_path argument";
33
Ben Chan213c6d92019-04-10 16:21:52 -070034 std::unique_ptr<char, base::FreeDeleter> result(
35 realpath(path.c_str(), nullptr));
Ben Chanadc5d002014-03-12 15:02:26 -070036 if (!result) {
François Degros8b4e31e2019-07-29 11:39:19 +100037 PLOG(ERROR) << "Cannot get real path of " << quote(path);
Ben Chanadc5d002014-03-12 15:02:26 -070038 return false;
39 }
40
41 *real_path = result.get();
42 return true;
43}
44
Sergei Datsenko0f014d22018-04-04 16:37:22 +100045bool Platform::PathExists(const std::string& path) const {
Ben Chan213c6d92019-04-10 16:21:52 -070046 return base::PathExists(base::FilePath(path));
Sergei Datsenko0f014d22018-04-04 16:37:22 +100047}
48
49bool Platform::DirectoryExists(const std::string& path) const {
Ben Chan213c6d92019-04-10 16:21:52 -070050 return base::DirectoryExists(base::FilePath(path));
Sergei Datsenko0f014d22018-04-04 16:37:22 +100051}
52
Ben Chan213c6d92019-04-10 16:21:52 -070053bool Platform::CreateDirectory(const std::string& path) const {
54 if (!base::CreateDirectory(base::FilePath(path))) {
François Degros8b4e31e2019-07-29 11:39:19 +100055 LOG(ERROR) << "Cannot create directory " << quote(path);
Ben Chan8dcede82011-07-25 20:56:13 -070056 return false;
57 }
François Degros8b4e31e2019-07-29 11:39:19 +100058 LOG(INFO) << "Created directory " << quote(path);
Ben Chan8dcede82011-07-25 20:56:13 -070059 return true;
60}
61
Ben Chan213c6d92019-04-10 16:21:52 -070062bool Platform::CreateOrReuseEmptyDirectory(const std::string& path) const {
Ben Chan29be9152011-07-25 14:39:48 -070063 CHECK(!path.empty()) << "Invalid path argument";
64
65 // Reuse the target path if it already exists and is empty.
66 // rmdir handles the cases when the target path exists but
67 // is not empty, is already mounted or is used by some process.
68 rmdir(path.c_str());
69 if (mkdir(path.c_str(), S_IRWXU) != 0) {
François Degros8b4e31e2019-07-29 11:39:19 +100070 PLOG(ERROR) << "Cannot create directory " << quote(path);
Ben Chan29be9152011-07-25 14:39:48 -070071 return false;
72 }
73 return true;
74}
75
Ben Chan8dcede82011-07-25 20:56:13 -070076bool Platform::CreateOrReuseEmptyDirectoryWithFallback(
Ben Chan213c6d92019-04-10 16:21:52 -070077 std::string* path,
Ben Chande0e3f62017-09-26 06:28:39 -070078 unsigned max_suffix_to_retry,
Ben Chanf8692882011-08-21 10:15:30 -070079 const std::set<std::string>& reserved_paths) const {
Ben Chan29be9152011-07-25 14:39:48 -070080 CHECK(path && !path->empty()) << "Invalid path argument";
81
Qijiang Fan52439042020-06-17 15:34:38 +090082 if (!base::Contains(reserved_paths, *path) &&
Eric Carusoe8f5ba12018-01-10 14:32:03 -080083 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 Chan213c6d92019-04-10 16:21:52 -070087 std::string fallback_path = GetDirectoryFallbackName(*path, suffix);
Qijiang Fan52439042020-06-17 15:34:38 +090088 if (!base::Contains(reserved_paths, fallback_path) &&
Ben Chanf8692882011-08-21 10:15:30 -070089 CreateOrReuseEmptyDirectory(fallback_path)) {
Ben Chan29be9152011-07-25 14:39:48 -070090 *path = fallback_path;
91 return true;
92 }
93 }
94 return false;
95}
96
Sergei Datsenkobcd8e462018-04-20 15:44:56 +100097bool Platform::CreateTemporaryDirInDir(const std::string& dir,
98 const std::string& prefix,
99 std::string* path) const {
Ben Chan213c6d92019-04-10 16:21:52 -0700100 base::FilePath dest;
101 bool result =
102 base::CreateTemporaryDirInDir(base::FilePath(dir), prefix, &dest);
Sergei Datsenkobcd8e462018-04-20 15:44:56 +1000103 if (result && path)
104 *path = dest.value();
105 return result;
106}
107
Sergei Datsenkoad2cb6a2018-05-15 17:34:26 +1000108int Platform::WriteFile(const std::string& file,
109 const char* data,
110 int size) const {
Ben Chan213c6d92019-04-10 16:21:52 -0700111 return base::WriteFile(base::FilePath(file), data, size);
Sergei Datsenkoad2cb6a2018-05-15 17:34:26 +1000112}
113
114int Platform::ReadFile(const std::string& file, char* data, int size) const {
Ben Chan213c6d92019-04-10 16:21:52 -0700115 return base::ReadFile(base::FilePath(file), data, size);
Sergei Datsenkobcd8e462018-04-20 15:44:56 +1000116}
117
Ben Chan213c6d92019-04-10 16:21:52 -0700118std::string Platform::GetDirectoryFallbackName(const std::string& path,
119 unsigned suffix) const {
Alex Vakulenkoe50371c2016-01-20 16:06:19 -0800120 if (!path.empty() && base::IsAsciiDigit(path[path.size() - 1]))
Ben Chanec4eaab2012-02-05 23:26:58 -0800121 return base::StringPrintf("%s (%u)", path.c_str(), suffix);
122
123 return base::StringPrintf("%s %u", path.c_str(), suffix);
124}
125
Ben Chan213c6d92019-04-10 16:21:52 -0700126bool Platform::GetGroupId(const std::string& group_name,
127 gid_t* group_id) const {
Ben Chan950db3d2018-10-31 13:55:02 -0700128 return brillo::userdb::GetGroupInfo(group_name, group_id);
Ben Chan1d5a8e72011-08-01 15:21:39 -0700129}
130
Ben Chan213c6d92019-04-10 16:21:52 -0700131bool Platform::GetUserAndGroupId(const std::string& user_name,
Ben Chande0e3f62017-09-26 06:28:39 -0700132 uid_t* user_id,
133 gid_t* group_id) const {
Ben Chan950db3d2018-10-31 13:55:02 -0700134 return brillo::userdb::GetUserInfo(user_name, user_id, group_id);
Ben Chan29be9152011-07-25 14:39:48 -0700135}
136
Ben Chan213c6d92019-04-10 16:21:52 -0700137bool Platform::GetOwnership(const std::string& path,
Ben Chande0e3f62017-09-26 06:28:39 -0700138 uid_t* user_id,
139 gid_t* group_id) const {
Ben Chanb1ac5a82011-08-02 17:53:55 -0700140 struct stat path_status;
141 if (stat(path.c_str(), &path_status) != 0) {
François Degros8b4e31e2019-07-29 11:39:19 +1000142 PLOG(ERROR) << "Cannot get ownership info for " << quote(path);
Ben Chanb1ac5a82011-08-02 17:53:55 -0700143 return false;
144 }
145
146 if (user_id)
147 *user_id = path_status.st_uid;
148
149 if (group_id)
150 *group_id = path_status.st_gid;
151
152 return true;
153}
154
Ben Chan213c6d92019-04-10 16:21:52 -0700155bool Platform::GetPermissions(const std::string& path, mode_t* mode) const {
Ben Chanb1ac5a82011-08-02 17:53:55 -0700156 CHECK(mode) << "Invalid mode argument";
157
158 struct stat path_status;
159 if (stat(path.c_str(), &path_status) != 0) {
François Degros8b4e31e2019-07-29 11:39:19 +1000160 PLOG(ERROR) << "Cannot get the permissions of " << quote(path);
Ben Chanb1ac5a82011-08-02 17:53:55 -0700161 return false;
162 }
163 *mode = path_status.st_mode;
164 return true;
165}
166
Ben Chan213c6d92019-04-10 16:21:52 -0700167bool Platform::SetMountUser(const std::string& user_name) {
Ben Chan98f8ae02011-10-04 16:34:34 -0700168 if (GetUserAndGroupId(user_name, &mount_user_id_, &mount_group_id_)) {
169 mount_user_ = user_name;
170 return true;
171 }
172 return false;
Ben Chan93dec932011-08-03 12:57:49 -0700173}
174
Ben Chan213c6d92019-04-10 16:21:52 -0700175bool Platform::RemoveEmptyDirectory(const std::string& path) const {
Ben Chan29be9152011-07-25 14:39:48 -0700176 if (rmdir(path.c_str()) != 0) {
François Degros8b4e31e2019-07-29 11:39:19 +1000177 PLOG(ERROR) << "Cannot remove directory " << quote(path);
Ben Chan29be9152011-07-25 14:39:48 -0700178 return false;
179 }
180 return true;
181}
182
Ben Chan213c6d92019-04-10 16:21:52 -0700183bool Platform::SetOwnership(const std::string& path,
Ben Chande0e3f62017-09-26 06:28:39 -0700184 uid_t user_id,
185 gid_t group_id) const {
Ben Chan8dcede82011-07-25 20:56:13 -0700186 if (chown(path.c_str(), user_id, group_id)) {
François Degros8b4e31e2019-07-29 11:39:19 +1000187 PLOG(ERROR) << "Cannot set ownership of " << quote(path) << " to uid "
188 << user_id << " and gid " << group_id;
Ben Chan8dcede82011-07-25 20:56:13 -0700189 return false;
190 }
191 return true;
192}
193
Ben Chan213c6d92019-04-10 16:21:52 -0700194bool Platform::SetPermissions(const std::string& path, mode_t mode) const {
Ben Chan8dcede82011-07-25 20:56:13 -0700195 if (chmod(path.c_str(), mode)) {
François Degros8b4e31e2019-07-29 11:39:19 +1000196 PLOG(ERROR) << "Cannot set permissions of " << quote(path) << " to "
Ben Chande0e3f62017-09-26 06:28:39 -0700197 << base::StringPrintf("%04o", mode);
Ben Chan8dcede82011-07-25 20:56:13 -0700198 return false;
199 }
200 return true;
201}
202
Ben Chan213c6d92019-04-10 16:21:52 -0700203MountErrorType Platform::Unmount(const std::string& path, int flags) const {
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100204 error_t error = 0;
Anand K Mistry68418e62018-08-22 11:37:04 +1000205 if (umount2(path.c_str(), flags) != 0) {
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100206 error = errno;
François Degros8b4e31e2019-07-29 11:39:19 +1000207 PLOG(ERROR) << "Cannot unmount " << quote(path) << " with flags " << flags;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100208 } else {
François Degros8b4e31e2019-07-29 11:39:19 +1000209 LOG(INFO) << "Unmounted " << quote(path) << " with flags " << flags;
Ben Chan8dcede82011-07-25 20:56:13 -0700210 }
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100211 switch (error) {
212 case 0:
François Degros3a796902019-07-12 14:49:58 +1000213 return MOUNT_ERROR_NONE;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100214 case ENOENT:
François Degros3a796902019-07-12 14:49:58 +1000215 return MOUNT_ERROR_PATH_NOT_MOUNTED;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100216 case EPERM:
François Degros3a796902019-07-12 14:49:58 +1000217 return MOUNT_ERROR_INSUFFICIENT_PERMISSIONS;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100218 case EBUSY:
François Degros3a796902019-07-12 14:49:58 +1000219 return MOUNT_ERROR_PATH_ALREADY_MOUNTED;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100220 default:
François Degros3a796902019-07-12 14:49:58 +1000221 return MOUNT_ERROR_UNKNOWN;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100222 }
Ben Chan8dcede82011-07-25 20:56:13 -0700223}
224
Ben Chan213c6d92019-04-10 16:21:52 -0700225MountErrorType Platform::Mount(const std::string& source_path,
226 const std::string& target_path,
227 const std::string& filesystem_type,
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100228 uint64_t options,
229 const std::string& data) const {
230 error_t error = 0;
Sam McNallyc56ae312018-05-22 13:14:27 +1000231 if (mount(source_path.c_str(), target_path.c_str(), filesystem_type.c_str(),
232 options, data.c_str()) != 0) {
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100233 error = errno;
François Degros8b4e31e2019-07-29 11:39:19 +1000234 PLOG(ERROR) << "Cannot mount " << quote(source_path) << " to "
235 << quote(target_path) << " as filesystem "
236 << quote(filesystem_type) << " with options " << options << " "
237 << quote(data);
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100238 } else {
François Degros8b4e31e2019-07-29 11:39:19 +1000239 LOG(INFO) << "Mounted " << quote(source_path) << " to "
240 << quote(target_path) << " as filesystem "
241 << quote(filesystem_type) << " with options " << options << " "
242 << quote(data);
Sam McNallyc56ae312018-05-22 13:14:27 +1000243 }
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100244 switch (error) {
245 case 0:
François Degros3a796902019-07-12 14:49:58 +1000246 return MOUNT_ERROR_NONE;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100247 case ENODEV:
François Degros3a796902019-07-12 14:49:58 +1000248 return MOUNT_ERROR_UNSUPPORTED_FILESYSTEM;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100249 case ENOENT:
250 case ENOTBLK:
251 case ENOTDIR:
François Degros3a796902019-07-12 14:49:58 +1000252 return MOUNT_ERROR_INVALID_PATH;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100253 case EPERM:
François Degros3a796902019-07-12 14:49:58 +1000254 return MOUNT_ERROR_INSUFFICIENT_PERMISSIONS;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100255 default:
François Degros3a796902019-07-12 14:49:58 +1000256 return MOUNT_ERROR_UNKNOWN;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100257 }
Sam McNallyc56ae312018-05-22 13:14:27 +1000258}
259
Ben Chan29be9152011-07-25 14:39:48 -0700260} // namespace cros_disks