blob: 8e387976bbd91d2976e7ad11706c3927c722425c [file] [log] [blame]
Luis Hector Chavez81efb332017-09-18 14:01:29 -07001// Copyright 2017 The Chromium OS Authors. All rights reserved.
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 "libcontainer/libcontainer_util.h"
6
7#include <errno.h>
8#include <fcntl.h>
9#if USE_device_mapper
10#include <libdevmapper.h>
11#endif
12#include <linux/loop.h>
13#include <sys/mount.h>
14#include <sys/stat.h>
15
16#include <memory>
17#include <vector>
18
19#include <base/bind.h>
20#include <base/bind_helpers.h>
21#include <base/callback_helpers.h>
22#include <base/files/scoped_file.h>
Luis Hector Chavez835d39e2017-09-19 15:16:31 -070023#include <base/logging.h>
Luis Hector Chavez81efb332017-09-18 14:01:29 -070024#include <base/strings/string_number_conversions.h>
25#include <base/strings/string_split.h>
26#include <base/strings/string_util.h>
27#include <base/strings/stringprintf.h>
28
29namespace libcontainer {
30
31namespace {
32
33constexpr base::FilePath::CharType kLoopdevCtlPath[] =
34 FILE_PATH_LITERAL("/dev/loop-control");
35#if USE_device_mapper
36constexpr base::FilePath::CharType kDevMapperPath[] =
37 FILE_PATH_LITERAL("/dev/mapper/");
38#endif
39
40} // namespace
41
Luis Hector Chavez835d39e2017-09-19 15:16:31 -070042SaveErrno::SaveErrno() : saved_errno_(errno) {}
43
44SaveErrno::~SaveErrno() {
45 errno = saved_errno_;
46}
47
Luis Hector Chavez81efb332017-09-18 14:01:29 -070048int GetUsernsOutsideId(const std::string& map, int id) {
49 if (map.empty())
50 return id;
51
52 std::string map_copy = map;
53 base::StringPiece map_piece(map_copy);
54
55 for (const auto& mapping : base::SplitStringPiece(
56 map_piece, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) {
57 std::vector<base::StringPiece> tokens = base::SplitStringPiece(
58 mapping, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
59
Luis Hector Chavez835d39e2017-09-19 15:16:31 -070060 if (tokens.size() != 3) {
61 LOG(ERROR) << "Malformed ugid mapping: '" << mapping << "'";
Luis Hector Chavez81efb332017-09-18 14:01:29 -070062 return -EINVAL;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -070063 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -070064
65 uint32_t inside, outside, length;
66 if (!base::StringToUint(tokens[0], &inside) ||
67 !base::StringToUint(tokens[1], &outside) ||
68 !base::StringToUint(tokens[2], &length)) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -070069 LOG(ERROR) << "Malformed ugid mapping: '" << mapping << "'";
Luis Hector Chavez81efb332017-09-18 14:01:29 -070070 return -EINVAL;
71 }
72
73 if (id >= inside && id <= (inside + length)) {
74 return (id - inside) + outside;
75 }
76 }
Luis Hector Chavez835d39e2017-09-19 15:16:31 -070077 VLOG(1) << "ugid " << id << " not found in mapping";
Luis Hector Chavez81efb332017-09-18 14:01:29 -070078
79 return -EINVAL;
80}
81
82int MakeDir(const base::FilePath& path, int uid, int gid, int mode) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -070083 if (mkdir(path.value().c_str(), mode)) {
84 PLOG_PRESERVE(ERROR) << "Failed to mkdir " << path.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -070085 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -070086 }
87 if (chmod(path.value().c_str(), mode)) {
88 PLOG_PRESERVE(ERROR) << "Failed to chmod " << path.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -070089 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -070090 }
91 if (chown(path.value().c_str(), uid, gid)) {
92 PLOG_PRESERVE(ERROR) << "Failed to chown " << path.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -070093 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -070094 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -070095 return 0;
96}
97
98int TouchFile(const base::FilePath& path, int uid, int gid, int mode) {
99 base::ScopedFD fd(open(path.value().c_str(), O_RDWR | O_CREAT, mode));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700100 if (!fd.is_valid()) {
101 PLOG_PRESERVE(ERROR) << "Failed to create " << path.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700102 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700103 }
104 if (fchown(fd.get(), uid, gid)) {
105 PLOG_PRESERVE(ERROR) << "Failed to chown " << path.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700106 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700107 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700108 return 0;
109}
110
111int LoopdevSetup(const base::FilePath& source,
112 base::FilePath* loopdev_path_out) {
113 base::ScopedFD source_fd(open(source.value().c_str(), O_RDONLY | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700114 if (!source_fd.is_valid()) {
115 PLOG_PRESERVE(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700116 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700117 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700118
119 base::ScopedFD control_fd(
120 open(kLoopdevCtlPath, O_RDWR | O_NOFOLLOW | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700121 if (!control_fd.is_valid()) {
122 PLOG_PRESERVE(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700123 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700124 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700125
126 while (true) {
127 int num = ioctl(control_fd.get(), LOOP_CTL_GET_FREE);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700128 if (num < 0) {
129 PLOG_PRESERVE(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700130 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700131 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700132
133 base::FilePath loopdev_path(base::StringPrintf("/dev/loop%i", num));
134 base::ScopedFD loop_fd(
135 open(loopdev_path.value().c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700136 if (!loop_fd.is_valid()) {
137 PLOG_PRESERVE(ERROR) << "Failed to open " << loopdev_path.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700138 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700139 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700140
141 if (ioctl(loop_fd.get(), LOOP_SET_FD, source_fd.get()) == 0) {
142 *loopdev_path_out = loopdev_path;
143 break;
144 }
145
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700146 if (errno != EBUSY) {
147 PLOG_PRESERVE(ERROR) << "Failed to ioctl(LOOP_SET_FD) "
148 << loopdev_path.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700149 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700150 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700151 }
152
153 return 0;
154}
155
156int LoopdevDetach(const base::FilePath& loopdev) {
157 base::ScopedFD fd(
158 open(loopdev.value().c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700159 if (!fd.is_valid()) {
160 PLOG_PRESERVE(ERROR) << "Failed to open " << loopdev.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700161 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700162 }
163 if (ioctl(fd.get(), LOOP_CLR_FD) < 0) {
164 PLOG_PRESERVE(ERROR) << "Failed to ioctl(LOOP_CLR_FD) for "
165 << loopdev.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700166 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700167 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700168
169 return 0;
170}
171
172int DeviceMapperSetup(const base::FilePath& source,
173 const std::string& verity_cmdline,
174 base::FilePath* dm_path_out,
175 std::string* dm_name_out) {
176#if USE_device_mapper
177 // Normalize the name into something unique-esque.
178 std::string dm_name =
179 base::StringPrintf("cros-containers-%s", source.value().c_str());
180 base::ReplaceChars(dm_name, "/", "_", &dm_name);
181
182 // Get the /dev path for the higher levels to mount.
183 base::FilePath dm_path = base::FilePath(kDevMapperPath).Append(dm_name);
184
185 // Insert the source path in the verity command line.
186 std::string verity = verity_cmdline;
187 base::ReplaceSubstringsAfterOffset(&verity, 0, "@DEV@", source.value());
188
189 // Extract the first three parameters for dm-verity settings.
190 char ttype[20];
191 unsigned long long start, size;
192 int n;
193 if (sscanf(verity.c_str(), "%llu %llu %10s %n", &start, &size, ttype, &n) !=
194 3) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700195 PLOG_PRESERVE(ERROR) << "Malformed verity string " << verity;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700196 return -errno;
197 }
198
199 /* Finally create the device mapper. */
200 std::unique_ptr<struct dm_task, decltype(&dm_task_destroy)> dmt(
201 dm_task_create(DM_DEVICE_CREATE), &dm_task_destroy);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700202 if (dmt == nullptr) {
203 PLOG_PRESERVE(ERROR) << "Failed to dm_task_create() for " << source.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700204 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700205 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700206
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700207 if (!dm_task_set_name(dmt.get(), dm_name.c_str())) {
208 PLOG_PRESERVE(ERROR) << "Failed to dm_task_set_name() for "
209 << source.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700210 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700211 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700212
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700213 if (!dm_task_set_ro(dmt.get())) {
214 PLOG_PRESERVE(ERROR) << "Failed to dm_task_set_ro() for " << source.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700215 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700216 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700217
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700218 if (!dm_task_add_target(dmt.get(), start, size, ttype, verity.c_str() + n)) {
219 PLOG_PRESERVE(ERROR) << "Failed to dm_task_add_target() for "
220 << source.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700221 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700222 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700223
224 uint32_t cookie = 0;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700225 if (!dm_task_set_cookie(dmt.get(), &cookie, 0)) {
226 PLOG_PRESERVE(ERROR) << "Failed to dm_task_set_cookie() for "
227 << source.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700228 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700229 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700230
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700231 if (!dm_task_run(dmt.get())) {
232 PLOG_PRESERVE(ERROR) << "Failed to dm_task_run() for " << source.value();
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700233 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700234 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700235
236 /* Make sure the node exists before we continue. */
237 dm_udev_wait(cookie);
238
239 *dm_path_out = dm_path;
240 *dm_name_out = dm_name;
241#endif
242 return 0;
243}
244
245// Tear down the device mapper target.
246int DeviceMapperDetach(const std::string& dm_name) {
247#if USE_device_mapper
248 struct dm_task* dmt = dm_task_create(DM_DEVICE_REMOVE);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700249 if (dmt == nullptr) {
250 PLOG_PRESERVE(ERROR) << "Failed to dm_task_run() for " << dm_name;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700251 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700252 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700253
254 base::ScopedClosureRunner teardown(
255 base::Bind(base::IgnoreResult(&dm_task_destroy), base::Unretained(dmt)));
256
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700257 if (!dm_task_set_name(dmt, dm_name.c_str())) {
258 PLOG_PRESERVE(ERROR) << "Failed to dm_task_set_name() for " << dm_name;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700259 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700260 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700261
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700262 if (!dm_task_run(dmt)) {
263 PLOG_PRESERVE(ERROR) << "Failed to dm_task_run() for " << dm_name;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700264 return -errno;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700265 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700266#endif
267 return 0;
268}
269
270int MountExternal(const std::string& src,
271 const std::string& dest,
272 const std::string& type,
273 unsigned long flags,
274 const std::string& data) {
275 bool remount_ro = false;
276
277 // R/O bind mounts have to be remounted since 'bind' and 'ro' can't both be
278 // specified in the original bind mount. Remount R/O after the initial mount.
279 if ((flags & MS_BIND) && (flags & MS_RDONLY)) {
280 remount_ro = true;
281 flags &= ~MS_RDONLY;
282 }
283
284 if (mount(src.c_str(),
285 dest.c_str(),
286 type.c_str(),
287 flags,
288 data.empty() ? nullptr : data.c_str()) == -1) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700289 PLOG_PRESERVE(ERROR) << "Failed to mount " << src << " to " << dest;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700290 return -errno;
291 }
292
293 if (remount_ro) {
294 flags |= MS_RDONLY;
295 if (mount(src.c_str(),
296 dest.c_str(),
297 nullptr,
298 flags | MS_REMOUNT,
299 data.empty() ? nullptr : data.c_str()) == -1) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700300 PLOG_PRESERVE(ERROR) << "Failed to remount " << src << " to " << dest;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700301 return -errno;
302 }
303 }
304
305 return 0;
306}
307
308} // namespace libcontainer