Linux 2.6.29 compat, credentials
[zfs.git] / module / zfs / zfs_vnops.c
index 821ecec..00534d8 100644 (file)
  *     return (error);                 // done, report error
  */
 
+/*
+ * Virus scanning is unsupported.  It would be possible to add a hook
+ * here to performance the required virus scan.  This could be done
+ * entirely in the kernel or potentially as an update to invoke a
+ * scanning utility.
+ */
+static int
+zfs_vscan(struct inode *ip, cred_t *cr, int async)
+{
+       return (0);
+}
+
+/* ARGSUSED */
+int
+zfs_open(struct inode *ip, int mode, int flag, cred_t *cr)
+{
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
+
+       ZFS_ENTER(zsb);
+       ZFS_VERIFY_ZP(zp);
+
+       /* Honor ZFS_APPENDONLY file attribute */
+       if ((mode & FMODE_WRITE) && (zp->z_pflags & ZFS_APPENDONLY) &&
+           ((flag & O_APPEND) == 0)) {
+               ZFS_EXIT(zsb);
+               return (EPERM);
+       }
+
+       /* Virus scan eligible files on open */
+       if (!zfs_has_ctldir(zp) && zsb->z_vscan && S_ISREG(ip->i_mode) &&
+           !(zp->z_pflags & ZFS_AV_QUARANTINED) && zp->z_size > 0) {
+               if (zfs_vscan(ip, cr, 0) != 0) {
+                       ZFS_EXIT(zsb);
+                       return (EACCES);
+               }
+       }
+
+       /* Keep a count of the synchronous opens in the znode */
+       if (flag & O_SYNC)
+               atomic_inc_32(&zp->z_sync_cnt);
+
+       ZFS_EXIT(zsb);
+       return (0);
+}
+EXPORT_SYMBOL(zfs_open);
+
+/* ARGSUSED */
+int
+zfs_close(struct inode *ip, int flag, cred_t *cr)
+{
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ITOZSB(ip);
+
+       ZFS_ENTER(zsb);
+       ZFS_VERIFY_ZP(zp);
+
+       /* Decrement the synchronous opens in the znode */
+       if (flag & O_SYNC)
+               zp->z_sync_cnt = 0;
+
+       if (!zfs_has_ctldir(zp) && zsb->z_vscan && S_ISREG(ip->i_mode) &&
+           !(zp->z_pflags & ZFS_AV_QUARANTINED) && zp->z_size > 0)
+               VERIFY(zfs_vscan(ip, cr, 1) == 0);
+
+       ZFS_EXIT(zsb);
+       return (0);
+}
+EXPORT_SYMBOL(zfs_close);
+
 #if defined(_KERNEL)
 /*
  * When a file is memory mapped, we must keep the IO data synchronized
@@ -1174,15 +1244,13 @@ zfs_create(struct inode *dip, char *name, vattr_t *vap, int excl,
                return (EILSEQ);
        }
 
-#ifdef HAVE_XVATTR
-       if (vap->va_mask & AT_XVATTR) {
+       if (vap->va_mask & ATTR_XVATTR) {
                if ((error = secpolicy_xvattr((xvattr_t *)vap,
                    crgetuid(cr), cr, vap->va_mode)) != 0) {
                        ZFS_EXIT(zsb);
                        return (error);
                }
        }
-#endif /* HAVE_XVATTR */
 
 top:
        *ipp = NULL;
