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>
28 #include "process-util.h"
29 #include "lockfile-util.h"
31 #include "btrfs-util.h"
32 #include "path-util.h"
33 #include "machine-pool.h"
35 #define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
36 #define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
38 static int check_btrfs(void) {
41 if (statfs("/var/lib/machines", &sfs
) < 0) {
45 if (statfs("/var/lib", &sfs
) < 0)
49 return F_TYPE_EQUAL(sfs
.f_type
, BTRFS_SUPER_MAGIC
);
52 static int setup_machine_raw(uint64_t size
, sd_bus_error
*error
) {
53 _cleanup_free_
char *tmp
= NULL
;
54 _cleanup_close_
int fd
= -1;
60 /* We want to be able to make use of btrfs-specific file
61 * system features, in particular subvolumes, reflinks and
62 * quota. Hence, if we detect that /var/lib/machines.raw is
63 * not located on btrfs, let's create a loopback file, place a
64 * btrfs file system into it, and mount it to
65 * /var/lib/machines. */
67 fd
= open("/var/lib/machines.raw", O_RDWR
|O_CLOEXEC
|O_NONBLOCK
|O_NOCTTY
);
75 return sd_bus_error_set_errnof(error
, errno
, "Failed to open /var/lib/machines.raw: %m");
77 r
= tempfn_xxxxxx("/var/lib/machines.raw", &tmp
);
81 (void) mkdir_p_label("/var/lib", 0755);
82 fd
= open(tmp
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0600);
84 return sd_bus_error_set_errnof(error
, errno
, "Failed to create /var/lib/machines.raw: %m");
86 if (fstatvfs(fd
, &ss
) < 0) {
87 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to determine free space on /var/lib/machines.raw: %m");
91 if (ss
.f_bsize
* ss
.f_bavail
< VAR_LIB_MACHINES_FREE_MIN
) {
92 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Not enough free disk space to set up /var/lib/machines.");
96 if (ftruncate(fd
, size
) < 0) {
97 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to enlarge /var/lib/machines.raw: %m");
103 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to fork mkfs.btrfs: %m");
111 reset_all_signal_handlers();
113 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
117 execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp
, NULL
);
124 r
= wait_for_terminate(pid
, &si
);
126 sd_bus_error_set_errnof(error
, r
, "Failed to wait for mkfs.btrfs: %m");
132 if (si
.si_code
!= CLD_EXITED
) {
133 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "mkfs.btrfs died abnormally.");
136 if (si
.si_status
== 99) {
137 r
= sd_bus_error_set_errnof(error
, ENOENT
, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
140 if (si
.si_status
!= 0) {
141 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "mkfs.btrfs failed with error code %i", si
.si_status
);
145 r
= rename_noreplace(AT_FDCWD
, tmp
, AT_FDCWD
, "/var/lib/machines.raw");
147 sd_bus_error_set_errnof(error
, r
, "Failed to move /var/lib/machines.raw into place: %m");
160 kill_and_sigcont(pid
, SIGKILL
);
165 int setup_machine_directory(uint64_t size
, sd_bus_error
*error
) {
166 _cleanup_release_lock_file_ LockFile lock_file
= LOCK_FILE_INIT
;
167 struct loop_info64 info
= {
168 .lo_flags
= LO_FLAGS_AUTOCLEAR
,
170 _cleanup_close_
int fd
= -1, control
= -1, loop
= -1;
171 _cleanup_free_
char* loopdev
= NULL
;
172 char tmpdir
[] = "/tmp/import-mount.XXXXXX", *mntdir
= NULL
;
173 bool tmpdir_made
= false, mntdir_made
= false, mntdir_mounted
= false;
174 char buf
[FORMAT_BYTES_MAX
];
177 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
178 if (size
== (uint64_t) -1)
179 size
= VAR_LIB_MACHINES_SIZE_START
;
180 else if (size
< 16*1024*1024)
183 /* Make sure we only set the directory up once at a time */
184 r
= make_lock_file("/run/systemd/machines.lock", LOCK_EX
, &lock_file
);
190 return sd_bus_error_set_errnof(error
, r
, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
192 (void) btrfs_subvol_make_label("/var/lib/machines");
194 r
= btrfs_quota_enable("/var/lib/machines", true);
196 log_warning_errno(r
, "Failed to enable quota, ignoring: %m");
201 if (path_is_mount_point("/var/lib/machines", true) > 0 ||
202 dir_is_empty("/var/lib/machines") == 0)
203 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.");
205 fd
= setup_machine_raw(size
, error
);
209 control
= open("/dev/loop-control", O_RDWR
|O_CLOEXEC
|O_NOCTTY
|O_NONBLOCK
);
211 return sd_bus_error_set_errnof(error
, errno
, "Failed to open /dev/loop-control: %m");
213 nr
= ioctl(control
, LOOP_CTL_GET_FREE
);
215 return sd_bus_error_set_errnof(error
, errno
, "Failed to allocate loop device: %m");
217 if (asprintf(&loopdev
, "/dev/loop%i", nr
) < 0) {
222 loop
= open(loopdev
, O_CLOEXEC
|O_RDWR
|O_NOCTTY
|O_NONBLOCK
);
224 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to open loopback device: %m");
228 if (ioctl(loop
, LOOP_SET_FD
, fd
) < 0) {
229 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to bind loopback device: %m");
233 if (ioctl(loop
, LOOP_SET_STATUS64
, &info
) < 0) {
234 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to enable auto-clear for loopback device: %m");
238 /* We need to make sure the new /var/lib/machines directory
239 * has an access mode of 0700 at the time it is first made
240 * available. mkfs will create it with 0755 however. Hence,
241 * let's mount the directory into an inaccessible directory
242 * below /tmp first, fix the access mode, and move it to the
243 * public place then. */
245 if (!mkdtemp(tmpdir
)) {
246 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount parent directory: %m");
251 mntdir
= strjoina(tmpdir
, "/mnt");
252 if (mkdir(mntdir
, 0700) < 0) {
253 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount directory: %m");
258 if (mount(loopdev
, mntdir
, "btrfs", 0, NULL
) < 0) {
259 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to mount loopback device: %m");
262 mntdir_mounted
= true;
264 r
= btrfs_quota_enable(mntdir
, true);
266 log_warning_errno(r
, "Failed to enable quota, ignoring: %m");
268 if (chmod(mntdir
, 0700) < 0) {
269 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to fix owner: %m");
273 (void) mkdir_p_label("/var/lib/machines", 0700);
275 if (mount(mntdir
, "/var/lib/machines", NULL
, MS_BIND
, NULL
) < 0) {
276 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to mount directory into right place: %m");
282 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
));
284 (void) umount2(mntdir
, MNT_DETACH
);
285 (void) rmdir(mntdir
);
286 (void) rmdir(tmpdir
);
292 (void) umount2(mntdir
, MNT_DETACH
);
295 (void) rmdir(mntdir
);
297 (void) rmdir(tmpdir
);
300 (void) ioctl(loop
, LOOP_CLR_FD
);
301 loop
= safe_close(loop
);
304 if (control
>= 0 && nr
>= 0)
305 (void) ioctl(control
, LOOP_CTL_REMOVE
, nr
);
310 static int sync_path(const char *p
) {
311 _cleanup_close_
int fd
= -1;
313 fd
= open(p
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
323 int grow_machine_directory(void) {
324 char buf
[FORMAT_BYTES_MAX
];
326 uint64_t old_size
, new_size
, max_add
;
329 /* Ensure the disk space data is accurate */
330 sync_path("/var/lib/machines");
331 sync_path("/var/lib/machines.raw");
333 if (statvfs("/var/lib/machines.raw", &a
) < 0)
336 if (statvfs("/var/lib/machines", &b
) < 0)
339 /* Don't grow if not enough disk space is available on the host */
340 if (((uint64_t) a
.f_bavail
* (uint64_t) a
.f_bsize
) <= VAR_LIB_MACHINES_FREE_MIN
)
343 /* Don't grow if at least 1/3th of the fs is still free */
344 if (b
.f_bavail
> b
.f_blocks
/ 3)
347 /* Calculate how much we are willing to add at maximum */
348 max_add
= ((uint64_t) a
.f_bavail
* (uint64_t) a
.f_bsize
) - VAR_LIB_MACHINES_FREE_MIN
;
350 /* Calculate the old size */
351 old_size
= (uint64_t) b
.f_blocks
* (uint64_t) b
.f_bsize
;
353 /* Calculate the new size as three times the size of what is used right now */
354 new_size
= ((uint64_t) b
.f_blocks
- (uint64_t) b
.f_bavail
) * (uint64_t) b
.f_bsize
* 3;
356 /* Always, grow at least to the start size */
357 if (new_size
< VAR_LIB_MACHINES_SIZE_START
)
358 new_size
= VAR_LIB_MACHINES_SIZE_START
;
360 /* If the new size is smaller than the old size, don't grow */
361 if (new_size
< old_size
)
364 /* Ensure we never add more than the maximum */
365 if (new_size
> old_size
+ max_add
)
366 new_size
= old_size
+ max_add
;
368 r
= btrfs_resize_loopback("/var/lib/machines", new_size
, true);
372 r
= btrfs_quota_limit("/var/lib/machines", new_size
);
376 log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf
, sizeof(buf
), new_size
));