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/prctl.h>
24 #include <sys/statvfs.h>
25 #include <sys/mount.h>
29 #include "btrfs-util.h"
30 #include "path-util.h"
31 #include "machine-pool.h"
33 #define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
34 #define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
36 static int check_btrfs(void) {
39 if (statfs("/var/lib/machines", &sfs
) < 0) {
43 if (statfs("/var/lib", &sfs
) < 0)
47 return F_TYPE_EQUAL(sfs
.f_type
, BTRFS_SUPER_MAGIC
);
50 static int setup_machine_raw(sd_bus_error
*error
) {
51 _cleanup_free_
char *tmp
= NULL
;
52 _cleanup_close_
int fd
= -1;
58 /* We want to be able to make use of btrfs-specific file
59 * system features, in particular subvolumes, reflinks and
60 * quota. Hence, if we detect that /var/lib/machines.raw is
61 * not located on btrfs, let's create a loopback file, place a
62 * btrfs file system into it, and mount it to
63 * /var/lib/machines. */
65 fd
= open("/var/lib/machines.raw", O_RDWR
|O_CLOEXEC
|O_NONBLOCK
|O_NOCTTY
);
73 return sd_bus_error_set_errnof(error
, errno
, "Failed to open /var/lib/machines.raw: %m");
75 r
= tempfn_xxxxxx("/var/lib/machines.raw", &tmp
);
79 (void) mkdir_p_label("/var/lib", 0755);
80 fd
= open(tmp
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0600);
82 return sd_bus_error_set_errnof(error
, errno
, "Failed to create /var/lib/machines.raw: %m");
84 if (fstatvfs(fd
, &ss
) < 0) {
85 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to determine free space on /var/lib/machines.raw: %m");
89 if (ss
.f_bsize
* ss
.f_bavail
< VAR_LIB_MACHINES_FREE_MIN
) {
90 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Not enough free disk space to set up /var/lib/machines.");
94 if (ftruncate(fd
, VAR_LIB_MACHINES_SIZE_START
) < 0) {
95 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to enlarge /var/lib/machines.raw: %m");
101 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to fork mkfs.btrfs: %m");
109 reset_all_signal_handlers();
111 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
115 execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp
, NULL
);
122 r
= wait_for_terminate(pid
, &si
);
124 sd_bus_error_set_errnof(error
, r
, "Failed to wait for mkfs.btrfs: %m");
130 if (si
.si_code
!= CLD_EXITED
) {
131 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "mkfs.btrfs died abnormally.");
134 if (si
.si_status
== 99) {
135 r
= sd_bus_error_set_errnof(error
, ENOENT
, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
138 if (si
.si_status
!= 0) {
139 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "mkfs.btrfs failed with error code %i", si
.si_status
);
143 if (renameat2(AT_FDCWD
, tmp
, AT_FDCWD
, "/var/lib/machines.raw", RENAME_NOREPLACE
) < 0) {
144 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to move /var/lib/machines.raw into place: %m");
158 kill_and_sigcont(pid
, SIGKILL
);
163 int setup_machine_directory(sd_bus_error
*error
) {
164 _cleanup_release_lock_file_ LockFile lock_file
= LOCK_FILE_INIT
;
165 struct loop_info64 info
= {
166 .lo_flags
= LO_FLAGS_AUTOCLEAR
,
168 _cleanup_close_
int fd
= -1, control
= -1, loop
= -1;
169 _cleanup_free_
char* loopdev
= NULL
;
170 char tmpdir
[] = "/tmp/import-mount.XXXXXX", *mntdir
= NULL
;
171 bool tmpdir_made
= false, mntdir_made
= false, mntdir_mounted
= false;
174 /* Make sure we only set the directory up once at a time */
175 r
= make_lock_file("/run/systemd/machines.lock", LOCK_EX
, &lock_file
);
181 return sd_bus_error_set_errnof(error
, r
, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
183 (void) btrfs_subvol_make_label("/var/lib/machines");
185 r
= btrfs_quota_enable("/var/lib/machines", true);
187 log_warning_errno(r
, "Failed to enable quota, ignoring: %m");
192 if (path_is_mount_point("/var/lib/machines", true) > 0 ||
193 dir_is_empty("/var/lib/machines") == 0)
194 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "/var/lib/machines is not a btrfs file system. Operation is not supported on legacy file systems.");
196 fd
= setup_machine_raw(error
);
200 control
= open("/dev/loop-control", O_RDWR
|O_CLOEXEC
|O_NOCTTY
|O_NONBLOCK
);
202 return sd_bus_error_set_errnof(error
, errno
, "Failed to open /dev/loop-control: %m");
204 nr
= ioctl(control
, LOOP_CTL_GET_FREE
);
206 return sd_bus_error_set_errnof(error
, errno
, "Failed to allocate loop device: %m");
208 if (asprintf(&loopdev
, "/dev/loop%i", nr
) < 0) {
213 loop
= open(loopdev
, O_CLOEXEC
|O_RDWR
|O_NOCTTY
|O_NONBLOCK
);
215 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to open loopback device: %m");
219 if (ioctl(loop
, LOOP_SET_FD
, fd
) < 0) {
220 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to bind loopback device: %m");
224 if (ioctl(loop
, LOOP_SET_STATUS64
, &info
) < 0) {
225 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to enable auto-clear for loopback device: %m");
229 /* We need to make sure the new /var/lib/machines directory
230 * has an access mode of 0700 at the time it is first made
231 * available. mkfs will create it with 0755 however. Hence,
232 * let's mount the directory into an inaccessible directory
233 * below /tmp first, fix the access mode, and move it to the
234 * public place then. */
236 if (!mkdtemp(tmpdir
)) {
237 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount parent directory: %m");
242 mntdir
= strjoina(tmpdir
, "/mnt");
243 if (mkdir(mntdir
, 0700) < 0) {
244 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount directory: %m");
249 if (mount(loopdev
, mntdir
, "btrfs", 0, NULL
) < 0) {
250 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to mount loopback device: %m");
253 mntdir_mounted
= true;
255 r
= btrfs_quota_enable(mntdir
, true);
257 log_warning_errno(r
, "Failed to enable quota, ignoring: %m");
259 if (chmod(mntdir
, 0700) < 0) {
260 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to fix owner: %m");
264 (void) mkdir_p_label("/var/lib/machines", 0700);
266 if (mount(mntdir
, "/var/lib/machines", NULL
, MS_BIND
, NULL
) < 0) {
267 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to mount directory into right place: %m");
271 (void) umount2(mntdir
, MNT_DETACH
);
272 (void) rmdir(mntdir
);
273 (void) rmdir(tmpdir
);
279 (void) umount2(mntdir
, MNT_DETACH
);
282 (void) rmdir(mntdir
);
284 (void) rmdir(tmpdir
);
287 (void) ioctl(loop
, LOOP_CLR_FD
);
288 loop
= safe_close(loop
);
291 if (control
>= 0 && nr
>= 0)
292 (void) ioctl(control
, LOOP_CTL_REMOVE
, nr
);