@@ -1613,15 +1681,13 @@ zfs_mkdir(struct inode *dip, char *dirname, vattr_t *vap, struct inode **ipp,
        if (flags & FIGNORECASE)
                zf |= ZCILOOK;
 
-#ifdef HAVE_XVATTR
-       if (vap->va_mask & AT_XVATTR) {
+       if (vap->va_mask & ATTR_XVATTR) {
                if ((error = secpolicy_xvattr((xvattr_t *)vap,
                    crgetuid(cr), cr, vap->va_mode)) != 0) {
                        ZFS_EXIT(zsb);
                        return (error);
                }
        }
-#endif /* HAVE_XVATTR */
 
        if ((error = zfs_acl_ids_create(dzp, 0, vap, cr,
            vsecp, &acl_ids)) != 0) {
@@ -2029,22 +2095,26 @@ EXPORT_SYMBOL(zfs_fsync);
  * vattr structure.
  *
  *     IN:     ip      - inode of file.
- *             stat    - kstat structure to fill in.
+ *             vap     - va_mask identifies requested attributes.
+ *                       If ATTR_XVATTR set, then optional attrs are requested
  *             flags   - ATTR_NOACLCHECK (CIFS server context)
  *             cr      - credentials of caller.
  *
- *     OUT:    stat    - filled in kstat values.
+ *     OUT:    vap     - attribute values.
+ *
+ *     RETURN: 0 (always succeeds)
  */
 /* ARGSUSED */
 int
-zfs_getattr(struct inode *ip, struct kstat *stat, int flags, cred_t *cr)
+zfs_getattr(struct inode *ip, vattr_t *vap, int flags, cred_t *cr)
 {
        znode_t *zp = ITOZ(ip);
        zfs_sb_t *zsb = ITOZSB(ip);
        int     error = 0;
        uint64_t links;
        uint64_t mtime[2], ctime[2];
-       uint32_t blksz;
+       xvattr_t *xvap = (xvattr_t *)vap;       /* vap may be an xvattr_t * */
+       xoptattr_t *xoap = NULL;
        boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;
        sa_bulk_attr_t bulk[2];
        int count = 0;
@@ -2052,7 +2122,7 @@ zfs_getattr(struct inode *ip, struct kstat *stat, int flags, cred_t *cr)
        ZFS_ENTER(zsb);
        ZFS_VERIFY_ZP(zp);
 
-       zfs_fuid_map_ids(zp, cr, &stat->uid, &stat->gid);
+       zfs_fuid_map_ids(zp, cr, &vap->va_uid, &vap->va_gid);
 
        SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zsb), NULL, &mtime, 16);
        SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zsb), NULL, &ctime, 16);
