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