Skip to content

Commit 21d5f25

Browse files
ixhamzaamotin
authored andcommitted
Validate mountpoint on path-based unmount using statx
Use statx to verify that path-based unmounts proceed only if the mountpoint reported by statx matches the MNTTAB entry reported by libzfs, aborting the operation if they differ. Align `zfs umount /path` behavior with `zfs umount dataset`. Reviewed-by: Alexander Motin <[email protected]> Signed-off-by: Ameer Hamza <[email protected]> Closes #17481
1 parent 7e945a5 commit 21d5f25

File tree

5 files changed

+89
-6
lines changed

5 files changed

+89
-6
lines changed

cmd/zfs/zfs_main.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7716,6 +7716,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
77167716
struct extmnttab entry;
77177717
const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";
77187718
ino_t path_inode;
7719+
char *zfs_mntpnt, *entry_mntpnt;
77197720

77207721
/*
77217722
* Search for the given (major,minor) pair in the mount table.
@@ -7757,6 +7758,24 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
77577758
goto out;
77587759
}
77597760

7761+
/*
7762+
* If the filesystem is mounted, check that the mountpoint matches
7763+
* the one in the mnttab entry w.r.t. provided path. If it doesn't,
7764+
* then we should not proceed further.
7765+
*/
7766+
entry_mntpnt = strdup(entry.mnt_mountp);
7767+
if (zfs_is_mounted(zhp, &zfs_mntpnt)) {
7768+
if (strcmp(zfs_mntpnt, entry_mntpnt) != 0) {
7769+
(void) fprintf(stderr, gettext("cannot %s '%s': "
7770+
"not an original mountpoint\n"), cmdname, path);
7771+
free(zfs_mntpnt);
7772+
free(entry_mntpnt);
7773+
goto out;
7774+
}
7775+
free(zfs_mntpnt);
7776+
}
7777+
free(entry_mntpnt);
7778+
77607779
if (op == OP_SHARE) {
77617780
char nfs_mnt_prop[ZFS_MAXPROPLEN];
77627781
char smbshare_prop[ZFS_MAXPROPLEN];

config/user-statx.m4

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
dnl #
2+
dnl # Check for statx() function and STATX_MNT_ID availability
3+
dnl #
4+
AC_DEFUN([ZFS_AC_CONFIG_USER_STATX], [
5+
AC_CHECK_HEADERS([linux/stat.h],
6+
[have_stat_headers=yes],
7+
[have_stat_headers=no])
8+
9+
AS_IF([test "x$have_stat_headers" = "xyes"], [
10+
AC_CHECK_FUNC([statx], [
11+
AC_DEFINE([HAVE_STATX], [1], [statx() is available])
12+
13+
dnl Check for STATX_MNT_ID availability
14+
AC_MSG_CHECKING([for STATX_MNT_ID])
15+
AC_COMPILE_IFELSE([
16+
AC_LANG_PROGRAM([[
17+
#include <linux/stat.h>
18+
]], [[
19+
struct statx stx;
20+
int mask = STATX_MNT_ID;
21+
(void)mask;
22+
(void)stx.stx_mnt_id;
23+
]])
24+
], [
25+
AC_MSG_RESULT([yes])
26+
AC_DEFINE([HAVE_STATX_MNT_ID], [1], [STATX_MNT_ID is available])
27+
], [
28+
AC_MSG_RESULT([no])
29+
])
30+
])
31+
], [
32+
AC_MSG_WARN([linux/stat.h not found; skipping statx support])
33+
])
34+
]) dnl end AC_DEFUN

config/user.m4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ AC_DEFUN([ZFS_AC_CONFIG_USER], [
1717
ZFS_AC_CONFIG_USER_LIBUDEV
1818
ZFS_AC_CONFIG_USER_LIBUUID
1919
ZFS_AC_CONFIG_USER_LIBBLKID
20+
ZFS_AC_CONFIG_USER_STATX
2021
])
2122
ZFS_AC_CONFIG_USER_LIBTIRPC
2223
ZFS_AC_CONFIG_USER_LIBCRYPTO

lib/libspl/include/os/linux/sys/stat.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131

3232
#include <sys/mount.h> /* for BLKGETSIZE64 */
3333

34+
#ifdef HAVE_STATX
35+
#include <fcntl.h>
36+
#include <linux/stat.h>
37+
#endif
38+
3439
/*
3540
* Emulate Solaris' behavior of returning the block device size in fstat64().
3641
*/

lib/libspl/os/linux/getmntany.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,21 @@ _sol_getmntent(FILE *fp, struct mnttab *mgetp)
8585
}
8686

