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