]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/machine-pool.c
journal: fix Inappropriate ioctl for device on ext4
[thirdparty/systemd.git] / src / shared / machine-pool.c
CommitLineData
432cea00
LP
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 "mkdir.h"
29#include "btrfs-util.h"
30#include "path-util.h"
31#include "machine-pool.h"
32
33#define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
34#define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
35
36static int check_btrfs(void) {
37 struct statfs sfs;
38
39 if (statfs("/var/lib/machines", &sfs) < 0) {
40 if (errno != ENOENT)
41 return -errno;
42
43 if (statfs("/var/lib", &sfs) < 0)
44 return -errno;
45 }
46
47 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
48}
49
50static int setup_machine_raw(sd_bus_error *error) {
51 _cleanup_free_ char *tmp = NULL;
52 _cleanup_close_ int fd = -1;
53 struct statvfs ss;
54 pid_t pid = 0;
55 siginfo_t si;
56 int r;
57
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. */
64
65 fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
66 if (fd >= 0) {
67 r = fd;
68 fd = -1;
69 return r;
70 }
71
72 if (errno != ENOENT)
73 return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m");
74
75 r = tempfn_xxxxxx("/var/lib/machines.raw", &tmp);
76 if (r < 0)
77 return r;
78
79 (void) mkdir_p_label("/var/lib", 0755);
80 fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600);
81 if (fd < 0)
82 return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m");
83
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");
86 goto fail;
87 }
88
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.");
91 goto fail;
92 }
93
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");
96 goto fail;
97 }
98
99 pid = fork();
100 if (pid < 0) {
101 r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m");
102 goto fail;
103 }
104
105 if (pid == 0) {
106
107 /* Child */
108
109 reset_all_signal_handlers();
110 reset_signal_mask();
111 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
112
113 fd = safe_close(fd);
114
115 execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL);
116 if (errno == ENOENT)
117 return 99;
118
119 _exit(EXIT_FAILURE);
120 }
121
122 r = wait_for_terminate(pid, &si);
123 if (r < 0) {
124 sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m");
125 goto fail;
126 }
127
128 pid = 0;
129
130 if (si.si_code != CLD_EXITED) {
131 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally.");
132 goto fail;
133 }
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");
136 goto fail;
137 }
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);
140 goto fail;
141 }
142
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");
145 goto fail;
146 }
147
148 r = fd;
149 fd = -1;
150
151 return r;
152
153fail:
154 if (tmp)
155 unlink_noerrno(tmp);
156
157 if (pid > 1)
158 kill_and_sigcont(pid, SIGKILL);
159
160 return r;
161}
162
163int setup_machine_directory(sd_bus_error *error) {
403e5b32 164 _cleanup_release_lock_file_ LockFile lock_file = LOCK_FILE_INIT;
432cea00
LP
165 struct loop_info64 info = {
166 .lo_flags = LO_FLAGS_AUTOCLEAR,
167 };
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;
172 int r, nr = -1;
173
403e5b32
LP
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);
176 if (r < 0)
177 return r;
178
432cea00
LP
179 r = check_btrfs();
180 if (r < 0)
181 return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
182 if (r > 0) {
183 (void) btrfs_subvol_make_label("/var/lib/machines");
184
185 r = btrfs_quota_enable("/var/lib/machines", true);
186 if (r < 0)
187 log_warning_errno(r, "Failed to enable quota, ignoring: %m");
188
189 return 0;
190 }
191
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.");
195
196 fd = setup_machine_raw(error);
197 if (fd < 0)
198 return fd;
199
200 control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
201 if (control < 0)
202 return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m");
203
204 nr = ioctl(control, LOOP_CTL_GET_FREE);
205 if (nr < 0)
206 return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m");
207
208 if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) {
209 r = -ENOMEM;
210 goto fail;
211 }
212
213 loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK);
214 if (loop < 0) {
215 r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m");
216 goto fail;
217 }
218
219 if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
220 r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m");
221 goto fail;
222 }
223
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");
226 goto fail;
227 }
228
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. */
235
236 if (!mkdtemp(tmpdir)) {
237 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m");
238 goto fail;
239 }
240 tmpdir_made = true;
241
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");
245 goto fail;
246 }
247 mntdir_made = true;
248
249 if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) {
250 r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m");
251 goto fail;
252 }
253 mntdir_mounted = true;
254
255 r = btrfs_quota_enable(mntdir, true);
256 if (r < 0)
257 log_warning_errno(r, "Failed to enable quota, ignoring: %m");
258
259 if (chmod(mntdir, 0700) < 0) {
260 r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m");
261 goto fail;
262 }
263
264 (void) mkdir_p_label("/var/lib/machines", 0700);
265
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");
268 goto fail;
269 }
270
271 (void) umount2(mntdir, MNT_DETACH);
272 (void) rmdir(mntdir);
273 (void) rmdir(tmpdir);
274
275 return 0;
276
277fail:
278 if (mntdir_mounted)
279 (void) umount2(mntdir, MNT_DETACH);
280
281 if (mntdir_made)
282 (void) rmdir(mntdir);
283 if (tmpdir_made)
284 (void) rmdir(tmpdir);
285
286 if (loop >= 0) {
287 (void) ioctl(loop, LOOP_CLR_FD);
288 loop = safe_close(loop);
289 }
290
291 if (control >= 0 && nr >= 0)
292 (void) ioctl(control, LOOP_CTL_REMOVE, nr);
293
294 return r;
295}