8787
static int
88-
getextmntent_impl(FILE *fp, struct extmnttab *mp)
88+
getextmntent_impl(FILE *fp, struct extmnttab *mp, uint64_t *mnt_id)
8989
{
9090
int ret;
9191
struct stat64 st;
9292

93+
*mnt_id = 0;
9394
ret = _sol_getmntent(fp, (struct mnttab *)mp);
9495
if (ret == 0) {
96+
#ifdef HAVE_STATX_MNT_ID
97+
struct statx stx;
98+
if (statx(AT_FDCWD, mp->mnt_mountp,
99+
AT_STATX_SYNC_AS_STAT | AT_SYMLINK_NOFOLLOW,
100+
STATX_MNT_ID, &stx) == 0 && (stx.stx_mask & STATX_MNT_ID))
101+
*mnt_id = stx.stx_mnt_id;
102+
#endif
95103
if (stat64(mp->mnt_mountp, &st) != 0) {
96104
mp->mnt_major = 0;
97105
mp->mnt_minor = 0;
@@ -110,6 +118,12 @@ getextmntent(const char *path, struct extmnttab *entry, struct stat64 *statbuf)
110118
struct stat64 st;
111119
FILE *fp;
112120
int match;
121+
boolean_t have_mnt_id = B_FALSE;
122+
uint64_t target_mnt_id = 0;
123+
uint64_t entry_mnt_id;
124+
#ifdef HAVE_STATX_MNT_ID
125+
struct statx stx;
126+
#endif
113127

114128
if (strlen(path) >= MAXPATHLEN) {
115129
(void) fprintf(stderr, "invalid object; pathname too long\n");
@@ -128,6 +142,13 @@ getextmntent(const char *path, struct extmnttab *entry, struct stat64 *statbuf)
128142
return (-1);
129143
}
130144

145+
#ifdef HAVE_STATX_MNT_ID
146+
if (statx(AT_FDCWD, path, AT_STATX_SYNC_AS_STAT | AT_SYMLINK_NOFOLLOW,
147+
STATX_MNT_ID, &stx) == 0 && (stx.stx_mask & STATX_MNT_ID)) {
148+
have_mnt_id = B_TRUE;
149+
target_mnt_id = stx.stx_mnt_id;
150+
}
151+
#endif
131152

132153
if ((fp = fopen(MNTTAB, "re")) == NULL) {
133154
(void) fprintf(stderr, "cannot open %s\n", MNTTAB);
@@ -139,12 +160,15 @@ getextmntent(const char *path, struct extmnttab *entry, struct stat64 *statbuf)
139160
*/
140161

141162
match = 0;
142-
while (getextmntent_impl(fp, entry) == 0) {
143-
if (makedev(entry->mnt_major, entry->mnt_minor) ==
144-
statbuf->st_dev) {
145-
match = 1;
146-
break;
163+
while (getextmntent_impl(fp, entry, &entry_mnt_id) == 0) {
164+
if (have_mnt_id) {
165+
match = (entry_mnt_id == target_mnt_id);
166+
} else {
167+
match = makedev(entry->mnt_major, entry->mnt_minor) ==
168+
statbuf->st_dev;
147169
}
170+
if (match)
171+
break;
148172
}
149173
(void) fclose(fp);
150174

0 commit comments

Comments
 (0)