@@ -2068,7 +2138,7 @@ zfs_getattr(struct inode *ip, struct kstat *stat, int flags, cred_t *cr)
         * always be allowed to read basic attributes of file.
         */
        if (!(zp->z_pflags & ZFS_ACL_TRIVIAL) &&
-           (stat->uid != crgetuid(cr))) {
+           (vap->va_uid != crgetuid(cr))) {
                if ((error = zfs_zaccess(zp, ACE_READ_ATTRIBUTES, 0,
                    skipaclchk, cr))) {
                        ZFS_EXIT(zsb);
@@ -2082,33 +2152,139 @@ zfs_getattr(struct inode *ip, struct kstat *stat, int flags, cred_t *cr)
         */
 
        mutex_enter(&zp->z_lock);
-       stat->ino = ip->i_ino;
-       stat->mode = zp->z_mode;
-       stat->uid = zp->z_uid;
-       stat->gid = zp->z_gid;
+       vap->va_type = vn_mode_to_vtype(zp->z_mode);
+       vap->va_mode = zp->z_mode;
+       vap->va_fsid = ZTOI(zp)->i_sb->s_dev;
+       vap->va_nodeid = zp->z_id;
        if ((zp->z_id == zsb->z_root) && zfs_show_ctldir(zp))
                links = zp->z_links + 1;
        else
                links = zp->z_links;
-       stat->nlink = MIN(links, ZFS_LINK_MAX);
-       stat->size = i_size_read(ip);
-       stat->rdev = ip->i_rdev;
-       stat->dev = ip->i_rdev;
+       vap->va_nlink = MIN(links, ZFS_LINK_MAX);
+       vap->va_size = i_size_read(ip);
+       vap->va_rdev = ip->i_rdev;
+       vap->va_seq = ip->i_generation;
+
+       /*
+        * Add in any requested optional attributes and the create time.
+        * Also set the corresponding bits in the returned attribute bitmap.
+        */
+       if ((xoap = xva_getxoptattr(xvap)) != NULL && zsb->z_use_fuids) {
+               if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) {
+                       xoap->xoa_archive =
+                           ((zp->z_pflags & ZFS_ARCHIVE) != 0);
+                       XVA_SET_RTN(xvap, XAT_ARCHIVE);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_READONLY)) {
+                       xoap->xoa_readonly =
+                           ((zp->z_pflags & ZFS_READONLY) != 0);
+                       XVA_SET_RTN(xvap, XAT_READONLY);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) {
+                       xoap->xoa_system =
+                           ((zp->z_pflags & ZFS_SYSTEM) != 0);
+                       XVA_SET_RTN(xvap, XAT_SYSTEM);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) {
+                       xoap->xoa_hidden =
+                           ((zp->z_pflags & ZFS_HIDDEN) != 0);
+                       XVA_SET_RTN(xvap, XAT_HIDDEN);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) {
+                       xoap->xoa_nounlink =
+                           ((zp->z_pflags & ZFS_NOUNLINK) != 0);
+                       XVA_SET_RTN(xvap, XAT_NOUNLINK);
+               }
 
-       ZFS_TIME_DECODE(&stat->atime, zp->z_atime);
-       ZFS_TIME_DECODE(&stat->mtime, mtime);
-       ZFS_TIME_DECODE(&stat->ctime, ctime);
+               if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) {
+                       xoap->xoa_immutable =
+                           ((zp->z_pflags & ZFS_IMMUTABLE) != 0);
+                       XVA_SET_RTN(xvap, XAT_IMMUTABLE);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) {
+                       xoap->xoa_appendonly =
+                           ((zp->z_pflags & ZFS_APPENDONLY) != 0);
+                       XVA_SET_RTN(xvap, XAT_APPENDONLY);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_NODUMP)) {
+                       xoap->xoa_nodump =
+                           ((zp->z_pflags & ZFS_NODUMP) != 0);
+                       XVA_SET_RTN(xvap, XAT_NODUMP);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_OPAQUE)) {
+                       xoap->xoa_opaque =
+                           ((zp->z_pflags & ZFS_OPAQUE) != 0);
+                       XVA_SET_RTN(xvap, XAT_OPAQUE);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) {
+                       xoap->xoa_av_quarantined =
+                           ((zp->z_pflags & ZFS_AV_QUARANTINED) != 0);
+                       XVA_SET_RTN(xvap, XAT_AV_QUARANTINED);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) {
+                       xoap->xoa_av_modified =
+                           ((zp->z_pflags & ZFS_AV_MODIFIED) != 0);
+                       XVA_SET_RTN(xvap, XAT_AV_MODIFIED);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP) &&
+                   S_ISREG(ip->i_mode)) {
+                       zfs_sa_get_scanstamp(zp, xvap);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) {
+                       uint64_t times[2];
+
+                       (void) sa_lookup(zp->z_sa_hdl, SA_ZPL_CRTIME(zsb),
+                           times, sizeof (times));
+                       ZFS_TIME_DECODE(&xoap->xoa_createtime, times);
+                       XVA_SET_RTN(xvap, XAT_CREATETIME);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_REPARSE)) {
+                       xoap->xoa_reparse = ((zp->z_pflags & ZFS_REPARSE) != 0);
+                       XVA_SET_RTN(xvap, XAT_REPARSE);
+               }
+               if (XVA_ISSET_REQ(xvap, XAT_GEN)) {
+                       xoap->xoa_generation = zp->z_gen;
+                       XVA_SET_RTN(xvap, XAT_GEN);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_OFFLINE)) {
+                       xoap->xoa_offline =
+                           ((zp->z_pflags & ZFS_OFFLINE) != 0);
+                       XVA_SET_RTN(xvap, XAT_OFFLINE);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_SPARSE)) {
+                       xoap->xoa_sparse =
+                           ((zp->z_pflags & ZFS_SPARSE) != 0);
+                       XVA_SET_RTN(xvap, XAT_SPARSE);
+               }
+       }
+
+       ZFS_TIME_DECODE(&vap->va_atime, zp->z_atime);
+       ZFS_TIME_DECODE(&vap->va_mtime, mtime);
+       ZFS_TIME_DECODE(&vap->va_ctime, ctime);
 
        mutex_exit(&zp->z_lock);
 
-       sa_object_size(zp->z_sa_hdl, &blksz, &stat->blocks);
-       stat->blksize = (1 << ip->i_blkbits);
+       sa_object_size(zp->z_sa_hdl, &vap->va_blksize, &vap->va_nblocks);
 
        if (zp->z_blksz == 0) {
                /*
                 * Block size hasn't been set; suggest maximal I/O transfers.
                 */
-               stat->blksize = zsb->z_max_blksz;
+               vap->va_blksize = zsb->z_max_blksz;
        }
 
        ZFS_EXIT(zsb);
@@ -2122,7 +2298,7 @@ EXPORT_SYMBOL(zfs_getattr);
  *
  *     IN:     ip      - inode of file to be modified.
  *             vap     - new attribute values.
- *                       If AT_XVATTR set, then optional attrs are being set
+ *                       If ATTR_XVATTR set, then optional attrs are being set
  *             flags   - ATTR_UTIME set if non-default time values provided.
  *                     - ATTR_NOACLCHECK (CIFS context only).
  *             cr      - credentials of caller.
