1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
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.
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.
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/>.
22 #include <sys/mount.h>
23 #include <sys/prctl.h>
24 #include <sys/statvfs.h>
27 #include "alloc-util.h"
28 #include "btrfs-util.h"
32 #include "lockfile-util.h"
33 #include "machine-pool.h"
35 #include "mount-util.h"
36 #include "parse-util.h"
37 #include "path-util.h"
38 #include "process-util.h"
39 #include "signal-util.h"
40 #include "stat-util.h"
41 #include "string-util.h"
44 #define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
45 #define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
47 static int check_btrfs(void) {
50 if (statfs("/var/lib/machines", &sfs
) < 0) {
54 if (statfs("/var/lib", &sfs
) < 0)
58 return F_TYPE_EQUAL(sfs
.f_type
, BTRFS_SUPER_MAGIC
);
61 static int setup_machine_raw(uint64_t size
, sd_bus_error
*error
) {
62 _cleanup_free_
char *tmp
= NULL
;
63 _cleanup_close_
int fd
= -1;
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. */
76 fd
= open("/var/lib/machines.raw", O_RDWR
|O_CLOEXEC
|O_NONBLOCK
|O_NOCTTY
);
84 return sd_bus_error_set_errnof(error
, errno
, "Failed to open /var/lib/machines.raw: %m");
86 r
= tempfn_xxxxxx("/var/lib/machines.raw", NULL
, &tmp
);
90 (void) mkdir_p_label("/var/lib", 0755);
91 fd
= open(tmp
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0600);
93 return sd_bus_error_set_errnof(error
, errno
, "Failed to create /var/lib/machines.raw: %m");
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");
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.");
105 if (ftruncate(fd
, size
) < 0) {
106 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to enlarge /var/lib/machines.raw: %m");
112 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to fork mkfs.btrfs: %m");
120 (void) reset_all_signal_handlers();
121 (void) reset_signal_mask();
122 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
126 execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp
, NULL
);
133 r
= wait_for_terminate(pid
, &si
);
135 sd_bus_error_set_errnof(error
, r
, "Failed to wait for mkfs.btrfs: %m");
141 if (si
.si_code
!= CLD_EXITED
) {
142 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "mkfs.btrfs died abnormally.");
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");
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
);
154 r
= rename_noreplace(AT_FDCWD
, tmp
, AT_FDCWD
, "/var/lib/machines.raw");
156 sd_bus_error_set_errnof(error
, r
, "Failed to move /var/lib/machines.raw into place: %m");
169 kill_and_sigcont(pid
, SIGKILL
);
174 int setup_machine_directory(uint64_t size
, sd_bus_error
*error
) {
175 _cleanup_release_lock_file_ LockFile lock_file
= LOCK_FILE_INIT
;
176 struct loop_info64 info
= {
177 .lo_flags
= LO_FLAGS_AUTOCLEAR
,
179 _cleanup_close_
int fd
= -1, control
= -1, loop
= -1;
180 _cleanup_free_
char* loopdev
= NULL
;
181 char tmpdir
[] = "/tmp/machine-pool.XXXXXX", *mntdir
= NULL
;
182 bool tmpdir_made
= false, mntdir_made
= false, mntdir_mounted
= false;
183 char buf
[FORMAT_BYTES_MAX
];
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)
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
);
199 return sd_bus_error_set_errnof(error
, r
, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
201 (void) btrfs_subvol_make_label("/var/lib/machines");
203 r
= btrfs_quota_enable("/var/lib/machines", true);
205 log_warning_errno(r
, "Failed to enable quota for /var/lib/machines, ignoring: %m");
207 r
= btrfs_subvol_auto_qgroup("/var/lib/machines", 0, true);
209 log_warning_errno(r
, "Failed to set up default quota hierarchy for /var/lib/machines, ignoring: %m");
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.");
219 r
= dir_is_populated("/var/lib/machines");
220 if (r
< 0 && r
!= -ENOENT
)
223 log_debug("/var/log/machines is already populated, not creating loopback file for it.");
227 r
= mkfs_exists("btrfs");
229 log_debug("mkfs.btrfs is missing, cannot create loopback file for /var/lib/machines.");
235 fd
= setup_machine_raw(size
, error
);
239 control
= open("/dev/loop-control", O_RDWR
|O_CLOEXEC
|O_NOCTTY
|O_NONBLOCK
);
241 return sd_bus_error_set_errnof(error
, errno
, "Failed to open /dev/loop-control: %m");
243 nr
= ioctl(control
, LOOP_CTL_GET_FREE
);
245 return sd_bus_error_set_errnof(error
, errno
, "Failed to allocate loop device: %m");
247 if (asprintf(&loopdev
, "/dev/loop%i", nr
) < 0) {
252 loop
= open(loopdev
, O_CLOEXEC
|O_RDWR
|O_NOCTTY
|O_NONBLOCK
);
254 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to open loopback device: %m");
258 if (ioctl(loop
, LOOP_SET_FD
, fd
) < 0) {
259 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to bind loopback device: %m");
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");
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. */
275 if (!mkdtemp(tmpdir
)) {
276 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount parent directory: %m");
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");
288 if (mount(loopdev
, mntdir
, "btrfs", 0, NULL
) < 0) {
289 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to mount loopback device: %m");
292 mntdir_mounted
= true;
294 r
= btrfs_quota_enable(mntdir
, true);
296 log_warning_errno(r
, "Failed to enable quota, ignoring: %m");
298 r
= btrfs_subvol_auto_qgroup(mntdir
, 0, true);
300 log_warning_errno(r
, "Failed to set up default quota hierarchy, ignoring: %m");
302 if (chmod(mntdir
, 0700) < 0) {
303 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to fix owner: %m");
307 (void) mkdir_p_label("/var/lib/machines", 0700);
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");
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
));
318 (void) umount2(mntdir
, MNT_DETACH
);
319 (void) rmdir(mntdir
);
320 (void) rmdir(tmpdir
);
326 (void) umount2(mntdir
, MNT_DETACH
);
329 (void) rmdir(mntdir
);
331 (void) rmdir(tmpdir
);
334 (void) ioctl(loop
, LOOP_CLR_FD
);
335 loop
= safe_close(loop
);
338 if (control
>= 0 && nr
>= 0)
339 (void) ioctl(control
, LOOP_CTL_REMOVE
, nr
);
344 static int sync_path(const char *p
) {
345 _cleanup_close_
int fd
= -1;
347 fd
= open(p
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
357 int grow_machine_directory(void) {
358 char buf
[FORMAT_BYTES_MAX
];
360 uint64_t old_size
, new_size
, max_add
;
363 /* Ensure the disk space data is accurate */
364 sync_path("/var/lib/machines");
365 sync_path("/var/lib/machines.raw");
367 if (statvfs("/var/lib/machines.raw", &a
) < 0)
370 if (statvfs("/var/lib/machines", &b
) < 0)
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
)
377 /* Don't grow if at least 1/3th of the fs is still free */
378 if (b
.f_bavail
> b
.f_blocks
/ 3)
381 /* Calculate how much we are willing to add at most */
382 max_add
= ((uint64_t) a
.f_bavail
* (uint64_t) a
.f_bsize
) - VAR_LIB_MACHINES_FREE_MIN
;
384 /* Calculate the old size */
385 old_size
= (uint64_t) b
.f_blocks
* (uint64_t) b
.f_bsize
;
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;
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
;
394 /* If the new size is smaller than the old size, don't grow */
395 if (new_size
< old_size
)
398 /* Ensure we never add more than the maximum */
399 if (new_size
> old_size
+ max_add
)
400 new_size
= old_size
+ max_add
;
402 r
= btrfs_resize_loopback("/var/lib/machines", new_size
, true);
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
);
412 log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf
, sizeof(buf
), new_size
));