int umount_verbose(
int error_log_level,
- const char *what,
+ const char *where,
int flags) {
- assert(what);
+ assert(where);
+
+ log_debug("Unmounting '%s'...", where);
+
+ 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) {
- log_debug("Umounting %s...", what);
+ /* 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(what, flags) < 0)
- return log_full_errno(error_log_level, errno,
- "Failed to unmount %s: %m", what);
+ 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;
}
}
}
+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);