mount-util: introduce umountat_detach_verbose()

This new helper allows unmounting paths by fd.
This commit is contained in:
Lennart Poettering
2025-01-20 11:31:55 +01:00
parent 2cf9635511
commit b979368075
3 changed files with 77 additions and 6 deletions
+52 -6
View File
@@ -736,16 +736,62 @@ int mount_verbose_full(
int umount_verbose(
int error_log_level,
const char *what,
const char *where,
int flags) {
assert(what);
assert(where);
log_debug("Umounting %s...", what);
log_debug("Unmounting '%s'...", where);
if (umount2(what, flags) < 0)
return log_full_errno(error_log_level, errno,
"Failed to unmount %s: %m", what);
if (umount2(where, flags) < 0)
return log_full_errno(error_log_level, errno, "Failed to unmount '%s': %m", where);
return 0;
}
int umountat_detach_verbose(
int error_log_level,
int fd,
const char *where) {
/* Similar to umountat_verbose(), but goes by fd + path. This implies MNT_DETACH, since to do this we
* must pin the inode in question via an fd. */
assert(fd >= 0 || fd == AT_FDCWD);
/* If neither fd nor path are specified take this as reference to the cwd */
if (fd == AT_FDCWD && isempty(where))
return umount_verbose(error_log_level, ".", MNT_DETACH|UMOUNT_NOFOLLOW);
/* If we don't actually take the fd into consideration for this operation shortcut things, so that we
* don't have to open the inode */
if (fd == AT_FDCWD || path_is_absolute(where))
return umount_verbose(error_log_level, where, MNT_DETACH|UMOUNT_NOFOLLOW);
_cleanup_free_ char *prefix = NULL;
const char *p;
if (fd_get_path(fd, &prefix) < 0)
p = "<fd>"; /* if we can't get the path, return something vaguely useful */
else
p = prefix;
_cleanup_free_ char *joined = isempty(where) ? strdup(p) : path_join(p, where);
log_debug("Unmounting '%s'...", strna(joined));
_cleanup_close_ int inode_fd = -EBADF;
int mnt_fd;
if (isempty(where))
mnt_fd = fd;
else {
inode_fd = openat(fd, where, O_PATH|O_CLOEXEC|O_NOFOLLOW);
if (inode_fd < 0)
return log_full_errno(error_log_level, errno, "Failed to pin '%s': %m", strna(joined));
mnt_fd = inode_fd;
}
if (umount2(FORMAT_PROC_FD_PATH(mnt_fd), MNT_DETACH) < 0)
return log_full_errno(error_log_level, errno, "Failed to unmount '%s': %m", strna(joined));
return 0;
}
+5
View File
@@ -79,6 +79,11 @@ int umount_verbose(
const char *where,
int flags);
int umountat_detach_verbose(
int error_log_level,
int fd,
const char *where);
int mount_option_mangle(
const char *options,
unsigned long mount_flags,
+20
View File
@@ -587,4 +587,24 @@ TEST(path_is_network_fs_harder) {
}
}
TEST(umountat) {
int r;
_cleanup_(rm_rf_physical_and_freep) char *p = NULL;
_cleanup_close_ int dfd = mkdtemp_open(NULL, O_CLOEXEC, &p);
ASSERT_OK(dfd);
ASSERT_OK(mkdirat(dfd, "foo", 0777));
_cleanup_free_ char *q = ASSERT_PTR(path_join(p, "foo"));
r = mount_nofollow_verbose(LOG_ERR, "tmpfs", q, "tmpfs", 0, NULL);
if (ERRNO_IS_NEG_PRIVILEGE(r))
return (void) log_tests_skipped("not running privileged");
ASSERT_OK(r);
ASSERT_OK(umountat_detach_verbose(LOG_ERR, dfd, "foo"));
ASSERT_ERROR(umountat_detach_verbose(LOG_ERR, dfd, "foo"), EINVAL);
}
DEFINE_TEST_MAIN(LOG_DEBUG);