@@ -2135,14 +2311,15 @@ EXPORT_SYMBOL(zfs_getattr);
  */
 /* ARGSUSED */
 int
-zfs_setattr(struct inode *ip, struct iattr *attr, int flags, cred_t *cr)
+zfs_setattr(struct inode *ip, vattr_t *vap, int flags, cred_t *cr)
 {
        znode_t         *zp = ITOZ(ip);
        zfs_sb_t        *zsb = ITOZSB(ip);
        zilog_t         *zilog;
        dmu_tx_t        *tx;
        vattr_t         oldva;
-       uint_t          mask = attr->ia_valid;
+       xvattr_t        *tmpxvattr;
+       uint_t          mask = vap->va_mask;
        uint_t          saved_mask;
        int             trim_mask = 0;
        uint64_t        new_mode;
@@ -2153,10 +2330,12 @@ zfs_setattr(struct inode *ip, struct iattr *attr, int flags, cred_t *cr)
        int             need_policy = FALSE;
        int             err, err2;
        zfs_fuid_info_t *fuidp = NULL;
+       xvattr_t *xvap = (xvattr_t *)vap;       /* vap may be an xvattr_t * */
+       xoptattr_t      *xoap;
+       zfs_acl_t       *aclp;
        boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;
-       zfs_acl_t       *aclp = NULL;
        boolean_t       fuid_dirtied = B_FALSE;
-       sa_bulk_attr_t  bulk[7], xattr_bulk[7];
+       sa_bulk_attr_t  *bulk, *xattr_bulk;
        int             count = 0, xattr_count = 0;
 
        if (mask == 0)
@@ -2171,9 +2350,11 @@ zfs_setattr(struct inode *ip, struct iattr *attr, int flags, cred_t *cr)
         * Make sure that if we have ephemeral uid/gid or xvattr specified
         * that file system is at proper version level
         */
+
        if (zsb->z_use_fuids == B_FALSE &&
-           (((mask & ATTR_UID) && IS_EPHEMERAL(attr->ia_uid)) ||
-           ((mask & ATTR_GID) && IS_EPHEMERAL(attr->ia_gid)))) {
+           (((mask & ATTR_UID) && IS_EPHEMERAL(vap->va_uid)) ||
+           ((mask & ATTR_GID) && IS_EPHEMERAL(vap->va_gid)) ||
+           (mask & ATTR_XVATTR))) {
                ZFS_EXIT(zsb);
                return (EINVAL);
        }
@@ -2188,9 +2369,45 @@ zfs_setattr(struct inode *ip, struct iattr *attr, int flags, cred_t *cr)
                return (EINVAL);
        }
 
+       /*
+        * If this is an xvattr_t, then get a pointer to the structure of
+        * optional attributes.  If this is NULL, then we have a vattr_t.
+        */
+       xoap = xva_getxoptattr(xvap);
+
+       tmpxvattr = kmem_alloc(sizeof(xvattr_t), KM_SLEEP);
+       xva_init(tmpxvattr);
+
+       bulk = kmem_alloc(sizeof(sa_bulk_attr_t) * 7, KM_SLEEP);
+       xattr_bulk = kmem_alloc(sizeof(sa_bulk_attr_t) * 7, KM_SLEEP);
+
+       /*
+        * Immutable files can only alter immutable bit and atime
+        */
+       if ((zp->z_pflags & ZFS_IMMUTABLE) &&
+           ((mask & (ATTR_SIZE|ATTR_UID|ATTR_GID|ATTR_MTIME|ATTR_MODE)) ||
+           ((mask & ATTR_XVATTR) && XVA_ISSET_REQ(xvap, XAT_CREATETIME)))) {
+               err = EPERM;
+               goto out3;
+       }
+
        if ((mask & ATTR_SIZE) && (zp->z_pflags & ZFS_READONLY)) {
-               ZFS_EXIT(zsb);
-               return (EPERM);
+               err = EPERM;
+               goto out3;
+       }
+
+       /*
+        * Verify timestamps doesn't overflow 32 bits.
+        * ZFS can handle large timestamps, but 32bit syscalls can't
+        * handle times greater than 2039.  This check should be removed
+        * once large timestamps are fully supported.
+        */
+       if (mask & (ATTR_ATIME | ATTR_MTIME)) {
+               if (((mask & ATTR_ATIME) && TIMESPEC_OVERFLOW(&vap->va_atime)) ||
+                   ((mask & ATTR_MTIME) && TIMESPEC_OVERFLOW(&vap->va_mtime))) {
+                       err = EOVERFLOW;
+                       goto out3;
+               }
        }
 
 top:
