1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2015 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <linux/loop.h>
29 #include <sys/ioctl.h>
30 #include <sys/mount.h>
31 #include <sys/prctl.h>
33 #include <sys/statfs.h>
34 #include <sys/statvfs.h>
37 #include "sd-bus-protocol.h"
40 #include "alloc-util.h"
41 #include "btrfs-util.h"
46 #include "lockfile-util.h"
48 #include "machine-pool.h"
52 #include "mount-util.h"
53 #include "parse-util.h"
54 #include "path-util.h"
55 #include "process-util.h"
56 #include "signal-util.h"
57 #include "stat-util.h"
58 #include "string-util.h"
60 #define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
61 #define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
63 static int check_btrfs(void) {
66 if (statfs("/var/lib/machines", &sfs
) < 0) {
70 if (statfs("/var/lib", &sfs
) < 0)
74 return F_TYPE_EQUAL(sfs
.f_type
, BTRFS_SUPER_MAGIC
);
77 static int setup_machine_raw(uint64_t size
, sd_bus_error
*error
) {
78 _cleanup_free_
char *tmp
= NULL
;
79 _cleanup_close_
int fd
= -1;
85 /* We want to be able to make use of btrfs-specific file
86 * system features, in particular subvolumes, reflinks and
87 * quota. Hence, if we detect that /var/lib/machines.raw is
88 * not located on btrfs, let's create a loopback file, place a
89 * btrfs file system into it, and mount it to
90 * /var/lib/machines. */
92 fd
= open("/var/lib/machines.raw", O_RDWR
|O_CLOEXEC
|O_NONBLOCK
|O_NOCTTY
);
100 return sd_bus_error_set_errnof(error
, errno
, "Failed to open /var/lib/machines.raw: %m");
102 r
= tempfn_xxxxxx("/var/lib/machines.raw", NULL
, &tmp
);
106 (void) mkdir_p_label("/var/lib", 0755);
107 fd
= open(tmp
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0600);
109 return sd_bus_error_set_errnof(error
, errno
, "Failed to create /var/lib/machines.raw: %m");
111 if (fstatvfs(fd
, &ss
) < 0) {
112 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to determine free space on /var/lib/machines.raw: %m");
116 if (ss
.f_bsize
* ss
.f_bavail
< VAR_LIB_MACHINES_FREE_MIN
) {
117 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Not enough free disk space to set up /var/lib/machines.");
121 if (ftruncate(fd
, size
) < 0) {
122 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to enlarge /var/lib/machines.raw: %m");
126 r
= safe_fork("(mkfs)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
, &pid
);
128 sd_bus_error_set_errnof(error
, r
, "Failed to fork mkfs.btrfs: %m");
137 execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp
, NULL
);
144 r
= wait_for_terminate(pid
, &si
);
146 sd_bus_error_set_errnof(error
, r
, "Failed to wait for mkfs.btrfs: %m");
152 if (si
.si_code
!= CLD_EXITED
) {
153 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "mkfs.btrfs died abnormally.");
156 if (si
.si_status
== 99) {
157 r
= sd_bus_error_set_errnof(error
, ENOENT
, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
160 if (si
.si_status
!= 0) {
161 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "mkfs.btrfs failed with error code %i", si
.si_status
);
165 r
= rename_noreplace(AT_FDCWD
, tmp
, AT_FDCWD
, "/var/lib/machines.raw");
167 sd_bus_error_set_errnof(error
, r
, "Failed to move /var/lib/machines.raw into place: %m");
180 kill_and_sigcont(pid
, SIGKILL
);
185 int setup_machine_directory(uint64_t size
, sd_bus_error
*error
) {
186 _cleanup_release_lock_file_ LockFile lock_file
= LOCK_FILE_INIT
;
187 struct loop_info64 info
= {
188 .lo_flags
= LO_FLAGS_AUTOCLEAR
,
190 _cleanup_close_
int fd
= -1, control
= -1, loop
= -1;
191 _cleanup_free_
char* loopdev
= NULL
;
192 char tmpdir
[] = "/tmp/machine-pool.XXXXXX", *mntdir
= NULL
;
193 bool tmpdir_made
= false, mntdir_made
= false, mntdir_mounted
= false;
194 char buf
[FORMAT_BYTES_MAX
];
197 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
198 if (size
== (uint64_t) -1)
199 size
= VAR_LIB_MACHINES_SIZE_START
;
200 else if (size
< 16*1024*1024)
203 /* Make sure we only set the directory up once at a time */
204 r
= make_lock_file("/run/systemd/machines.lock", LOCK_EX
, &lock_file
);
210 return sd_bus_error_set_errnof(error
, r
, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
212 (void) btrfs_subvol_make_label("/var/lib/machines");
214 r
= btrfs_quota_enable("/var/lib/machines", true);
216 log_warning_errno(r
, "Failed to enable quota for /var/lib/machines, ignoring: %m");
218 r
= btrfs_subvol_auto_qgroup("/var/lib/machines", 0, true);
220 log_warning_errno(r
, "Failed to set up default quota hierarchy for /var/lib/machines, ignoring: %m");
225 if (path_is_mount_point("/var/lib/machines", NULL
, AT_SYMLINK_FOLLOW
) > 0) {
226 log_debug("/var/lib/machines is already a mount point, not creating loopback file for it.");
230 r
= dir_is_populated("/var/lib/machines");
231 if (r
< 0 && r
!= -ENOENT
)
234 log_debug("/var/log/machines is already populated, not creating loopback file for it.");
238 r
= mkfs_exists("btrfs");
240 return sd_bus_error_set_errnof(error
, ENOENT
, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
244 fd
= setup_machine_raw(size
, error
);
248 control
= open("/dev/loop-control", O_RDWR
|O_CLOEXEC
|O_NOCTTY
|O_NONBLOCK
);
250 return sd_bus_error_set_errnof(error
, errno
, "Failed to open /dev/loop-control: %m");
252 nr
= ioctl(control
, LOOP_CTL_GET_FREE
);
254 return sd_bus_error_set_errnof(error
, errno
, "Failed to allocate loop device: %m");
256 if (asprintf(&loopdev
, "/dev/loop%i", nr
) < 0) {
261 loop
= open(loopdev
, O_CLOEXEC
|O_RDWR
|O_NOCTTY
|O_NONBLOCK
);
263 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to open loopback device: %m");
267 if (ioctl(loop
, LOOP_SET_FD
, fd
) < 0) {
268 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to bind loopback device: %m");
272 if (ioctl(loop
, LOOP_SET_STATUS64
, &info
) < 0) {
273 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to enable auto-clear for loopback device: %m");
277 /* We need to make sure the new /var/lib/machines directory
278 * has an access mode of 0700 at the time it is first made
279 * available. mkfs will create it with 0755 however. Hence,
280 * let's mount the directory into an inaccessible directory
281 * below /tmp first, fix the access mode, and move it to the
282 * public place then. */
284 if (!mkdtemp(tmpdir
)) {
285 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount parent directory: %m");
290 mntdir
= strjoina(tmpdir
, "/mnt");
291 if (mkdir(mntdir
, 0700) < 0) {
292 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount directory: %m");
297 if (mount(loopdev
, mntdir
, "btrfs", 0, NULL
) < 0) {
298 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to mount loopback device: %m");
301 mntdir_mounted
= true;
303 r
= btrfs_quota_enable(mntdir
, true);
305 log_warning_errno(r
, "Failed to enable quota, ignoring: %m");
307 r
= btrfs_subvol_auto_qgroup(mntdir
, 0, true);
309 log_warning_errno(r
, "Failed to set up default quota hierarchy, ignoring: %m");
311 if (chmod(mntdir
, 0700) < 0) {
312 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to fix owner: %m");
316 (void) mkdir_p_label("/var/lib/machines", 0700);
318 if (mount(mntdir
, "/var/lib/machines", NULL
, MS_BIND
, NULL
) < 0) {
319 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to mount directory into right place: %m");
325 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
));
327 (void) umount2(mntdir
, MNT_DETACH
);
328 (void) rmdir(mntdir
);
329 (void) rmdir(tmpdir
);
335 (void) umount2(mntdir
, MNT_DETACH
);
338 (void) rmdir(mntdir
);
340 (void) rmdir(tmpdir
);
343 (void) ioctl(loop
, LOOP_CLR_FD
);
344 loop
= safe_close(loop
);
347 if (control
>= 0 && nr
>= 0)
348 (void) ioctl(control
, LOOP_CTL_REMOVE
, nr
);
353 static int sync_path(const char *p
) {
354 _cleanup_close_
int fd
= -1;
356 fd
= open(p
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
366 int grow_machine_directory(void) {
367 char buf
[FORMAT_BYTES_MAX
];
369 uint64_t old_size
, new_size
, max_add
;
372 /* Ensure the disk space data is accurate */
373 sync_path("/var/lib/machines");
374 sync_path("/var/lib/machines.raw");
376 if (statvfs("/var/lib/machines.raw", &a
) < 0)
379 if (statvfs("/var/lib/machines", &b
) < 0)
382 /* Don't grow if not enough disk space is available on the host */
383 if (((uint64_t) a
.f_bavail
* (uint64_t) a
.f_bsize
) <= VAR_LIB_MACHINES_FREE_MIN
)
386 /* Don't grow if at least 1/3th of the fs is still free */
387 if (b
.f_bavail
> b
.f_blocks
/ 3)
390 /* Calculate how much we are willing to add at most */
391 max_add
= ((uint64_t) a
.f_bavail
* (uint64_t) a
.f_bsize
) - VAR_LIB_MACHINES_FREE_MIN
;
393 /* Calculate the old size */
394 old_size
= (uint64_t) b
.f_blocks
* (uint64_t) b
.f_bsize
;
396 /* Calculate the new size as three times the size of what is used right now */
397 new_size
= ((uint64_t) b
.f_blocks
- (uint64_t) b
.f_bavail
) * (uint64_t) b
.f_bsize
* 3;
399 /* Always, grow at least to the start size */
400 if (new_size
< VAR_LIB_MACHINES_SIZE_START
)
401 new_size
= VAR_LIB_MACHINES_SIZE_START
;
403 /* If the new size is smaller than the old size, don't grow */
404 if (new_size
< old_size
)
407 /* Ensure we never add more than the maximum */
408 if (new_size
> old_size
+ max_add
)
409 new_size
= old_size
+ max_add
;
411 r
= btrfs_resize_loopback("/var/lib/machines", new_size
, true);
415 /* Also bump the quota, of both the subvolume leaf qgroup, as
416 * well as of any subtree quota group by the same id but a
417 * higher level, if it exists. */
418 (void) btrfs_qgroup_set_limit("/var/lib/machines", 0, new_size
);
419 (void) btrfs_subvol_set_subtree_quota_limit("/var/lib/machines", 0, new_size
);
421 log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf
, sizeof(buf
), new_size
));