1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 ProFUSION embedded systems
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.
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.
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/>.
25 #include <sys/mount.h>
28 #include <linux/loop.h>
29 #include <linux/dm-ioctl.h>
33 #include "mount-setup.h"
37 typedef struct MountPoint {
41 LIST_FIELDS (struct MountPoint, mount_point);
44 static void mount_point_free(MountPoint **head, MountPoint *m) {
48 LIST_REMOVE(MountPoint, mount_point, *head, m);
54 static void mount_points_list_free(MountPoint **head) {
58 mount_point_free(head, *head);
61 static int mount_points_list_get(MountPoint **head) {
62 FILE *proc_self_mountinfo;
69 if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
80 if ((k = fscanf(proc_self_mountinfo,
81 "%*s " /* (1) mount id */
82 "%*s " /* (2) parent id */
83 "%*s " /* (3) major:minor */
85 "%ms " /* (5) mount point */
86 "%*s" /* (6) mount options */
87 "%*[^-]" /* (7) optional fields */
88 "- " /* (8) separator */
89 "%*s " /* (9) file system type */
90 "%*s" /* (10) mount source */
91 "%*s" /* (11) mount options 2 */
92 "%*[^\n]", /* some rubbish at the end */
98 log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
104 /* If we encounter a bind mount, don't try to remount
105 * the source dir too early */
106 skip_ro = !streq(root, "/");
117 /* Ignore mount points we can't unmount because they
118 * are API or because we are keeping them open (like
120 if (mount_point_is_api(p) ||
121 mount_point_ignore(p) ||
122 path_equal(p, "/dev/console")) {
127 if (!(m = new0(MountPoint, 1))) {
134 m->skip_ro = skip_ro;
135 LIST_PREPEND(MountPoint, mount_point, *head, m);
141 fclose(proc_self_mountinfo);
146 static int swap_list_get(MountPoint **head) {
153 if (!(proc_swaps = fopen("/proc/swaps", "re")))
154 return (errno == ENOENT) ? 0 : -errno;
156 (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
160 char *dev = NULL, *d;
163 if ((k = fscanf(proc_swaps,
164 "%ms " /* device/file */
165 "%*s " /* type of swap */
166 "%*s " /* swap size */
168 "%*s\n", /* priority */
174 log_warning("Failed to parse /proc/swaps:%u.", i);
180 if (endswith(dev, "(deleted)")) {
193 if (!(swap = new0(MountPoint, 1))) {
200 LIST_PREPEND(MountPoint, mount_point, *head, swap);
211 static int loopback_list_get(MountPoint **head) {
214 struct udev_enumerate *e = NULL;
215 struct udev_list_entry *item = NULL, *first = NULL;
219 if (!(udev = udev_new())) {
224 if (!(e = udev_enumerate_new(udev))) {
229 if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
230 udev_enumerate_add_match_sysname(e, "loop*") < 0) {
235 if (udev_enumerate_scan_devices(e) < 0) {
240 first = udev_enumerate_get_list_entry(e);
241 udev_list_entry_foreach(item, first) {
243 struct udev_device *d;
247 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
252 if (!(dn = udev_device_get_devnode(d))) {
253 udev_device_unref(d);
258 udev_device_unref(d);
265 if (!(lb = new0(MountPoint, 1))) {
272 LIST_PREPEND(MountPoint, mount_point, *head, lb);
279 udev_enumerate_unref(e);
287 static int dm_list_get(MountPoint **head) {
290 struct udev_enumerate *e = NULL;
291 struct udev_list_entry *item = NULL, *first = NULL;
295 if (!(udev = udev_new())) {
300 if (!(e = udev_enumerate_new(udev))) {
305 if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
306 udev_enumerate_add_match_sysname(e, "dm-*") < 0) {
311 if (udev_enumerate_scan_devices(e) < 0) {
316 first = udev_enumerate_get_list_entry(e);
318 udev_list_entry_foreach(item, first) {
320 struct udev_device *d;
325 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
330 devnum = udev_device_get_devnum(d);
331 dn = udev_device_get_devnode(d);
333 if (major(devnum) == 0 || !dn) {
334 udev_device_unref(d);
339 udev_device_unref(d);
346 if (!(m = new(MountPoint, 1))) {
354 LIST_PREPEND(MountPoint, mount_point, *head, m);
361 udev_enumerate_unref(e);
369 static int delete_loopback(const char *device) {
372 if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0)
373 return errno == ENOENT ? 0 : -errno;
375 r = ioctl(fd, LOOP_CLR_FD, 0);
376 close_nointr_nofail(fd);
381 /* ENXIO: not bound, so no error */
388 static int delete_dm(dev_t devnum) {
392 assert(major(devnum) != 0);
394 if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0)
398 dm.version[0] = DM_VERSION_MAJOR;
399 dm.version[1] = DM_VERSION_MINOR;
400 dm.version[2] = DM_VERSION_PATCHLEVEL;
402 dm.data_size = sizeof(dm);
405 r = ioctl(fd, DM_DEV_REMOVE, &dm);
406 close_nointr_nofail(fd);
408 return r >= 0 ? 0 : -errno;
411 static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
417 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
418 if (path_equal(m->path, "/")
419 #ifndef HAVE_SPLIT_USR
420 || path_equal(m->path, "/usr")
427 /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
428 if (umount2(m->path, MNT_FORCE) == 0) {
429 log_info("Unmounted %s.", m->path);
433 mount_point_free(head, m);
434 } else if (log_error) {
435 log_warning("Could not unmount %s: %m", m->path);
443 static int mount_points_list_remount_read_only(MountPoint **head, bool *changed) {
449 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
456 /* Trying to remount read-only */
457 if (mount(NULL, m->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
461 mount_point_free(head, m);
463 log_warning("Could not remount as read-only %s: %m", m->path);
471 static int swap_points_list_off(MountPoint **head, bool *changed) {
477 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
478 if (swapoff(m->path) == 0) {
482 mount_point_free(head, m);
484 log_warning("Could not deactivate swap %s: %m", m->path);
492 static int loopback_points_list_detach(MountPoint **head, bool *changed) {
499 k = lstat("/", &root_st);
501 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
503 struct stat loopback_st;
506 major(root_st.st_dev) != 0 &&
507 lstat(m->path, &loopback_st) >= 0 &&
508 root_st.st_dev == loopback_st.st_rdev) {
513 if ((r = delete_loopback(m->path)) >= 0) {
515 if (r > 0 && changed)
518 mount_point_free(head, m);
520 log_warning("Could not delete loopback %s: %m", m->path);
528 static int dm_points_list_detach(MountPoint **head, bool *changed) {
535 k = lstat("/", &root_st);
537 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
541 major(root_st.st_dev) != 0 &&
542 root_st.st_dev == m->devnum) {
547 if ((r = delete_dm(m->devnum)) >= 0) {
549 if (r > 0 && changed)
552 mount_point_free(head, m);
554 log_warning("Could not delete dm %s: %m", m->path);
562 int umount_all(bool *changed) {
566 LIST_HEAD(MountPoint, mp_list_head);
568 LIST_HEAD_INIT(MountPoint, mp_list_head);
570 r = mount_points_list_get(&mp_list_head);
574 /* retry umount, until nothing can be umounted anymore */
576 umount_changed = false;
578 mount_points_list_umount(&mp_list_head, &umount_changed, false);
582 } while (umount_changed);
584 /* umount one more time with logging enabled */
585 r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
589 r = mount_points_list_remount_read_only(&mp_list_head, changed);
592 mount_points_list_free(&mp_list_head);
597 int swapoff_all(bool *changed) {
599 LIST_HEAD(MountPoint, swap_list_head);
601 LIST_HEAD_INIT(MountPoint, swap_list_head);
603 r = swap_list_get(&swap_list_head);
607 r = swap_points_list_off(&swap_list_head, changed);
610 mount_points_list_free(&swap_list_head);
615 int loopback_detach_all(bool *changed) {
617 LIST_HEAD(MountPoint, loopback_list_head);
619 LIST_HEAD_INIT(MountPoint, loopback_list_head);
621 r = loopback_list_get(&loopback_list_head);
625 r = loopback_points_list_detach(&loopback_list_head, changed);
628 mount_points_list_free(&loopback_list_head);
633 int dm_detach_all(bool *changed) {
635 LIST_HEAD(MountPoint, dm_list_head);
637 LIST_HEAD_INIT(MountPoint, dm_list_head);
639 r = dm_list_get(&dm_list_head);
643 r = dm_points_list_detach(&dm_list_head, changed);
646 mount_points_list_free(&dm_list_head);