@@ -2199,8 +2416,8 @@ top:
 
        /* Can this be moved to before the top label? */
        if (zsb->z_vfs->mnt_flags & MNT_READONLY) {
-               ZFS_EXIT(zsb);
-               return (EROFS);
+               err = EROFS;
+               goto out3;
        }
 
        /*
@@ -2209,10 +2426,9 @@ top:
 
        if (mask & ATTR_SIZE) {
                err = zfs_zaccess(zp, ACE_WRITE_DATA, 0, skipaclchk, cr);
-               if (err) {
-                       ZFS_EXIT(zsb);
-                       return (err);
-               }
+               if (err)
+                       goto out3;
+
                /*
                 * XXX - Note, we are not providing any open
                 * mode flags here (like FNDELAY), so we may
@@ -2220,18 +2436,26 @@ top:
                 * should be addressed in openat().
                 */
                /* XXX - would it be OK to generate a log record here? */
-               err = zfs_freesp(zp, attr->ia_size, 0, 0, FALSE);
-               if (err) {
-                       ZFS_EXIT(zsb);
-                       return (err);
-               }
+               err = zfs_freesp(zp, vap->va_size, 0, 0, FALSE);
+               if (err)
+                       goto out3;
 
                /* Careful negative Linux return code here */
-               err = -vmtruncate(ip, attr->ia_size);
-               if (err) {
-                       ZFS_EXIT(zsb);
-                       return (err);
-               }
+               err = -vmtruncate(ip, vap->va_size);
+               if (err)
+                       goto out3;
+       }
+
+       if (mask & (ATTR_ATIME|ATTR_MTIME) ||
+           ((mask & ATTR_XVATTR) && (XVA_ISSET_REQ(xvap, XAT_HIDDEN) ||
+           XVA_ISSET_REQ(xvap, XAT_READONLY) ||
+           XVA_ISSET_REQ(xvap, XAT_ARCHIVE) ||
+           XVA_ISSET_REQ(xvap, XAT_OFFLINE) ||
+           XVA_ISSET_REQ(xvap, XAT_SPARSE) ||
+           XVA_ISSET_REQ(xvap, XAT_CREATETIME) ||
+           XVA_ISSET_REQ(xvap, XAT_SYSTEM)))) {
+               need_policy = zfs_zaccess(zp, ACE_WRITE_ATTRIBUTES, 0,
+                   skipaclchk, cr);
        }
 
        if (mask & (ATTR_UID|ATTR_GID)) {
@@ -2245,19 +2469,18 @@ top:
                 */
 
                if (!(mask & ATTR_MODE))
-                       attr->ia_mode = zp->z_mode;
+                       vap->va_mode = zp->z_mode;
 
                /*
                 * Take ownership or chgrp to group we are a member of
                 */
 
-               take_owner = (mask & ATTR_UID) &&
-                   (attr->ia_uid == crgetuid(cr));
+               take_owner = (mask & ATTR_UID) && (vap->va_uid == crgetuid(cr));
                take_group = (mask & ATTR_GID) &&
-                   zfs_groupmember(zsb, attr->ia_gid, cr);
+                   zfs_groupmember(zsb, vap->va_gid, cr);
 
                /*
-                * If both AT_UID and AT_GID are set then take_owner and
+                * If both ATTR_UID and ATTR_GID are set then take_owner and
                 * take_group must both be set in order to allow taking
                 * ownership.
                 *
@@ -2274,7 +2497,7 @@ top:
                                /*
                                 * Remove setuid/setgid for non-privileged users
                                 */
-                               (void) secpolicy_setid_clear(attr, cr);
+                               (void) secpolicy_setid_clear(vap, cr);
                                trim_mask = (mask & (ATTR_UID|ATTR_GID));
                        } else {
                                need_policy =  TRUE;
@@ -2287,17 +2510,98 @@ top:
        mutex_enter(&zp->z_lock);
        oldva.va_mode = zp->z_mode;
        zfs_fuid_map_ids(zp, cr, &oldva.va_uid, &oldva.va_gid);
+       if (mask & ATTR_XVATTR) {
+               /*
+                * Update xvattr mask to include only those attributes
+                * that are actually changing.
+                *
+                * the bits will be restored prior to actually setting
+                * the attributes so the caller thinks they were set.
+                */
+               if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) {
+                       if (xoap->xoa_appendonly !=
+                           ((zp->z_pflags & ZFS_APPENDONLY) != 0)) {
+                               need_policy = TRUE;
+                       } else {
+                               XVA_CLR_REQ(xvap, XAT_APPENDONLY);
+                               XVA_SET_REQ(tmpxvattr, XAT_APPENDONLY);
+                       }
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) {
+                       if (xoap->xoa_nounlink !=
+                           ((zp->z_pflags & ZFS_NOUNLINK) != 0)) {
+                               need_policy = TRUE;
+                       } else {
+                               XVA_CLR_REQ(xvap, XAT_NOUNLINK);
+                               XVA_SET_REQ(tmpxvattr, XAT_NOUNLINK);
+                       }
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) {
+                       if (xoap->xoa_immutable !=
+                           ((zp->z_pflags & ZFS_IMMUTABLE) != 0)) {
+                               need_policy = TRUE;
+                       } else {
+                               XVA_CLR_REQ(xvap, XAT_IMMUTABLE);
+                               XVA_SET_REQ(tmpxvattr, XAT_IMMUTABLE);
+                       }
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_NODUMP)) {
+                       if (xoap->xoa_nodump !=
+                           ((zp->z_pflags & ZFS_NODUMP) != 0)) {
+                               need_policy = TRUE;
+                       } else {
+                               XVA_CLR_REQ(xvap, XAT_NODUMP);
+                               XVA_SET_REQ(tmpxvattr, XAT_NODUMP);
+                       }
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) {
+                       if (xoap->xoa_av_modified !=
+                           ((zp->z_pflags & ZFS_AV_MODIFIED) != 0)) {
+                               need_policy = TRUE;
+                       } else {
+                               XVA_CLR_REQ(xvap, XAT_AV_MODIFIED);
+                               XVA_SET_REQ(tmpxvattr, XAT_AV_MODIFIED);
+                       }
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) {
+                       if ((!S_ISREG(ip->i_mode) &&
+                           xoap->xoa_av_quarantined) ||
+                           xoap->xoa_av_quarantined !=
+                           ((zp->z_pflags & ZFS_AV_QUARANTINED) != 0)) {
+                               need_policy = TRUE;
+                       } else {
+                               XVA_CLR_REQ(xvap, XAT_AV_QUARANTINED);
+                               XVA_SET_REQ(tmpxvattr, XAT_AV_QUARANTINED);
+                       }
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_REPARSE)) {
+                       mutex_exit(&zp->z_lock);
+                       err = EPERM;
+                       goto out3;
+               }
+
+               if (need_policy == FALSE &&
+                   (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP) ||
+                   XVA_ISSET_REQ(xvap, XAT_OPAQUE))) {
+                       need_policy = TRUE;
+               }
+       }
 
        mutex_exit(&zp->z_lock);
 
        if (mask & ATTR_MODE) {
                if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr) == 0) {
-                       err = secpolicy_setid_setsticky_clear(ip, attr,
+                       err = secpolicy_setid_setsticky_clear(ip, vap,
                            &oldva, cr);
-                       if (err) {
-                               ZFS_EXIT(zsb);
-                               return (err);
-                       }
+                       if (err)
+                               goto out3;
+
                        trim_mask |= ATTR_MODE;
                } else {
                        need_policy = TRUE;
@@ -2314,25 +2618,23 @@ top:
                 */
 
                if (trim_mask) {
-                       saved_mask = attr->ia_valid;
-                       attr->ia_valid &= ~trim_mask;
+                       saved_mask = vap->va_mask;
+                       vap->va_mask &= ~trim_mask;
                }
-               err = secpolicy_vnode_setattr(cr, ip, attr, &oldva, flags,
+               err = secpolicy_vnode_setattr(cr, ip, vap, &oldva, flags,
                    (int (*)(void *, int, cred_t *))zfs_zaccess_unix, zp);
-               if (err) {
-                       ZFS_EXIT(zsb);
-                       return (err);
-               }
+               if (err)
+                       goto out3;
 
                if (trim_mask)
-                       attr->ia_valid |= saved_mask;
+                       vap->va_mask |= saved_mask;
        }
 
        /*
         * secpolicy_vnode_setattr, or take ownership may have
         * changed va_mask
         */
-       mask = attr->ia_valid;
+       mask = vap->va_mask;
 
        if ((mask & (ATTR_UID | ATTR_GID))) {
                err = sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zsb),
@@ -2345,7 +2647,7 @@ top:
                }
                if (mask & ATTR_UID) {
                        new_uid = zfs_fuid_create(zsb,
-                           (uint64_t)attr->ia_uid, cr, ZFS_OWNER, &fuidp);
+                           (uint64_t)vap->va_uid, cr, ZFS_OWNER, &fuidp);
                        if (new_uid != zp->z_uid &&
                            zfs_fuid_overquota(zsb, B_FALSE, new_uid)) {
                                if (attrzp)
@@ -2356,7 +2658,7 @@ top:
                }
 
                if (mask & ATTR_GID) {
-                       new_gid = zfs_fuid_create(zsb, (uint64_t)attr->ia_gid,
+                       new_gid = zfs_fuid_create(zsb, (uint64_t)vap->va_gid,
                            cr, ZFS_GROUP, &fuidp);
                        if (new_gid != zp->z_gid &&
                            zfs_fuid_overquota(zsb, B_TRUE, new_gid)) {
@@ -2372,7 +2674,7 @@ top:
        if (mask & ATTR_MODE) {
                uint64_t pmode = zp->z_mode;
                uint64_t acl_obj;
-               new_mode = (pmode & S_IFMT) | (attr->ia_mode & ~S_IFMT);
+               new_mode = (pmode & S_IFMT) | (vap->va_mode & ~S_IFMT);
 
                zfs_acl_chmod_setattr(zp, &aclp, new_mode);
 
@@ -2400,7 +2702,11 @@ top:
                mutex_exit(&zp->z_lock);
                dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE);
        } else {
-               dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
+               if ((mask & ATTR_XVATTR) &&
+                   XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP))
+                       dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE);
+               else
+                       dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
        }
 
        if (attrzp) {
@@ -2488,7 +2794,7 @@ top:
                SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zsb), NULL,
                    &new_mode, sizeof (new_mode));
                zp->z_mode = new_mode;
