]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
e3478379 | 2 | /*** |
96b2fb93 | 3 | Copyright © 2010 ProFUSION embedded systems |
e3478379 FF |
4 | ***/ |
5 | ||
6 | #include <errno.h> | |
7 | #include <fcntl.h> | |
01234e1f | 8 | #include <linux/dm-ioctl.h> |
0b220a5f HK |
9 | #include <linux/major.h> |
10 | #include <linux/raid/md_u.h> | |
4f5dd394 | 11 | #include <linux/loop.h> |
e3478379 FF |
12 | #include <sys/mount.h> |
13 | #include <sys/swap.h> | |
ca78ad1d ZJS |
14 | #include <sys/stat.h> |
15 | #include <sys/types.h> | |
16 | #include <unistd.h> | |
e3478379 | 17 | |
48f46254 LP |
18 | #if HAVE_VALGRIND_MEMCHECK_H |
19 | #include <valgrind/memcheck.h> | |
20 | #endif | |
21 | ||
6bcf00ed | 22 | #include "sd-device.h" |
b4bbcaa9 | 23 | |
b5efdb8a | 24 | #include "alloc-util.h" |
18c528e9 | 25 | #include "blockdev-util.h" |
6dc68a00 | 26 | #include "chase-symlinks.h" |
28db6fbf | 27 | #include "constants.h" |
8437c059 | 28 | #include "device-util.h" |
20596876 | 29 | #include "dirent-util.h" |
07630cea | 30 | #include "escape.h" |
3ffd4af2 | 31 | #include "fd-util.h" |
20596876 JJ |
32 | #include "fileio.h" |
33 | #include "fs-util.h" | |
471b48ed | 34 | #include "fstab-util.h" |
fb36b133 | 35 | #include "libmount-util.h" |
6dc68a00 | 36 | #include "mkdir.h" |
e3478379 | 37 | #include "mount-setup.h" |
18c528e9 | 38 | #include "mount-util.h" |
049af8ad | 39 | #include "mountpoint-util.h" |
20596876 | 40 | #include "parse-util.h" |
9eb977db | 41 | #include "path-util.h" |
dccca82b | 42 | #include "process-util.h" |
6dc68a00 | 43 | #include "random-util.h" |
d5641e0d | 44 | #include "signal-util.h" |
6dc68a00 | 45 | #include "stat-util.h" |
07630cea | 46 | #include "string-util.h" |
da9fc98d | 47 | #include "strv.h" |
bf819d3a | 48 | #include "sync-util.h" |
3ffd4af2 | 49 | #include "umount.h" |
024f268d | 50 | #include "virt.h" |
e3478379 | 51 | |
12aad1d0 LP |
52 | static void mount_point_free(MountPoint **head, MountPoint *m) { |
53 | assert(head); | |
54 | assert(m); | |
e3478379 | 55 | |
71fda00f | 56 | LIST_REMOVE(mount_point, *head, m); |
12aad1d0 LP |
57 | |
58 | free(m->path); | |
3bc341be | 59 | free(m->remount_options); |
12aad1d0 | 60 | free(m); |
e3478379 FF |
61 | } |
62 | ||
6fa392bf | 63 | void mount_points_list_free(MountPoint **head) { |
12aad1d0 LP |
64 | assert(head); |
65 | ||
66 | while (*head) | |
67 | mount_point_free(head, *head); | |
e3478379 FF |
68 | } |
69 | ||
6fa392bf | 70 | int mount_points_list_get(const char *mountinfo, MountPoint **head) { |
13dcfe46 ZJS |
71 | _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL; |
72 | _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL; | |
527b7a42 | 73 | int r; |
e3478379 | 74 | |
12aad1d0 LP |
75 | assert(head); |
76 | ||
e2857b3d | 77 | r = libmount_parse(mountinfo, NULL, &table, &iter); |
95b862b0 | 78 | if (r < 0) |
f795267e | 79 | return log_error_errno(r, "Failed to parse %s: %m", mountinfo ?: "/proc/self/mountinfo"); |
95b862b0 ZJS |
80 | |
81 | for (;;) { | |
c905aaf6 | 82 | _cleanup_free_ char *options = NULL, *remount_options = NULL; |
95b862b0 | 83 | struct libmnt_fs *fs; |
66c91c3a | 84 | const char *path, *fstype; |
95b862b0 | 85 | unsigned long remount_flags = 0u; |
f11efcbe | 86 | bool try_remount_ro, is_api_vfs; |
9d1b2b22 | 87 | _cleanup_free_ MountPoint *m = NULL; |
e3478379 | 88 | |
13dcfe46 | 89 | r = mnt_table_next_fs(table, iter, &fs); |
c905aaf6 | 90 | if (r == 1) /* EOF */ |
95b862b0 | 91 | break; |
527b7a42 | 92 | if (r < 0) |
f795267e | 93 | return log_error_errno(r, "Failed to get next entry from %s: %m", mountinfo ?: "/proc/self/mountinfo"); |
95b862b0 ZJS |
94 | |
95 | path = mnt_fs_get_target(fs); | |
96 | if (!path) | |
97 | continue; | |
98 | ||
95b862b0 | 99 | fstype = mnt_fs_get_fstype(fs); |
e3478379 | 100 | |
83f3bf4b LP |
101 | /* Combine the generic VFS options with the FS-specific options. Duplicates are not a problem |
102 | * here, because the only options that should come up twice are typically ro/rw, which are | |
103 | * turned into MS_RDONLY or the inversion of it. | |
66c91c3a | 104 | * |
83f3bf4b LP |
105 | * Even if there are duplicates later in mount_option_mangle() they shouldn't hurt anyways as |
106 | * they override each other. */ | |
c2bc710b | 107 | if (!strextend_with_separator(&options, ",", mnt_fs_get_vfs_options(fs))) |
66c91c3a | 108 | return log_oom(); |
c2bc710b | 109 | if (!strextend_with_separator(&options, ",", mnt_fs_get_fs_options(fs))) |
66c91c3a | 110 | return log_oom(); |
111 | ||
83f3bf4b LP |
112 | /* Ignore mount points we can't unmount because they are API or because we are keeping them |
113 | * open (like /dev/console). Also, ignore all mounts below API file systems, since they are | |
114 | * likely virtual too, and hence not worth spending time on. Also, in unprivileged containers | |
115 | * we might lack the rights to unmount these things, hence don't bother. */ | |
9d1b2b22 ZJS |
116 | if (mount_point_is_api(path) || |
117 | mount_point_ignore(path) || | |
118 | PATH_STARTSWITH_SET(path, "/dev", "/sys", "/proc")) | |
2054a5b8 | 119 | continue; |
2054a5b8 | 120 | |
f11efcbe LP |
121 | is_api_vfs = fstype_is_api_vfs(fstype); |
122 | ||
83f3bf4b LP |
123 | /* If we are in a container, don't attempt to read-only mount anything as that brings no real |
124 | * benefits, but might confuse the host, as we remount the superblock here, not the bind | |
125 | * mount. | |
1d62d22d | 126 | * |
83f3bf4b LP |
127 | * If the filesystem is a network fs, also skip the remount. It brings no value (we cannot |
128 | * leave a "dirty fs") and could hang if the network is down. Note that umount2() is more | |
129 | * careful and will not hang because of the network being down. */ | |
95b862b0 ZJS |
130 | try_remount_ro = detect_container() <= 0 && |
131 | !fstype_is_network(fstype) && | |
f11efcbe | 132 | !is_api_vfs && |
95b862b0 ZJS |
133 | !fstype_is_ro(fstype) && |
134 | !fstab_test_yes_no_option(options, "ro\0rw\0"); | |
3bc341be | 135 | |
95b862b0 | 136 | if (try_remount_ro) { |
83f3bf4b LP |
137 | /* mount(2) states that mount flags and options need to be exactly the same as they |
138 | * were when the filesystem was mounted, except for the desired changes. So we | |
139 | * reconstruct both here and adjust them for the later remount call too. */ | |
3bc341be | 140 | |
95b862b0 ZJS |
141 | r = mnt_fs_get_propagation(fs, &remount_flags); |
142 | if (r < 0) { | |
143 | log_warning_errno(r, "mnt_fs_get_propagation() failed for %s, ignoring: %m", path); | |
144 | continue; | |
145 | } | |
3bc341be | 146 | |
95b862b0 ZJS |
147 | r = mount_option_mangle(options, remount_flags, &remount_flags, &remount_options); |
148 | if (r < 0) { | |
149 | log_warning_errno(r, "mount_option_mangle failed for %s, ignoring: %m", path); | |
150 | continue; | |
151 | } | |
3bc341be JJ |
152 | |
153 | /* MS_BIND is special. If it is provided it will only make the mount-point | |
154 | * read-only. If left out, the super block itself is remounted, which we want. */ | |
95b862b0 | 155 | remount_flags = (remount_flags|MS_REMOUNT|MS_RDONLY) & ~MS_BIND; |
3bc341be | 156 | } |
471b48ed | 157 | |
efc90b98 | 158 | m = new(MountPoint, 1); |
95b862b0 ZJS |
159 | if (!m) |
160 | return log_oom(); | |
161 | ||
6dc68a00 VD |
162 | r = libmount_is_leaf(table, fs); |
163 | if (r < 0) | |
164 | return log_error_errno(r, "Failed to get children mounts for %s from %s: %m", path, mountinfo ?: "/proc/self/mountinfo"); | |
165 | bool leaf = r; | |
166 | ||
efc90b98 LP |
167 | *m = (MountPoint) { |
168 | .remount_options = remount_options, | |
169 | .remount_flags = remount_flags, | |
170 | .try_remount_ro = try_remount_ro, | |
f11efcbe LP |
171 | |
172 | /* Unmount sysfs/procfs/… lazily, since syncing doesn't matter there, and it's OK if | |
173 | * something keeps an fd open to it. */ | |
174 | .umount_lazily = is_api_vfs, | |
6dc68a00 | 175 | .leaf = leaf, |
efc90b98 LP |
176 | }; |
177 | ||
9d1b2b22 ZJS |
178 | m->path = strdup(path); |
179 | if (!m->path) | |
180 | return log_oom(); | |
181 | ||
efc90b98 | 182 | TAKE_PTR(remount_options); |
95b862b0 | 183 | |
9d1b2b22 | 184 | LIST_PREPEND(mount_point, *head, TAKE_PTR(m)); |
e3478379 FF |
185 | } |
186 | ||
c3544e8d | 187 | return 0; |
e3478379 FF |
188 | } |
189 | ||
1fd8edb5 | 190 | int swap_list_get(const char *swaps, MountPoint **head) { |
71ae04c4 ZJS |
191 | _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL; |
192 | _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL; | |
527b7a42 | 193 | int r; |
e3478379 | 194 | |
12aad1d0 LP |
195 | assert(head); |
196 | ||
71ae04c4 ZJS |
197 | t = mnt_new_table(); |
198 | i = mnt_new_iter(MNT_ITER_FORWARD); | |
199 | if (!t || !i) | |
200 | return log_oom(); | |
e3478379 | 201 | |
71ae04c4 | 202 | r = mnt_table_parse_swaps(t, swaps); |
2cdd0d61 LP |
203 | if (r == -ENOENT) /* no /proc/swaps is fine */ |
204 | return 0; | |
71ae04c4 | 205 | if (r < 0) |
f795267e | 206 | return log_error_errno(r, "Failed to parse %s: %m", swaps ?: "/proc/swaps"); |
71ae04c4 ZJS |
207 | |
208 | for (;;) { | |
209 | struct libmnt_fs *fs; | |
9d1b2b22 | 210 | _cleanup_free_ MountPoint *swap = NULL; |
71ae04c4 | 211 | const char *source; |
71ae04c4 ZJS |
212 | |
213 | r = mnt_table_next_fs(t, i, &fs); | |
c905aaf6 | 214 | if (r == 1) /* EOF */ |
71ae04c4 ZJS |
215 | break; |
216 | if (r < 0) | |
f795267e | 217 | return log_error_errno(r, "Failed to get next entry from %s: %m", swaps ?: "/proc/swaps"); |
e3478379 | 218 | |
71ae04c4 ZJS |
219 | source = mnt_fs_get_source(fs); |
220 | if (!source) | |
e3478379 | 221 | continue; |
e3478379 | 222 | |
527b7a42 | 223 | swap = new0(MountPoint, 1); |
595c66a3 | 224 | if (!swap) |
4e201419 | 225 | return log_oom(); |
e3478379 | 226 | |
9d1b2b22 ZJS |
227 | swap->path = strdup(source); |
228 | if (!swap->path) | |
4e201419 | 229 | return log_oom(); |
9d1b2b22 ZJS |
230 | |
231 | LIST_PREPEND(mount_point, *head, TAKE_PTR(swap)); | |
e3478379 FF |
232 | } |
233 | ||
e1d75803 | 234 | return 0; |
e3478379 FF |
235 | } |
236 | ||
12aad1d0 | 237 | static int loopback_list_get(MountPoint **head) { |
6bcf00ed YW |
238 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; |
239 | sd_device *d; | |
06acf2d4 | 240 | int r; |
e3478379 | 241 | |
12aad1d0 LP |
242 | assert(head); |
243 | ||
6bcf00ed YW |
244 | r = sd_device_enumerator_new(&e); |
245 | if (r < 0) | |
246 | return r; | |
e3478379 | 247 | |
6bcf00ed YW |
248 | r = sd_device_enumerator_allow_uninitialized(e); |
249 | if (r < 0) | |
250 | return r; | |
e3478379 | 251 | |
6bcf00ed | 252 | r = sd_device_enumerator_add_match_subsystem(e, "block", true); |
06acf2d4 LP |
253 | if (r < 0) |
254 | return r; | |
255 | ||
6bcf00ed | 256 | r = sd_device_enumerator_add_match_sysname(e, "loop*"); |
06acf2d4 LP |
257 | if (r < 0) |
258 | return r; | |
259 | ||
6bcf00ed | 260 | r = sd_device_enumerator_add_match_sysattr(e, "loop/backing_file", NULL, true); |
06acf2d4 LP |
261 | if (r < 0) |
262 | return r; | |
e3478379 | 263 | |
8437c059 | 264 | FOREACH_DEVICE(e, d) { |
209c6f03 | 265 | _cleanup_free_ char *p = NULL; |
6bcf00ed | 266 | const char *dn; |
209c6f03 | 267 | MountPoint *lb; |
63135a2d | 268 | dev_t devnum; |
e3478379 | 269 | |
63135a2d LP |
270 | if (sd_device_get_devnum(d, &devnum) < 0 || |
271 | sd_device_get_devname(d, &dn) < 0) | |
2d9a3397 | 272 | continue; |
b854a7e7 | 273 | |
209c6f03 YW |
274 | p = strdup(dn); |
275 | if (!p) | |
276 | return -ENOMEM; | |
277 | ||
278 | lb = new(MountPoint, 1); | |
a6dcd229 | 279 | if (!lb) |
1ca208fb | 280 | return -ENOMEM; |
e3478379 | 281 | |
209c6f03 YW |
282 | *lb = (MountPoint) { |
283 | .path = TAKE_PTR(p), | |
63135a2d | 284 | .devnum = devnum, |
209c6f03 | 285 | }; |
a6dcd229 | 286 | |
71fda00f | 287 | LIST_PREPEND(mount_point, *head, lb); |
e3478379 FF |
288 | } |
289 | ||
1ca208fb | 290 | return 0; |
e3478379 FF |
291 | } |
292 | ||
12aad1d0 | 293 | static int dm_list_get(MountPoint **head) { |
6bcf00ed YW |
294 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; |
295 | sd_device *d; | |
06acf2d4 | 296 | int r; |
d48141ba | 297 | |
12aad1d0 LP |
298 | assert(head); |
299 | ||
6bcf00ed YW |
300 | r = sd_device_enumerator_new(&e); |
301 | if (r < 0) | |
302 | return r; | |
d48141ba | 303 | |
6bcf00ed YW |
304 | r = sd_device_enumerator_allow_uninitialized(e); |
305 | if (r < 0) | |
306 | return r; | |
d48141ba | 307 | |
6bcf00ed | 308 | r = sd_device_enumerator_add_match_subsystem(e, "block", true); |
06acf2d4 LP |
309 | if (r < 0) |
310 | return r; | |
d48141ba | 311 | |
6bcf00ed | 312 | r = sd_device_enumerator_add_match_sysname(e, "dm-*"); |
06acf2d4 LP |
313 | if (r < 0) |
314 | return r; | |
d48141ba | 315 | |
8437c059 | 316 | FOREACH_DEVICE(e, d) { |
209c6f03 | 317 | _cleanup_free_ char *p = NULL; |
6bcf00ed | 318 | const char *dn; |
209c6f03 | 319 | MountPoint *m; |
6bcf00ed | 320 | dev_t devnum; |
d48141ba | 321 | |
209c6f03 YW |
322 | if (sd_device_get_devnum(d, &devnum) < 0 || |
323 | sd_device_get_devname(d, &dn) < 0) | |
6bcf00ed | 324 | continue; |
d48141ba | 325 | |
209c6f03 YW |
326 | p = strdup(dn); |
327 | if (!p) | |
328 | return -ENOMEM; | |
d48141ba | 329 | |
209c6f03 | 330 | m = new(MountPoint, 1); |
a6dcd229 | 331 | if (!m) |
1ca208fb | 332 | return -ENOMEM; |
d48141ba | 333 | |
209c6f03 YW |
334 | *m = (MountPoint) { |
335 | .path = TAKE_PTR(p), | |
336 | .devnum = devnum, | |
337 | }; | |
a6dcd229 | 338 | |
71fda00f | 339 | LIST_PREPEND(mount_point, *head, m); |
d48141ba LP |
340 | } |
341 | ||
1ca208fb | 342 | return 0; |
d48141ba LP |
343 | } |
344 | ||
0b220a5f HK |
345 | static int md_list_get(MountPoint **head) { |
346 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
347 | sd_device *d; | |
348 | int r; | |
349 | ||
350 | assert(head); | |
351 | ||
352 | r = sd_device_enumerator_new(&e); | |
353 | if (r < 0) | |
354 | return r; | |
355 | ||
356 | r = sd_device_enumerator_allow_uninitialized(e); | |
357 | if (r < 0) | |
358 | return r; | |
359 | ||
360 | r = sd_device_enumerator_add_match_subsystem(e, "block", true); | |
361 | if (r < 0) | |
362 | return r; | |
363 | ||
364 | r = sd_device_enumerator_add_match_sysname(e, "md*"); | |
365 | if (r < 0) | |
366 | return r; | |
367 | ||
3a3b022d MT |
368 | /* Filter out partitions. */ |
369 | r = sd_device_enumerator_add_match_property(e, "DEVTYPE", "disk"); | |
370 | if (r < 0) | |
371 | return r; | |
372 | ||
0b220a5f HK |
373 | FOREACH_DEVICE(e, d) { |
374 | _cleanup_free_ char *p = NULL; | |
3a3b022d | 375 | const char *dn, *md_level; |
0b220a5f HK |
376 | MountPoint *m; |
377 | dev_t devnum; | |
378 | ||
379 | if (sd_device_get_devnum(d, &devnum) < 0 || | |
380 | sd_device_get_devname(d, &dn) < 0) | |
381 | continue; | |
382 | ||
3a3b022d MT |
383 | r = sd_device_get_property_value(d, "MD_LEVEL", &md_level); |
384 | if (r < 0) { | |
385 | log_warning_errno(r, "Failed to get MD_LEVEL property for %s, ignoring: %m", dn); | |
386 | continue; | |
387 | } | |
388 | ||
83f3bf4b LP |
389 | /* MD "containers" are a special type of MD devices, used for external metadata. Since it |
390 | * doesn't provide RAID functionality in itself we don't need to stop it. */ | |
3a3b022d MT |
391 | if (streq(md_level, "container")) |
392 | continue; | |
393 | ||
0b220a5f HK |
394 | p = strdup(dn); |
395 | if (!p) | |
396 | return -ENOMEM; | |
397 | ||
398 | m = new(MountPoint, 1); | |
399 | if (!m) | |
400 | return -ENOMEM; | |
401 | ||
402 | *m = (MountPoint) { | |
403 | .path = TAKE_PTR(p), | |
404 | .devnum = devnum, | |
405 | }; | |
406 | ||
407 | LIST_PREPEND(mount_point, *head, m); | |
408 | } | |
409 | ||
410 | return 0; | |
411 | } | |
412 | ||
e3478379 | 413 | static int delete_loopback(const char *device) { |
254d1313 | 414 | _cleanup_close_ int fd = -EBADF; |
b877c3b0 | 415 | struct loop_info64 info; |
e3478379 | 416 | |
0494cae0 JJ |
417 | assert(device); |
418 | ||
03e334a1 | 419 | fd = open(device, O_RDONLY|O_CLOEXEC); |
4534b32c LP |
420 | if (fd < 0) { |
421 | log_debug_errno(errno, "Failed to open loopback device %s: %m", device); | |
c4f8bd1a | 422 | return errno == ENOENT ? 0 : -errno; |
4534b32c LP |
423 | } |
424 | ||
425 | /* Loopback block devices don't sync in-flight blocks when we clear the fd, hence sync explicitly | |
426 | * first */ | |
427 | if (fsync(fd) < 0) | |
428 | log_debug_errno(errno, "Failed to sync loop block device %s, ignoring: %m", device); | |
e3478379 | 429 | |
b877c3b0 LP |
430 | if (ioctl(fd, LOOP_CLR_FD, 0) < 0) { |
431 | if (errno == ENXIO) /* Nothing bound, didn't do anything */ | |
432 | return 0; | |
433 | ||
4ca8072f LP |
434 | if (errno != EBUSY) |
435 | return log_debug_errno(errno, "Failed to clear loopback device %s: %m", device); | |
436 | ||
437 | if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) { | |
438 | if (errno == ENXIO) /* What? Suddenly detached after all? That's fine by us then. */ | |
439 | return 1; | |
440 | ||
441 | log_debug_errno(errno, "Failed to invoke LOOP_GET_STATUS64 on loopback device %s, ignoring: %m", device); | |
442 | return -EBUSY; /* propagate original error */ | |
443 | } | |
444 | ||
48f46254 LP |
445 | #if HAVE_VALGRIND_MEMCHECK_H |
446 | VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); | |
447 | #endif | |
448 | ||
4ca8072f LP |
449 | if (FLAGS_SET(info.lo_flags, LO_FLAGS_AUTOCLEAR)) /* someone else already set LO_FLAGS_AUTOCLEAR for us? fine by us */ |
450 | return -EBUSY; /* propagate original error */ | |
451 | ||
452 | info.lo_flags |= LO_FLAGS_AUTOCLEAR; | |
453 | if (ioctl(fd, LOOP_SET_STATUS64, &info) < 0) { | |
454 | if (errno == ENXIO) /* Suddenly detached after all? Fine by us */ | |
455 | return 1; | |
456 | ||
457 | log_debug_errno(errno, "Failed to set LO_FLAGS_AUTOCLEAR flag for loop device %s, ignoring: %m", device); | |
458 | } else | |
459 | log_debug("Successfully set LO_FLAGS_AUTOCLEAR flag for loop device %s.", device); | |
460 | ||
461 | return -EBUSY; | |
b877c3b0 LP |
462 | } |
463 | ||
464 | if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) { | |
465 | /* If the LOOP_CLR_FD above succeeded we'll see ENXIO here. */ | |
466 | if (errno == ENXIO) | |
467 | log_debug("Successfully detached loopback device %s.", device); | |
468 | else | |
469 | log_debug_errno(errno, "Failed to invoke LOOP_GET_STATUS64 on loopback device %s, ignoring: %m", device); /* the LOOP_CLR_FD at least worked, let's hope for the best */ | |
470 | ||
12aad1d0 | 471 | return 1; |
b877c3b0 | 472 | } |
12aad1d0 | 473 | |
48f46254 LP |
474 | #if HAVE_VALGRIND_MEMCHECK_H |
475 | VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); | |
476 | #endif | |
477 | ||
b877c3b0 LP |
478 | /* Linux makes LOOP_CLR_FD succeed whenever LO_FLAGS_AUTOCLEAR is set without actually doing |
479 | * anything. Very confusing. Let's hence not claim we did anything in this case. */ | |
480 | if (FLAGS_SET(info.lo_flags, LO_FLAGS_AUTOCLEAR)) | |
481 | log_debug("Successfully called LOOP_CLR_FD on a loopback device %s with autoclear set, which is a NOP.", device); | |
482 | else | |
483 | log_debug("Weird, LOOP_CLR_FD succeeded but the device is still attached on %s.", device); | |
12aad1d0 | 484 | |
b877c3b0 | 485 | return -EBUSY; /* Nothing changed, the device is still attached, hence it apparently is still busy */ |
e3478379 FF |
486 | } |
487 | ||
e55299da | 488 | static int delete_dm(MountPoint *m) { |
254d1313 | 489 | _cleanup_close_ int fd = -EBADF; |
e55299da | 490 | int r; |
cf139e60 | 491 | |
e55299da LP |
492 | assert(m); |
493 | assert(major(m->devnum) != 0); | |
494 | assert(m->path); | |
d48141ba | 495 | |
e62d8c39 ZJS |
496 | fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC); |
497 | if (fd < 0) | |
d48141ba LP |
498 | return -errno; |
499 | ||
e55299da LP |
500 | r = fsync_path_at(AT_FDCWD, m->path); |
501 | if (r < 0) | |
502 | log_debug_errno(r, "Failed to sync DM block device %s, ignoring: %m", m->path); | |
503 | ||
7c248223 | 504 | return RET_NERRNO(ioctl(fd, DM_DEV_REMOVE, &(struct dm_ioctl) { |
e55299da LP |
505 | .version = { |
506 | DM_VERSION_MAJOR, | |
507 | DM_VERSION_MINOR, | |
508 | DM_VERSION_PATCHLEVEL | |
509 | }, | |
510 | .data_size = sizeof(struct dm_ioctl), | |
511 | .dev = m->devnum, | |
7c248223 | 512 | })); |
d48141ba LP |
513 | } |
514 | ||
0b220a5f | 515 | static int delete_md(MountPoint *m) { |
254d1313 | 516 | _cleanup_close_ int fd = -EBADF; |
0b220a5f | 517 | |
1a269c4e | 518 | assert(m); |
0b220a5f | 519 | assert(major(m->devnum) != 0); |
1a269c4e | 520 | assert(m->path); |
0b220a5f HK |
521 | |
522 | fd = open(m->path, O_RDONLY|O_CLOEXEC|O_EXCL); | |
523 | if (fd < 0) | |
524 | return -errno; | |
525 | ||
32c4626c LP |
526 | if (fsync(fd) < 0) |
527 | log_debug_errno(errno, "Failed to sync MD block device %s, ignoring: %m", m->path); | |
528 | ||
7c248223 | 529 | return RET_NERRNO(ioctl(fd, STOP_ARRAY, NULL)); |
0b220a5f HK |
530 | } |
531 | ||
c826cd3f ZJS |
532 | static bool nonunmountable_path(const char *path) { |
533 | return path_equal(path, "/") | |
349cc4a5 | 534 | #if ! HAVE_SPLIT_USR |
c826cd3f ZJS |
535 | || path_equal(path, "/usr") |
536 | #endif | |
537 | || path_startswith(path, "/run/initramfs"); | |
538 | } | |
539 | ||
20596876 | 540 | static void log_umount_blockers(const char *mnt) { |
05768ae3 LP |
541 | _cleanup_free_ char *blockers = NULL; |
542 | int r; | |
543 | ||
20596876 JJ |
544 | _cleanup_closedir_ DIR *dir = opendir("/proc"); |
545 | if (!dir) | |
05768ae3 | 546 | return (void) log_warning_errno(errno, "Failed to open /proc/: %m"); |
20596876 JJ |
547 | |
548 | FOREACH_DIRENT_ALL(de, dir, break) { | |
549 | if (!IN_SET(de->d_type, DT_DIR, DT_UNKNOWN)) | |
550 | continue; | |
551 | ||
552 | pid_t pid; | |
553 | if (parse_pid(de->d_name, &pid) < 0) | |
554 | continue; | |
555 | ||
05768ae3 LP |
556 | _cleanup_free_ char *fdp = path_join(de->d_name, "fd"); |
557 | if (!fdp) | |
558 | return (void) log_oom(); | |
20596876 | 559 | |
05768ae3 LP |
560 | _cleanup_closedir_ DIR *fd_dir = xopendirat(dirfd(dir), fdp, 0); |
561 | if (!fd_dir) { | |
562 | if (errno != ENOENT) /* process gone by now? */ | |
563 | log_debug_errno(errno, "Failed to open /proc/%s/, ignoring: %m",fdp); | |
20596876 | 564 | continue; |
05768ae3 | 565 | } |
20596876 | 566 | |
05768ae3 | 567 | bool culprit = false; |
20596876 | 568 | FOREACH_DIRENT(fd_de, fd_dir, break) { |
05768ae3 | 569 | _cleanup_free_ char *open_file = NULL; |
20596876 | 570 | |
05768ae3 LP |
571 | r = readlinkat_malloc(dirfd(fd_dir), fd_de->d_name, &open_file); |
572 | if (r < 0) { | |
573 | if (r != -ENOENT) /* fd closed by now */ | |
574 | log_debug_errno(r, "Failed to read link /proc/%s/%s, ignoring: %m", fdp, fd_de->d_name); | |
20596876 | 575 | continue; |
05768ae3 | 576 | } |
20596876 | 577 | |
05768ae3 LP |
578 | if (path_startswith(open_file, mnt)) { |
579 | culprit = true; | |
580 | break; | |
581 | } | |
582 | } | |
20596876 | 583 | |
05768ae3 LP |
584 | if (!culprit) |
585 | continue; | |
20596876 | 586 | |
05768ae3 LP |
587 | _cleanup_free_ char *comm = NULL; |
588 | r = get_process_comm(pid, &comm); | |
589 | if (r < 0) { | |
590 | if (r != -ESRCH) /* process gone by now */ | |
591 | log_debug_errno(r, "Failed to read process name of PID " PID_FMT ": %m", pid); | |
592 | continue; | |
593 | } | |
20596876 | 594 | |
05768ae3 LP |
595 | if (!strextend_with_separator(&blockers, ", ", comm)) |
596 | return (void) log_oom(); | |
20596876 | 597 | |
05768ae3 LP |
598 | if (!strextend(&blockers, "(", de->d_name, ")")) |
599 | return (void) log_oom(); | |
20596876 JJ |
600 | } |
601 | ||
602 | if (blockers) | |
603 | log_warning("Unmounting '%s' blocked by: %s", mnt, blockers); | |
604 | } | |
605 | ||
5125b677 | 606 | static int remount_with_timeout(MountPoint *m, bool last_try) { |
b293bb23 LP |
607 | _cleanup_(close_pairp) int pfd[2] = PIPE_EBADF; |
608 | _cleanup_(sigkill_nowaitp) pid_t pid = 0; | |
d5641e0d KW |
609 | int r; |
610 | ||
611 | BLOCK_SIGNALS(SIGCHLD); | |
612 | ||
0494cae0 | 613 | assert(m); |
0494cae0 | 614 | |
b293bb23 LP |
615 | r = pipe2(pfd, O_CLOEXEC|O_NONBLOCK); |
616 | if (r < 0) | |
617 | return r; | |
618 | ||
273d76f4 YW |
619 | /* Due to the possibility of a remount operation hanging, we fork a child process and set a |
620 | * timeout. If the timeout lapses, the assumption is that the particular remount failed. */ | |
911f8f01 YW |
621 | r = safe_fork_full("(sd-remount)", |
622 | NULL, | |
623 | pfd, ELEMENTSOF(pfd), | |
624 | FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &pid); | |
4c253ed1 | 625 | if (r < 0) |
b6e1fff1 | 626 | return r; |
4c253ed1 | 627 | if (r == 0) { |
b293bb23 LP |
628 | pfd[0] = safe_close(pfd[0]); |
629 | ||
b85c1905 | 630 | log_info("Remounting '%s' read-only with options '%s'.", m->path, strempty(m->remount_options)); |
d5641e0d KW |
631 | |
632 | /* Start the mount operation here in the child */ | |
3bc341be | 633 | r = mount(NULL, m->path, NULL, m->remount_flags, m->remount_options); |
d5641e0d | 634 | if (r < 0) |
5125b677 JJ |
635 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, |
636 | errno, | |
637 | "Failed to remount '%s' read-only: %m", | |
638 | m->path); | |
d5641e0d | 639 | |
b293bb23 | 640 | (void) write(pfd[1], &r, sizeof(r)); /* try to send errno up */ |
d5641e0d KW |
641 | _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); |
642 | } | |
643 | ||
b293bb23 LP |
644 | pfd[1] = safe_close(pfd[1]); |
645 | ||
d5641e0d | 646 | r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC); |
b293bb23 | 647 | if (r == -ETIMEDOUT) |
00adeed9 | 648 | log_error_errno(r, "Remounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid); |
b293bb23 LP |
649 | else if (r == -EPROTO) { |
650 | /* Try to read error code from child */ | |
651 | if (read(pfd[0], &r, sizeof(r)) == sizeof(r)) | |
652 | log_debug_errno(r, "Remounting '%s' failed abnormally, child process " PID_FMT " failed: %m", m->path, pid); | |
653 | else | |
5afaf407 | 654 | r = log_debug_errno(EPROTO, "Remounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid); |
b293bb23 LP |
655 | TAKE_PID(pid); /* child exited (just not as we expected) hence don't kill anymore */ |
656 | } else if (r < 0) | |
00adeed9 | 657 | log_error_errno(r, "Remounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid); |
d5641e0d KW |
658 | |
659 | return r; | |
660 | } | |
661 | ||
5125b677 | 662 | static int umount_with_timeout(MountPoint *m, bool last_try) { |
b293bb23 LP |
663 | _cleanup_(close_pairp) int pfd[2] = PIPE_EBADF; |
664 | _cleanup_(sigkill_nowaitp) pid_t pid = 0; | |
d5641e0d KW |
665 | int r; |
666 | ||
667 | BLOCK_SIGNALS(SIGCHLD); | |
668 | ||
0494cae0 JJ |
669 | assert(m); |
670 | ||
b293bb23 LP |
671 | r = pipe2(pfd, O_CLOEXEC|O_NONBLOCK); |
672 | if (r < 0) | |
673 | return r; | |
674 | ||
273d76f4 YW |
675 | /* Due to the possibility of a umount operation hanging, we fork a child process and set a |
676 | * timeout. If the timeout lapses, the assumption is that the particular umount failed. */ | |
911f8f01 YW |
677 | r = safe_fork_full("(sd-umount)", |
678 | NULL, | |
679 | pfd, ELEMENTSOF(pfd), | |
680 | FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &pid); | |
4c253ed1 | 681 | if (r < 0) |
b6e1fff1 | 682 | return r; |
4c253ed1 | 683 | if (r == 0) { |
b293bb23 LP |
684 | pfd[0] = safe_close(pfd[0]); |
685 | ||
d5641e0d KW |
686 | log_info("Unmounting '%s'.", m->path); |
687 | ||
83f3bf4b LP |
688 | /* Start the mount operation here in the child Using MNT_FORCE causes some filesystems |
689 | * (e.g. FUSE and NFS and other network filesystems) to abort any pending requests and return | |
690 | * -EIO rather than blocking indefinitely. If the filesysten is "busy", this may allow | |
691 | * processes to die, thus making the filesystem less busy so the unmount might succeed | |
692 | * (rather than return EBUSY). */ | |
f11efcbe LP |
693 | r = RET_NERRNO(umount2(m->path, |
694 | UMOUNT_NOFOLLOW | /* Don't follow symlinks: this should never happen unless our mount list was wrong */ | |
695 | (m->umount_lazily ? MNT_DETACH : MNT_FORCE))); | |
20596876 JJ |
696 | if (r < 0) { |
697 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Failed to unmount %s: %m", m->path); | |
698 | ||
699 | if (r == -EBUSY && last_try) | |
700 | log_umount_blockers(m->path); | |
701 | } | |
d5641e0d | 702 | |
b293bb23 | 703 | (void) write(pfd[1], &r, sizeof(r)); /* try to send errno up */ |
d5641e0d KW |
704 | _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); |
705 | } | |
706 | ||
b293bb23 LP |
707 | pfd[1] = safe_close(pfd[1]); |
708 | ||
d5641e0d | 709 | r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC); |
b293bb23 | 710 | if (r == -ETIMEDOUT) |
00adeed9 | 711 | log_error_errno(r, "Unmounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid); |
b293bb23 LP |
712 | else if (r == -EPROTO) { |
713 | /* Try to read error code from child */ | |
714 | if (read(pfd[0], &r, sizeof(r)) == sizeof(r)) | |
715 | log_debug_errno(r, "Unmounting '%s' failed abnormally, child process " PID_FMT " failed: %m", m->path, pid); | |
716 | else | |
717 | r = log_debug_errno(EPROTO, "Unmounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid); | |
718 | TAKE_PID(pid); /* It died, but abnormally, no purpose in killing */ | |
719 | } else if (r < 0) | |
00adeed9 | 720 | log_error_errno(r, "Unmounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid); |
d5641e0d KW |
721 | |
722 | return r; | |
723 | } | |
724 | ||
49f80dce LP |
725 | /* This includes remounting readonly, which changes the kernel mount options. Therefore the list passed to |
726 | * this function is invalidated, and should not be reused. */ | |
5125b677 | 727 | static int mount_points_list_umount(MountPoint **head, bool *changed, bool last_try) { |
6dc68a00 VD |
728 | int n_failed = 0, r; |
729 | _cleanup_free_ char *resolved_mounts_path = NULL; | |
e3478379 | 730 | |
12aad1d0 | 731 | assert(head); |
0494cae0 | 732 | assert(changed); |
12aad1d0 | 733 | |
116e6d96 | 734 | LIST_FOREACH(mount_point, m, *head) { |
1d62d22d | 735 | if (m->try_remount_ro) { |
49f80dce | 736 | /* We always try to remount directories read-only first, before we go on and umount |
93bd1577 LP |
737 | * them. |
738 | * | |
49f80dce LP |
739 | * Mount points can be stacked. If a mount point is stacked below / or /usr, we |
740 | * cannot umount or remount it directly, since there is no way to refer to the | |
741 | * underlying mount. There's nothing we can do about it for the general case, but we | |
742 | * can do something about it if it is aliased somewhere else via a bind mount. If we | |
743 | * explicitly remount the super block of that alias read-only we hence should be | |
744 | * relatively safe regarding keeping a dirty fs we cannot otherwise see. | |
d5641e0d | 745 | * |
49f80dce LP |
746 | * Since the remount can hang in the instance of remote filesystems, we remount |
747 | * asynchronously and skip the subsequent umount if it fails. */ | |
5125b677 | 748 | if (remount_with_timeout(m, last_try) < 0) { |
8645ffd1 JJ |
749 | /* Remount failed, but try unmounting anyway, |
750 | * unless this is a mount point we want to skip. */ | |
751 | if (nonunmountable_path(m->path)) { | |
c826cd3f | 752 | n_failed++; |
8645ffd1 JJ |
753 | continue; |
754 | } | |
c826cd3f | 755 | } |
93bd1577 LP |
756 | } |
757 | ||
49f80dce LP |
758 | /* Skip / and /usr since we cannot unmount that anyway, since we are running from it. They |
759 | * have already been remounted ro. */ | |
c826cd3f | 760 | if (nonunmountable_path(m->path)) |
e3478379 FF |
761 | continue; |
762 | ||
d5641e0d | 763 | /* Trying to umount */ |
6dc68a00 VD |
764 | r = umount_with_timeout(m, last_try); |
765 | if (r < 0) | |
d5641e0d | 766 | n_failed++; |
0494cae0 JJ |
767 | else |
768 | *changed = true; | |
6dc68a00 VD |
769 | |
770 | /* If a mount is busy, we move it to not keep parent mount points busy. | |
771 | * If a mount point is not a leaf, moving it would invalidate our mount table. | |
772 | * More moving will occur in next iteration with a fresh mount table. | |
773 | */ | |
774 | if (r != -EBUSY || !m->leaf) | |
775 | continue; | |
776 | ||
777 | _cleanup_free_ char *dirname = NULL; | |
778 | ||
779 | r = path_extract_directory(m->path, &dirname); | |
780 | if (r < 0) { | |
781 | n_failed++; | |
782 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Cannot find directory for %s: %m", m->path); | |
783 | continue; | |
784 | } | |
785 | ||
786 | /* We need to canonicalize /run/shutdown/mounts. We cannot compare inodes, since /run | |
787 | * might be bind mounted somewhere we want to unmount. And we need to move all mounts in | |
788 | * /run/shutdown/mounts from there. | |
789 | */ | |
790 | if (!resolved_mounts_path) | |
791 | (void) chase_symlinks("/run/shutdown/mounts", NULL, 0, &resolved_mounts_path, NULL); | |
792 | if (!path_equal(dirname, resolved_mounts_path)) { | |
793 | char newpath[STRLEN("/run/shutdown/mounts/") + 16 + 1]; | |
794 | ||
795 | xsprintf(newpath, "/run/shutdown/mounts/%016" PRIx64, random_u64()); | |
796 | ||
797 | /* on error of is_dir, assume directory */ | |
798 | if (is_dir(m->path, true) != 0) { | |
799 | r = mkdir_p(newpath, 0000); | |
800 | if (r < 0) { | |
801 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not create directory %s: %m", newpath); | |
802 | continue; | |
803 | } | |
804 | } else { | |
805 | r = touch_file(newpath, /* parents= */ true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0700); | |
806 | if (r < 0) { | |
807 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not create file %s: %m", newpath); | |
808 | continue; | |
809 | } | |
810 | } | |
811 | ||
812 | log_info("Moving mount %s to %s.", m->path, newpath); | |
813 | ||
814 | r = RET_NERRNO(mount(m->path, newpath, NULL, MS_MOVE, NULL)); | |
815 | if (r < 0) { | |
816 | n_failed++; | |
817 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not move %s to %s: %m", m->path, newpath); | |
818 | } else | |
819 | *changed = true; | |
820 | } | |
e3478379 FF |
821 | } |
822 | ||
12aad1d0 | 823 | return n_failed; |
e3478379 FF |
824 | } |
825 | ||
12aad1d0 | 826 | static int swap_points_list_off(MountPoint **head, bool *changed) { |
12aad1d0 LP |
827 | int n_failed = 0; |
828 | ||
829 | assert(head); | |
0494cae0 | 830 | assert(changed); |
e3478379 | 831 | |
80a226b2 | 832 | LIST_FOREACH(mount_point, m, *head) { |
735e0712 | 833 | log_info("Deactivating swap %s.", m->path); |
88287615 | 834 | if (swapoff(m->path) < 0) { |
56f64d95 | 835 | log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path); |
12aad1d0 | 836 | n_failed++; |
88287615 | 837 | continue; |
e3478379 | 838 | } |
88287615 LP |
839 | |
840 | *changed = true; | |
841 | mount_point_free(head, m); | |
e3478379 FF |
842 | } |
843 | ||
12aad1d0 | 844 | return n_failed; |
e3478379 FF |
845 | } |
846 | ||
5125b677 | 847 | static int loopback_points_list_detach(MountPoint **head, bool *changed, bool last_try) { |
63135a2d LP |
848 | int n_failed = 0, r; |
849 | dev_t rootdev = 0; | |
12aad1d0 LP |
850 | |
851 | assert(head); | |
0494cae0 | 852 | assert(changed); |
12aad1d0 | 853 | |
63135a2d | 854 | (void) get_block_device("/", &rootdev); |
7fc942b2 | 855 | |
80a226b2 | 856 | LIST_FOREACH(mount_point, m, *head) { |
63135a2d | 857 | if (major(rootdev) != 0 && rootdev == m->devnum) { |
313cefa1 | 858 | n_failed++; |
7fc942b2 LP |
859 | continue; |
860 | } | |
e3478379 | 861 | |
735e0712 | 862 | log_info("Detaching loopback %s.", m->path); |
bce93b7a | 863 | r = delete_loopback(m->path); |
88287615 | 864 | if (r < 0) { |
5125b677 | 865 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not detach loopback %s: %m", m->path); |
12aad1d0 | 866 | n_failed++; |
88287615 | 867 | continue; |
e3478379 | 868 | } |
88287615 LP |
869 | if (r > 0) |
870 | *changed = true; | |
871 | ||
872 | mount_point_free(head, m); | |
e3478379 FF |
873 | } |
874 | ||
12aad1d0 | 875 | return n_failed; |
e3478379 FF |
876 | } |
877 | ||
5125b677 | 878 | static int dm_points_list_detach(MountPoint **head, bool *changed, bool last_try) { |
33e8d8af | 879 | int n_failed = 0, r; |
63135a2d | 880 | dev_t rootdev = 0; |
12aad1d0 LP |
881 | |
882 | assert(head); | |
0494cae0 | 883 | assert(changed); |
12aad1d0 | 884 | |
63135a2d | 885 | (void) get_block_device("/", &rootdev); |
7fc942b2 | 886 | |
80a226b2 | 887 | LIST_FOREACH(mount_point, m, *head) { |
0494cae0 JJ |
888 | if (major(rootdev) != 0 && rootdev == m->devnum) { |
889 | n_failed ++; | |
890 | continue; | |
891 | } | |
7fc942b2 | 892 | |
88287615 | 893 | log_info("Detaching DM %s (%u:%u).", m->path, major(m->devnum), minor(m->devnum)); |
e55299da | 894 | r = delete_dm(m); |
88287615 | 895 | if (r < 0) { |
5125b677 | 896 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not detach DM %s: %m", m->path); |
12aad1d0 | 897 | n_failed++; |
88287615 | 898 | continue; |
d48141ba | 899 | } |
88287615 LP |
900 | |
901 | *changed = true; | |
902 | mount_point_free(head, m); | |
d48141ba LP |
903 | } |
904 | ||
12aad1d0 | 905 | return n_failed; |
d48141ba LP |
906 | } |
907 | ||
5125b677 | 908 | static int md_points_list_detach(MountPoint **head, bool *changed, bool last_try) { |
0b220a5f HK |
909 | int n_failed = 0, r; |
910 | dev_t rootdev = 0; | |
911 | ||
912 | assert(head); | |
913 | assert(changed); | |
914 | ||
915 | (void) get_block_device("/", &rootdev); | |
916 | ||
80a226b2 | 917 | LIST_FOREACH(mount_point, m, *head) { |
0b220a5f HK |
918 | if (major(rootdev) != 0 && rootdev == m->devnum) { |
919 | n_failed ++; | |
920 | continue; | |
921 | } | |
922 | ||
923 | log_info("Stopping MD %s (%u:%u).", m->path, major(m->devnum), minor(m->devnum)); | |
924 | r = delete_md(m); | |
925 | if (r < 0) { | |
5125b677 | 926 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not stop MD %s: %m", m->path); |
0b220a5f HK |
927 | n_failed++; |
928 | continue; | |
929 | } | |
930 | ||
931 | *changed = true; | |
932 | mount_point_free(head, m); | |
933 | } | |
934 | ||
935 | return n_failed; | |
936 | } | |
937 | ||
5125b677 | 938 | static int umount_all_once(bool *changed, bool last_try) { |
a6dcd229 | 939 | _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head); |
88287615 | 940 | int r; |
e3478379 | 941 | |
0494cae0 JJ |
942 | assert(changed); |
943 | ||
71fda00f | 944 | LIST_HEAD_INIT(mp_list_head); |
6fa392bf | 945 | r = mount_points_list_get(NULL, &mp_list_head); |
e3478379 | 946 | if (r < 0) |
a6dcd229 | 947 | return r; |
116e6d96 | 948 | |
5125b677 | 949 | return mount_points_list_umount(&mp_list_head, changed, last_try); |
116e6d96 AJ |
950 | } |
951 | ||
5125b677 | 952 | int umount_all(bool *changed, bool last_try) { |
116e6d96 AJ |
953 | bool umount_changed; |
954 | int r; | |
955 | ||
0494cae0 JJ |
956 | assert(changed); |
957 | ||
83f3bf4b LP |
958 | /* Retry umount, until nothing can be umounted anymore. Mounts are processed in order, newest |
959 | * first. The retries are needed when an old mount has been moved, to a path inside a newer mount. */ | |
6f7f51f7 HH |
960 | do { |
961 | umount_changed = false; | |
3e085b6c | 962 | |
5125b677 | 963 | r = umount_all_once(&umount_changed, last_try); |
6f7f51f7 HH |
964 | if (umount_changed) |
965 | *changed = true; | |
3e085b6c LP |
966 | } while (umount_changed); |
967 | ||
e3478379 FF |
968 | return r; |
969 | } | |
970 | ||
12aad1d0 | 971 | int swapoff_all(bool *changed) { |
a6dcd229 | 972 | _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, swap_list_head); |
e3478379 | 973 | int r; |
e3478379 | 974 | |
0494cae0 JJ |
975 | assert(changed); |
976 | ||
71fda00f | 977 | LIST_HEAD_INIT(swap_list_head); |
e3478379 | 978 | |
1fd8edb5 | 979 | r = swap_list_get(NULL, &swap_list_head); |
e3478379 | 980 | if (r < 0) |
a6dcd229 | 981 | return r; |
e3478379 | 982 | |
a6dcd229 | 983 | return swap_points_list_off(&swap_list_head, changed); |
e3478379 FF |
984 | } |
985 | ||
5125b677 | 986 | int loopback_detach_all(bool *changed, bool last_try) { |
a6dcd229 | 987 | _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, loopback_list_head); |
e3478379 | 988 | int r; |
e3478379 | 989 | |
0494cae0 JJ |
990 | assert(changed); |
991 | ||
71fda00f | 992 | LIST_HEAD_INIT(loopback_list_head); |
e3478379 FF |
993 | |
994 | r = loopback_list_get(&loopback_list_head); | |
995 | if (r < 0) | |
a6dcd229 | 996 | return r; |
e3478379 | 997 | |
5125b677 | 998 | return loopback_points_list_detach(&loopback_list_head, changed, last_try); |
e3478379 | 999 | } |
d48141ba | 1000 | |
5125b677 | 1001 | int dm_detach_all(bool *changed, bool last_try) { |
a6dcd229 | 1002 | _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, dm_list_head); |
d48141ba | 1003 | int r; |
d48141ba | 1004 | |
0494cae0 JJ |
1005 | assert(changed); |
1006 | ||
71fda00f | 1007 | LIST_HEAD_INIT(dm_list_head); |
d48141ba LP |
1008 | |
1009 | r = dm_list_get(&dm_list_head); | |
1010 | if (r < 0) | |
a6dcd229 | 1011 | return r; |
d48141ba | 1012 | |
5125b677 | 1013 | return dm_points_list_detach(&dm_list_head, changed, last_try); |
d48141ba | 1014 | } |
0b220a5f | 1015 | |
5125b677 | 1016 | int md_detach_all(bool *changed, bool last_try) { |
0b220a5f HK |
1017 | _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, md_list_head); |
1018 | int r; | |
1019 | ||
1020 | assert(changed); | |
1021 | ||
1022 | LIST_HEAD_INIT(md_list_head); | |
1023 | ||
1024 | r = md_list_get(&md_list_head); | |
1025 | if (r < 0) | |
1026 | return r; | |
1027 | ||
5125b677 | 1028 | return md_points_list_detach(&md_list_head, changed, last_try); |
0b220a5f | 1029 | } |