From b8ee7969457e5b234dc203dd87c4d98580656927 Mon Sep 17 00:00:00 2001 From: Tony Hutter Date: Wed, 5 Nov 2025 16:22:03 -0800 Subject: [PATCH] Linux 6.17 compat: Fix broken projectquota on 6.17 We need to specifically use the FX_XFLAG_* macros in zpl_ioctl_*attr() codepaths, and the FS_*_FL macros in the zpl_ioctl_*flags() codepaths. The earlier code just assumes the FS_*_FL macros for both codepaths. The 6.17 kernel add a bitmask check in copy_fsxattr_from_user() that exposed this error via failing 'projectquota' ZTS tests. Reviewed-by: Alexander Motin Reviewed-by: Brian Behlendorf Signed-off-by: Tony Hutter Closes #17884 Closes #17869 --- cmd/zfs/zfs_project.c | 36 ++++++++--- include/sys/zfs_project.h | 10 ++- lib/libspl/include/Makefile.am | 1 + lib/libspl/include/os/linux/sys/vfs.h | 33 ++++++++++ module/os/linux/zfs/zpl_file.c | 93 ++++++++++++++++++++++----- 5 files changed, 143 insertions(+), 30 deletions(-) create mode 100644 lib/libspl/include/os/linux/sys/vfs.h diff --git a/cmd/zfs/zfs_project.c b/cmd/zfs/zfs_project.c index fbf5e6cbd..8925e6672 100644 --- a/cmd/zfs/zfs_project.c +++ b/cmd/zfs/zfs_project.c @@ -145,11 +145,11 @@ zfs_project_handle_one(const char *name, zfs_project_control_t *zpc) switch (zpc->zpc_op) { case ZFS_PROJECT_OP_LIST: (void) printf("%5u %c %s\n", fsx.fsx_projid, - (fsx.fsx_xflags & ZFS_PROJINHERIT_FL) ? 'P' : '-', name); + (fsx.fsx_xflags & FS_XFLAG_PROJINHERIT) ? 'P' : '-', name); goto out; case ZFS_PROJECT_OP_CHECK: if (fsx.fsx_projid == zpc->zpc_expected_projid && - fsx.fsx_xflags & ZFS_PROJINHERIT_FL) + fsx.fsx_xflags & FS_XFLAG_PROJINHERIT) goto out; if (!zpc->zpc_newline) { @@ -164,29 +164,30 @@ zfs_project_handle_one(const char *name, zfs_project_control_t *zpc) "(%u/%u)\n", name, fsx.fsx_projid, (uint32_t)zpc->zpc_expected_projid); - if (!(fsx.fsx_xflags & ZFS_PROJINHERIT_FL)) + if (!(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT)) (void) printf("%s - project inherit flag is not set\n", name); goto out; case ZFS_PROJECT_OP_CLEAR: - if (!(fsx.fsx_xflags & ZFS_PROJINHERIT_FL) && + if (!(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT) && (zpc->zpc_keep_projid || fsx.fsx_projid == ZFS_DEFAULT_PROJID)) goto out; - fsx.fsx_xflags &= ~ZFS_PROJINHERIT_FL; + fsx.fsx_xflags &= ~FS_XFLAG_PROJINHERIT; if (!zpc->zpc_keep_projid) fsx.fsx_projid = ZFS_DEFAULT_PROJID; break; case ZFS_PROJECT_OP_SET: if (fsx.fsx_projid == zpc->zpc_expected_projid && - (!zpc->zpc_set_flag || fsx.fsx_xflags & ZFS_PROJINHERIT_FL)) + (!zpc->zpc_set_flag || + fsx.fsx_xflags & FS_XFLAG_PROJINHERIT)) goto out; fsx.fsx_projid = zpc->zpc_expected_projid; if (zpc->zpc_set_flag) - fsx.fsx_xflags |= ZFS_PROJINHERIT_FL; + fsx.fsx_xflags |= FS_XFLAG_PROJINHERIT; break; default: ASSERT(0); @@ -194,11 +195,30 @@ zfs_project_handle_one(const char *name, zfs_project_control_t *zpc) } ret = ioctl(fd, ZFS_IOC_FSSETXATTR, &fsx); - if (ret) + if (ret) { (void) fprintf(stderr, gettext("failed to set xattr for %s: %s\n"), name, strerror(errno)); + if (errno == ENOTSUP) { + char *kver = zfs_version_kernel(); + /* + * Special case: a module/userspace version mismatch can + * return ENOTSUP due to us fixing the XFLAGs bits in + * #17884. In that case give a hint to the user that + * they should take action to make the versions match. + */ + if (strcmp(kver, ZFS_META_ALIAS) != 0) { + fprintf(stderr, + gettext("Warning: The zfs module version " + "(%s) and userspace\nversion (%s) do not " + "match up. This may be the\ncause of the " + "\"Operation not supported\" error.\n"), + kver, ZFS_META_ALIAS); + } + } + } + out: close(fd); return (ret); diff --git a/include/sys/zfs_project.h b/include/sys/zfs_project.h index 714c87a0d..a368f49e1 100644 --- a/include/sys/zfs_project.h +++ b/include/sys/zfs_project.h @@ -35,18 +35,16 @@ #include -#ifdef FS_PROJINHERIT_FL -#define ZFS_PROJINHERIT_FL FS_PROJINHERIT_FL -#else -#define ZFS_PROJINHERIT_FL 0x20000000 -#endif - #ifdef FS_IOC_FSGETXATTR typedef struct fsxattr zfsxattr_t; #define ZFS_IOC_FSGETXATTR FS_IOC_FSGETXATTR #define ZFS_IOC_FSSETXATTR FS_IOC_FSSETXATTR #else +/* Non-Linux OS */ +#define FS_PROJINHERIT_FL 0x20000000 +#define FS_XFLAG_PROJINHERIT FS_PROJINHERIT_FL + struct zfsxattr { uint32_t fsx_xflags; /* xflags field value (get/set) */ uint32_t fsx_extsize; /* extsize field value (get/set) */ diff --git a/lib/libspl/include/Makefile.am b/lib/libspl/include/Makefile.am index 8c286142f..f270da9b5 100644 --- a/lib/libspl/include/Makefile.am +++ b/lib/libspl/include/Makefile.am @@ -76,6 +76,7 @@ libspl_sys_HEADERS += \ %D%/os/linux/sys/param.h \ %D%/os/linux/sys/stat.h \ %D%/os/linux/sys/sysmacros.h \ + %D%/os/linux/sys/vfs.h \ %D%/os/linux/sys/zfs_context_os.h libspl_ia32_HEADERS = \ diff --git a/lib/libspl/include/os/linux/sys/vfs.h b/lib/libspl/include/os/linux/sys/vfs.h new file mode 100644 index 000000000..c7b567ff4 --- /dev/null +++ b/lib/libspl/include/os/linux/sys/vfs.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: CDDL-1.0 +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* Copyright 2025 by Lawrence Livermore National Security, LLC. */ + +/* This is the Linux userspace version of include/os/linux/spl/sys/vfs.h */ + +#ifndef _LIBSPL_SYS_VFS_H +#define _LIBSPL_SYS_VFS_H + +#include +#include + +#endif diff --git a/module/os/linux/zfs/zpl_file.c b/module/os/linux/zfs/zpl_file.c index ef7bd7352..841de46f8 100644 --- a/module/os/linux/zfs/zpl_file.c +++ b/module/os/linux/zfs/zpl_file.c @@ -684,28 +684,44 @@ zpl_fadvise(struct file *filp, loff_t offset, loff_t len, int advice) return (error); } -#define ZFS_FL_USER_VISIBLE (FS_FL_USER_VISIBLE | ZFS_PROJINHERIT_FL) -#define ZFS_FL_USER_MODIFIABLE (FS_FL_USER_MODIFIABLE | ZFS_PROJINHERIT_FL) +#define ZFS_FL_USER_VISIBLE (FS_FL_USER_VISIBLE | FS_PROJINHERIT_FL) +#define ZFS_FL_USER_MODIFIABLE (FS_FL_USER_MODIFIABLE | FS_PROJINHERIT_FL) + + +static struct { + uint64_t zfs_flag; + uint32_t fs_flag; + uint32_t xflag; +} flags_lookup[] = { + {ZFS_IMMUTABLE, FS_IMMUTABLE_FL, FS_XFLAG_IMMUTABLE}, + {ZFS_APPENDONLY, FS_APPEND_FL, FS_XFLAG_APPEND}, + {ZFS_NODUMP, FS_NODUMP_FL, FS_XFLAG_NODUMP}, + {ZFS_PROJINHERIT, FS_PROJINHERIT_FL, FS_XFLAG_PROJINHERIT} +}; static uint32_t __zpl_ioctl_getflags(struct inode *ip) { uint64_t zfs_flags = ITOZ(ip)->z_pflags; uint32_t ioctl_flags = 0; + for (int i = 0; i < ARRAY_SIZE(flags_lookup); i++) + if (zfs_flags & flags_lookup[i].zfs_flag) + ioctl_flags |= flags_lookup[i].fs_flag; - if (zfs_flags & ZFS_IMMUTABLE) - ioctl_flags |= FS_IMMUTABLE_FL; + return (ioctl_flags); +} - if (zfs_flags & ZFS_APPENDONLY) - ioctl_flags |= FS_APPEND_FL; +static uint32_t +__zpl_ioctl_getxflags(struct inode *ip) +{ + uint64_t zfs_flags = ITOZ(ip)->z_pflags; + uint32_t ioctl_flags = 0; - if (zfs_flags & ZFS_NODUMP) - ioctl_flags |= FS_NODUMP_FL; + for (int i = 0; i < ARRAY_SIZE(flags_lookup); i++) + if (zfs_flags & flags_lookup[i].zfs_flag) + ioctl_flags |= flags_lookup[i].xflag; - if (zfs_flags & ZFS_PROJINHERIT) - ioctl_flags |= ZFS_PROJINHERIT_FL; - - return (ioctl_flags & ZFS_FL_USER_VISIBLE); + return (ioctl_flags); } /* @@ -719,6 +735,7 @@ zpl_ioctl_getflags(struct file *filp, void __user *arg) int err; flags = __zpl_ioctl_getflags(file_inode(filp)); + flags = flags & ZFS_FL_USER_VISIBLE; err = copy_to_user(arg, &flags, sizeof (flags)); return (err); @@ -742,7 +759,7 @@ __zpl_ioctl_setflags(struct inode *ip, uint32_t ioctl_flags, xvattr_t *xva) xoptattr_t *xoap; if (ioctl_flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL | - ZFS_PROJINHERIT_FL)) + FS_PROJINHERIT_FL)) return (-EOPNOTSUPP); if (ioctl_flags & ~ZFS_FL_USER_MODIFIABLE) @@ -773,7 +790,51 @@ __zpl_ioctl_setflags(struct inode *ip, uint32_t ioctl_flags, xvattr_t *xva) xoap->xoa_appendonly); FLAG_CHANGE(FS_NODUMP_FL, ZFS_NODUMP, XAT_NODUMP, xoap->xoa_nodump); - FLAG_CHANGE(ZFS_PROJINHERIT_FL, ZFS_PROJINHERIT, XAT_PROJINHERIT, + FLAG_CHANGE(FS_PROJINHERIT_FL, ZFS_PROJINHERIT, XAT_PROJINHERIT, + xoap->xoa_projinherit); + +#undef FLAG_CHANGE + + return (0); +} + +static int +__zpl_ioctl_setxflags(struct inode *ip, uint32_t ioctl_flags, xvattr_t *xva) +{ + uint64_t zfs_flags = ITOZ(ip)->z_pflags; + xoptattr_t *xoap; + + if (ioctl_flags & ~(FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND | + FS_XFLAG_NODUMP | FS_XFLAG_PROJINHERIT)) + return (-EOPNOTSUPP); + + if ((fchange(ioctl_flags, zfs_flags, FS_XFLAG_IMMUTABLE, + ZFS_IMMUTABLE) || + fchange(ioctl_flags, zfs_flags, FS_XFLAG_APPEND, ZFS_APPENDONLY)) && + !capable(CAP_LINUX_IMMUTABLE)) + return (-EPERM); + + if (!zpl_inode_owner_or_capable(zfs_init_idmap, ip)) + return (-EACCES); + + xva_init(xva); + xoap = xva_getxoptattr(xva); + +#define FLAG_CHANGE(iflag, zflag, xflag, xfield) do { \ + if (((ioctl_flags & (iflag)) && !(zfs_flags & (zflag))) || \ + ((zfs_flags & (zflag)) && !(ioctl_flags & (iflag)))) { \ + XVA_SET_REQ(xva, (xflag)); \ + (xfield) = ((ioctl_flags & (iflag)) != 0); \ + } \ +} while (0) + + FLAG_CHANGE(FS_XFLAG_IMMUTABLE, ZFS_IMMUTABLE, XAT_IMMUTABLE, + xoap->xoa_immutable); + FLAG_CHANGE(FS_XFLAG_APPEND, ZFS_APPENDONLY, XAT_APPENDONLY, + xoap->xoa_appendonly); + FLAG_CHANGE(FS_XFLAG_NODUMP, ZFS_NODUMP, XAT_NODUMP, + xoap->xoa_nodump); + FLAG_CHANGE(FS_XFLAG_PROJINHERIT, ZFS_PROJINHERIT, XAT_PROJINHERIT, xoap->xoa_projinherit); #undef FLAG_CHANGE @@ -814,7 +875,7 @@ zpl_ioctl_getxattr(struct file *filp, void __user *arg) struct inode *ip = file_inode(filp); int err; - fsx.fsx_xflags = __zpl_ioctl_getflags(ip); + fsx.fsx_xflags = __zpl_ioctl_getxflags(ip); fsx.fsx_projid = ITOZ(ip)->z_projid; err = copy_to_user(arg, &fsx, sizeof (fsx)); @@ -838,7 +899,7 @@ zpl_ioctl_setxattr(struct file *filp, void __user *arg) if (!zpl_is_valid_projid(fsx.fsx_projid)) return (-EINVAL); - err = __zpl_ioctl_setflags(ip, fsx.fsx_xflags, &xva); + err = __zpl_ioctl_setxflags(ip, fsx.fsx_xflags, &xva); if (err) return (err);