-               ASSERT3U((uintptr_t)aclp, !=, NULL);
+               ASSERT3P(aclp, !=, NULL);
                err = zfs_aclset_common(zp, aclp, cr, tx);
                ASSERT3U(err, ==, 0);
                if (zp->z_acl_cached)
@@ -2499,13 +2805,13 @@ top:
 
 
        if (mask & ATTR_ATIME) {
-               ZFS_TIME_ENCODE(&attr->ia_atime, zp->z_atime);
+               ZFS_TIME_ENCODE(&vap->va_atime, zp->z_atime);
                SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ATIME(zsb), NULL,
                    &zp->z_atime, sizeof (zp->z_atime));
        }
 
        if (mask & ATTR_MTIME) {
-               ZFS_TIME_ENCODE(&attr->ia_mtime, mtime);
+               ZFS_TIME_ENCODE(&vap->va_mtime, mtime);
                SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zsb), NULL,
                    mtime, sizeof (mtime));
        }
@@ -2536,11 +2842,43 @@ top:
         * update from toggling bit
         */
 
+       if (xoap && (mask & ATTR_XVATTR)) {
+
+               /*
+                * restore trimmed off masks
+                * so that return masks can be set for caller.
+                */
+
+               if (XVA_ISSET_REQ(tmpxvattr, XAT_APPENDONLY)) {
+                       XVA_SET_REQ(xvap, XAT_APPENDONLY);
+               }
+               if (XVA_ISSET_REQ(tmpxvattr, XAT_NOUNLINK)) {
+                       XVA_SET_REQ(xvap, XAT_NOUNLINK);
+               }
+               if (XVA_ISSET_REQ(tmpxvattr, XAT_IMMUTABLE)) {
+                       XVA_SET_REQ(xvap, XAT_IMMUTABLE);
+               }
+               if (XVA_ISSET_REQ(tmpxvattr, XAT_NODUMP)) {
+                       XVA_SET_REQ(xvap, XAT_NODUMP);
+               }
+               if (XVA_ISSET_REQ(tmpxvattr, XAT_AV_MODIFIED)) {
+                       XVA_SET_REQ(xvap, XAT_AV_MODIFIED);
+               }
+               if (XVA_ISSET_REQ(tmpxvattr, XAT_AV_QUARANTINED)) {
+                       XVA_SET_REQ(xvap, XAT_AV_QUARANTINED);
+               }
+
+               if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP))
+                       ASSERT(S_ISREG(ip->i_mode));
+
+               zfs_xvattr_set(zp, xvap, tx);
+       }
+
        if (fuid_dirtied)
                zfs_fuid_sync(zsb, tx);
 
        if (mask != 0)
