]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/machine-pool.c
sysusers: do not reject users with already present /etc/shadow entries
[thirdparty/systemd.git] / src / shared / machine-pool.c
CommitLineData
432cea00
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2015 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <sys/prctl.h>
23#include <sys/vfs.h>
24#include <sys/statvfs.h>
25#include <sys/mount.h>
26
27#include "util.h"
28#include "mkdir.h"
29#include "btrfs-util.h"
30#include "path-util.h"
31#include "machine-pool.h"
32
33#define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
34#define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
35
36static int check_btrfs(void) {
37 struct statfs sfs;
38
39 if (statfs("/var/lib/machines", &sfs) < 0) {
40 if (errno != ENOENT)
41 return -errno;
42
43 if (statfs("/var/lib", &sfs) < 0)
44 return -errno;
45 }
46
47 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
48}
49
4cee5eed 50static int setup_machine_raw(uint64_t size, sd_bus_error *error) {
432cea00
LP
51 _cleanup_free_ char *tmp = NULL;
52 _cleanup_close_ int fd = -1;
53 struct statvfs ss;
54 pid_t pid = 0;
55 siginfo_t si;
56 int r;
57
58 /* We want to be able to make use of btrfs-specific file
59 * system features, in particular subvolumes, reflinks and
60 * quota. Hence, if we detect that /var/lib/machines.raw is
61 * not located on btrfs, let's create a loopback file, place a
62 * btrfs file system into it, and mount it to
63 * /var/lib/machines. */
64
65 fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
66 if (fd >= 0) {
67 r = fd;
68 fd = -1;
69 return r;
70 }
71
72 if (errno != ENOENT)
73 return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m");
74
75 r = tempfn_xxxxxx("/var/lib/machines.raw", &tmp);
76 if (r < 0)
77 return r;
78
79 (void) mkdir_p_label("/var/lib", 0755);
80 fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600);
81 if (fd < 0)
82 return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m");
83
84 if (fstatvfs(fd, &ss) < 0) {
85 r = sd_bus_error_set_errnof(error, errno, "Failed to determine free space on /var/lib/machines.raw: %m");
86 goto fail;
87 }
88
89 if (ss.f_bsize * ss.f_bavail < VAR_LIB_MACHINES_FREE_MIN) {
90 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Not enough free disk space to set up /var/lib/machines.");
91 goto fail;
92 }
93
4cee5eed 94 if (ftruncate(fd, size) < 0) {
432cea00
LP
95 r = sd_bus_error_set_errnof(error, errno, "Failed to enlarge /var/lib/machines.raw: %m");
96 goto fail;
97 }
98
99 pid = fork();
100 if (pid < 0) {
101 r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m");
102 goto fail;
103 }
104
105 if (pid == 0) {
106
107 /* Child */
108
109 reset_all_signal_handlers();
110 reset_signal_mask();
111 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
112
113 fd = safe_close(fd);
114
115 execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL);
116 if (errno == ENOENT)
117 return 99;
118
119 _exit(EXIT_FAILURE);
120 }
121
122 r = wait_for_terminate(pid, &si);
123 if (r < 0) {
124 sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m");
125 goto fail;
126 }
127
128 pid = 0;
129
130 if (si.si_code != CLD_EXITED) {
131 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally.");
132 goto fail;
133 }
134 if (si.si_status == 99) {
135 r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
136 goto fail;
137 }
138 if (si.si_status != 0) {
139 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status);
140 goto fail;
141 }
142
143 if (renameat2(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw", RENAME_NOREPLACE) < 0) {
144 r = sd_bus_error_set_errnof(error, errno, "Failed to move /var/lib/machines.raw into place: %m");
145 goto fail;
146 }
147
148 r = fd;
149 fd = -1;
150
151 return r;
152
153fail:
154 if (tmp)
155 unlink_noerrno(tmp);
156
157 if (pid > 1)
158 kill_and_sigcont(pid, SIGKILL);
159
160 return r;
161}
162
4cee5eed 163int setup_machine_directory(uint64_t size, sd_bus_error *error) {
403e5b32 164 _cleanup_release_lock_file_ LockFile lock_file = LOCK_FILE_INIT;
432cea00
LP
165 struct loop_info64 info = {
166 .lo_flags = LO_FLAGS_AUTOCLEAR,
167 };
168 _cleanup_close_ int fd = -1, control = -1, loop = -1;
169 _cleanup_free_ char* loopdev = NULL;
170 char tmpdir[] = "/tmp/import-mount.XXXXXX", *mntdir = NULL;
171 bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false;
26166c88 172 char buf[FORMAT_BYTES_MAX];
432cea00
LP
173 int r, nr = -1;
174
4cee5eed
LP
175 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
176 if (size == (uint64_t) -1)
177 size = VAR_LIB_MACHINES_SIZE_START;
178 else if (size < 16*1024*1024)
179 size = 16*1024*1024;
180
403e5b32
LP
181 /* Make sure we only set the directory up once at a time */
182 r = make_lock_file("/run/systemd/machines.lock", LOCK_EX, &lock_file);
183 if (r < 0)
184 return r;
185
432cea00
LP
186 r = check_btrfs();
187 if (r < 0)
188 return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
189 if (r > 0) {
190 (void) btrfs_subvol_make_label("/var/lib/machines");
191
192 r = btrfs_quota_enable("/var/lib/machines", true);
193 if (r < 0)
194 log_warning_errno(r, "Failed to enable quota, ignoring: %m");
195
196 return 0;
197 }
198
199 if (path_is_mount_point("/var/lib/machines", true) > 0 ||
200 dir_is_empty("/var/lib/machines") == 0)
201 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "/var/lib/machines is not a btrfs file system. Operation is not supported on legacy file systems.");
202
4cee5eed 203 fd = setup_machine_raw(size, error);
432cea00
LP
204 if (fd < 0)
205 return fd;
206
207 control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
208 if (control < 0)
209 return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m");
210
211 nr = ioctl(control, LOOP_CTL_GET_FREE);
212 if (nr < 0)
213 return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m");
214
215 if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) {
216 r = -ENOMEM;
217 goto fail;
218 }
219
220 loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK);
221 if (loop < 0) {
222 r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m");
223 goto fail;
224 }
225
226 if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
227 r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m");
228 goto fail;
229 }
230
231 if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
232 r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m");
233 goto fail;
234 }
235
236 /* We need to make sure the new /var/lib/machines directory
237 * has an access mode of 0700 at the time it is first made
238 * available. mkfs will create it with 0755 however. Hence,
239 * let's mount the directory into an inaccessible directory
240 * below /tmp first, fix the access mode, and move it to the
241 * public place then. */
242
243 if (!mkdtemp(tmpdir)) {
244 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m");
245 goto fail;
246 }
247 tmpdir_made = true;
248
249 mntdir = strjoina(tmpdir, "/mnt");
250 if (mkdir(mntdir, 0700) < 0) {
251 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m");
252 goto fail;
253 }
254 mntdir_made = true;
255
256 if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) {
257 r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m");
258 goto fail;
259 }
260 mntdir_mounted = true;
261
262 r = btrfs_quota_enable(mntdir, true);
263 if (r < 0)
264 log_warning_errno(r, "Failed to enable quota, ignoring: %m");
265
266 if (chmod(mntdir, 0700) < 0) {
267 r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m");
268 goto fail;
269 }
270
271 (void) mkdir_p_label("/var/lib/machines", 0700);
272
273 if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) {
274 r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m");
275 goto fail;
276 }
277
26166c88
LP
278 (void) syncfs(fd);
279
280 log_info("Set up /var/lib/machines as btrfs loopback file system of size %s mounted on /var/lib/machines.raw.", format_bytes(buf, sizeof(buf), size));
281
432cea00
LP
282 (void) umount2(mntdir, MNT_DETACH);
283 (void) rmdir(mntdir);
284 (void) rmdir(tmpdir);
285
286 return 0;
287
288fail:
289 if (mntdir_mounted)
290 (void) umount2(mntdir, MNT_DETACH);
291
292 if (mntdir_made)
293 (void) rmdir(mntdir);
294 if (tmpdir_made)
295 (void) rmdir(tmpdir);
296
297 if (loop >= 0) {
298 (void) ioctl(loop, LOOP_CLR_FD);
299 loop = safe_close(loop);
300 }
301
302 if (control >= 0 && nr >= 0)
303 (void) ioctl(control, LOOP_CTL_REMOVE, nr);
304
305 return r;
306}
26166c88
LP
307
308static int sync_path(const char *p) {
309 _cleanup_close_ int fd = -1;
310
311 fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
312 if (fd < 0)
313 return -errno;
314
315 if (syncfs(fd) < 0)
316 return -errno;
317
318 return 0;
319}
320
321int grow_machine_directory(void) {
322 char buf[FORMAT_BYTES_MAX];
323 struct statvfs a, b;
324 uint64_t old_size, new_size, max_add;
325 int r;
326
327 /* Ensure the disk space data is accurate */
328 sync_path("/var/lib/machines");
329 sync_path("/var/lib/machines.raw");
330
331 if (statvfs("/var/lib/machines.raw", &a) < 0)
332 return -errno;
333
334 if (statvfs("/var/lib/machines", &b) < 0)
335 return -errno;
336
337 /* Don't grow if not enough disk space is available on the host */
338 if (((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) <= VAR_LIB_MACHINES_FREE_MIN)
339 return 0;
340
341 /* Don't grow if at least 1/3th of the fs is still free */
342 if (b.f_bavail > b.f_blocks / 3)
343 return 0;
344
345 /* Calculate how much we are willing to add at maximum */
346 max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN;
347
348 /* Calculate the old size */
349 old_size = (uint64_t) b.f_blocks * (uint64_t) b.f_bsize;
350
351 /* Calculate the new size as three times the size of what is used right now */
352 new_size = ((uint64_t) b.f_blocks - (uint64_t) b.f_bavail) * (uint64_t) b.f_bsize * 3;
353
354 /* Always, grow at least to the start size */
355 if (new_size < VAR_LIB_MACHINES_SIZE_START)
356 new_size = VAR_LIB_MACHINES_SIZE_START;
357
358 /* If the new size is smaller than the old size, don't grow */
359 if (new_size < old_size)
360 return 0;
361
362 /* Ensure we never add more than the maximum */
363 if (new_size > old_size + max_add)
364 new_size = old_size + max_add;
365
366 r = btrfs_resize_loopback("/var/lib/machines", new_size, true);
367 if (r <= 0)
368 return r;
369
370 r = btrfs_quota_limit("/var/lib/machines", new_size);
371 if (r < 0)
372 return r;
373
374 log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf, sizeof(buf), new_size));
375 return 1;
376}