diff --git a/src/shutdown/detach-dm.c b/src/shutdown/detach-dm.c new file mode 100644 index 0000000000..71be74377d --- /dev/null +++ b/src/shutdown/detach-dm.c @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/*** + Copyright © 2010 ProFUSION embedded systems +***/ + +#include +#include + +#include "sd-device.h" + +#include "alloc-util.h" +#include "blockdev-util.h" +#include "detach-dm.h" +#include "device-util.h" +#include "devnum-util.h" +#include "errno-util.h" +#include "fd-util.h" +#include "sync-util.h" + +typedef struct DeviceMapper { + char *path; + dev_t devnum; + LIST_FIELDS(struct DeviceMapper, device_mapper); +} DeviceMapper; + +static void device_mapper_free(DeviceMapper **head, DeviceMapper *m) { + assert(head); + assert(m); + + LIST_REMOVE(device_mapper, *head, m); + + free(m->path); + free(m); +} + +static void device_mapper_list_free(DeviceMapper **head) { + assert(head); + + while (*head) + device_mapper_free(head, *head); +} + +static int dm_list_get(DeviceMapper **head) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + sd_device *d; + int r; + + assert(head); + + r = sd_device_enumerator_new(&e); + if (r < 0) + return r; + + r = sd_device_enumerator_allow_uninitialized(e); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_subsystem(e, "block", true); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_sysname(e, "dm-*"); + if (r < 0) + return r; + + FOREACH_DEVICE(e, d) { + _cleanup_free_ char *p = NULL; + const char *dn; + DeviceMapper *m; + dev_t devnum; + + if (sd_device_get_devnum(d, &devnum) < 0 || + sd_device_get_devname(d, &dn) < 0) + continue; + + p = strdup(dn); + if (!p) + return -ENOMEM; + + m = new(DeviceMapper, 1); + if (!m) + return -ENOMEM; + + *m = (DeviceMapper) { + .path = TAKE_PTR(p), + .devnum = devnum, + }; + + LIST_PREPEND(device_mapper, *head, m); + } + + return 0; +} + +static int delete_dm(DeviceMapper *m) { + _cleanup_close_ int fd = -EBADF; + int r; + + assert(m); + assert(major(m->devnum) != 0); + assert(m->path); + + fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC); + if (fd < 0) + return -errno; + + r = fsync_path_at(AT_FDCWD, m->path); + if (r < 0) + log_debug_errno(r, "Failed to sync DM block device %s, ignoring: %m", m->path); + + return RET_NERRNO(ioctl(fd, DM_DEV_REMOVE, &(struct dm_ioctl) { + .version = { + DM_VERSION_MAJOR, + DM_VERSION_MINOR, + DM_VERSION_PATCHLEVEL + }, + .data_size = sizeof(struct dm_ioctl), + .dev = m->devnum, + })); +} + +static int dm_points_list_detach(DeviceMapper **head, bool *changed, bool last_try) { + int n_failed = 0, r; + dev_t rootdev = 0, usrdev = 0; + + assert(head); + assert(changed); + + (void) get_block_device("/", &rootdev); + (void) get_block_device("/usr", &usrdev); + + LIST_FOREACH(device_mapper, m, *head) { + if ((major(rootdev) != 0 && rootdev == m->devnum) || + (major(usrdev) != 0 && usrdev == m->devnum)) { + log_debug("Not detaching DM %s that backs the OS itself, skipping.", m->path); + n_failed ++; + continue; + } + + log_info("Detaching DM %s (" DEVNUM_FORMAT_STR ").", m->path, DEVNUM_FORMAT_VAL(m->devnum)); + r = delete_dm(m); + if (r < 0) { + log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not detach DM %s: %m", m->path); + n_failed++; + continue; + } + + *changed = true; + device_mapper_free(head, m); + } + + return n_failed; +} + +int dm_detach_all(bool *changed, bool last_try) { + _cleanup_(device_mapper_list_free) LIST_HEAD(DeviceMapper, dm_list_head); + int r; + + assert(changed); + + LIST_HEAD_INIT(dm_list_head); + + r = dm_list_get(&dm_list_head); + if (r < 0) + return r; + + return dm_points_list_detach(&dm_list_head, changed, last_try); +} diff --git a/src/shutdown/detach-dm.h b/src/shutdown/detach-dm.h new file mode 100644 index 0000000000..b5f50a3bbf --- /dev/null +++ b/src/shutdown/detach-dm.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +/*** + Copyright © 2010 ProFUSION embedded systems +***/ + +#include + +int dm_detach_all(bool *changed, bool last_try); diff --git a/src/shutdown/detach-loopback.c b/src/shutdown/detach-loopback.c new file mode 100644 index 0000000000..f45960856b --- /dev/null +++ b/src/shutdown/detach-loopback.c @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/*** + Copyright © 2010 ProFUSION embedded systems +***/ + +#include +#include +#include + +#if HAVE_VALGRIND_MEMCHECK_H +#include +#endif + +#include "sd-device.h" + +#include "alloc-util.h" +#include "blockdev-util.h" +#include "detach-loopback.h" +#include "device-util.h" +#include "fd-util.h" + +typedef struct LoopbackDevice { + char *path; + dev_t devnum; + LIST_FIELDS(struct LoopbackDevice, loopback_device); +} LoopbackDevice; + +static void loopback_device_free(LoopbackDevice **head, LoopbackDevice *m) { + assert(head); + assert(m); + + LIST_REMOVE(loopback_device, *head, m); + + free(m->path); + free(m); +} + +static void loopback_device_list_free(LoopbackDevice **head) { + assert(head); + + while (*head) + loopback_device_free(head, *head); +} + +static int loopback_list_get(LoopbackDevice **head) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + sd_device *d; + int r; + + assert(head); + + r = sd_device_enumerator_new(&e); + if (r < 0) + return r; + + r = sd_device_enumerator_allow_uninitialized(e); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_subsystem(e, "block", true); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_sysname(e, "loop*"); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_sysattr(e, "loop/backing_file", NULL, true); + if (r < 0) + return r; + + FOREACH_DEVICE(e, d) { + _cleanup_free_ char *p = NULL; + const char *dn; + LoopbackDevice *lb; + dev_t devnum; + + if (sd_device_get_devnum(d, &devnum) < 0 || + sd_device_get_devname(d, &dn) < 0) + continue; + + p = strdup(dn); + if (!p) + return -ENOMEM; + + lb = new(LoopbackDevice, 1); + if (!lb) + return -ENOMEM; + + *lb = (LoopbackDevice) { + .path = TAKE_PTR(p), + .devnum = devnum, + }; + + LIST_PREPEND(loopback_device, *head, lb); + } + + return 0; +} + +static int delete_loopback(const char *device) { + _cleanup_close_ int fd = -EBADF; + struct loop_info64 info; + + assert(device); + + fd = open(device, O_RDONLY|O_CLOEXEC); + if (fd < 0) { + log_debug_errno(errno, "Failed to open loopback device %s: %m", device); + return errno == ENOENT ? 0 : -errno; + } + + /* Loopback block devices don't sync in-flight blocks when we clear the fd, hence sync explicitly + * first */ + if (fsync(fd) < 0) + log_debug_errno(errno, "Failed to sync loop block device %s, ignoring: %m", device); + + if (ioctl(fd, LOOP_CLR_FD, 0) < 0) { + if (errno == ENXIO) /* Nothing bound, didn't do anything */ + return 0; + + if (errno != EBUSY) + return log_debug_errno(errno, "Failed to clear loopback device %s: %m", device); + + if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) { + if (errno == ENXIO) /* What? Suddenly detached after all? That's fine by us then. */ + return 1; + + log_debug_errno(errno, "Failed to invoke LOOP_GET_STATUS64 on loopback device %s, ignoring: %m", device); + return -EBUSY; /* propagate original error */ + } + +#if HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); +#endif + + if (FLAGS_SET(info.lo_flags, LO_FLAGS_AUTOCLEAR)) /* someone else already set LO_FLAGS_AUTOCLEAR for us? fine by us */ + return -EBUSY; /* propagate original error */ + + info.lo_flags |= LO_FLAGS_AUTOCLEAR; + if (ioctl(fd, LOOP_SET_STATUS64, &info) < 0) { + if (errno == ENXIO) /* Suddenly detached after all? Fine by us */ + return 1; + + log_debug_errno(errno, "Failed to set LO_FLAGS_AUTOCLEAR flag for loop device %s, ignoring: %m", device); + } else + log_debug("Successfully set LO_FLAGS_AUTOCLEAR flag for loop device %s.", device); + + return -EBUSY; + } + + if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) { + /* If the LOOP_CLR_FD above succeeded we'll see ENXIO here. */ + if (errno == ENXIO) + log_debug("Successfully detached loopback device %s.", device); + else + log_debug_errno(errno, "Failed to invoke LOOP_GET_STATUS64 on loopback device %s, ignoring: %m", device); /* the LOOP_CLR_FD at least worked, let's hope for the best */ + + return 1; + } + +#if HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); +#endif + + /* Linux makes LOOP_CLR_FD succeed whenever LO_FLAGS_AUTOCLEAR is set without actually doing + * anything. Very confusing. Let's hence not claim we did anything in this case. */ + if (FLAGS_SET(info.lo_flags, LO_FLAGS_AUTOCLEAR)) + log_debug("Successfully called LOOP_CLR_FD on a loopback device %s with autoclear set, which is a NOP.", device); + else + log_debug("Weird, LOOP_CLR_FD succeeded but the device is still attached on %s.", device); + + return -EBUSY; /* Nothing changed, the device is still attached, hence it apparently is still busy */ +} + +static int loopback_points_list_detach(LoopbackDevice **head, bool *changed, bool last_try) { + int n_failed = 0, r; + dev_t rootdev = 0, usrdev = 0; + + assert(head); + assert(changed); + + (void) get_block_device_harder("/", &rootdev); + (void) block_get_whole_disk(rootdev, &rootdev); + + (void) get_block_device_harder("/usr", &usrdev); + (void) block_get_whole_disk(usrdev, &usrdev); + + LIST_FOREACH(loopback_device, m, *head) { + if ((major(rootdev) != 0 && rootdev == m->devnum) || + (major(usrdev) != 0 && usrdev == m->devnum)) { + log_debug("Not detaching loopback device %s that backs the OS itself, skipping.", m->path); + n_failed++; + continue; + } + + log_info("Detaching loopback %s.", m->path); + r = delete_loopback(m->path); + if (r < 0) { + log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not detach loopback %s: %m", m->path); + n_failed++; + continue; + } + if (r > 0) + *changed = true; + + loopback_device_free(head, m); + } + + return n_failed; +} + +int loopback_detach_all(bool *changed, bool last_try) { + _cleanup_(loopback_device_list_free) LIST_HEAD(LoopbackDevice, loopback_list_head); + int r; + + assert(changed); + + LIST_HEAD_INIT(loopback_list_head); + + r = loopback_list_get(&loopback_list_head); + if (r < 0) + return r; + + return loopback_points_list_detach(&loopback_list_head, changed, last_try); +} diff --git a/src/shutdown/detach-loopback.h b/src/shutdown/detach-loopback.h new file mode 100644 index 0000000000..d6d73f31af --- /dev/null +++ b/src/shutdown/detach-loopback.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +/*** + Copyright © 2010 ProFUSION embedded systems +***/ + +#include + +int loopback_detach_all(bool *changed, bool last_try); diff --git a/src/shutdown/detach-md.c b/src/shutdown/detach-md.c new file mode 100644 index 0000000000..ae592f6f31 --- /dev/null +++ b/src/shutdown/detach-md.c @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/*** + Copyright © 2010 ProFUSION embedded systems +***/ + +#include +#include +#include +#include + +#include "sd-device.h" + +#include "alloc-util.h" +#include "blockdev-util.h" +#include "detach-md.h" +#include "device-util.h" +#include "devnum-util.h" +#include "errno-util.h" +#include "fd-util.h" +#include "string-util.h" + +typedef struct RaidDevice { + char *path; + dev_t devnum; + LIST_FIELDS(struct RaidDevice, raid_device); +} RaidDevice; + +static void raid_device_free(RaidDevice **head, RaidDevice *m) { + assert(head); + assert(m); + + LIST_REMOVE(raid_device, *head, m); + + free(m->path); + free(m); +} + +static void raid_device_list_free(RaidDevice **head) { + assert(head); + + while (*head) + raid_device_free(head, *head); +} + +static int md_list_get(RaidDevice **head) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + sd_device *d; + int r; + + assert(head); + + r = sd_device_enumerator_new(&e); + if (r < 0) + return r; + + r = sd_device_enumerator_allow_uninitialized(e); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_subsystem(e, "block", true); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_sysname(e, "md*"); + if (r < 0) + return r; + + /* Filter out partitions. */ + r = sd_device_enumerator_add_match_property(e, "DEVTYPE", "disk"); + if (r < 0) + return r; + + FOREACH_DEVICE(e, d) { + _cleanup_free_ char *p = NULL; + const char *dn, *md_level; + RaidDevice *m; + dev_t devnum; + + if (sd_device_get_devnum(d, &devnum) < 0 || + sd_device_get_devname(d, &dn) < 0) + continue; + + r = sd_device_get_property_value(d, "MD_LEVEL", &md_level); + if (r < 0) { + log_warning_errno(r, "Failed to get MD_LEVEL property for %s, ignoring: %m", dn); + continue; + } + + /* MD "containers" are a special type of MD devices, used for external metadata. Since it + * doesn't provide RAID functionality in itself we don't need to stop it. */ + if (streq(md_level, "container")) + continue; + + p = strdup(dn); + if (!p) + return -ENOMEM; + + m = new(RaidDevice, 1); + if (!m) + return -ENOMEM; + + *m = (RaidDevice) { + .path = TAKE_PTR(p), + .devnum = devnum, + }; + + LIST_PREPEND(raid_device, *head, m); + } + + return 0; +} + +static int delete_md(RaidDevice *m) { + _cleanup_close_ int fd = -EBADF; + + assert(m); + assert(major(m->devnum) != 0); + assert(m->path); + + fd = open(m->path, O_RDONLY|O_CLOEXEC|O_EXCL); + if (fd < 0) + return -errno; + + if (fsync(fd) < 0) + log_debug_errno(errno, "Failed to sync MD block device %s, ignoring: %m", m->path); + + return RET_NERRNO(ioctl(fd, STOP_ARRAY, NULL)); +} + +static int md_points_list_detach(RaidDevice **head, bool *changed, bool last_try) { + int n_failed = 0, r; + dev_t rootdev = 0, usrdev = 0; + + assert(head); + assert(changed); + + (void) get_block_device("/", &rootdev); + (void) get_block_device("/usr", &usrdev); + + LIST_FOREACH(raid_device, m, *head) { + if ((major(rootdev) != 0 && rootdev == m->devnum) || + (major(usrdev) != 0 && usrdev == m->devnum)) { + log_debug("Not detaching MD %s that backs the OS itself, skipping.", m->path); + n_failed ++; + continue; + } + + log_info("Stopping MD %s (" DEVNUM_FORMAT_STR ").", m->path, DEVNUM_FORMAT_VAL(m->devnum)); + r = delete_md(m); + if (r < 0) { + log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not stop MD %s: %m", m->path); + n_failed++; + continue; + } + + *changed = true; + raid_device_free(head, m); + } + + return n_failed; +} + +int md_detach_all(bool *changed, bool last_try) { + _cleanup_(raid_device_list_free) LIST_HEAD(RaidDevice, md_list_head); + int r; + + assert(changed); + + LIST_HEAD_INIT(md_list_head); + + r = md_list_get(&md_list_head); + if (r < 0) + return r; + + return md_points_list_detach(&md_list_head, changed, last_try); +} diff --git a/src/shutdown/detach-md.h b/src/shutdown/detach-md.h new file mode 100644 index 0000000000..3784598208 --- /dev/null +++ b/src/shutdown/detach-md.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +/*** + Copyright © 2010 ProFUSION embedded systems +***/ + +#include + +int md_detach_all(bool *changed, bool last_try); diff --git a/src/shutdown/detach-swap.c b/src/shutdown/detach-swap.c new file mode 100644 index 0000000000..fd7dcdf943 --- /dev/null +++ b/src/shutdown/detach-swap.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/*** + Copyright © 2010 ProFUSION embedded systems +***/ + +#include + +#include "alloc-util.h" +#include "detach-swap.h" +#include "libmount-util.h" + +static void swap_device_free(SwapDevice **head, SwapDevice *m) { + assert(head); + assert(m); + + LIST_REMOVE(swap_device, *head, m); + + free(m->path); + free(m); +} + +void swap_devices_list_free(SwapDevice **head) { + assert(head); + + while (*head) + swap_device_free(head, *head); +} + +int swap_list_get(const char *swaps, SwapDevice **head) { + _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL; + int r; + + assert(head); + + t = mnt_new_table(); + i = mnt_new_iter(MNT_ITER_FORWARD); + if (!t || !i) + return log_oom(); + + r = mnt_table_parse_swaps(t, swaps); + if (r == -ENOENT) /* no /proc/swaps is fine */ + return 0; + if (r < 0) + return log_error_errno(r, "Failed to parse %s: %m", swaps ?: "/proc/swaps"); + + for (;;) { + struct libmnt_fs *fs; + _cleanup_free_ SwapDevice *swap = NULL; + const char *source; + + r = mnt_table_next_fs(t, i, &fs); + if (r == 1) /* EOF */ + break; + if (r < 0) + return log_error_errno(r, "Failed to get next entry from %s: %m", swaps ?: "/proc/swaps"); + + source = mnt_fs_get_source(fs); + if (!source) + continue; + + swap = new0(SwapDevice, 1); + if (!swap) + return log_oom(); + + swap->path = strdup(source); + if (!swap->path) + return log_oom(); + + LIST_PREPEND(swap_device, *head, TAKE_PTR(swap)); + } + + return 0; +} + +static int swap_points_list_off(SwapDevice **head, bool *changed) { + int n_failed = 0; + + assert(head); + assert(changed); + + LIST_FOREACH(swap_device, m, *head) { + log_info("Deactivating swap %s.", m->path); + if (swapoff(m->path) < 0) { + log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path); + n_failed++; + continue; + } + + *changed = true; + swap_device_free(head, m); + } + + return n_failed; +} + +int swapoff_all(bool *changed) { + _cleanup_(swap_devices_list_free) LIST_HEAD(SwapDevice, swap_list_head); + int r; + + assert(changed); + + LIST_HEAD_INIT(swap_list_head); + + r = swap_list_get(NULL, &swap_list_head); + if (r < 0) + return r; + + return swap_points_list_off(&swap_list_head, changed); +} diff --git a/src/shutdown/detach-swap.h b/src/shutdown/detach-swap.h new file mode 100644 index 0000000000..1ebf5eb908 --- /dev/null +++ b/src/shutdown/detach-swap.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +/*** + Copyright © 2010 ProFUSION embedded systems +***/ + +#include + +#include "list.h" + +int swapoff_all(bool *changed); + +/* This is exported just for testing */ +typedef struct SwapDevice { + char *path; + LIST_FIELDS(struct SwapDevice, swap_device); +} SwapDevice; + +int swap_list_get(const char *swaps, SwapDevice **head); +void swap_devices_list_free(SwapDevice **head); diff --git a/src/shutdown/meson.build b/src/shutdown/meson.build index 1e28528b97..6538882692 100644 --- a/src/shutdown/meson.build +++ b/src/shutdown/meson.build @@ -1,6 +1,10 @@ # SPDX-License-Identifier: LGPL-2.1-or-later systemd_shutdown_sources = files( + 'detach-dm.c', + 'detach-loopback.c', + 'detach-md.c', + 'detach-swap.c', 'shutdown.c', 'umount.c', ) @@ -8,6 +12,7 @@ systemd_shutdown_sources = files( tests += [ { 'sources' : files( + 'detach-swap.c', 'test-umount.c', 'umount.c', ), diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index cf0351cf78..08cc114d3a 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -21,8 +21,12 @@ #include "binfmt-util.h" #include "cgroup-setup.h" #include "cgroup-util.h" -#include "coredump-util.h" #include "constants.h" +#include "coredump-util.h" +#include "detach-dm.h" +#include "detach-loopback.h" +#include "detach-md.h" +#include "detach-swap.h" #include "errno-util.h" #include "exec-util.h" #include "fd-util.h" @@ -448,7 +452,7 @@ int main(int argc, char *argv[]) { } else if (r > 0) log_info("Not all file systems unmounted, %d left.", r); else - log_error_errno(r, "Failed to unmount file systems: %m"); + log_error_errno(r, "Unable to unmount file systems: %m"); } if (need_swapoff) { @@ -460,7 +464,7 @@ int main(int argc, char *argv[]) { } else if (r > 0) log_info("Not all swaps deactivated, %d left.", r); else - log_error_errno(r, "Failed to deactivate swaps: %m"); + log_error_errno(r, "Unable to deactivate swaps: %m"); } if (need_loop_detach) { @@ -472,7 +476,7 @@ int main(int argc, char *argv[]) { } else if (r > 0) log_info("Not all loop devices detached, %d left.", r); else - log_error_errno(r, "Failed to detach loop devices: %m"); + log_error_errno(r, "Unable to detach loop devices: %m"); } if (need_md_detach) { @@ -484,7 +488,7 @@ int main(int argc, char *argv[]) { } else if (r > 0) log_info("Not all MD devices stopped, %d left.", r); else - log_error_errno(r, "Failed to stop MD devices: %m"); + log_error_errno(r, "Unable to stop MD devices: %m"); } if (need_dm_detach) { @@ -496,7 +500,7 @@ int main(int argc, char *argv[]) { } else if (r > 0) log_info("Not all DM devices detached, %d left.", r); else - log_error_errno(r, "Failed to detach DM devices: %m"); + log_error_errno(r, "Unable to detach DM devices: %m"); } if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach @@ -563,7 +567,7 @@ int main(int argc, char *argv[]) { } if (need_umount || need_swapoff || need_loop_detach || need_dm_detach || need_md_detach) - log_error("Failed to finalize%s%s%s%s%s ignoring.", + log_error("Unable to finalize remaining%s%s%s%s%s ignoring.", need_umount ? " file systems," : "", need_swapoff ? " swap devices," : "", need_loop_detach ? " loop devices," : "", diff --git a/src/shutdown/test-umount.c b/src/shutdown/test-umount.c index 80ec9b6dd6..93da2e0fc3 100644 --- a/src/shutdown/test-umount.c +++ b/src/shutdown/test-umount.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" +#include "detach-swap.h" #include "errno-util.h" #include "log.h" #include "path-util.h" @@ -23,12 +24,11 @@ static void test_mount_points_list_one(const char *fname) { assert_se(mount_points_list_get(fname, &mp_list_head) >= 0); LIST_FOREACH(mount_point, m, mp_list_head) - log_debug("path=%s o=%s f=0x%lx try-ro=%s dev=%u:%u", + log_debug("path=%s o=%s f=0x%lx try-ro=%s", m->path, strempty(m->remount_options), m->remount_flags, - yes_no(m->try_remount_ro), - major(m->devnum), minor(m->devnum)); + yes_no(m->try_remount_ro)); } TEST(mount_points_list) { @@ -39,7 +39,7 @@ TEST(mount_points_list) { } static void test_swap_list_one(const char *fname) { - _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head); + _cleanup_(swap_devices_list_free) LIST_HEAD(SwapDevice, sd_list_head); _cleanup_free_ char *testdata_fname = NULL; int r; @@ -50,19 +50,14 @@ static void test_swap_list_one(const char *fname) { fname = testdata_fname; } - LIST_HEAD_INIT(mp_list_head); - r = swap_list_get(fname, &mp_list_head); + LIST_HEAD_INIT(sd_list_head); + r = swap_list_get(fname, &sd_list_head); if (ERRNO_IS_PRIVILEGE(r)) return; assert_se(r >= 0); - LIST_FOREACH(mount_point, m, mp_list_head) - log_debug("path=%s o=%s f=0x%lx try-ro=%s dev=%u:%u", - m->path, - strempty(m->remount_options), - m->remount_flags, - yes_no(m->try_remount_ro), - major(m->devnum), minor(m->devnum)); + LIST_FOREACH(swap_device, m, sd_list_head) + log_debug("path=%s", m->path); } TEST(swap_list) { diff --git a/src/shutdown/umount.c b/src/shutdown/umount.c index 221d29ba6c..6cbe2befa2 100644 --- a/src/shutdown/umount.c +++ b/src/shutdown/umount.c @@ -5,30 +5,14 @@ #include #include -#include -#include -#include -#include #include -#include #include #include #include -#if HAVE_VALGRIND_MEMCHECK_H -#include -#endif - -#include "sd-device.h" - #include "alloc-util.h" -#include "blockdev-util.h" #include "chase.h" -#include "constants.h" -#include "device-util.h" -#include "devnum-util.h" #include "dirent-util.h" -#include "escape.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" @@ -39,14 +23,9 @@ #include "mount-util.h" #include "mountpoint-util.h" #include "parse-util.h" -#include "path-util.h" #include "process-util.h" #include "random-util.h" #include "signal-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "sync-util.h" #include "umount.h" #include "virt.h" @@ -188,348 +167,6 @@ int mount_points_list_get(const char *mountinfo, MountPoint **head) { return 0; } -int swap_list_get(const char *swaps, MountPoint **head) { - _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL; - _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL; - int r; - - assert(head); - - t = mnt_new_table(); - i = mnt_new_iter(MNT_ITER_FORWARD); - if (!t || !i) - return log_oom(); - - r = mnt_table_parse_swaps(t, swaps); - if (r == -ENOENT) /* no /proc/swaps is fine */ - return 0; - if (r < 0) - return log_error_errno(r, "Failed to parse %s: %m", swaps ?: "/proc/swaps"); - - for (;;) { - struct libmnt_fs *fs; - _cleanup_free_ MountPoint *swap = NULL; - const char *source; - - r = mnt_table_next_fs(t, i, &fs); - if (r == 1) /* EOF */ - break; - if (r < 0) - return log_error_errno(r, "Failed to get next entry from %s: %m", swaps ?: "/proc/swaps"); - - source = mnt_fs_get_source(fs); - if (!source) - continue; - - swap = new0(MountPoint, 1); - if (!swap) - return log_oom(); - - swap->path = strdup(source); - if (!swap->path) - return log_oom(); - - LIST_PREPEND(mount_point, *head, TAKE_PTR(swap)); - } - - return 0; -} - -static int loopback_list_get(MountPoint **head) { - _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; - sd_device *d; - int r; - - assert(head); - - r = sd_device_enumerator_new(&e); - if (r < 0) - return r; - - r = sd_device_enumerator_allow_uninitialized(e); - if (r < 0) - return r; - - r = sd_device_enumerator_add_match_subsystem(e, "block", true); - if (r < 0) - return r; - - r = sd_device_enumerator_add_match_sysname(e, "loop*"); - if (r < 0) - return r; - - r = sd_device_enumerator_add_match_sysattr(e, "loop/backing_file", NULL, true); - if (r < 0) - return r; - - FOREACH_DEVICE(e, d) { - _cleanup_free_ char *p = NULL; - const char *dn; - MountPoint *lb; - dev_t devnum; - - if (sd_device_get_devnum(d, &devnum) < 0 || - sd_device_get_devname(d, &dn) < 0) - continue; - - p = strdup(dn); - if (!p) - return -ENOMEM; - - lb = new(MountPoint, 1); - if (!lb) - return -ENOMEM; - - *lb = (MountPoint) { - .path = TAKE_PTR(p), - .devnum = devnum, - }; - - LIST_PREPEND(mount_point, *head, lb); - } - - return 0; -} - -static int dm_list_get(MountPoint **head) { - _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; - sd_device *d; - int r; - - assert(head); - - r = sd_device_enumerator_new(&e); - if (r < 0) - return r; - - r = sd_device_enumerator_allow_uninitialized(e); - if (r < 0) - return r; - - r = sd_device_enumerator_add_match_subsystem(e, "block", true); - if (r < 0) - return r; - - r = sd_device_enumerator_add_match_sysname(e, "dm-*"); - if (r < 0) - return r; - - FOREACH_DEVICE(e, d) { - _cleanup_free_ char *p = NULL; - const char *dn; - MountPoint *m; - dev_t devnum; - - if (sd_device_get_devnum(d, &devnum) < 0 || - sd_device_get_devname(d, &dn) < 0) - continue; - - p = strdup(dn); - if (!p) - return -ENOMEM; - - m = new(MountPoint, 1); - if (!m) - return -ENOMEM; - - *m = (MountPoint) { - .path = TAKE_PTR(p), - .devnum = devnum, - }; - - LIST_PREPEND(mount_point, *head, m); - } - - return 0; -} - -static int md_list_get(MountPoint **head) { - _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; - sd_device *d; - int r; - - assert(head); - - r = sd_device_enumerator_new(&e); - if (r < 0) - return r; - - r = sd_device_enumerator_allow_uninitialized(e); - if (r < 0) - return r; - - r = sd_device_enumerator_add_match_subsystem(e, "block", true); - if (r < 0) - return r; - - r = sd_device_enumerator_add_match_sysname(e, "md*"); - if (r < 0) - return r; - - /* Filter out partitions. */ - r = sd_device_enumerator_add_match_property(e, "DEVTYPE", "disk"); - if (r < 0) - return r; - - FOREACH_DEVICE(e, d) { - _cleanup_free_ char *p = NULL; - const char *dn, *md_level; - MountPoint *m; - dev_t devnum; - - if (sd_device_get_devnum(d, &devnum) < 0 || - sd_device_get_devname(d, &dn) < 0) - continue; - - r = sd_device_get_property_value(d, "MD_LEVEL", &md_level); - if (r < 0) { - log_warning_errno(r, "Failed to get MD_LEVEL property for %s, ignoring: %m", dn); - continue; - } - - /* MD "containers" are a special type of MD devices, used for external metadata. Since it - * doesn't provide RAID functionality in itself we don't need to stop it. */ - if (streq(md_level, "container")) - continue; - - p = strdup(dn); - if (!p) - return -ENOMEM; - - m = new(MountPoint, 1); - if (!m) - return -ENOMEM; - - *m = (MountPoint) { - .path = TAKE_PTR(p), - .devnum = devnum, - }; - - LIST_PREPEND(mount_point, *head, m); - } - - return 0; -} - -static int delete_loopback(const char *device) { - _cleanup_close_ int fd = -EBADF; - struct loop_info64 info; - - assert(device); - - fd = open(device, O_RDONLY|O_CLOEXEC); - if (fd < 0) { - log_debug_errno(errno, "Failed to open loopback device %s: %m", device); - return errno == ENOENT ? 0 : -errno; - } - - /* Loopback block devices don't sync in-flight blocks when we clear the fd, hence sync explicitly - * first */ - if (fsync(fd) < 0) - log_debug_errno(errno, "Failed to sync loop block device %s, ignoring: %m", device); - - if (ioctl(fd, LOOP_CLR_FD, 0) < 0) { - if (errno == ENXIO) /* Nothing bound, didn't do anything */ - return 0; - - if (errno != EBUSY) - return log_debug_errno(errno, "Failed to clear loopback device %s: %m", device); - - if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) { - if (errno == ENXIO) /* What? Suddenly detached after all? That's fine by us then. */ - return 1; - - log_debug_errno(errno, "Failed to invoke LOOP_GET_STATUS64 on loopback device %s, ignoring: %m", device); - return -EBUSY; /* propagate original error */ - } - -#if HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); -#endif - - if (FLAGS_SET(info.lo_flags, LO_FLAGS_AUTOCLEAR)) /* someone else already set LO_FLAGS_AUTOCLEAR for us? fine by us */ - return -EBUSY; /* propagate original error */ - - info.lo_flags |= LO_FLAGS_AUTOCLEAR; - if (ioctl(fd, LOOP_SET_STATUS64, &info) < 0) { - if (errno == ENXIO) /* Suddenly detached after all? Fine by us */ - return 1; - - log_debug_errno(errno, "Failed to set LO_FLAGS_AUTOCLEAR flag for loop device %s, ignoring: %m", device); - } else - log_debug("Successfully set LO_FLAGS_AUTOCLEAR flag for loop device %s.", device); - - return -EBUSY; - } - - if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) { - /* If the LOOP_CLR_FD above succeeded we'll see ENXIO here. */ - if (errno == ENXIO) - log_debug("Successfully detached loopback device %s.", device); - else - log_debug_errno(errno, "Failed to invoke LOOP_GET_STATUS64 on loopback device %s, ignoring: %m", device); /* the LOOP_CLR_FD at least worked, let's hope for the best */ - - return 1; - } - -#if HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); -#endif - - /* Linux makes LOOP_CLR_FD succeed whenever LO_FLAGS_AUTOCLEAR is set without actually doing - * anything. Very confusing. Let's hence not claim we did anything in this case. */ - if (FLAGS_SET(info.lo_flags, LO_FLAGS_AUTOCLEAR)) - log_debug("Successfully called LOOP_CLR_FD on a loopback device %s with autoclear set, which is a NOP.", device); - else - log_debug("Weird, LOOP_CLR_FD succeeded but the device is still attached on %s.", device); - - return -EBUSY; /* Nothing changed, the device is still attached, hence it apparently is still busy */ -} - -static int delete_dm(MountPoint *m) { - _cleanup_close_ int fd = -EBADF; - int r; - - assert(m); - assert(major(m->devnum) != 0); - assert(m->path); - - fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC); - if (fd < 0) - return -errno; - - r = fsync_path_at(AT_FDCWD, m->path); - if (r < 0) - log_debug_errno(r, "Failed to sync DM block device %s, ignoring: %m", m->path); - - return RET_NERRNO(ioctl(fd, DM_DEV_REMOVE, &(struct dm_ioctl) { - .version = { - DM_VERSION_MAJOR, - DM_VERSION_MINOR, - DM_VERSION_PATCHLEVEL - }, - .data_size = sizeof(struct dm_ioctl), - .dev = m->devnum, - })); -} - -static int delete_md(MountPoint *m) { - _cleanup_close_ int fd = -EBADF; - - assert(m); - assert(major(m->devnum) != 0); - assert(m->path); - - fd = open(m->path, O_RDONLY|O_CLOEXEC|O_EXCL); - if (fd < 0) - return -errno; - - if (fsync(fd) < 0) - log_debug_errno(errno, "Failed to sync MD block device %s, ignoring: %m", m->path); - - return RET_NERRNO(ioctl(fd, STOP_ARRAY, NULL)); -} - static bool nonunmountable_path(const char *path) { return path_equal(path, "/") #if ! HAVE_SPLIT_USR @@ -824,118 +461,6 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool last_ return n_failed; } -static int swap_points_list_off(MountPoint **head, bool *changed) { - int n_failed = 0; - - assert(head); - assert(changed); - - LIST_FOREACH(mount_point, m, *head) { - log_info("Deactivating swap %s.", m->path); - if (swapoff(m->path) < 0) { - log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path); - n_failed++; - continue; - } - - *changed = true; - mount_point_free(head, m); - } - - return n_failed; -} - -static int loopback_points_list_detach(MountPoint **head, bool *changed, bool last_try) { - int n_failed = 0, r; - dev_t rootdev = 0; - - assert(head); - assert(changed); - - (void) get_block_device("/", &rootdev); - - LIST_FOREACH(mount_point, m, *head) { - if (major(rootdev) != 0 && rootdev == m->devnum) { - n_failed++; - continue; - } - - log_info("Detaching loopback %s.", m->path); - r = delete_loopback(m->path); - if (r < 0) { - log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not detach loopback %s: %m", m->path); - n_failed++; - continue; - } - if (r > 0) - *changed = true; - - mount_point_free(head, m); - } - - return n_failed; -} - -static int dm_points_list_detach(MountPoint **head, bool *changed, bool last_try) { - int n_failed = 0, r; - dev_t rootdev = 0; - - assert(head); - assert(changed); - - (void) get_block_device("/", &rootdev); - - LIST_FOREACH(mount_point, m, *head) { - if (major(rootdev) != 0 && rootdev == m->devnum) { - n_failed ++; - continue; - } - - log_info("Detaching DM %s (" DEVNUM_FORMAT_STR ").", m->path, DEVNUM_FORMAT_VAL(m->devnum)); - r = delete_dm(m); - if (r < 0) { - log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not detach DM %s: %m", m->path); - n_failed++; - continue; - } - - *changed = true; - mount_point_free(head, m); - } - - return n_failed; -} - -static int md_points_list_detach(MountPoint **head, bool *changed, bool last_try) { - int n_failed = 0, r; - dev_t rootdev = 0; - - assert(head); - assert(changed); - - (void) get_block_device("/", &rootdev); - - LIST_FOREACH(mount_point, m, *head) { - if (major(rootdev) != 0 && rootdev == m->devnum) { - n_failed ++; - continue; - } - - log_info("Stopping MD %s (" DEVNUM_FORMAT_STR ").", m->path, DEVNUM_FORMAT_VAL(m->devnum)); - r = delete_md(m); - if (r < 0) { - log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not stop MD %s: %m", m->path); - n_failed++; - continue; - } - - *changed = true; - mount_point_free(head, m); - } - - return n_failed; -} - static int umount_all_once(bool *changed, bool last_try) { _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head); int r; @@ -968,63 +493,3 @@ int umount_all(bool *changed, bool last_try) { return r; } - -int swapoff_all(bool *changed) { - _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, swap_list_head); - int r; - - assert(changed); - - LIST_HEAD_INIT(swap_list_head); - - r = swap_list_get(NULL, &swap_list_head); - if (r < 0) - return r; - - return swap_points_list_off(&swap_list_head, changed); -} - -int loopback_detach_all(bool *changed, bool last_try) { - _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, loopback_list_head); - int r; - - assert(changed); - - LIST_HEAD_INIT(loopback_list_head); - - r = loopback_list_get(&loopback_list_head); - if (r < 0) - return r; - - return loopback_points_list_detach(&loopback_list_head, changed, last_try); -} - -int dm_detach_all(bool *changed, bool last_try) { - _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, dm_list_head); - int r; - - assert(changed); - - LIST_HEAD_INIT(dm_list_head); - - r = dm_list_get(&dm_list_head); - if (r < 0) - return r; - - return dm_points_list_detach(&dm_list_head, changed, last_try); -} - -int md_detach_all(bool *changed, bool last_try) { - _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, md_list_head); - int r; - - assert(changed); - - LIST_HEAD_INIT(md_list_head); - - r = md_list_get(&md_list_head); - if (r < 0) - return r; - - return md_points_list_detach(&md_list_head, changed, last_try); -} diff --git a/src/shutdown/umount.h b/src/shutdown/umount.h index a742ac0af5..f8f9ae8038 100644 --- a/src/shutdown/umount.h +++ b/src/shutdown/umount.h @@ -5,13 +5,11 @@ Copyright © 2010 ProFUSION embedded systems ***/ +#include + #include "list.h" int umount_all(bool *changed, bool last_try); -int swapoff_all(bool *changed); -int loopback_detach_all(bool *changed, bool last_try); -int dm_detach_all(bool *changed, bool last_try); -int md_detach_all(bool *changed, bool last_try); /* This is exported just for testing */ typedef struct MountPoint { @@ -21,10 +19,8 @@ typedef struct MountPoint { bool try_remount_ro; bool umount_lazily; bool leaf; - dev_t devnum; LIST_FIELDS(struct MountPoint, mount_point); } MountPoint; int mount_points_list_get(const char *mountinfo, MountPoint **head); void mount_points_list_free(MountPoint **head); -int swap_list_get(const char *swaps, MountPoint **head);