]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/machine-pool.c
util-lib: split out file attribute calls to chattr-util.[ch]
[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
07630cea 22#include <sys/mount.h>
432cea00 23#include <sys/prctl.h>
432cea00 24#include <sys/statvfs.h>
07630cea 25#include <sys/vfs.h>
432cea00 26
07630cea 27#include "btrfs-util.h"
3ffd4af2 28#include "fd-util.h"
0d39fa9c 29#include "fileio.h"
cd2eb9e9 30#include "lockfile-util.h"
3ffd4af2 31#include "machine-pool.h"
432cea00 32#include "mkdir.h"
6bedfcbb 33#include "parse-util.h"
432cea00 34#include "path-util.h"
07630cea 35#include "process-util.h"
24882e06 36#include "signal-util.h"
07630cea
LP
37#include "string-util.h"
38#include "util.h"
4349cd7c 39#include "mount-util.h"
432cea00
LP
40
41#define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
42#define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
43
44static int check_btrfs(void) {
45 struct statfs sfs;
46
47 if (statfs("/var/lib/machines", &sfs) < 0) {
48 if (errno != ENOENT)
49 return -errno;
50
51 if (statfs("/var/lib", &sfs) < 0)
52 return -errno;
53 }
54
55 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
56}
57
4cee5eed 58static int setup_machine_raw(uint64_t size, sd_bus_error *error) {
432cea00
LP
59 _cleanup_free_ char *tmp = NULL;
60 _cleanup_close_ int fd = -1;
61 struct statvfs ss;
62 pid_t pid = 0;
63 siginfo_t si;
64 int r;
65
66 /* We want to be able to make use of btrfs-specific file
67 * system features, in particular subvolumes, reflinks and
68 * quota. Hence, if we detect that /var/lib/machines.raw is
69 * not located on btrfs, let's create a loopback file, place a
70 * btrfs file system into it, and mount it to
71 * /var/lib/machines. */
72
73 fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
74 if (fd >= 0) {
75 r = fd;
76 fd = -1;
77 return r;
78 }
79
80 if (errno != ENOENT)
81 return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m");
82
14bcf25c 83 r = tempfn_xxxxxx("/var/lib/machines.raw", NULL, &tmp);
432cea00
LP
84 if (r < 0)
85 return r;
86
87 (void) mkdir_p_label("/var/lib", 0755);
88 fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600);
89 if (fd < 0)
90 return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m");
91
92 if (fstatvfs(fd, &ss) < 0) {
93 r = sd_bus_error_set_errnof(error, errno, "Failed to determine free space on /var/lib/machines.raw: %m");
94 goto fail;
95 }
96
97 if (ss.f_bsize * ss.f_bavail < VAR_LIB_MACHINES_FREE_MIN) {
98 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Not enough free disk space to set up /var/lib/machines.");
99 goto fail;
100 }
101
4cee5eed 102 if (ftruncate(fd, size) < 0) {
432cea00
LP
103 r = sd_bus_error_set_errnof(error, errno, "Failed to enlarge /var/lib/machines.raw: %m");
104 goto fail;
105 }
106
107 pid = fork();
108 if (pid < 0) {
109 r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m");
110 goto fail;
111 }
112
113 if (pid == 0) {
114
115 /* Child */
116
ce30c8dc
LP
117 (void) reset_all_signal_handlers();
118 (void) reset_signal_mask();
432cea00
LP
119 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
120
121 fd = safe_close(fd);
122
123 execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL);
124 if (errno == ENOENT)
125 return 99;
126
127 _exit(EXIT_FAILURE);
128 }
129
130 r = wait_for_terminate(pid, &si);
131 if (r < 0) {
132 sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m");
133 goto fail;
134 }
135
136 pid = 0;
137
138 if (si.si_code != CLD_EXITED) {
139 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally.");
140 goto fail;
141 }
142 if (si.si_status == 99) {
143 r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
144 goto fail;
145 }
146 if (si.si_status != 0) {
147 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status);
148 goto fail;
149 }
150
f85ef957
AC
151 r = rename_noreplace(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw");
152 if (r < 0) {
153 sd_bus_error_set_errnof(error, r, "Failed to move /var/lib/machines.raw into place: %m");
432cea00
LP
154 goto fail;
155 }
156
157 r = fd;
158 fd = -1;
159
160 return r;
161
162fail:
132764a2 163 unlink_noerrno(tmp);
432cea00
LP
164
165 if (pid > 1)
166 kill_and_sigcont(pid, SIGKILL);
167
168 return r;
169}
170
4cee5eed 171int setup_machine_directory(uint64_t size, sd_bus_error *error) {
403e5b32 172 _cleanup_release_lock_file_ LockFile lock_file = LOCK_FILE_INIT;
432cea00
LP
173 struct loop_info64 info = {
174 .lo_flags = LO_FLAGS_AUTOCLEAR,
175 };
176 _cleanup_close_ int fd = -1, control = -1, loop = -1;
177 _cleanup_free_ char* loopdev = NULL;
5bcd08db 178 char tmpdir[] = "/tmp/machine-pool.XXXXXX", *mntdir = NULL;
432cea00 179 bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false;
26166c88 180 char buf[FORMAT_BYTES_MAX];
432cea00
LP
181 int r, nr = -1;
182
4cee5eed
LP
183 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
184 if (size == (uint64_t) -1)
185 size = VAR_LIB_MACHINES_SIZE_START;
186 else if (size < 16*1024*1024)
187 size = 16*1024*1024;
188
403e5b32
LP
189 /* Make sure we only set the directory up once at a time */
190 r = make_lock_file("/run/systemd/machines.lock", LOCK_EX, &lock_file);
191 if (r < 0)
192 return r;
193
432cea00
LP
194 r = check_btrfs();
195 if (r < 0)
196 return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
197 if (r > 0) {
198 (void) btrfs_subvol_make_label("/var/lib/machines");
199
200 r = btrfs_quota_enable("/var/lib/machines", true);
201 if (r < 0)
5bcd08db 202 log_warning_errno(r, "Failed to enable quota for /var/lib/machines, ignoring: %m");
432cea00 203
5bcd08db
LP
204 r = btrfs_subvol_auto_qgroup("/var/lib/machines", 0, true);
205 if (r < 0)
206 log_warning_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines, ignoring: %m");
207
208 return 1;
209 }
210
211 if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0) {
212 log_debug("/var/lib/machines is already a mount point, not creating loopback file for it.");
432cea00
LP
213 return 0;
214 }
215
5bcd08db
LP
216 r = dir_is_populated("/var/lib/machines");
217 if (r < 0 && r != -ENOENT)
218 return r;
219 if (r > 0) {
220 log_debug("/var/log/machines is already populated, not creating loopback file for it.");
221 return 0;
222 }
223
224 r = mkfs_exists("btrfs");
225 if (r == -ENOENT) {
226 log_debug("mkfs.btrfs is missing, cannot create loopback file for /var/lib/machines.");
227 return 0;
228 }
229 if (r < 0)
230 return r;
432cea00 231
4cee5eed 232 fd = setup_machine_raw(size, error);
432cea00
LP
233 if (fd < 0)
234 return fd;
235
236 control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
237 if (control < 0)
238 return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m");
239
240 nr = ioctl(control, LOOP_CTL_GET_FREE);
241 if (nr < 0)
242 return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m");
243
244 if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) {
245 r = -ENOMEM;
246 goto fail;
247 }
248
249 loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK);
250 if (loop < 0) {
251 r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m");
252 goto fail;
253 }
254
255 if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
256 r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m");
257 goto fail;
258 }
259
260 if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
261 r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m");
262 goto fail;
263 }
264
265 /* We need to make sure the new /var/lib/machines directory
266 * has an access mode of 0700 at the time it is first made
267 * available. mkfs will create it with 0755 however. Hence,
268 * let's mount the directory into an inaccessible directory
269 * below /tmp first, fix the access mode, and move it to the
270 * public place then. */
271
272 if (!mkdtemp(tmpdir)) {
273 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m");
274 goto fail;
275 }
276 tmpdir_made = true;
277
278 mntdir = strjoina(tmpdir, "/mnt");
279 if (mkdir(mntdir, 0700) < 0) {
280 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m");
281 goto fail;
282 }
283 mntdir_made = true;
284
285 if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) {
286 r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m");
287 goto fail;
288 }
289 mntdir_mounted = true;
290
291 r = btrfs_quota_enable(mntdir, true);
292 if (r < 0)
293 log_warning_errno(r, "Failed to enable quota, ignoring: %m");
294
5bcd08db
LP
295 r = btrfs_subvol_auto_qgroup(mntdir, 0, true);
296 if (r < 0)
297 log_warning_errno(r, "Failed to set up default quota hierarchy, ignoring: %m");
298
432cea00
LP
299 if (chmod(mntdir, 0700) < 0) {
300 r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m");
301 goto fail;
302 }
303
304 (void) mkdir_p_label("/var/lib/machines", 0700);
305
306 if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) {
307 r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m");
308 goto fail;
309 }
310
26166c88
LP
311 (void) syncfs(fd);
312
313 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));
314
432cea00
LP
315 (void) umount2(mntdir, MNT_DETACH);
316 (void) rmdir(mntdir);
317 (void) rmdir(tmpdir);
318
5bcd08db 319 return 1;
432cea00
LP
320
321fail:
322 if (mntdir_mounted)
323 (void) umount2(mntdir, MNT_DETACH);
324
325 if (mntdir_made)
326 (void) rmdir(mntdir);
327 if (tmpdir_made)
328 (void) rmdir(tmpdir);
329
330 if (loop >= 0) {
331 (void) ioctl(loop, LOOP_CLR_FD);
332 loop = safe_close(loop);
333 }
334
335 if (control >= 0 && nr >= 0)
336 (void) ioctl(control, LOOP_CTL_REMOVE, nr);
337
338 return r;
339}
26166c88
LP
340
341static int sync_path(const char *p) {
342 _cleanup_close_ int fd = -1;
343
344 fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
345 if (fd < 0)
346 return -errno;
347
348 if (syncfs(fd) < 0)
349 return -errno;
350
351 return 0;
352}
353
354int grow_machine_directory(void) {
355 char buf[FORMAT_BYTES_MAX];
356 struct statvfs a, b;
357 uint64_t old_size, new_size, max_add;
358 int r;
359
360 /* Ensure the disk space data is accurate */
361 sync_path("/var/lib/machines");
362 sync_path("/var/lib/machines.raw");
363
364 if (statvfs("/var/lib/machines.raw", &a) < 0)
365 return -errno;
366
367 if (statvfs("/var/lib/machines", &b) < 0)
368 return -errno;
369
370 /* Don't grow if not enough disk space is available on the host */
371 if (((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) <= VAR_LIB_MACHINES_FREE_MIN)
372 return 0;
373
374 /* Don't grow if at least 1/3th of the fs is still free */
375 if (b.f_bavail > b.f_blocks / 3)
376 return 0;
377
378 /* Calculate how much we are willing to add at maximum */
379 max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN;
380
381 /* Calculate the old size */
382 old_size = (uint64_t) b.f_blocks * (uint64_t) b.f_bsize;
383
384 /* Calculate the new size as three times the size of what is used right now */
385 new_size = ((uint64_t) b.f_blocks - (uint64_t) b.f_bavail) * (uint64_t) b.f_bsize * 3;
386
387 /* Always, grow at least to the start size */
388 if (new_size < VAR_LIB_MACHINES_SIZE_START)
389 new_size = VAR_LIB_MACHINES_SIZE_START;
390
391 /* If the new size is smaller than the old size, don't grow */
392 if (new_size < old_size)
393 return 0;
394
395 /* Ensure we never add more than the maximum */
396 if (new_size > old_size + max_add)
397 new_size = old_size + max_add;
398
399 r = btrfs_resize_loopback("/var/lib/machines", new_size, true);
400 if (r <= 0)
401 return r;
402
5bcd08db
LP
403 /* Also bump the quota, of both the subvolume leaf qgroup, as
404 * well as of any subtree quota group by the same id but a
405 * higher level, if it exists. */
406 (void) btrfs_qgroup_set_limit("/var/lib/machines", 0, new_size);
407 (void) btrfs_subvol_set_subtree_quota_limit("/var/lib/machines", 0, new_size);
26166c88
LP
408
409 log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf, sizeof(buf), new_size));
410 return 1;
411}