X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=module%2Fzfs%2Fzfs_vnops.c;h=00534d845ca6cd9e8ad8ee5f4373cd8ed9c54c39;hb=0d3ac5e7356d29fbb7d2880c0a0c457656355ca0;hp=a8019ba5ca9949e5772bc58847fe905a46c26441;hpb=3558fd73b5d863304102f6745c26e0b592aca60a;p=zfs.git diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c index a8019ba..00534d8 100644 --- a/module/zfs/zfs_vnops.c +++ b/module/zfs/zfs_vnops.c @@ -163,32 +163,77 @@ * return (error); // done, report error */ -#if defined(_KERNEL) && defined(HAVE_MMAP) /* - * Utility functions to map and unmap a single physical page. These - * are used to manage the mappable copies of ZFS file data, and therefore - * do not update ref/mod bits. + * 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. */ -caddr_t -zfs_map_page(page_t *pp, enum seg_rw rw) +static int +zfs_vscan(struct inode *ip, cred_t *cr, int async) { - if (kpm_enable) - return (hat_kpm_mapin(pp, 0)); - ASSERT(rw == S_READ || rw == S_WRITE); - return (ppmapin(pp, PROT_READ | ((rw == S_WRITE) ? PROT_WRITE : 0), - (caddr_t)-1)); + return (0); } -void -zfs_unmap_page(page_t *pp, caddr_t addr) +/* ARGSUSED */ +int +zfs_open(struct inode *ip, int mode, int flag, cred_t *cr) { - if (kpm_enable) { - hat_kpm_mapout(pp, 0, addr); - } else { - ppmapout(addr); + 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 * between the DMU cache and the memory mapped pages. What this means: @@ -197,25 +242,39 @@ zfs_unmap_page(page_t *pp, caddr_t addr) * the page and the dmu buffer. */ static void -update_pages(struct inode *ip, int64_t start, int len, objset_t *os, - uint64_t oid) +update_pages(struct inode *ip, int64_t start, int len, + objset_t *os, uint64_t oid) { + struct address_space *mp = ip->i_mapping; + struct page *pp; + uint64_t nbytes; int64_t off; + void *pb; - off = start & PAGEOFFSET; - for (start &= PAGEMASK; len > 0; start += PAGESIZE) { - page_t *pp; - uint64_t nbytes = MIN(PAGESIZE - off, len); + off = start & (PAGE_CACHE_SIZE-1); + for (start &= PAGE_CACHE_MASK; len > 0; start += PAGE_CACHE_SIZE) { + nbytes = MIN(PAGE_CACHE_SIZE - off, len); - if (pp = page_lookup(ip, start, SE_SHARED)) { - caddr_t va; + pp = find_lock_page(mp, start >> PAGE_CACHE_SHIFT); + if (pp) { + if (mapping_writably_mapped(mp)) + flush_dcache_page(pp); - va = zfs_map_page(pp, S_WRITE); - (void) dmu_read(os, oid, start+off, nbytes, va+off, + pb = kmap(pp); + (void) dmu_read(os, oid, start+off, nbytes, pb+off, DMU_READ_PREFETCH); - zfs_unmap_page(pp, va); - page_unlock(pp); + kunmap(pp); + + if (mapping_writably_mapped(mp)) + flush_dcache_page(pp); + + mark_page_accessed(pp); + SetPageUptodate(pp); + ClearPageError(pp); + unlock_page(pp); + page_cache_release(pp); } + len -= nbytes; off = 0; } @@ -234,28 +293,39 @@ update_pages(struct inode *ip, int64_t start, int len, objset_t *os, static int mappedread(struct inode *ip, int nbytes, uio_t *uio) { + struct address_space *mp = ip->i_mapping; + struct page *pp; znode_t *zp = ITOZ(ip); objset_t *os = ITOZSB(ip)->z_os; int64_t start, off; + uint64_t bytes; int len = nbytes; int error = 0; + void *pb; start = uio->uio_loffset; - off = start & PAGEOFFSET; - for (start &= PAGEMASK; len > 0; start += PAGESIZE) { - page_t *pp; - uint64_t bytes = MIN(PAGESIZE - off, len); - - if (pp = page_lookup(ip, start, SE_SHARED)) { - caddr_t va; - - va = zfs_map_page(pp, S_READ); - error = uiomove(va + off, bytes, UIO_READ, uio); - zfs_unmap_page(pp, va); - page_unlock(pp); + off = start & (PAGE_CACHE_SIZE-1); + for (start &= PAGE_CACHE_MASK; len > 0; start += PAGE_CACHE_SIZE) { + bytes = MIN(PAGE_CACHE_SIZE - off, len); + + pp = find_lock_page(mp, start >> PAGE_CACHE_SHIFT); + if (pp) { + ASSERT(PageUptodate(pp)); + + pb = kmap(pp); + error = uiomove(pb + off, bytes, UIO_READ, uio); + kunmap(pp); + + if (mapping_writably_mapped(mp)) + flush_dcache_page(pp); + + mark_page_accessed(pp); + unlock_page(pp); + page_cache_release(pp); } else { error = dmu_read_uio(os, zp->z_id, uio, bytes); } + len -= bytes; off = 0; if (error) @@ -263,7 +333,7 @@ mappedread(struct inode *ip, int nbytes, uio_t *uio) } return (error); } -#endif /* _KERNEL && HAVE_MMAP */ +#endif /* _KERNEL */ offset_t zfs_read_chunk_size = 1024 * 1024; /* Tunable */ @@ -273,7 +343,8 @@ offset_t zfs_read_chunk_size = 1024 * 1024; /* Tunable */ * IN: ip - inode of file to be read from. * uio - structure supplying read location, range info, * and return buffer. - * ioflag - SYNC flags; used to provide FRSYNC semantics. + * ioflag - FSYNC flags; used to provide FRSYNC semantics. + * O_DIRECT flag; used to bypass page cache. * cr - credentials of caller. * * OUT: uio - updated offset and range, buffer filled. @@ -394,15 +465,11 @@ zfs_read(struct inode *ip, uio_t *uio, int ioflag, cred_t *cr) nbytes = MIN(n, zfs_read_chunk_size - P2PHASE(uio->uio_loffset, zfs_read_chunk_size)); -/* XXX: Drop this, ARC update handled by zpl layer */ -#ifdef HAVE_MMAP - if (vn_has_cached_data(ip)) + if (zp->z_is_mapped && !(ioflag & O_DIRECT)) error = mappedread(ip, nbytes, uio); else error = dmu_read_uio(os, zp->z_id, uio, nbytes); -#else - error = dmu_read_uio(os, zp->z_id, uio, nbytes); -#endif /* HAVE_MMAP */ + if (error) { /* convert checksum errors into IO errors */ if (error == ECKSUM) @@ -429,6 +496,7 @@ EXPORT_SYMBOL(zfs_read); * uio - structure supplying write location, range info, * and data buffer. * ioflag - FAPPEND flag set if in append mode. + * O_DIRECT flag; used to bypass page cache. * cr - credentials of caller. * * OUT: uio - updated offset and range. @@ -700,13 +768,9 @@ again: ASSERT(tx_bytes <= uio->uio_resid); uioskip(uio, tx_bytes); } -/* XXX: Drop this, ARC update handled by zpl layer */ -#ifdef HAVE_MMAP - if (tx_bytes && vn_has_cached_data(ip)) { - update_pages(ip, woff, - tx_bytes, zsb->z_os, zp->z_id); - } -#endif /* HAVE_MMAP */ + + if (tx_bytes && zp->z_is_mapped && !(ioflag & O_DIRECT)) + update_pages(ip, woff, tx_bytes, zsb->z_os, zp->z_id); /* * If we made no progress, we're done. If we made even @@ -1180,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; @@ -1619,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) { @@ -2035,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; @@ -2058,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); @@ -2074,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); @@ -2088,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); @@ -2128,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. @@ -2141,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; @@ -2159,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) @@ -2177,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); } @@ -2194,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: @@ -2205,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; } /* @@ -2215,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 @@ -2226,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)) { @@ -2251,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. * @@ -2280,7 +2497,7 @@ top: /* * Remove setuid/setgid for non-privileged users */ - secpolicy_setid_clear(attr, cr); + (void) secpolicy_setid_clear(vap, cr); trim_mask = (mask & (ATTR_UID|ATTR_GID)); } else { need_policy = TRUE; @@ -2293,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; @@ -2320,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), @@ -2351,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) @@ -2362,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)) { @@ -2378,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); @@ -2406,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) { @@ -2494,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) @@ -2505,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)); } @@ -2542,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)) @@ -2581,13 +2913,17 @@ out: } else { err2 = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx); dmu_tx_commit(tx); - zfs_inode_update(zp); + zfs_inode_update(zp); } 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); } @@ -3175,7 +3511,6 @@ top: zfs_dirent_unlock(dl); *ipp = ZTOI(zp); - iput(ZTOI(zp)); if (zsb->z_os->os_sync == ZFS_SYNC_ALWAYS) zil_commit(zilog, 0); @@ -3189,8 +3524,9 @@ EXPORT_SYMBOL(zfs_symlink); * Return, in the buffer contained in the provided uio structure, * the symbolic path referred to by ip. * - * IN: dentry - dentry of symbolic link. - * nd - namedata for symlink + * IN: ip - inode of symbolic link + * uio - structure to contain the link path. + * cr - credentials of caller. * * RETURN: 0 if success * error code if failure @@ -3200,47 +3536,29 @@ EXPORT_SYMBOL(zfs_symlink); */ /* ARGSUSED */ int -zfs_follow_link(struct dentry *dentry, struct nameidata *nd) +zfs_readlink(struct inode *ip, uio_t *uio, cred_t *cr) { - struct inode *ip = dentry->d_inode; znode_t *zp = ITOZ(ip); zfs_sb_t *zsb = ITOZSB(ip); - struct iovec iov; - uio_t uio; int error; ZFS_ENTER(zsb); ZFS_VERIFY_ZP(zp); - iov.iov_len = MAXPATHLEN + 1; - iov.iov_base = kmem_zalloc(iov.iov_len, KM_SLEEP); - - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_resid = iov.iov_len; - uio.uio_segflg = UIO_SYSSPACE; - mutex_enter(&zp->z_lock); if (zp->z_is_sa) - error = sa_lookup_uio(zp->z_sa_hdl, SA_ZPL_SYMLINK(zsb), &uio); + error = sa_lookup_uio(zp->z_sa_hdl, + SA_ZPL_SYMLINK(zsb), uio); else - error = zfs_sa_readlink(zp, &uio); + error = zfs_sa_readlink(zp, uio); mutex_exit(&zp->z_lock); ZFS_ACCESSTIME_STAMP(zsb, zp); zfs_inode_update(zp); - - if (error) { - kmem_free(iov.iov_base, iov.iov_len); - nd_set_link(nd, ERR_PTR(error)); - } else { - nd_set_link(nd, iov.iov_base); - } - ZFS_EXIT(zsb); return (error); } -EXPORT_SYMBOL(zfs_follow_link); +EXPORT_SYMBOL(zfs_readlink); /* * Insert a new entry into directory tdip referencing sip. @@ -3392,6 +3710,7 @@ top: } EXPORT_SYMBOL(zfs_link); +#ifdef HAVE_MMAP /* * zfs_null_putapage() is used when the file system has been force * unmounted. It just drops the pages. @@ -3547,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; @@ -3627,48 +3945,28 @@ out: ZFS_EXIT(zfsvfs); return (error); } +#endif /* HAVE_MMAP */ /*ARGSUSED*/ void -zfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) +zfs_inactive(struct inode *ip) { - znode_t *zp = VTOZ(vp); - zfsvfs_t *zfsvfs = zp->z_zfsvfs; + znode_t *zp = ITOZ(ip); + zfs_sb_t *zsb = ITOZSB(ip); int error; - rw_enter(&zfsvfs->z_teardown_inactive_lock, RW_READER); - if (zp->z_sa_hdl == NULL) { - /* - * The fs has been unmounted, or we did a - * suspend/resume and this file no longer exists. - */ - if (vn_has_cached_data(vp)) { - (void) pvn_vplist_dirty(vp, 0, zfs_null_putapage, - B_INVAL, cr); - } +#ifdef HAVE_SNAPSHOT + /* Early return for snapshot inode? */ +#endif /* HAVE_SNAPSHOT */ - mutex_enter(&zp->z_lock); - mutex_enter(&vp->v_lock); - ASSERT(vp->v_count == 1); - vp->v_count = 0; - mutex_exit(&vp->v_lock); - mutex_exit(&zp->z_lock); - rw_exit(&zfsvfs->z_teardown_inactive_lock); - zfs_znode_free(zp); + rw_enter(&zsb->z_teardown_inactive_lock, RW_READER); + if (zp->z_sa_hdl == NULL) { + rw_exit(&zsb->z_teardown_inactive_lock); return; } - /* - * Attempt to push any data in the page cache. If this fails - * we will get kicked out later in zfs_zinactive(). - */ - if (vn_has_cached_data(vp)) { - (void) pvn_vplist_dirty(vp, 0, zfs_putapage, B_INVAL|B_ASYNC, - cr); - } - if (zp->z_atime_dirty && zp->z_unlinked == 0) { - dmu_tx_t *tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_t *tx = dmu_tx_create(zsb->z_os); dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); zfs_sa_upgrade_txholds(tx, zp); @@ -3703,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); @@ -3712,13 +4009,14 @@ zfs_seek(struct inode *ip, offset_t ooff, offset_t *noffp, } EXPORT_SYMBOL(zfs_seek); +#ifdef HAVE_MMAP /* * Pre-filter the generic locking function to trap attempts to place * a mandatory lock on a memory mapped file. */ 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; @@ -3846,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; @@ -3934,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; @@ -4009,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); @@ -4042,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); @@ -4056,6 +4351,7 @@ zfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, return (0); } +#endif /* HAVE_MMAP */ /* * convoff - converts the given data (start, whence) to the @@ -4064,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); } @@ -4077,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; @@ -4093,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;