-               zfs_log_setattr(zilog, tx, TX_SETATTR, zp, attr, mask, fuidp);
+               zfs_log_setattr(zilog, tx, TX_SETATTR, zp, vap, mask, fuidp);
 
        mutex_exit(&zp->z_lock);
        if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE))
@@ -2582,6 +2920,10 @@ out2:
        if (zsb->z_os->os_sync == ZFS_SYNC_ALWAYS)
                zil_commit(zilog, 0);
 
+out3:
+       kmem_free(xattr_bulk, sizeof(sa_bulk_attr_t) * 7);
+       kmem_free(bulk, sizeof(sa_bulk_attr_t) * 7);
+       kmem_free(tmpxvattr, sizeof(xvattr_t));
        ZFS_EXIT(zsb);
        return (err);
 }
@@ -3524,8 +3866,7 @@ out:
  */
 /*ARGSUSED*/
 static int
-zfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr,
-    caller_context_t *ct)
+zfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr)
 {
        znode_t         *zp = VTOZ(vp);
        zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
@@ -3660,8 +4001,7 @@ EXPORT_SYMBOL(zfs_inactive);
  */
 /* ARGSUSED */
 int
-zfs_seek(struct inode *ip, offset_t ooff, offset_t *noffp,
-    caller_context_t *ct)
+zfs_seek(struct inode *ip, offset_t ooff, offset_t *noffp)
 {
        if (S_ISDIR(ip->i_mode))
                return (0);
@@ -3676,7 +4016,7 @@ EXPORT_SYMBOL(zfs_seek);
  */
 static int
 zfs_frlock(vnode_t *vp, int cmd, flock64_t *bfp, int flag, offset_t offset,
-    flk_callback_t *flk_cbp, cred_t *cr, caller_context_t *ct)
+    flk_callback_t *flk_cbp, cred_t *cr)
 {
        znode_t *zp = VTOZ(vp);
        zfsvfs_t *zfsvfs = zp->z_zfsvfs;
@@ -3804,7 +4144,7 @@ zfs_fillpage(vnode_t *vp, u_offset_t off, struct seg *seg,
 static int
 zfs_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp,
        page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr,
-       enum seg_rw rw, cred_t *cr, caller_context_t *ct)
+       enum seg_rw rw, cred_t *cr)
 {
        znode_t         *zp = VTOZ(vp);
        zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
@@ -3892,8 +4232,7 @@ out:
 /*ARGSUSED*/
 static int
 zfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp,
-    size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
-    caller_context_t *ct)
+    size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr)
 {
        znode_t *zp = VTOZ(vp);
        zfsvfs_t *zfsvfs = zp->z_zfsvfs;
@@ -3967,8 +4306,7 @@ zfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp,
 /* ARGSUSED */
 static int
 zfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
-    size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
-    caller_context_t *ct)
+    size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr)
 {
        uint64_t pages = btopr(len);
 
@@ -4000,8 +4338,7 @@ zfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
 /* ARGSUSED */
 static int
 zfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
-    size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr,
-    caller_context_t *ct)
+    size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr)
 {
        uint64_t pages = btopr(len);
 
@@ -4023,11 +4360,11 @@ zfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
 int
 convoff(struct inode *ip, flock64_t *lckdat, int  whence, offset_t offset)
 {
-       struct kstat stat;
+       vattr_t vap;
        int error;
 
        if ((lckdat->l_whence == 2) || (whence == 2)) {
-               if ((error = zfs_getattr(ip, &stat, 0, CRED()) != 0))
+               if ((error = zfs_getattr(ip, &vap, 0, CRED()) != 0))
                        return (error);
        }
 
@@ -4036,7 +4373,7 @@ convoff(struct inode *ip, flock64_t *lckdat, int  whence, offset_t offset)
                lckdat->l_start += offset;
                break;
        case 2:
-               lckdat->l_start += stat.size;
+               lckdat->l_start += vap.va_size;
                /* FALLTHRU */
        case 0:
                break;
@@ -4052,7 +4389,7 @@ convoff(struct inode *ip, flock64_t *lckdat, int  whence, offset_t offset)
                lckdat->l_start -= offset;
                break;
        case 2:
-               lckdat->l_start -= stat.size;
+               lckdat->l_start -= vap.va_size;
                /* FALLTHRU */
        case 0:
                break;