]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/umount.c
core: fix mount setup to work with non-existing mount points
[thirdparty/systemd.git] / src / core / umount.c
CommitLineData
e3478379
FF
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 ProFUSION embedded systems
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
e3478379
FF
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
5430f7f2 16 Lesser General Public License for more details.
e3478379 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
e3478379
FF
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <fcntl.h>
24#include <string.h>
25#include <sys/mount.h>
26#include <sys/swap.h>
e3478379 27#include <linux/loop.h>
d48141ba 28#include <linux/dm-ioctl.h>
e3478379
FF
29
30#include "list.h"
31#include "mount-setup.h"
32#include "umount.h"
9eb977db 33#include "path-util.h"
e3478379 34#include "util.h"
024f268d 35#include "virt.h"
1ca208fb
ZJS
36#include "libudev.h"
37#include "udev-util.h"
e3478379
FF
38
39typedef struct MountPoint {
40 char *path;
2d9a3397 41 dev_t devnum;
71fda00f 42 LIST_FIELDS(struct MountPoint, mount_point);
e3478379
FF
43} MountPoint;
44
12aad1d0
LP
45static void mount_point_free(MountPoint **head, MountPoint *m) {
46 assert(head);
47 assert(m);
e3478379 48
71fda00f 49 LIST_REMOVE(mount_point, *head, m);
12aad1d0
LP
50
51 free(m->path);
52 free(m);
e3478379
FF
53}
54
12aad1d0
LP
55static void mount_points_list_free(MountPoint **head) {
56 assert(head);
57
58 while (*head)
59 mount_point_free(head, *head);
e3478379
FF
60}
61
12aad1d0 62static int mount_points_list_get(MountPoint **head) {
c3544e8d 63 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
e3478379 64 unsigned int i;
e3478379 65
12aad1d0
LP
66 assert(head);
67
c3544e8d
LP
68 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
69 if (!proc_self_mountinfo)
e3478379
FF
70 return -errno;
71
72 for (i = 1;; i++) {
c3544e8d
LP
73 _cleanup_free_ char *path = NULL;
74 char *p = NULL;
12aad1d0 75 MountPoint *m;
c3544e8d 76 int k;
e3478379 77
c3544e8d
LP
78 k = fscanf(proc_self_mountinfo,
79 "%*s " /* (1) mount id */
80 "%*s " /* (2) parent id */
81 "%*s " /* (3) major:minor */
82 "%*s " /* (4) root */
83 "%ms " /* (5) mount point */
84 "%*s" /* (6) mount options */
85 "%*[^-]" /* (7) optional fields */
86 "- " /* (8) separator */
87 "%*s " /* (9) file system type */
88 "%*s" /* (10) mount source */
89 "%*s" /* (11) mount options 2 */
90 "%*[^\n]", /* some rubbish at the end */
91 &path);
92 if (k != 1) {
e3478379
FF
93 if (k == EOF)
94 break;
95
96 log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
e3478379
FF
97 continue;
98 }
99
2054a5b8 100 p = cunescape(path);
c3544e8d
LP
101 if (!p)
102 return -ENOMEM;
e3478379 103
46108b3b
LP
104 /* Ignore mount points we can't unmount because they
105 * are API or because we are keeping them open (like
874d3404
LP
106 * /dev/console). Also, ignore all mounts below API
107 * file systems, since they are likely virtual too,
108 * and hence not worth spending time on. Also, in
109 * unprivileged containers we might lack the rights to
110 * unmount these things, hence don't bother. */
46108b3b
LP
111 if (mount_point_is_api(p) ||
112 mount_point_ignore(p) ||
874d3404
LP
113 path_startswith(p, "/dev") ||
114 path_startswith(p, "/sys") ||
115 path_startswith(p, "/proc")) {
2054a5b8
LP
116 free(p);
117 continue;
118 }
119
c3544e8d
LP
120 m = new0(MountPoint, 1);
121 if (!m) {
2054a5b8 122 free(p);
c3544e8d 123 return -ENOMEM;
e3478379 124 }
e3478379 125
12aad1d0 126 m->path = p;
71fda00f 127 LIST_PREPEND(mount_point, *head, m);
e3478379
FF
128 }
129
c3544e8d 130 return 0;
e3478379
FF
131}
132
12aad1d0 133static int swap_list_get(MountPoint **head) {
e1d75803 134 _cleanup_fclose_ FILE *proc_swaps = NULL;
e3478379 135 unsigned int i;
e3478379 136
12aad1d0
LP
137 assert(head);
138
e3478379 139 if (!(proc_swaps = fopen("/proc/swaps", "re")))
dee87d61 140 return (errno == ENOENT) ? 0 : -errno;
e3478379
FF
141
142 (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
143
144 for (i = 2;; i++) {
145 MountPoint *swap;
146 char *dev = NULL, *d;
147 int k;
148
149 if ((k = fscanf(proc_swaps,
150 "%ms " /* device/file */
151 "%*s " /* type of swap */
152 "%*s " /* swap size */
153 "%*s " /* used */
154 "%*s\n", /* priority */
155 &dev)) != 1) {
156
157 if (k == EOF)
158 break;
159
160 log_warning("Failed to parse /proc/swaps:%u.", i);
161
162 free(dev);
163 continue;
164 }
165
a87f0f72 166 if (endswith(dev, " (deleted)")) {
e3478379
FF
167 free(dev);
168 continue;
169 }
170
171 d = cunescape(dev);
172 free(dev);
173
174 if (!d) {
e1d75803 175 return -ENOMEM;
e3478379
FF
176 }
177
2d9a3397 178 if (!(swap = new0(MountPoint, 1))) {
e3478379 179 free(d);
e1d75803 180 return -ENOMEM;
e3478379
FF
181 }
182
2d9a3397 183 swap->path = d;
71fda00f 184 LIST_PREPEND(mount_point, *head, swap);
e3478379
FF
185 }
186
e1d75803 187 return 0;
e3478379
FF
188}
189
12aad1d0 190static int loopback_list_get(MountPoint **head) {
1ca208fb 191 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
e3478379 192 struct udev_list_entry *item = NULL, *first = NULL;
06acf2d4
LP
193 _cleanup_udev_unref_ struct udev *udev = NULL;
194 int r;
e3478379 195
12aad1d0
LP
196 assert(head);
197
1ca208fb
ZJS
198 udev = udev_new();
199 if (!udev)
200 return -ENOMEM;
e3478379 201
1ca208fb
ZJS
202 e = udev_enumerate_new(udev);
203 if (!e)
204 return -ENOMEM;
e3478379 205
06acf2d4
LP
206 r = udev_enumerate_add_match_subsystem(e, "block");
207 if (r < 0)
208 return r;
209
210 r = udev_enumerate_add_match_sysname(e, "loop*");
211 if (r < 0)
212 return r;
213
214 r = udev_enumerate_add_match_sysattr(e, "loop/backing_file", NULL);
215 if (r < 0)
216 return r;
e3478379 217
06acf2d4
LP
218 r = udev_enumerate_scan_devices(e);
219 if (r < 0)
220 return r;
e3478379
FF
221
222 first = udev_enumerate_get_list_entry(e);
e3478379
FF
223 udev_list_entry_foreach(item, first) {
224 MountPoint *lb;
1ca208fb 225 _cleanup_udev_device_unref_ struct udev_device *d;
e3478379 226 char *loop;
b854a7e7 227 const char *dn;
e3478379 228
1ca208fb
ZJS
229 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
230 if (!d)
231 return -ENOMEM;
e3478379 232
1ca208fb
ZJS
233 dn = udev_device_get_devnode(d);
234 if (!dn)
2d9a3397 235 continue;
b854a7e7 236
2d9a3397 237 loop = strdup(dn);
1ca208fb
ZJS
238 if (!loop)
239 return -ENOMEM;
b854a7e7 240
1ca208fb
ZJS
241 lb = new0(MountPoint, 1);
242 if (!lb) {
e3478379 243 free(loop);
1ca208fb 244 return -ENOMEM;
e3478379
FF
245 }
246
2d9a3397 247 lb->path = loop;
71fda00f 248 LIST_PREPEND(mount_point, *head, lb);
e3478379
FF
249 }
250
1ca208fb 251 return 0;
e3478379
FF
252}
253
12aad1d0 254static int dm_list_get(MountPoint **head) {
1ca208fb 255 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
d48141ba 256 struct udev_list_entry *item = NULL, *first = NULL;
06acf2d4
LP
257 _cleanup_udev_unref_ struct udev *udev = NULL;
258 int r;
d48141ba 259
12aad1d0
LP
260 assert(head);
261
1ca208fb
ZJS
262 udev = udev_new();
263 if (!udev)
264 return -ENOMEM;
d48141ba 265
1ca208fb
ZJS
266 e = udev_enumerate_new(udev);
267 if (!e)
268 return -ENOMEM;
d48141ba 269
06acf2d4
LP
270 r = udev_enumerate_add_match_subsystem(e, "block");
271 if (r < 0)
272 return r;
d48141ba 273
06acf2d4
LP
274 r = udev_enumerate_add_match_sysname(e, "dm-*");
275 if (r < 0)
276 return r;
d48141ba 277
06acf2d4
LP
278 r = udev_enumerate_scan_devices(e);
279 if (r < 0)
280 return r;
d48141ba 281
06acf2d4 282 first = udev_enumerate_get_list_entry(e);
d48141ba 283 udev_list_entry_foreach(item, first) {
2d9a3397 284 MountPoint *m;
1ca208fb 285 _cleanup_udev_device_unref_ struct udev_device *d;
2d9a3397
LP
286 dev_t devnum;
287 char *node;
288 const char *dn;
d48141ba 289
1ca208fb
ZJS
290 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
291 if (!d)
292 return -ENOMEM;
d48141ba 293
2d9a3397
LP
294 devnum = udev_device_get_devnum(d);
295 dn = udev_device_get_devnode(d);
1ca208fb 296 if (major(devnum) == 0 || !dn)
2d9a3397 297 continue;
d48141ba 298
2d9a3397 299 node = strdup(dn);
1ca208fb
ZJS
300 if (!node)
301 return -ENOMEM;
d48141ba 302
1ca208fb
ZJS
303 m = new(MountPoint, 1);
304 if (!m) {
2d9a3397 305 free(node);
1ca208fb 306 return -ENOMEM;
d48141ba
LP
307 }
308
2d9a3397
LP
309 m->path = node;
310 m->devnum = devnum;
71fda00f 311 LIST_PREPEND(mount_point, *head, m);
d48141ba
LP
312 }
313
1ca208fb 314 return 0;
d48141ba
LP
315}
316
e3478379 317static int delete_loopback(const char *device) {
03e334a1
LP
318 _cleanup_close_ int fd = -1;
319 int r;
e3478379 320
03e334a1
LP
321 fd = open(device, O_RDONLY|O_CLOEXEC);
322 if (fd < 0)
c4f8bd1a 323 return errno == ENOENT ? 0 : -errno;
e3478379 324
ce726252 325 r = ioctl(fd, LOOP_CLR_FD, 0);
12aad1d0
LP
326 if (r >= 0)
327 return 1;
328
ce726252 329 /* ENXIO: not bound, so no error */
12aad1d0
LP
330 if (errno == ENXIO)
331 return 0;
332
333 return -errno;
e3478379
FF
334}
335
2d9a3397 336static int delete_dm(dev_t devnum) {
7fd1b19b 337 _cleanup_close_ int fd = -1;
e62d8c39 338 int r;
b92bea5d
ZJS
339 struct dm_ioctl dm = {
340 .version = {DM_VERSION_MAJOR,
341 DM_VERSION_MINOR,
342 DM_VERSION_PATCHLEVEL},
343 .data_size = sizeof(dm),
344 .dev = devnum,
345 };
d48141ba 346
2d9a3397 347 assert(major(devnum) != 0);
d48141ba 348
e62d8c39
ZJS
349 fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
350 if (fd < 0)
d48141ba
LP
351 return -errno;
352
d48141ba 353 r = ioctl(fd, DM_DEV_REMOVE, &dm);
d48141ba
LP
354 return r >= 0 ? 0 : -errno;
355}
356
31657718 357static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
12aad1d0
LP
358 MountPoint *m, *n;
359 int n_failed = 0;
e3478379 360
12aad1d0
LP
361 assert(head);
362
363 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
93bd1577
LP
364
365 /* If we are in a container, don't attempt to
366 read-only mount anything as that brings no real
367 benefits, but might confuse the host, as we remount
368 the superblock here, not the bind mound. */
369 if (detect_container(NULL) <= 0) {
370 /* We always try to remount directories
371 * read-only first, before we go on and umount
372 * them.
373 *
374 * Mount points can be stacked. If a mount
375 * point is stacked below / or /usr, we
ab06eef8 376 * cannot umount or remount it directly,
93bd1577
LP
377 * since there is no way to refer to the
378 * underlying mount. There's nothing we can do
379 * about it for the general case, but we can
380 * do something about it if it is aliased
381 * somehwere else via a bind mount. If we
382 * explicitly remount the super block of that
383 * alias read-only we hence should be
384 * relatively safe regarding keeping the fs we
385 * can otherwise not see dirty. */
386 mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
387 }
388
389 /* Skip / and /usr since we cannot unmount that
5a6f9d23
HG
390 * anyway, since we are running from it. They have
391 * already been remounted ro. */
9cacf564
LP
392 if (path_equal(m->path, "/")
393#ifndef HAVE_SPLIT_USR
394 || path_equal(m->path, "/usr")
395#endif
14088340 396 )
e3478379
FF
397 continue;
398
0c08f5cd
LP
399 /* Trying to umount. We don't force here since we rely
400 * on busy NFS and FUSE file systems to return EBUSY
401 * until we closed everything on top of them. */
bce93b7a 402 log_info("Unmounting %s.", m->path);
0c08f5cd 403 if (umount2(m->path, 0) == 0) {
12aad1d0
LP
404 if (changed)
405 *changed = true;
406
407 mount_point_free(head, m);
31657718 408 } else if (log_error) {
56f64d95 409 log_warning_errno(errno, "Could not unmount %s: %m", m->path);
12aad1d0 410 n_failed++;
e3478379
FF
411 }
412 }
413
12aad1d0 414 return n_failed;
e3478379
FF
415}
416
12aad1d0
LP
417static int swap_points_list_off(MountPoint **head, bool *changed) {
418 MountPoint *m, *n;
419 int n_failed = 0;
420
421 assert(head);
e3478379 422
12aad1d0 423 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
735e0712 424 log_info("Deactivating swap %s.", m->path);
12aad1d0
LP
425 if (swapoff(m->path) == 0) {
426 if (changed)
427 *changed = true;
428
429 mount_point_free(head, m);
430 } else {
56f64d95 431 log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path);
12aad1d0 432 n_failed++;
e3478379
FF
433 }
434 }
435
12aad1d0 436 return n_failed;
e3478379
FF
437}
438
12aad1d0
LP
439static int loopback_points_list_detach(MountPoint **head, bool *changed) {
440 MountPoint *m, *n;
7fc942b2
LP
441 int n_failed = 0, k;
442 struct stat root_st;
12aad1d0
LP
443
444 assert(head);
445
7fc942b2
LP
446 k = lstat("/", &root_st);
447
12aad1d0
LP
448 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
449 int r;
7fc942b2
LP
450 struct stat loopback_st;
451
452 if (k >= 0 &&
453 major(root_st.st_dev) != 0 &&
454 lstat(m->path, &loopback_st) >= 0 &&
455 root_st.st_dev == loopback_st.st_rdev) {
456 n_failed ++;
457 continue;
458 }
e3478379 459
735e0712 460 log_info("Detaching loopback %s.", m->path);
bce93b7a
MS
461 r = delete_loopback(m->path);
462 if (r >= 0) {
12aad1d0
LP
463 if (r > 0 && changed)
464 *changed = true;
465
466 mount_point_free(head, m);
467 } else {
56f64d95 468 log_warning_errno(errno, "Could not detach loopback %s: %m", m->path);
12aad1d0 469 n_failed++;
e3478379
FF
470 }
471 }
472
12aad1d0 473 return n_failed;
e3478379
FF
474}
475
12aad1d0
LP
476static int dm_points_list_detach(MountPoint **head, bool *changed) {
477 MountPoint *m, *n;
7fc942b2
LP
478 int n_failed = 0, k;
479 struct stat root_st;
12aad1d0
LP
480
481 assert(head);
482
7fc942b2
LP
483 k = lstat("/", &root_st);
484
12aad1d0
LP
485 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
486 int r;
487
7fc942b2
LP
488 if (k >= 0 &&
489 major(root_st.st_dev) != 0 &&
490 root_st.st_dev == m->devnum) {
491 n_failed ++;
492 continue;
493 }
494
735e0712 495 log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
bce93b7a
MS
496 r = delete_dm(m->devnum);
497 if (r >= 0) {
c6784066 498 if (changed)
12aad1d0 499 *changed = true;
d48141ba 500
12aad1d0
LP
501 mount_point_free(head, m);
502 } else {
56f64d95 503 log_warning_errno(errno, "Could not detach DM %s: %m", m->path);
12aad1d0 504 n_failed++;
d48141ba
LP
505 }
506 }
507
12aad1d0 508 return n_failed;
d48141ba
LP
509}
510
12aad1d0 511int umount_all(bool *changed) {
e3478379 512 int r;
6f7f51f7 513 bool umount_changed;
e3478379
FF
514 LIST_HEAD(MountPoint, mp_list_head);
515
71fda00f 516 LIST_HEAD_INIT(mp_list_head);
e3478379
FF
517 r = mount_points_list_get(&mp_list_head);
518 if (r < 0)
519 goto end;
520
6f7f51f7
HH
521 /* retry umount, until nothing can be umounted anymore */
522 do {
523 umount_changed = false;
3e085b6c
LP
524
525 mount_points_list_umount(&mp_list_head, &umount_changed, false);
6f7f51f7
HH
526 if (umount_changed)
527 *changed = true;
3e085b6c
LP
528
529 } while (umount_changed);
530
31657718
HH
531 /* umount one more time with logging enabled */
532 r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
e3478379
FF
533 if (r <= 0)
534 goto end;
535
e3478379
FF
536 end:
537 mount_points_list_free(&mp_list_head);
538
539 return r;
540}
541
12aad1d0 542int swapoff_all(bool *changed) {
e3478379
FF
543 int r;
544 LIST_HEAD(MountPoint, swap_list_head);
545
71fda00f 546 LIST_HEAD_INIT(swap_list_head);
e3478379
FF
547
548 r = swap_list_get(&swap_list_head);
549 if (r < 0)
550 goto end;
551
12aad1d0 552 r = swap_points_list_off(&swap_list_head, changed);
e3478379
FF
553
554 end:
555 mount_points_list_free(&swap_list_head);
556
557 return r;
558}
559
12aad1d0 560int loopback_detach_all(bool *changed) {
e3478379
FF
561 int r;
562 LIST_HEAD(MountPoint, loopback_list_head);
563
71fda00f 564 LIST_HEAD_INIT(loopback_list_head);
e3478379
FF
565
566 r = loopback_list_get(&loopback_list_head);
567 if (r < 0)
568 goto end;
569
12aad1d0 570 r = loopback_points_list_detach(&loopback_list_head, changed);
e3478379
FF
571
572 end:
573 mount_points_list_free(&loopback_list_head);
574
575 return r;
576}
d48141ba 577
12aad1d0 578int dm_detach_all(bool *changed) {
d48141ba
LP
579 int r;
580 LIST_HEAD(MountPoint, dm_list_head);
581
71fda00f 582 LIST_HEAD_INIT(dm_list_head);
d48141ba
LP
583
584 r = dm_list_get(&dm_list_head);
585 if (r < 0)
586 goto end;
587
12aad1d0 588 r = dm_points_list_detach(&dm_list_head, changed);
d48141ba
LP
589
590 end:
591 mount_points_list_free(&dm_list_head);
592
593 return r;
594}