chiark / gitweb /
importd: take a lock while we set up /var/lib/machines/
[elogind.git] / src / shared / machine-pool.c
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
36 static 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
50 static 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
153 fail:
154         if (tmp)
155                 unlink_noerrno(tmp);
156
157         if (pid > 1)
158                 kill_and_sigcont(pid, SIGKILL);
159
160         return r;
161 }
162
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,
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
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
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
277 fail:
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 }