From 47a30221c3b76a5dbd1e0d4e69b5ce9518177c62 Mon Sep 17 00:00:00 2001 From: Ryan Moeller Date: Fri, 5 Mar 2021 13:02:57 +0000 Subject: [PATCH] Intentionally allow ZFS_READONLY in zfs_write ZFS_READONLY represents the "DOS R/O" attribute. When that flag is set, we should behave as if write access were not granted by anything in the ACL. In particular: We _must_ allow writes after opening the file r/w, then setting the DOS R/O attribute, and writing some more. (Similar to how you can write after fchmod(fd, 0444).) Restore these semantics which were lost on FreeBSD when refactoring zfs_write. To my knowledge Linux does not actually expose this flag, but we'll need it to eventually so I've added the supporting checks. Signed-off-by: Ryan Moeller --- module/os/linux/zfs/zfs_acl.c | 26 +++++++++++++++++++++----- module/zfs/zfs_vnops.c | 6 ++++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/module/os/linux/zfs/zfs_acl.c b/module/os/linux/zfs/zfs_acl.c index 2628325c0ba9..f8bf55f75e97 100644 --- a/module/os/linux/zfs/zfs_acl.c +++ b/module/os/linux/zfs/zfs_acl.c @@ -2214,13 +2214,11 @@ zfs_zaccess_dataset_check(znode_t *zp, uint32_t v4_mode) } /* - * Only check for READONLY on non-directories. + * Intentionally allow ZFS_READONLY through here. + * See zfs_zaccess_common(). */ if ((v4_mode & WRITE_MASK_DATA) && - ((!S_ISDIR(ZTOI(zp)->i_mode) && - (zp->z_pflags & (ZFS_READONLY | ZFS_IMMUTABLE))) || - (S_ISDIR(ZTOI(zp)->i_mode) && - (zp->z_pflags & ZFS_IMMUTABLE)))) { + (zp->z_pflags & ZFS_IMMUTABLE)) { return (SET_ERROR(EPERM)); } @@ -2434,6 +2432,24 @@ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode, return (0); } + /* + * Note: ZFS_READONLY represents the "DOS R/O" attribute. + * When that flag is set, we should behave as if write access + * were not granted by anything in the ACL. In particular: + * We _must_ allow writes after opening the file r/w, then + * setting the DOS R/O attribute, and writing some more. + * (Similar to how you can write after fchmod(fd, 0444).) + * + * Therefore ZFS_READONLY is ignored in the dataset check + * above, and checked here as if part of the ACL check. + * Also note: DOS R/O is ignored for directories. + */ + if ((v4_mode & WRITE_MASK_DATA) && + S_ISDIR(ZTOI(zp)->i_mode) && + (zp->z_pflags & ZFS_READONLY)) { + return (SET_ERROR(EPERM)); + } + return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr)); } diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c index a35c17f86f93..0af03e9233b3 100644 --- a/module/zfs/zfs_vnops.c +++ b/module/zfs/zfs_vnops.c @@ -350,9 +350,11 @@ zfs_write(znode_t *zp, zfs_uio_t *uio, int ioflag, cred_t *cr) } /* - * If immutable or not appending then return EPERM + * If immutable or not appending then return EPERM. + * Intentionally allow ZFS_READONLY through here. + * See zfs_zaccess_common() */ - if ((zp->z_pflags & (ZFS_IMMUTABLE | ZFS_READONLY)) || + if ((zp->z_pflags & ZFS_IMMUTABLE) || ((zp->z_pflags & ZFS_APPENDONLY) && !(ioflag & O_APPEND) && (zfs_uio_offset(uio) < zp->z_size))) { ZFS_EXIT(zfsvfs);