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 if (mount_point_is_api(p) || mount_point_ignore(p)) {
122 if (!(m = new0(MountPoint, 1))) {
129 m->skip_ro = skip_ro;
130 LIST_PREPEND(MountPoint, mount_point, *head, m);
136 fclose(proc_self_mountinfo);
141 static int swap_list_get(MountPoint **head) {
148 if (!(proc_swaps = fopen("/proc/swaps", "re")))
149 return (errno == ENOENT) ? 0 : -errno;
151 (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
155 char *dev = NULL, *d;
158 if ((k = fscanf(proc_swaps,
159 "%ms " /* device/file */
160 "%*s " /* type of swap */
161 "%*s " /* swap size */
163 "%*s\n", /* priority */
169 log_warning("Failed to parse /proc/swaps:%u.", i);
175 if (endswith(dev, "(deleted)")) {
188 if (!(swap = new0(MountPoint, 1))) {
195 LIST_PREPEND(MountPoint, mount_point, *head, swap);
206 static int loopback_list_get(MountPoint **head) {
209 struct udev_enumerate *e = NULL;
210 struct udev_list_entry *item = NULL, *first = NULL;
214 if (!(udev = udev_new())) {
219 if (!(e = udev_enumerate_new(udev))) {
224 if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
225 udev_enumerate_add_match_sysname(e, "loop*") < 0) {
230 if (udev_enumerate_scan_devices(e) < 0) {
235 first = udev_enumerate_get_list_entry(e);
236 udev_list_entry_foreach(item, first) {
238 struct udev_device *d;
242 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
247 if (!(dn = udev_device_get_devnode(d))) {
248 udev_device_unref(d);
253 udev_device_unref(d);
260 if (!(lb = new0(MountPoint, 1))) {
267 LIST_PREPEND(MountPoint, mount_point, *head, lb);
274 udev_enumerate_unref(e);
282 static int dm_list_get(MountPoint **head) {
285 struct udev_enumerate *e = NULL;
286 struct udev_list_entry *item = NULL, *first = NULL;
290 if (!(udev = udev_new())) {
295 if (!(e = udev_enumerate_new(udev))) {
300 if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
301 udev_enumerate_add_match_sysname(e, "dm-*") < 0) {
306 if (udev_enumerate_scan_devices(e) < 0) {
311 first = udev_enumerate_get_list_entry(e);
313 udev_list_entry_foreach(item, first) {
315 struct udev_device *d;
320 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
325 devnum = udev_device_get_devnum(d);
326 dn = udev_device_get_devnode(d);
328 if (major(devnum) == 0 || !dn) {
329 udev_device_unref(d);
334 udev_device_unref(d);
341 if (!(m = new(MountPoint, 1))) {
349 LIST_PREPEND(MountPoint, mount_point, *head, m);
356 udev_enumerate_unref(e);
364 static int delete_loopback(const char *device) {
367 if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0)
368 return errno == ENOENT ? 0 : -errno;
370 r = ioctl(fd, LOOP_CLR_FD, 0);
371 close_nointr_nofail(fd);
376 /* ENXIO: not bound, so no error */
383 static int delete_dm(dev_t devnum) {
387 assert(major(devnum) != 0);
389 if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0)
393 dm.version[0] = DM_VERSION_MAJOR;
394 dm.version[1] = DM_VERSION_MINOR;
395 dm.version[2] = DM_VERSION_PATCHLEVEL;
397 dm.data_size = sizeof(dm);
400 r = ioctl(fd, DM_DEV_REMOVE, &dm);
401 close_nointr_nofail(fd);
403 return r >= 0 ? 0 : -errno;
406 static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
412 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
413 if (path_equal(m->path, "/")
414 #ifndef HAVE_SPLIT_USR
415 || path_equal(m->path, "/usr")
422 /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
423 if (umount2(m->path, MNT_FORCE) == 0) {
424 log_info("Unmounted %s.", m->path);
428 mount_point_free(head, m);
429 } else if (log_error) {
430 log_warning("Could not unmount %s: %m", m->path);
438 static int mount_points_list_remount_read_only(MountPoint **head, bool *changed) {
444 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
451 /* Trying to remount read-only */
452 if (mount(NULL, m->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
456 mount_point_free(head, m);
458 log_warning("Could not remount as read-only %s: %m", m->path);
466 static int swap_points_list_off(MountPoint **head, bool *changed) {
472 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
473 if (swapoff(m->path) == 0) {
477 mount_point_free(head, m);
479 log_warning("Could not deactivate swap %s: %m", m->path);
487 static int loopback_points_list_detach(MountPoint **head, bool *changed) {
494 k = lstat("/", &root_st);
496 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
498 struct stat loopback_st;
501 major(root_st.st_dev) != 0 &&
502 lstat(m->path, &loopback_st) >= 0 &&
503 root_st.st_dev == loopback_st.st_rdev) {
508 if ((r = delete_loopback(m->path)) >= 0) {
510 if (r > 0 && changed)
513 mount_point_free(head, m);
515 log_warning("Could not delete loopback %s: %m", m->path);
523 static int dm_points_list_detach(MountPoint **head, bool *changed) {
530 k = lstat("/", &root_st);
532 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
536 major(root_st.st_dev) != 0 &&
537 root_st.st_dev == m->devnum) {
542 if ((r = delete_dm(m->devnum)) >= 0) {
544 if (r > 0 && changed)
547 mount_point_free(head, m);
549 log_warning("Could not delete dm %s: %m", m->path);
557 int umount_all(bool *changed) {
561 LIST_HEAD(MountPoint, mp_list_head);
563 LIST_HEAD_INIT(MountPoint, mp_list_head);
565 r = mount_points_list_get(&mp_list_head);
569 /* retry umount, until nothing can be umounted anymore */
571 umount_changed = false;
573 mount_points_list_umount(&mp_list_head, &umount_changed, false);
577 } while (umount_changed);
579 /* umount one more time with logging enabled */
580 r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
584 r = mount_points_list_remount_read_only(&mp_list_head, changed);
587 mount_points_list_free(&mp_list_head);
592 int swapoff_all(bool *changed) {
594 LIST_HEAD(MountPoint, swap_list_head);
596 LIST_HEAD_INIT(MountPoint, swap_list_head);
598 r = swap_list_get(&swap_list_head);
602 r = swap_points_list_off(&swap_list_head, changed);
605 mount_points_list_free(&swap_list_head);
610 int loopback_detach_all(bool *changed) {
612 LIST_HEAD(MountPoint, loopback_list_head);
614 LIST_HEAD_INIT(MountPoint, loopback_list_head);
616 r = loopback_list_get(&loopback_list_head);
620 r = loopback_points_list_detach(&loopback_list_head, changed);
623 mount_points_list_free(&loopback_list_head);
628 int dm_detach_all(bool *changed) {
630 LIST_HEAD(MountPoint, dm_list_head);
632 LIST_HEAD_INIT(MountPoint, dm_list_head);
634 r = dm_list_get(&dm_list_head);
638 r = dm_points_list_detach(&dm_list_head, changed);
641 mount_points_list_free(&dm_list_head);