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