blob: 1cd0c6605007161190beaf24e7e34aab763c44e5 [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
Qijiang Fan713061e2021-03-08 15:45:12 +090015#include <base/check.h>
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>
Heng-Ruey Hsu57595062016-05-24 16:48:58 +080018#include <base/memory/free_deleter.h>
Mike Frysinger38ae98d2012-04-11 12:03:44 -040019#include <base/stl_util.h>
Ben Chan97e20d42014-02-05 18:38:07 -080020#include <base/strings/string_util.h>
21#include <base/strings/stringprintf.h>
Ben Chan950db3d2018-10-31 13:55:02 -070022#include <brillo/userdb_utils.h>
Ben Chan29be9152011-07-25 14:39:48 -070023
François Degros8b4e31e2019-07-29 11:39:19 +100024#include "cros-disks/quote.h"
25
Ben Chan460439f2011-09-13 09:16:28 -070026namespace cros_disks {
Ben Chan29be9152011-07-25 14:39:48 -070027
Ben Chan8dcede82011-07-25 20:56:13 -070028Platform::Platform()
Ben Chande0e3f62017-09-26 06:28:39 -070029 : mount_group_id_(0), mount_user_id_(0), mount_user_("root") {}
Ben Chan29be9152011-07-25 14:39:48 -070030
Ben Chan213c6d92019-04-10 16:21:52 -070031bool Platform::GetRealPath(const std::string& path,
32 std::string* real_path) const {
Ben Chanadc5d002014-03-12 15:02:26 -070033 CHECK(real_path) << "Invalid real_path argument";
34
Ben Chan213c6d92019-04-10 16:21:52 -070035 std::unique_ptr<char, base::FreeDeleter> result(
36 realpath(path.c_str(), nullptr));
Ben Chanadc5d002014-03-12 15:02:26 -070037 if (!result) {
Austin Tankianga57f5772021-05-18 18:41:56 +100038 PLOG(ERROR) << "Cannot get real path of " << redact(path);
Ben Chanadc5d002014-03-12 15:02:26 -070039 return false;
40 }
41
42 *real_path = result.get();
43 return true;
44}
45
Sergei Datsenko0f014d22018-04-04 16:37:22 +100046bool Platform::PathExists(const std::string& path) const {
Ben Chan213c6d92019-04-10 16:21:52 -070047 return base::PathExists(base::FilePath(path));
Sergei Datsenko0f014d22018-04-04 16:37:22 +100048}
49
50bool Platform::DirectoryExists(const std::string& path) const {
Ben Chan213c6d92019-04-10 16:21:52 -070051 return base::DirectoryExists(base::FilePath(path));
Sergei Datsenko0f014d22018-04-04 16:37:22 +100052}
53
Sergei Datsenkoe2d1d0b2020-11-18 12:48:13 +110054bool Platform::Lstat(const std::string& path, base::stat_wrapper_t* out) const {
55 return base::File::Lstat(path.c_str(), out) == 0;
56}
57
Ben Chan213c6d92019-04-10 16:21:52 -070058bool Platform::CreateDirectory(const std::string& path) const {
59 if (!base::CreateDirectory(base::FilePath(path))) {
François Degros8b4e31e2019-07-29 11:39:19 +100060 LOG(ERROR) << "Cannot create directory " << quote(path);
Ben Chan8dcede82011-07-25 20:56:13 -070061 return false;
62 }
François Degros8b4e31e2019-07-29 11:39:19 +100063 LOG(INFO) << "Created directory " << quote(path);
Ben Chan8dcede82011-07-25 20:56:13 -070064 return true;
65}
66
Ben Chan213c6d92019-04-10 16:21:52 -070067bool Platform::CreateOrReuseEmptyDirectory(const std::string& path) const {
Ben Chan29be9152011-07-25 14:39:48 -070068 CHECK(!path.empty()) << "Invalid path argument";
69
70 // Reuse the target path if it already exists and is empty.
71 // rmdir handles the cases when the target path exists but
72 // is not empty, is already mounted or is used by some process.
73 rmdir(path.c_str());
74 if (mkdir(path.c_str(), S_IRWXU) != 0) {
François Degrosc309d932021-02-04 15:12:30 +110075 PLOG(ERROR) << "Cannot create directory " << redact(path);
Ben Chan29be9152011-07-25 14:39:48 -070076 return false;
77 }
78 return true;
79}
80
Ben Chan8dcede82011-07-25 20:56:13 -070081bool Platform::CreateOrReuseEmptyDirectoryWithFallback(
Ben Chan213c6d92019-04-10 16:21:52 -070082 std::string* path,
Ben Chande0e3f62017-09-26 06:28:39 -070083 unsigned max_suffix_to_retry,
Sergei Datsenko7e7c7642021-01-08 19:15:34 +110084 const std::unordered_set<std::string>& reserved_paths) const {
Ben Chan29be9152011-07-25 14:39:48 -070085 CHECK(path && !path->empty()) << "Invalid path argument";
86
Qijiang Fan52439042020-06-17 15:34:38 +090087 if (!base::Contains(reserved_paths, *path) &&
Eric Carusoe8f5ba12018-01-10 14:32:03 -080088 CreateOrReuseEmptyDirectory(*path))
Ben Chan29be9152011-07-25 14:39:48 -070089 return true;
90
91 for (unsigned suffix = 1; suffix <= max_suffix_to_retry; ++suffix) {
Ben Chan213c6d92019-04-10 16:21:52 -070092 std::string fallback_path = GetDirectoryFallbackName(*path, suffix);
Qijiang Fan52439042020-06-17 15:34:38 +090093 if (!base::Contains(reserved_paths, fallback_path) &&
Ben Chanf8692882011-08-21 10:15:30 -070094 CreateOrReuseEmptyDirectory(fallback_path)) {
Ben Chan29be9152011-07-25 14:39:48 -070095 *path = fallback_path;
96 return true;
97 }
98 }
99 return false;
100}
101
Sergei Datsenkobcd8e462018-04-20 15:44:56 +1000102bool Platform::CreateTemporaryDirInDir(const std::string& dir,
103 const std::string& prefix,
104 std::string* path) const {
Ben Chan213c6d92019-04-10 16:21:52 -0700105 base::FilePath dest;
106 bool result =
107 base::CreateTemporaryDirInDir(base::FilePath(dir), prefix, &dest);
Sergei Datsenkobcd8e462018-04-20 15:44:56 +1000108 if (result && path)
109 *path = dest.value();
110 return result;
111}
112
Sergei Datsenkoad2cb6a2018-05-15 17:34:26 +1000113int Platform::WriteFile(const std::string& file,
114 const char* data,
115 int size) const {
Ben Chan213c6d92019-04-10 16:21:52 -0700116 return base::WriteFile(base::FilePath(file), data, size);
Sergei Datsenkoad2cb6a2018-05-15 17:34:26 +1000117}
118
119int Platform::ReadFile(const std::string& file, char* data, int size) const {
Ben Chan213c6d92019-04-10 16:21:52 -0700120 return base::ReadFile(base::FilePath(file), data, size);
Sergei Datsenkobcd8e462018-04-20 15:44:56 +1000121}
122
Ben Chan213c6d92019-04-10 16:21:52 -0700123std::string Platform::GetDirectoryFallbackName(const std::string& path,
124 unsigned suffix) const {
Alex Vakulenkoe50371c2016-01-20 16:06:19 -0800125 if (!path.empty() && base::IsAsciiDigit(path[path.size() - 1]))
Ben Chanec4eaab2012-02-05 23:26:58 -0800126 return base::StringPrintf("%s (%u)", path.c_str(), suffix);
127
128 return base::StringPrintf("%s %u", path.c_str(), suffix);
129}
130
Ben Chan213c6d92019-04-10 16:21:52 -0700131bool Platform::GetGroupId(const std::string& group_name,
132 gid_t* group_id) const {
Ben Chan950db3d2018-10-31 13:55:02 -0700133 return brillo::userdb::GetGroupInfo(group_name, group_id);
Ben Chan1d5a8e72011-08-01 15:21:39 -0700134}
135
Ben Chan213c6d92019-04-10 16:21:52 -0700136bool Platform::GetUserAndGroupId(const std::string& user_name,
Ben Chande0e3f62017-09-26 06:28:39 -0700137 uid_t* user_id,
138 gid_t* group_id) const {
Ben Chan950db3d2018-10-31 13:55:02 -0700139 return brillo::userdb::GetUserInfo(user_name, user_id, group_id);
Ben Chan29be9152011-07-25 14:39:48 -0700140}
141
Ben Chan213c6d92019-04-10 16:21:52 -0700142bool Platform::GetOwnership(const std::string& path,
Ben Chande0e3f62017-09-26 06:28:39 -0700143 uid_t* user_id,
144 gid_t* group_id) const {
Ben Chanb1ac5a82011-08-02 17:53:55 -0700145 struct stat path_status;
146 if (stat(path.c_str(), &path_status) != 0) {
François Degros8b4e31e2019-07-29 11:39:19 +1000147 PLOG(ERROR) << "Cannot get ownership info for " << quote(path);
Ben Chanb1ac5a82011-08-02 17:53:55 -0700148 return false;
149 }
150
151 if (user_id)
152 *user_id = path_status.st_uid;
153
154 if (group_id)
155 *group_id = path_status.st_gid;
156
157 return true;
158}
159
Ben Chan213c6d92019-04-10 16:21:52 -0700160bool Platform::GetPermissions(const std::string& path, mode_t* mode) const {
Ben Chanb1ac5a82011-08-02 17:53:55 -0700161 CHECK(mode) << "Invalid mode argument";
162
163 struct stat path_status;
164 if (stat(path.c_str(), &path_status) != 0) {
François Degros8b4e31e2019-07-29 11:39:19 +1000165 PLOG(ERROR) << "Cannot get the permissions of " << quote(path);
Ben Chanb1ac5a82011-08-02 17:53:55 -0700166 return false;
167 }
168 *mode = path_status.st_mode;
169 return true;
170}
171
Ben Chan213c6d92019-04-10 16:21:52 -0700172bool Platform::SetMountUser(const std::string& user_name) {
Ben Chan98f8ae02011-10-04 16:34:34 -0700173 if (GetUserAndGroupId(user_name, &mount_user_id_, &mount_group_id_)) {
174 mount_user_ = user_name;
175 return true;
176 }
177 return false;
Ben Chan93dec932011-08-03 12:57:49 -0700178}
179
Ben Chan213c6d92019-04-10 16:21:52 -0700180bool Platform::RemoveEmptyDirectory(const std::string& path) const {
Ben Chan29be9152011-07-25 14:39:48 -0700181 if (rmdir(path.c_str()) != 0) {
François Degros8b4e31e2019-07-29 11:39:19 +1000182 PLOG(ERROR) << "Cannot remove directory " << quote(path);
Ben Chan29be9152011-07-25 14:39:48 -0700183 return false;
184 }
185 return true;
186}
187
Ben Chan213c6d92019-04-10 16:21:52 -0700188bool Platform::SetOwnership(const std::string& path,
Ben Chande0e3f62017-09-26 06:28:39 -0700189 uid_t user_id,
190 gid_t group_id) const {
Ben Chan8dcede82011-07-25 20:56:13 -0700191 if (chown(path.c_str(), user_id, group_id)) {
François Degros8b4e31e2019-07-29 11:39:19 +1000192 PLOG(ERROR) << "Cannot set ownership of " << quote(path) << " to uid "
193 << user_id << " and gid " << group_id;
Ben Chan8dcede82011-07-25 20:56:13 -0700194 return false;
195 }
196 return true;
197}
198
Ben Chan213c6d92019-04-10 16:21:52 -0700199bool Platform::SetPermissions(const std::string& path, mode_t mode) const {
Ben Chan8dcede82011-07-25 20:56:13 -0700200 if (chmod(path.c_str(), mode)) {
François Degros8b4e31e2019-07-29 11:39:19 +1000201 PLOG(ERROR) << "Cannot set permissions of " << quote(path) << " to "
Ben Chande0e3f62017-09-26 06:28:39 -0700202 << base::StringPrintf("%04o", mode);
Ben Chan8dcede82011-07-25 20:56:13 -0700203 return false;
204 }
205 return true;
206}
207
Ben Chan213c6d92019-04-10 16:21:52 -0700208MountErrorType Platform::Unmount(const std::string& path, int flags) const {
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100209 error_t error = 0;
Anand K Mistry68418e62018-08-22 11:37:04 +1000210 if (umount2(path.c_str(), flags) != 0) {
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100211 error = errno;
François Degros8b4e31e2019-07-29 11:39:19 +1000212 PLOG(ERROR) << "Cannot unmount " << quote(path) << " with flags " << flags;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100213 } else {
François Degros8b4e31e2019-07-29 11:39:19 +1000214 LOG(INFO) << "Unmounted " << quote(path) << " with flags " << flags;
Ben Chan8dcede82011-07-25 20:56:13 -0700215 }
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100216 switch (error) {
217 case 0:
François Degros3a796902019-07-12 14:49:58 +1000218 return MOUNT_ERROR_NONE;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100219 case ENOENT:
François Degros3a796902019-07-12 14:49:58 +1000220 return MOUNT_ERROR_PATH_NOT_MOUNTED;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100221 case EPERM:
François Degros3a796902019-07-12 14:49:58 +1000222 return MOUNT_ERROR_INSUFFICIENT_PERMISSIONS;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100223 case EBUSY:
François Degros3a796902019-07-12 14:49:58 +1000224 return MOUNT_ERROR_PATH_ALREADY_MOUNTED;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100225 default:
François Degros3a796902019-07-12 14:49:58 +1000226 return MOUNT_ERROR_UNKNOWN;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100227 }
Ben Chan8dcede82011-07-25 20:56:13 -0700228}
229
Ben Chan213c6d92019-04-10 16:21:52 -0700230MountErrorType Platform::Mount(const std::string& source_path,
231 const std::string& target_path,
232 const std::string& filesystem_type,
François Degros5abcca22020-09-10 16:51:13 +1000233 const uint64_t flags,
234 const std::string& options) const {
Sergei Datsenkoe8faba52020-10-06 21:45:22 +1100235 // Pass the nosymfollow option as both a flag and a string option for
236 // compatibility across kernels. The mount syscall ignores unknown flags,
237 // so kernels that don't have MS_NOSYMFOLLOW will pick up nosymfollow from
238 // the data parameter through the chromiumos LSM. Kernels that do have
239 // MS_NOSYMFOLLOW will pick up the same behavior directly from the flag;
240 // our LSM ignores the string option in that case.
241 //
242 // TODO(b/152074038): Remove the string option once all devices have been
243 // upreved to a kernel that supports MS_NOSYMFOLLOW (currently 5.4+).
244 std::string mount_options = options;
245 if ((flags & MS_NOSYMFOLLOW) == MS_NOSYMFOLLOW) {
Sergei Datsenko84c2d0d2020-10-12 11:02:35 +1100246 if (!mount_options.empty()) {
Sergei Datsenkoe8faba52020-10-06 21:45:22 +1100247 mount_options += ",";
248 }
Sergei Datsenko84c2d0d2020-10-12 11:02:35 +1100249 mount_options += "nosymfollow";
Sergei Datsenkoe8faba52020-10-06 21:45:22 +1100250 }
251
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100252 error_t error = 0;
Sam McNallyc56ae312018-05-22 13:14:27 +1000253 if (mount(source_path.c_str(), target_path.c_str(), filesystem_type.c_str(),
Sergei Datsenkoe8faba52020-10-06 21:45:22 +1100254 flags, mount_options.c_str()) != 0) {
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100255 error = errno;
François Degros5abcca22020-09-10 16:51:13 +1000256 PLOG(ERROR) << "Cannot create mount point " << quote(target_path) << " for "
257 << quote(source_path) << " as filesystem "
258 << quote(filesystem_type) << " with flags 0x" << std::hex
Sergei Datsenkoe8faba52020-10-06 21:45:22 +1100259 << flags << " and options " << quote(mount_options);
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100260 } else {
François Degros5abcca22020-09-10 16:51:13 +1000261 LOG(INFO) << "Created mount point " << quote(target_path) << " for "
262 << quote(source_path) << " as filesystem "
263 << quote(filesystem_type) << " with flags 0x" << std::hex << flags
Sergei Datsenkoe8faba52020-10-06 21:45:22 +1100264 << " and options " << quote(mount_options);
Sam McNallyc56ae312018-05-22 13:14:27 +1000265 }
François Degros5abcca22020-09-10 16:51:13 +1000266
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100267 switch (error) {
268 case 0:
François Degros3a796902019-07-12 14:49:58 +1000269 return MOUNT_ERROR_NONE;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100270 case ENODEV:
François Degros3a796902019-07-12 14:49:58 +1000271 return MOUNT_ERROR_UNSUPPORTED_FILESYSTEM;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100272 case ENOENT:
273 case ENOTBLK:
274 case ENOTDIR:
François Degros3a796902019-07-12 14:49:58 +1000275 return MOUNT_ERROR_INVALID_PATH;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100276 case EPERM:
François Degros3a796902019-07-12 14:49:58 +1000277 return MOUNT_ERROR_INSUFFICIENT_PERMISSIONS;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100278 default:
François Degros3a796902019-07-12 14:49:58 +1000279 return MOUNT_ERROR_UNKNOWN;
Sergei Datsenkob362e4a2019-04-03 17:23:24 +1100280 }
Sam McNallyc56ae312018-05-22 13:14:27 +1000281}
282
Ben Chan29be9152011-07-25 14:39:48 -0700283} // namespace cros_disks