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