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