mountpoint-util: several tweaks for fd_is_mount_point(), drop support for kernels without /proc/pid/fdinfo/ (#36093)
This commit is contained in:
@@ -1113,8 +1113,6 @@ int fds_are_same_mount(int fd1, int fd2) {
|
||||
int mntid;
|
||||
|
||||
r = path_get_mnt_id_at_fallback(fd1, "", &mntid);
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
return true; /* skip the mount ID check */
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(mntid >= 0);
|
||||
@@ -1127,8 +1125,6 @@ int fds_are_same_mount(int fd1, int fd2) {
|
||||
int mntid;
|
||||
|
||||
r = path_get_mnt_id_at_fallback(fd2, "", &mntid);
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
return true; /* skip the mount ID check */
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(mntid >= 0);
|
||||
|
||||
@@ -163,3 +163,12 @@ static inline int xopenat_lock(int dir_fd, const char *path, int open_flags, Loc
|
||||
int link_fd(int fd, int newdirfd, const char *newpath);
|
||||
|
||||
int linkat_replace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
|
||||
|
||||
static inline int at_flags_normalize_nofollow(int flags) {
|
||||
if (FLAGS_SET(flags, AT_SYMLINK_FOLLOW)) {
|
||||
assert(!FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW));
|
||||
flags &= ~AT_SYMLINK_FOLLOW;
|
||||
} else
|
||||
flags |= AT_SYMLINK_NOFOLLOW;
|
||||
return flags;
|
||||
}
|
||||
|
||||
+76
-82
@@ -37,10 +37,10 @@
|
||||
#define ORIGINAL_MAX_HANDLE_SZ 128
|
||||
|
||||
bool is_name_to_handle_at_fatal_error(int err) {
|
||||
/* name_to_handle_at() can return "acceptable" errors that are due to the context. For example the
|
||||
* kernel does not support name_to_handle_at() at all (ENOSYS), or the syscall was blocked
|
||||
* (EACCES/EPERM; maybe through seccomp, because we are running inside of a container), or the mount
|
||||
* point is not triggered yet (EOVERFLOW, think autofs+nfs4), or some general name_to_handle_at()
|
||||
/* name_to_handle_at() can return "acceptable" errors that are due to the context. For example
|
||||
* the file system does not support name_to_handle_at() (EOPNOTSUPP), or the syscall was blocked
|
||||
* (EACCES/EPERM; maybe through seccomp, because we are running inside of a container), or
|
||||
* the mount point is not triggered yet (EOVERFLOW, think autofs+nfs4), or some general name_to_handle_at()
|
||||
* flakiness (EINVAL). However other errors are not supposed to happen and therefore are considered
|
||||
* fatal ones. */
|
||||
|
||||
@@ -137,9 +137,7 @@ int name_to_handle_at_try_fid(
|
||||
* (i.e. older than Linux 6.5). */
|
||||
|
||||
r = name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags | AT_HANDLE_FID);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
if (is_name_to_handle_at_fatal_error(r))
|
||||
if (r >= 0 || is_name_to_handle_at_fatal_error(r))
|
||||
return r;
|
||||
|
||||
return name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags & ~AT_HANDLE_FID);
|
||||
@@ -149,11 +147,10 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *ret_mn
|
||||
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_free_ char *fdinfo = NULL;
|
||||
_cleanup_close_ int subfd = -EBADF;
|
||||
char *p;
|
||||
int r;
|
||||
|
||||
assert(ret_mnt_id);
|
||||
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
assert(ret_mnt_id);
|
||||
|
||||
if ((flags & AT_EMPTY_PATH) && isempty(filename))
|
||||
xsprintf(path, "/proc/self/fdinfo/%i", fd);
|
||||
@@ -166,16 +163,16 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *ret_mn
|
||||
}
|
||||
|
||||
r = read_full_virtual_file(path, &fdinfo, NULL);
|
||||
if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
|
||||
return proc_mounted() > 0 ? -EOPNOTSUPP : -ENOSYS;
|
||||
if (r == -ENOENT)
|
||||
return proc_fd_enoent_errno();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = find_line_startswith(fdinfo, "mnt_id:");
|
||||
if (!p) /* The mnt_id field is a relatively new addition */
|
||||
return -EOPNOTSUPP;
|
||||
char *p = find_line_startswith(fdinfo, "mnt_id:");
|
||||
if (!p)
|
||||
return -EBADMSG;
|
||||
|
||||
p += strspn(p, WHITESPACE);
|
||||
p = skip_leading_chars(p, /* bad = */ NULL);
|
||||
p[strcspn(p, WHITESPACE)] = 0;
|
||||
|
||||
return safe_atoi(p, ret_mnt_id);
|
||||
@@ -215,28 +212,36 @@ bool file_handle_equal(const struct file_handle *a, const struct file_handle *b)
|
||||
return memcmp_nn(a->f_handle, a->handle_bytes, b->f_handle, b->handle_bytes) == 0;
|
||||
}
|
||||
|
||||
int fd_is_mount_point(int fd, const char *filename, int flags) {
|
||||
_cleanup_free_ struct file_handle *h = NULL, *h_parent = NULL;
|
||||
int mount_id = -1, mount_id_parent = -1;
|
||||
bool nosupp = false, check_st_dev = true;
|
||||
STRUCT_STATX_DEFINE(sx);
|
||||
struct stat a, b;
|
||||
int is_mount_point_at(int fd, const char *filename, int flags) {
|
||||
bool fd_is_self;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
|
||||
|
||||
if (!filename) {
|
||||
/* If the file name is specified as NULL we'll see if the specified 'fd' is a mount
|
||||
* point. That's only supported if the kernel supports statx(), or if the inode specified via
|
||||
* 'fd' refers to a directory. Otherwise, we'll have to fail (ENOTDIR), because we have no
|
||||
* kernel API to query the information we need. */
|
||||
flags |= AT_EMPTY_PATH;
|
||||
filename = "";
|
||||
} else if (!filename_possibly_with_slash_suffix(filename))
|
||||
/* Insist that the specified filename is actually a filename, and not a path, i.e. some inode further
|
||||
* up or down the tree then immediately below the specified directory fd. */
|
||||
return -EINVAL;
|
||||
if (isempty(filename)) {
|
||||
if (fd == AT_FDCWD)
|
||||
filename = ".";
|
||||
else {
|
||||
/* If the file name is empty we'll see if the specified 'fd' is a mount point.
|
||||
* That's only supported if the kernel supports statx(), or if the inode specified
|
||||
* via 'fd' refers to a directory. Otherwise, we'll have to fail (ENOTDIR), because
|
||||
* we have no kernel API to query the information we need. */
|
||||
flags |= AT_EMPTY_PATH;
|
||||
filename = "";
|
||||
}
|
||||
|
||||
fd_is_self = true;
|
||||
} else if (STR_IN_SET(filename, ".", "./"))
|
||||
fd_is_self = true;
|
||||
else {
|
||||
/* Insist that the specified filename is actually a filename, and not a path, i.e. some inode
|
||||
* further up or down the tree then immediately below the specified directory fd. */
|
||||
if (!filename_possibly_with_slash_suffix(filename))
|
||||
return -EINVAL;
|
||||
|
||||
fd_is_self = false;
|
||||
}
|
||||
|
||||
/* First we will try statx()' STATX_ATTR_MOUNT_ROOT attribute, which is our ideal API, available
|
||||
* since kernel 5.8.
|
||||
@@ -249,18 +254,17 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
|
||||
* If that didn't work we will try to read the mount id from /proc/self/fdinfo/<fd>. This is almost
|
||||
* as good as name_to_handle_at(), however, does not return the opaque file handle. The opaque file
|
||||
* handle is pretty useful to detect the root directory, which we should always consider a mount
|
||||
* point. Hence we use this only as fallback. Exporting the mnt_id in fdinfo is a pretty recent
|
||||
* kernel addition.
|
||||
* point. Hence we use this only as fallback.
|
||||
*
|
||||
* As last fallback we do traditional fstat() based st_dev comparisons. This is how things were
|
||||
* traditionally done, but unionfs breaks this since it exposes file systems with a variety of st_dev
|
||||
* reported. Also, btrfs subvolumes have different st_dev, even though they aren't real mounts of
|
||||
* their own. */
|
||||
* Note that traditionally the check is done via fstat()-based st_dev comparisons. However, various
|
||||
* file systems don't guarantee same st_dev across single fs anymore, e.g. unionfs exposes file systems
|
||||
* with a variety of st_dev reported. Also, btrfs subvolumes have different st_dev, even though
|
||||
* they aren't real mounts of their own. */
|
||||
|
||||
if (statx(fd,
|
||||
filename,
|
||||
(FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : AT_SYMLINK_NOFOLLOW) |
|
||||
(flags & AT_EMPTY_PATH) |
|
||||
STRUCT_STATX_DEFINE(sx);
|
||||
|
||||
if (statx(fd, filename,
|
||||
at_flags_normalize_nofollow(flags) |
|
||||
AT_NO_AUTOMOUNT | /* don't trigger automounts – mounts are a local concept, hence no need to trigger automounts to determine STATX_ATTR_MOUNT_ROOT */
|
||||
AT_STATX_DONT_SYNC, /* don't go to the network for this – for similar reasons */
|
||||
STATX_TYPE,
|
||||
@@ -274,6 +278,10 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
|
||||
} else if (FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT)) /* yay! */
|
||||
return FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT);
|
||||
|
||||
_cleanup_free_ struct file_handle *h = NULL, *h_parent = NULL;
|
||||
int mount_id = -1, mount_id_parent = -1;
|
||||
bool nosupp = false;
|
||||
|
||||
r = name_to_handle_at_try_fid(fd, filename, &h, &mount_id, flags);
|
||||
if (r < 0) {
|
||||
if (is_name_to_handle_at_fatal_error(r))
|
||||
@@ -281,13 +289,12 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(r))
|
||||
goto fallback_fdinfo;
|
||||
|
||||
/* This kernel or file system does not support name_to_handle_at(), hence let's see
|
||||
* if the upper fs supports it (in which case it is a mount point), otherwise fall
|
||||
* back to the traditional stat() logic */
|
||||
/* This file system does not support name_to_handle_at(), hence let's see if the upper fs
|
||||
* supports it (in which case it is a mount point), otherwise fall back to the fdinfo logic. */
|
||||
nosupp = true;
|
||||
}
|
||||
|
||||
if (isempty(filename))
|
||||
if (fd_is_self)
|
||||
r = name_to_handle_at_try_fid(fd, "..", &h_parent, &mount_id_parent, 0); /* can't work for non-directories 😢 */
|
||||
else
|
||||
r = name_to_handle_at_try_fid(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
|
||||
@@ -319,12 +326,10 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
|
||||
|
||||
fallback_fdinfo:
|
||||
r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r))
|
||||
goto fallback_fstat;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (isempty(filename))
|
||||
if (fd_is_self)
|
||||
r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent); /* can't work for non-directories 😢 */
|
||||
else
|
||||
r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
|
||||
@@ -336,38 +341,33 @@ fallback_fdinfo:
|
||||
|
||||
/* Hmm, so, the mount ids are the same. This leaves one special case though for the root file
|
||||
* system. For that, let's see if the parent directory has the same inode as we are interested
|
||||
* in. Hence, let's also do fstat() checks now, too, but avoid the st_dev comparisons, since they
|
||||
* aren't that useful on unionfs mounts. */
|
||||
check_st_dev = false;
|
||||
* in. */
|
||||
|
||||
struct stat a, b;
|
||||
|
||||
fallback_fstat:
|
||||
/* yay for fstatat() taking a different set of flags than the other _at() above */
|
||||
if (flags & AT_SYMLINK_FOLLOW)
|
||||
flags &= ~AT_SYMLINK_FOLLOW;
|
||||
else
|
||||
flags |= AT_SYMLINK_NOFOLLOW;
|
||||
if (fstatat(fd, filename, &a, flags) < 0)
|
||||
if (fstatat(fd, filename, &a, at_flags_normalize_nofollow(flags)) < 0)
|
||||
return -errno;
|
||||
|
||||
if (isempty(filename))
|
||||
if (fd_is_self)
|
||||
r = fstatat(fd, "..", &b, 0);
|
||||
else
|
||||
r = fstatat(fd, "", &b, AT_EMPTY_PATH);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
/* A directory with same device and inode as its parent? Must be the root directory */
|
||||
if (stat_inode_same(&a, &b))
|
||||
return 1;
|
||||
|
||||
return check_st_dev && (a.st_dev != b.st_dev);
|
||||
/* A directory with same device and inode as its parent must be the root directory. Otherwise
|
||||
* not a mount point.
|
||||
*
|
||||
* NB: we avoid inode_same_at() here because it internally attempts name_to_handle_at_try_fid() first,
|
||||
* which is redundant. */
|
||||
return stat_inode_same(&a, &b);
|
||||
}
|
||||
|
||||
/* flags can be AT_SYMLINK_FOLLOW or 0 */
|
||||
int path_is_mount_point_full(const char *path, const char *root, int flags) {
|
||||
_cleanup_free_ char *canonical = NULL;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
_cleanup_close_ int dfd = -EBADF;
|
||||
_cleanup_free_ char *fn = NULL;
|
||||
|
||||
assert(path);
|
||||
assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
|
||||
@@ -375,22 +375,16 @@ int path_is_mount_point_full(const char *path, const char *root, int flags) {
|
||||
if (path_equal(path, "/"))
|
||||
return 1;
|
||||
|
||||
/* we need to resolve symlinks manually, we can't just rely on fd_is_mount_point() to do that for us;
|
||||
/* we need to resolve symlinks manually, we can't just rely on is_mount_point_at() to do that for us;
|
||||
* if we have a structure like /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
|
||||
* look at needs to be /usr, not /. */
|
||||
if (FLAGS_SET(flags, AT_SYMLINK_FOLLOW)) {
|
||||
r = chase(path, root, CHASE_TRAIL_SLASH, &canonical, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
dfd = chase_and_open_parent(path, root,
|
||||
CHASE_TRAIL_SLASH|(FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : CHASE_NOFOLLOW),
|
||||
&fn);
|
||||
if (dfd < 0)
|
||||
return dfd;
|
||||
|
||||
path = canonical;
|
||||
}
|
||||
|
||||
fd = open_parent(path, O_PATH|O_CLOEXEC, 0);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
return fd_is_mount_point(fd, last_path_component(path), flags);
|
||||
return is_mount_point_at(dfd, fn, flags);
|
||||
}
|
||||
|
||||
int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret) {
|
||||
@@ -400,7 +394,7 @@ int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret) {
|
||||
assert(ret);
|
||||
|
||||
r = name_to_handle_at_loop(dir_fd, path, NULL, ret, isempty(path) ? AT_EMPTY_PATH : 0);
|
||||
if (r == 0 || is_name_to_handle_at_fatal_error(r))
|
||||
if (r >= 0 || is_name_to_handle_at_fatal_error(r))
|
||||
return r;
|
||||
|
||||
return fd_fdinfo_mnt_id(dir_fd, path, isempty(path) ? AT_EMPTY_PATH : 0, ret);
|
||||
|
||||
@@ -49,7 +49,7 @@ static inline int path_get_mnt_id(const char *path, int *ret) {
|
||||
return path_get_mnt_id_at(AT_FDCWD, path, ret);
|
||||
}
|
||||
|
||||
int fd_is_mount_point(int fd, const char *filename, int flags);
|
||||
int is_mount_point_at(int fd, const char *filename, int flags);
|
||||
int path_is_mount_point_full(const char *path, const char *root, int flags);
|
||||
static inline int path_is_mount_point(const char *path) {
|
||||
return path_is_mount_point_full(path, NULL, 0);
|
||||
|
||||
@@ -384,7 +384,7 @@ int recurse_dir(
|
||||
if (sx_valid && FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT))
|
||||
is_mount = FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT);
|
||||
else {
|
||||
r = fd_is_mount_point(dir_fd, de->entries[i]->d_name, 0);
|
||||
r = is_mount_point_at(dir_fd, de->entries[i]->d_name, 0);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to determine whether %s is a submount, assuming not: %m", p);
|
||||
|
||||
|
||||
@@ -1762,7 +1762,7 @@ static int action_umount(const char *path) {
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Failed to resolve path '%s': %m", path);
|
||||
|
||||
r = fd_is_mount_point(fd, NULL, 0);
|
||||
r = is_mount_point_at(fd, NULL, 0);
|
||||
if (r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "'%s' is not a mount point", canonical);
|
||||
if (r < 0)
|
||||
|
||||
+1
-1
@@ -1147,7 +1147,7 @@ static int fd_copy_directory(
|
||||
if (buf.st_dev != original_device)
|
||||
continue;
|
||||
|
||||
r = fd_is_mount_point(dirfd(d), de->d_name, 0);
|
||||
r = is_mount_point_at(dirfd(d), de->d_name, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
|
||||
@@ -329,7 +329,7 @@ int machine_id_commit(const char *root) {
|
||||
if (!etc_machine_id)
|
||||
return log_oom();
|
||||
|
||||
r = fd_is_mount_point(etc_fd, "machine-id", /* flags= */ 0);
|
||||
r = is_mount_point_at(etc_fd, "machine-id", /* flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
|
||||
if (r == 0) {
|
||||
|
||||
@@ -1346,7 +1346,7 @@ int fd_make_mount_point(int fd) {
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = fd_is_mount_point(fd, NULL, 0);
|
||||
r = is_mount_point_at(fd, NULL, 0);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to determine whether file descriptor is a mount point: %m");
|
||||
if (r > 0)
|
||||
|
||||
@@ -97,7 +97,7 @@ int pcrextend_file_system_word(const char *path, char **ret_word, char **ret_nor
|
||||
if (dfd < 0)
|
||||
return log_error_errno(dfd, "Failed to open path '%s': %m", path);
|
||||
|
||||
r = fd_is_mount_point(dfd, NULL, 0);
|
||||
r = is_mount_point_at(dfd, NULL, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if path '%s' is mount point: %m", normalized_path);
|
||||
if (r == 0)
|
||||
|
||||
+1
-1
@@ -219,7 +219,7 @@ static int rm_rf_inner_child(
|
||||
return 0;
|
||||
|
||||
/* Stop at mount points */
|
||||
r = fd_is_mount_point(fd, fname, 0);
|
||||
r = is_mount_point_at(fd, fname, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
|
||||
@@ -452,14 +452,14 @@ TEST(fd_make_mount_point) {
|
||||
fd = open(s, O_PATH|O_CLOEXEC);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
assert_se(fd_is_mount_point(fd, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(is_mount_point_at(fd, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
|
||||
assert_se(fd_make_mount_point(fd) > 0);
|
||||
|
||||
/* Reopen the inode so that we end up on the new mount */
|
||||
fd2 = open(s, O_PATH|O_CLOEXEC);
|
||||
|
||||
assert_se(fd_is_mount_point(fd2, NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(is_mount_point_at(fd2, NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
|
||||
assert_se(fd_make_mount_point(fd2) == 0);
|
||||
|
||||
|
||||
@@ -271,8 +271,9 @@ TEST(path_is_mount_point) {
|
||||
assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||
}
|
||||
|
||||
TEST(fd_is_mount_point) {
|
||||
TEST(is_mount_point_at) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
|
||||
_cleanup_free_ char *pwd = NULL;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
|
||||
@@ -280,44 +281,51 @@ TEST(fd_is_mount_point) {
|
||||
assert_se(fd >= 0);
|
||||
|
||||
/* Not allowed, since "/" is a path, not a plain filename */
|
||||
assert_se(fd_is_mount_point(fd, "/", 0) == -EINVAL);
|
||||
assert_se(fd_is_mount_point(fd, ".", 0) == -EINVAL);
|
||||
assert_se(fd_is_mount_point(fd, "./", 0) == -EINVAL);
|
||||
assert_se(fd_is_mount_point(fd, "..", 0) == -EINVAL);
|
||||
assert_se(fd_is_mount_point(fd, "../", 0) == -EINVAL);
|
||||
assert_se(fd_is_mount_point(fd, "", 0) == -EINVAL);
|
||||
assert_se(fd_is_mount_point(fd, "/proc", 0) == -EINVAL);
|
||||
assert_se(fd_is_mount_point(fd, "/proc/", 0) == -EINVAL);
|
||||
assert_se(fd_is_mount_point(fd, "proc/sys", 0) == -EINVAL);
|
||||
assert_se(fd_is_mount_point(fd, "proc/sys/", 0) == -EINVAL);
|
||||
assert_se(is_mount_point_at(fd, "/", 0) == -EINVAL);
|
||||
assert_se(is_mount_point_at(fd, "..", 0) == -EINVAL);
|
||||
assert_se(is_mount_point_at(fd, "../", 0) == -EINVAL);
|
||||
assert_se(is_mount_point_at(fd, "/proc", 0) == -EINVAL);
|
||||
assert_se(is_mount_point_at(fd, "/proc/", 0) == -EINVAL);
|
||||
assert_se(is_mount_point_at(fd, "proc/sys", 0) == -EINVAL);
|
||||
assert_se(is_mount_point_at(fd, "proc/sys/", 0) == -EINVAL);
|
||||
|
||||
/* This one definitely is a mount point */
|
||||
assert_se(fd_is_mount_point(fd, "proc", 0) > 0);
|
||||
assert_se(fd_is_mount_point(fd, "proc/", 0) > 0);
|
||||
assert_se(is_mount_point_at(fd, "proc", 0) > 0);
|
||||
assert_se(is_mount_point_at(fd, "proc/", 0) > 0);
|
||||
|
||||
safe_close(fd);
|
||||
fd = open("/tmp", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
assert_se(mkdtemp_malloc("/tmp/not-mounted-XXXXXX", &tmpdir) >= 0);
|
||||
assert_se(fd_is_mount_point(fd, basename(tmpdir), 0) == 0);
|
||||
assert_se(fd_is_mount_point(fd, strjoina(basename(tmpdir), "/"), 0) == 0);
|
||||
assert_se(is_mount_point_at(fd, basename(tmpdir), 0) == 0);
|
||||
assert_se(is_mount_point_at(fd, strjoina(basename(tmpdir), "/"), 0) == 0);
|
||||
|
||||
safe_close(fd);
|
||||
fd = open("/proc", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
assert_se(fd_is_mount_point(fd, NULL, 0) > 0);
|
||||
assert_se(fd_is_mount_point(fd, "", 0) == -EINVAL);
|
||||
assert_se(fd_is_mount_point(fd, "version", 0) == 0);
|
||||
ASSERT_OK_POSITIVE(is_mount_point_at(fd, NULL, 0));
|
||||
ASSERT_OK_POSITIVE(is_mount_point_at(fd, "", 0));
|
||||
ASSERT_OK_POSITIVE(is_mount_point_at(fd, ".", 0));
|
||||
ASSERT_OK_POSITIVE(is_mount_point_at(fd, "./", 0));
|
||||
ASSERT_OK_ZERO(is_mount_point_at(fd, "version", 0));
|
||||
|
||||
ASSERT_OK(safe_getcwd(&pwd));
|
||||
ASSERT_OK_ERRNO(fchdir(fd));
|
||||
|
||||
ASSERT_OK_POSITIVE(is_mount_point_at(AT_FDCWD, NULL, 0));
|
||||
ASSERT_OK_POSITIVE(is_mount_point_at(AT_FDCWD, "", 0));
|
||||
ASSERT_OK_POSITIVE(is_mount_point_at(AT_FDCWD, "./", 0));
|
||||
|
||||
ASSERT_OK_ERRNO(chdir(pwd));
|
||||
|
||||
safe_close(fd);
|
||||
fd = open("/proc/version", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
r = fd_is_mount_point(fd, NULL, 0);
|
||||
r = is_mount_point_at(fd, NULL, 0);
|
||||
assert_se(IN_SET(r, 0, -ENOTDIR)); /* on old kernels we can't determine if regular files are mount points if we have no directory fd */
|
||||
assert_se(fd_is_mount_point(fd, "", 0) == -EINVAL);
|
||||
|
||||
if (!mount_new_api_supported())
|
||||
return;
|
||||
@@ -346,7 +354,7 @@ TEST(fd_is_mount_point) {
|
||||
ASSERT_OK(readlinkat_malloc(fd, "regular", &t));
|
||||
ASSERT_STREQ(t, "/usr");
|
||||
|
||||
ASSERT_OK(fd_is_mount_point(fd, "regular", 0));
|
||||
ASSERT_OK(is_mount_point_at(fd, "regular", 0));
|
||||
}
|
||||
|
||||
TEST(ms_nosymfollow_supported) {
|
||||
|
||||
@@ -744,7 +744,7 @@ static int dir_cleanup(
|
||||
if (S_ISDIR(sx.stx_mode)) {
|
||||
int q;
|
||||
|
||||
q = fd_is_mount_point(dirfd(d), de->d_name, 0);
|
||||
q = is_mount_point_at(dirfd(d), de->d_name, 0);
|
||||
if (q < 0)
|
||||
log_debug_errno(q, "Failed to determine whether \"%s/%s\" is a mount point, ignoring: %m", p, de->d_name);
|
||||
else if (q > 0) {
|
||||
@@ -2687,7 +2687,7 @@ static int rm_if_wrong_type_safe(
|
||||
}
|
||||
|
||||
/* Do not remove mount points. */
|
||||
r = fd_is_mount_point(parent_fd, name, follow_links ? AT_SYMLINK_FOLLOW : 0);
|
||||
r = is_mount_point_at(parent_fd, name, follow_links ? AT_SYMLINK_FOLLOW : 0);
|
||||
if (r < 0)
|
||||
(void) log_warning_errno(r, "Failed to check if \"%s/%s\" is a mount point: %m; continuing.",
|
||||
parent_name ?: "...", name);
|
||||
|
||||
Reference in New Issue
Block a user