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"
35 #include "path-util.h"
38 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")))
78 if ((k = fscanf(proc_self_mountinfo,
79 "%*s " /* (1) mount id */
80 "%*s " /* (2) parent id */
81 "%*s " /* (3) major:minor */
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 */
95 log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
109 /* Ignore mount points we can't unmount because they
110 * are API or because we are keeping them open (like
112 if (mount_point_is_api(p) ||
113 mount_point_ignore(p) ||
114 path_equal(p, "/dev/console")) {
119 if (!(m = new0(MountPoint, 1))) {
126 LIST_PREPEND(MountPoint, mount_point, *head, m);
132 fclose(proc_self_mountinfo);
137 static int swap_list_get(MountPoint **head) {
144 if (!(proc_swaps = fopen("/proc/swaps", "re")))
145 return (errno == ENOENT) ? 0 : -errno;
147 (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
151 char *dev = NULL, *d;
154 if ((k = fscanf(proc_swaps,
155 "%ms " /* device/file */
156 "%*s " /* type of swap */
157 "%*s " /* swap size */
159 "%*s\n", /* priority */
165 log_warning("Failed to parse /proc/swaps:%u.", i);
171 if (endswith(dev, "(deleted)")) {
184 if (!(swap = new0(MountPoint, 1))) {
191 LIST_PREPEND(MountPoint, mount_point, *head, swap);
202 static int loopback_list_get(MountPoint **head) {
205 struct udev_enumerate *e = NULL;
206 struct udev_list_entry *item = NULL, *first = NULL;
210 if (!(udev = udev_new())) {
215 if (!(e = udev_enumerate_new(udev))) {
220 if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
221 udev_enumerate_add_match_sysname(e, "loop*") < 0) {
226 if (udev_enumerate_scan_devices(e) < 0) {
231 first = udev_enumerate_get_list_entry(e);
232 udev_list_entry_foreach(item, first) {
234 struct udev_device *d;
238 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
243 if (!(dn = udev_device_get_devnode(d))) {
244 udev_device_unref(d);
249 udev_device_unref(d);
256 if (!(lb = new0(MountPoint, 1))) {
263 LIST_PREPEND(MountPoint, mount_point, *head, lb);
270 udev_enumerate_unref(e);
278 static int dm_list_get(MountPoint **head) {
281 struct udev_enumerate *e = NULL;
282 struct udev_list_entry *item = NULL, *first = NULL;
286 if (!(udev = udev_new())) {
291 if (!(e = udev_enumerate_new(udev))) {
296 if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
297 udev_enumerate_add_match_sysname(e, "dm-*") < 0) {
302 if (udev_enumerate_scan_devices(e) < 0) {
307 first = udev_enumerate_get_list_entry(e);
309 udev_list_entry_foreach(item, first) {
311 struct udev_device *d;
316 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
321 devnum = udev_device_get_devnum(d);
322 dn = udev_device_get_devnode(d);
324 if (major(devnum) == 0 || !dn) {
325 udev_device_unref(d);
330 udev_device_unref(d);
337 if (!(m = new(MountPoint, 1))) {
345 LIST_PREPEND(MountPoint, mount_point, *head, m);
352 udev_enumerate_unref(e);
360 static int delete_loopback(const char *device) {
363 if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0)
364 return errno == ENOENT ? 0 : -errno;
366 r = ioctl(fd, LOOP_CLR_FD, 0);
367 close_nointr_nofail(fd);
372 /* ENXIO: not bound, so no error */
379 static int delete_dm(dev_t devnum) {
383 assert(major(devnum) != 0);
385 if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0)
389 dm.version[0] = DM_VERSION_MAJOR;
390 dm.version[1] = DM_VERSION_MINOR;
391 dm.version[2] = DM_VERSION_PATCHLEVEL;
393 dm.data_size = sizeof(dm);
396 r = ioctl(fd, DM_DEV_REMOVE, &dm);
397 close_nointr_nofail(fd);
399 return r >= 0 ? 0 : -errno;
402 static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
408 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
409 if (path_equal(m->path, "/")
410 #ifndef HAVE_SPLIT_USR
411 || path_equal(m->path, "/usr")
418 /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
419 if (umount2(m->path, MNT_FORCE) == 0) {
420 log_info("Unmounted %s.", m->path);
424 mount_point_free(head, m);
425 } else if (log_error) {
426 log_warning("Could not unmount %s: %m", m->path);
434 static int mount_points_list_remount_read_only(MountPoint **head, bool *changed) {
440 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
442 /* Trying to remount read-only */
443 if (mount(NULL, m->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
447 mount_point_free(head, m);
449 log_warning("Could not remount as read-only %s: %m", m->path);
457 static int swap_points_list_off(MountPoint **head, bool *changed) {
463 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
464 if (swapoff(m->path) == 0) {
468 mount_point_free(head, m);
470 log_warning("Could not deactivate swap %s: %m", m->path);
478 static int loopback_points_list_detach(MountPoint **head, bool *changed) {
485 k = lstat("/", &root_st);
487 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
489 struct stat loopback_st;
492 major(root_st.st_dev) != 0 &&
493 lstat(m->path, &loopback_st) >= 0 &&
494 root_st.st_dev == loopback_st.st_rdev) {
499 if ((r = delete_loopback(m->path)) >= 0) {
501 if (r > 0 && changed)
504 mount_point_free(head, m);
506 log_warning("Could not delete loopback %s: %m", m->path);
514 static int dm_points_list_detach(MountPoint **head, bool *changed) {
521 k = lstat("/", &root_st);
523 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
527 major(root_st.st_dev) != 0 &&
528 root_st.st_dev == m->devnum) {
533 if ((r = delete_dm(m->devnum)) >= 0) {
535 if (r > 0 && changed)
538 mount_point_free(head, m);
540 log_warning("Could not delete dm %s: %m", m->path);
548 int umount_all(bool *changed) {
552 LIST_HEAD(MountPoint, mp_list_head);
554 LIST_HEAD_INIT(MountPoint, mp_list_head);
556 r = mount_points_list_get(&mp_list_head);
560 /* retry umount, until nothing can be umounted anymore */
562 umount_changed = false;
564 mount_points_list_umount(&mp_list_head, &umount_changed, false);
568 } while (umount_changed);
570 /* umount one more time with logging enabled */
571 r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
575 r = mount_points_list_remount_read_only(&mp_list_head, changed);
578 mount_points_list_free(&mp_list_head);
583 int swapoff_all(bool *changed) {
585 LIST_HEAD(MountPoint, swap_list_head);
587 LIST_HEAD_INIT(MountPoint, swap_list_head);
589 r = swap_list_get(&swap_list_head);
593 r = swap_points_list_off(&swap_list_head, changed);
596 mount_points_list_free(&swap_list_head);
601 int loopback_detach_all(bool *changed) {
603 LIST_HEAD(MountPoint, loopback_list_head);
605 LIST_HEAD_INIT(MountPoint, loopback_list_head);
607 r = loopback_list_get(&loopback_list_head);
611 r = loopback_points_list_detach(&loopback_list_head, changed);
614 mount_points_list_free(&loopback_list_head);
619 int dm_detach_all(bool *changed) {
621 LIST_HEAD(MountPoint, dm_list_head);
623 LIST_HEAD_INIT(MountPoint, dm_list_head);
625 r = dm_list_get(&dm_list_head);
629 r = dm_points_list_detach(&dm_list_head, changed);
632 mount_points_list_free(&dm_list_head);