diff --git a/config/kernel-inode-set-flags.m4 b/config/kernel-inode-set-flags.m4 new file mode 100644 index 000000000000..e0ad26796dd0 --- /dev/null +++ b/config/kernel-inode-set-flags.m4 @@ -0,0 +1,18 @@ +dnl # +dnl # 3.15 API change +dnl # inode_set_flags introduced to set i_flags +dnl # +AC_DEFUN([ZFS_AC_KERNEL_INODE_SET_FLAGS], [ + AC_MSG_CHECKING([whether inode_set_flags() exists]) + ZFS_LINUX_TRY_COMPILE([ + #include + ],[ + struct inode inode; + inode_set_flags(&inode, S_IMMUTABLE, S_IMMUTABLE); + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_INODE_SET_FLAGS, 1, [inode_set_flags() exists]) + ],[ + AC_MSG_RESULT(no) + ]) +]) diff --git a/config/kernel.m4 b/config/kernel.m4 index b66631a9c993..08f544a78728 100644 --- a/config/kernel.m4 +++ b/config/kernel.m4 @@ -56,6 +56,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [ ZFS_AC_KERNEL_INODE_OPERATIONS_CHECK_ACL_WITH_FLAGS ZFS_AC_KERNEL_INODE_OPERATIONS_GET_ACL ZFS_AC_KERNEL_INODE_OPERATIONS_SET_ACL + ZFS_AC_KERNEL_INODE_SET_FLAGS ZFS_AC_KERNEL_GET_ACL_HANDLE_CACHE ZFS_AC_KERNEL_SHOW_OPTIONS ZFS_AC_KERNEL_FILE_INODE diff --git a/configure.ac b/configure.ac index efe61305df1f..20a67dc4f089 100644 --- a/configure.ac +++ b/configure.ac @@ -180,6 +180,7 @@ AC_CONFIG_FILES([ tests/zfs-tests/tests/functional/cachefile/Makefile tests/zfs-tests/tests/functional/casenorm/Makefile tests/zfs-tests/tests/functional/checksum/Makefile + tests/zfs-tests/tests/functional/chattr/Makefile tests/zfs-tests/tests/functional/clean_mirror/Makefile tests/zfs-tests/tests/functional/cli_root/Makefile tests/zfs-tests/tests/functional/cli_root/zdb/Makefile diff --git a/include/sys/xvattr.h b/include/sys/xvattr.h index 578b3341acf1..53945d85b98b 100644 --- a/include/sys/xvattr.h +++ b/include/sys/xvattr.h @@ -225,7 +225,7 @@ typedef struct xvattr { * of requested attributes (xva_reqattrmap[]). */ #define XVA_SET_REQ(xvap, attr) \ - ASSERT((xvap)->xva_vattr.va_mask | AT_XVATTR); \ + ASSERT((xvap)->xva_vattr.va_mask & AT_XVATTR); \ ASSERT((xvap)->xva_magic == XVA_MAGIC); \ (xvap)->xva_reqattrmap[XVA_INDEX(attr)] |= XVA_ATTRBIT(attr) /* @@ -233,7 +233,7 @@ typedef struct xvattr { * of requested attributes (xva_reqattrmap[]). */ #define XVA_CLR_REQ(xvap, attr) \ - ASSERT((xvap)->xva_vattr.va_mask | AT_XVATTR); \ + ASSERT((xvap)->xva_vattr.va_mask & AT_XVATTR); \ ASSERT((xvap)->xva_magic == XVA_MAGIC); \ (xvap)->xva_reqattrmap[XVA_INDEX(attr)] &= ~XVA_ATTRBIT(attr) @@ -242,7 +242,7 @@ typedef struct xvattr { * of returned attributes (xva_rtnattrmap[]). */ #define XVA_SET_RTN(xvap, attr) \ - ASSERT((xvap)->xva_vattr.va_mask | AT_XVATTR); \ + ASSERT((xvap)->xva_vattr.va_mask & AT_XVATTR); \ ASSERT((xvap)->xva_magic == XVA_MAGIC); \ (XVA_RTNATTRMAP(xvap))[XVA_INDEX(attr)] |= XVA_ATTRBIT(attr) @@ -251,7 +251,7 @@ typedef struct xvattr { * to see of the corresponding attribute bit is set. If so, returns non-zero. */ #define XVA_ISSET_REQ(xvap, attr) \ - ((((xvap)->xva_vattr.va_mask | AT_XVATTR) && \ + ((((xvap)->xva_vattr.va_mask & AT_XVATTR) && \ ((xvap)->xva_magic == XVA_MAGIC) && \ ((xvap)->xva_mapsize > XVA_INDEX(attr))) ? \ ((xvap)->xva_reqattrmap[XVA_INDEX(attr)] & XVA_ATTRBIT(attr)) : 0) @@ -261,7 +261,7 @@ typedef struct xvattr { * to see of the corresponding attribute bit is set. If so, returns non-zero. */ #define XVA_ISSET_RTN(xvap, attr) \ - ((((xvap)->xva_vattr.va_mask | AT_XVATTR) && \ + ((((xvap)->xva_vattr.va_mask & AT_XVATTR) && \ ((xvap)->xva_magic == XVA_MAGIC) && \ ((xvap)->xva_mapsize > XVA_INDEX(attr))) ? \ ((XVA_RTNATTRMAP(xvap))[XVA_INDEX(attr)] & XVA_ATTRBIT(attr)) : 0) diff --git a/module/zfs/zfs_znode.c b/module/zfs/zfs_znode.c index 624e92696148..92241d6a5ce2 100644 --- a/module/zfs/zfs_znode.c +++ b/module/zfs/zfs_znode.c @@ -479,6 +479,34 @@ zfs_inode_set_ops(zfs_sb_t *zsb, struct inode *ip) } } +void +zfs_set_inode_flags(znode_t *zp, struct inode *ip) +{ + /* + * Linux and Solaris have different sets of file attributes, so we + * restrict this conversion to the intersection of the two. + */ +#ifdef HAVE_INODE_SET_FLAGS + unsigned int flags = 0; + if (zp->z_pflags & ZFS_IMMUTABLE) + flags |= S_IMMUTABLE; + if (zp->z_pflags & ZFS_APPENDONLY) + flags |= S_APPEND; + + inode_set_flags(ip, flags, S_IMMUTABLE|S_APPEND); +#else + if (zp->z_pflags & ZFS_IMMUTABLE) + ip->i_flags |= S_IMMUTABLE; + else + ip->i_flags &= ~S_IMMUTABLE; + + if (zp->z_pflags & ZFS_APPENDONLY) + ip->i_flags |= S_APPEND; + else + ip->i_flags &= ~S_APPEND; +#endif +} + /* * Update the embedded inode given the znode. We should work toward * eliminating this function as soon as possible by removing values @@ -588,6 +616,7 @@ zfs_znode_alloc(zfs_sb_t *zsb, dmu_buf_t *db, int blksz, set_nlink(ip, (uint32_t)links); zfs_uid_write(ip, z_uid); zfs_gid_write(ip, z_gid); + zfs_set_inode_flags(zp, ip); /* Cache the xattr parent id */ if (zp->z_pflags & ZFS_XATTR) @@ -918,6 +947,7 @@ void zfs_xvattr_set(znode_t *zp, xvattr_t *xvap, dmu_tx_t *tx) { xoptattr_t *xoap; + boolean_t update_inode = B_FALSE; xoap = xva_getxoptattr(xvap); ASSERT(xoap); @@ -929,7 +959,6 @@ zfs_xvattr_set(znode_t *zp, xvattr_t *xvap, dmu_tx_t *tx) ×, sizeof (times), tx); XVA_SET_RTN(xvap, XAT_CREATETIME); } - if (XVA_ISSET_REQ(xvap, XAT_READONLY)) { ZFS_ATTR_SET(zp, ZFS_READONLY, xoap->xoa_readonly, zp->z_pflags, tx); @@ -955,11 +984,8 @@ zfs_xvattr_set(znode_t *zp, xvattr_t *xvap, dmu_tx_t *tx) zp->z_pflags, tx); XVA_SET_RTN(xvap, XAT_IMMUTABLE); - ZTOI(zp)->i_flags |= S_IMMUTABLE; - } else { - ZTOI(zp)->i_flags &= ~S_IMMUTABLE; + update_inode = B_TRUE; } - if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) { ZFS_ATTR_SET(zp, ZFS_NOUNLINK, xoap->xoa_nounlink, zp->z_pflags, tx); @@ -970,12 +996,8 @@ zfs_xvattr_set(znode_t *zp, xvattr_t *xvap, dmu_tx_t *tx) zp->z_pflags, tx); XVA_SET_RTN(xvap, XAT_APPENDONLY); - ZTOI(zp)->i_flags |= S_APPEND; - } else { - - ZTOI(zp)->i_flags &= ~S_APPEND; + update_inode = B_TRUE; } - if (XVA_ISSET_REQ(xvap, XAT_NODUMP)) { ZFS_ATTR_SET(zp, ZFS_NODUMP, xoap->xoa_nodump, zp->z_pflags, tx); @@ -1015,6 +1037,9 @@ zfs_xvattr_set(znode_t *zp, xvattr_t *xvap, dmu_tx_t *tx) zp->z_pflags, tx); XVA_SET_RTN(xvap, XAT_SPARSE); } + + if (update_inode) + zfs_set_inode_flags(zp, ZTOI(zp)); } int @@ -1220,12 +1245,12 @@ zfs_rezget(znode_t *zp) zp->z_unlinked = (ZTOI(zp)->i_nlink == 0); set_nlink(ZTOI(zp), (uint32_t)links); + zfs_set_inode_flags(zp, ZTOI(zp)); zp->z_blksz = doi.doi_data_block_size; zp->z_atime_dirty = 0; zfs_inode_update(zp); - zfs_znode_hold_exit(zsb, zh); return (0); diff --git a/module/zfs/zpl_file.c b/module/zfs/zpl_file.c index a225220285af..332fb992e7a2 100644 --- a/module/zfs/zpl_file.c +++ b/module/zfs/zpl_file.c @@ -737,8 +737,7 @@ zpl_ioctl_getflags(struct file *filp, void __user *arg) * is outside of our jurisdiction. */ -#define fchange(f0, f1, b0, b1) ((((f0) & (b0)) == (b0)) != \ - (((b1) & (f1)) == (f1))) +#define fchange(f0, f1, b0, b1) (!((f0) & (b0)) != !((f1) & (b1))) static int zpl_ioctl_setflags(struct file *filp, void __user *arg) diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run index 5d146c6bdebe..5e16395b9020 100644 --- a/tests/runfiles/linux.run +++ b/tests/runfiles/linux.run @@ -60,6 +60,9 @@ tests = ['cache_002_pos', 'cache_003_pos', 'cache_004_neg', [tests/functional/casenorm] tests = ['case_all_values', 'norm_all_values'] +[tests/functional/chattr] +tests = ['chattr_001_pos', 'chattr_002_neg'] + [tests/functional/checksum] tests = ['run_edonr_test', 'run_sha2_test', 'run_skein_test', 'filetest_001_pos'] diff --git a/tests/zfs-tests/tests/functional/Makefile.am b/tests/zfs-tests/tests/functional/Makefile.am index 670f994fe823..b974e5930108 100644 --- a/tests/zfs-tests/tests/functional/Makefile.am +++ b/tests/zfs-tests/tests/functional/Makefile.am @@ -5,6 +5,7 @@ SUBDIRS = \ cache \ cachefile \ casenorm \ + chattr \ checksum \ clean_mirror \ cli_root \ diff --git a/tests/zfs-tests/tests/functional/chattr/Makefile.am b/tests/zfs-tests/tests/functional/chattr/Makefile.am new file mode 100644 index 000000000000..431208e587ec --- /dev/null +++ b/tests/zfs-tests/tests/functional/chattr/Makefile.am @@ -0,0 +1,6 @@ +pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/chattr +dist_pkgdata_SCRIPTS = \ + setup.ksh \ + cleanup.ksh \ + chattr_001_pos.ksh \ + chattr_002_neg.ksh diff --git a/tests/zfs-tests/tests/functional/chattr/chattr_001_pos.ksh b/tests/zfs-tests/tests/functional/chattr/chattr_001_pos.ksh new file mode 100755 index 000000000000..73e4e2e5a36d --- /dev/null +++ b/tests/zfs-tests/tests/functional/chattr/chattr_001_pos.ksh @@ -0,0 +1,75 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 http://www.opensolaris.org/os/licensing. +# 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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2013 by Delphix. All rights reserved. +# +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib + +# +# +# DESCRIPTION: +# Check whether chattr works as expected +# +# +# STRATEGY: +# 1. Create 3 files +# 2. Use chattr to make them writable, immutable and appendonly +# 3. Try to write and append to each file +# + +set -A files writable immutable append + +function cleanup +{ + for i in ${files[*]}; do + log_must chattr -ia $TESTDIR/$i + log_must rm -f $TESTDIR/$i + done +} + +log_onexit cleanup + +log_assert "Check whether chattr works as expected" + +log_must touch $TESTDIR/writable +log_must touch $TESTDIR/immutable +log_must touch $TESTDIR/append + +log_must chattr -i $TESTDIR/writable +log_must chattr +i $TESTDIR/immutable +log_must chattr +a $TESTDIR/append + +log_must echo test > $TESTDIR/writable +log_must echo test >> $TESTDIR/writable +log_mustnot echo test > $TESTDIR/immutable +log_mustnot echo test >> $TESTDIR/immutable +log_mustnot echo test > $TESTDIR/append +log_must echo test >> $TESTDIR/append + +log_pass "chattr works as expected" diff --git a/tests/zfs-tests/tests/functional/chattr/chattr_002_neg.ksh b/tests/zfs-tests/tests/functional/chattr/chattr_002_neg.ksh new file mode 100755 index 000000000000..8712b7ed2eb6 --- /dev/null +++ b/tests/zfs-tests/tests/functional/chattr/chattr_002_neg.ksh @@ -0,0 +1,81 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 http://www.opensolaris.org/os/licensing. +# 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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2013 by Delphix. All rights reserved. +# +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib + +# +# +# DESCRIPTION: +# Check whether unprivileged user can chattr +# +# +# STRATEGY: +# 1. Create 3 files +# 2. Use chattr to make them writable, immutable and appendonly +# 3. Try to chattr with unprivileged user +# + +set -A files writable immutable append + +function cleanup +{ + for i in ${files[*]}; do + log_must chattr -ia $TESTDIR/$i + log_must rm -f $TESTDIR/$i + done + log_must $CHMOD 0755 $TESTDIR +} + +log_onexit cleanup + +log_assert "Check whether unprivileged user can chattr" + +log_must $CHMOD 0777 $TESTDIR + +log_must user_run $QUSER1 touch $TESTDIR/writable +log_must user_run $QUSER1 touch $TESTDIR/immutable +log_must user_run $QUSER1 touch $TESTDIR/append + +log_must chattr -i $TESTDIR/writable +log_must chattr +i $TESTDIR/immutable +log_must chattr +a $TESTDIR/append + +log_must user_run $QUSER1 chattr -i $TESTDIR/writable +log_must user_run $QUSER1 chattr -a $TESTDIR/writable +log_must user_run $QUSER1 chattr +i $TESTDIR/immutable +log_must user_run $QUSER1 chattr +a $TESTDIR/append + +log_mustnot user_run $QUSER1 chattr +i $TESTDIR/writable +log_mustnot user_run $QUSER1 chattr +a $TESTDIR/writable +log_mustnot user_run $QUSER1 chattr -i $TESTDIR/immutable +log_mustnot user_run $QUSER1 chattr -a $TESTDIR/append + +log_pass "Unprivileged user cannot chattr as expected" diff --git a/tests/zfs-tests/tests/functional/chattr/cleanup.ksh b/tests/zfs-tests/tests/functional/chattr/cleanup.ksh new file mode 100755 index 000000000000..59875d609ad9 --- /dev/null +++ b/tests/zfs-tests/tests/functional/chattr/cleanup.ksh @@ -0,0 +1,37 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 http://www.opensolaris.org/os/licensing. +# 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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2013 by Delphix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib + +log_must clean_user_group + +default_cleanup diff --git a/tests/zfs-tests/tests/functional/chattr/setup.ksh b/tests/zfs-tests/tests/functional/chattr/setup.ksh new file mode 100755 index 000000000000..1c27075a10c3 --- /dev/null +++ b/tests/zfs-tests/tests/functional/chattr/setup.ksh @@ -0,0 +1,44 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 http://www.opensolaris.org/os/licensing. +# 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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2013 by Delphix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib + +verify_runnable "both" + +log_must clean_user_group + +log_must add_group $QGROUP +log_must add_user $QGROUP $QUSER1 +log_must add_user $QGROUP $QUSER2 + +DISK=${DISKS%% *} +default_setup $DISK