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