Module Name: src Committed By: christos Date: Sat Apr 18 19:18:34 UTC 2020
Modified Files: src/sys/modules/ffs: Makefile src/sys/rump/fs/lib/libffs: Makefile src/sys/ufs: files.ufs src/sys/ufs/ffs: ffs_alloc.c ffs_balloc.c ffs_extern.h ffs_inode.c ffs_snapshot.c ffs_vnops.c fs.h src/sys/ufs/ufs: extattr.h inode.h ufs_extern.h ufs_inode.c ufs_vnops.c Added Files: src/sys/ufs/ffs: ffs_extattr.c Log Message: Extended attribute support for ffsv2, from FreeBSD. To generate a diff of this commit: cvs rdiff -u -r1.13 -r1.14 src/sys/modules/ffs/Makefile cvs rdiff -u -r1.17 -r1.18 src/sys/rump/fs/lib/libffs/Makefile cvs rdiff -u -r1.46 -r1.47 src/sys/ufs/files.ufs cvs rdiff -u -r1.166 -r1.167 src/sys/ufs/ffs/ffs_alloc.c cvs rdiff -u -r1.63 -r1.64 src/sys/ufs/ffs/ffs_balloc.c cvs rdiff -u -r0 -r1.1 src/sys/ufs/ffs/ffs_extattr.c cvs rdiff -u -r1.85 -r1.86 src/sys/ufs/ffs/ffs_extern.h cvs rdiff -u -r1.126 -r1.127 src/sys/ufs/ffs/ffs_inode.c cvs rdiff -u -r1.151 -r1.152 src/sys/ufs/ffs/ffs_snapshot.c cvs rdiff -u -r1.130 -r1.131 src/sys/ufs/ffs/ffs_vnops.c cvs rdiff -u -r1.66 -r1.67 src/sys/ufs/ffs/fs.h cvs rdiff -u -r1.11 -r1.12 src/sys/ufs/ufs/extattr.h cvs rdiff -u -r1.76 -r1.77 src/sys/ufs/ufs/inode.h cvs rdiff -u -r1.84 -r1.85 src/sys/ufs/ufs/ufs_extern.h cvs rdiff -u -r1.109 -r1.110 src/sys/ufs/ufs/ufs_inode.c cvs rdiff -u -r1.251 -r1.252 src/sys/ufs/ufs/ufs_vnops.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/modules/ffs/Makefile diff -u src/sys/modules/ffs/Makefile:1.13 src/sys/modules/ffs/Makefile:1.14 --- src/sys/modules/ffs/Makefile:1.13 Mon Aug 19 05:31:30 2019 +++ src/sys/modules/ffs/Makefile Sat Apr 18 15:18:33 2020 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.13 2019/08/19 09:31:30 christos Exp $ +# $NetBSD: Makefile,v 1.14 2020/04/18 19:18:33 christos Exp $ .include "../Makefile.inc" @@ -11,7 +11,7 @@ CPPFLAGS+= -DUFS_EXTATTR CWARNFLAGS.clang= -Wno-conversion SRCS+= ffs_alloc.c ffs_balloc.c ffs_inode.c ffs_subr.c ffs_tables.c \ - ffs_vfsops.c ffs_vnops.c ffs_snapshot.c \ + ffs_vfsops.c ffs_vnops.c ffs_snapshot.c ffs_extattr.c \ ffs_bswap.c ffs_wapbl.c ffs_appleufs.c ffs_quota2.c WARNS= 3 Index: src/sys/rump/fs/lib/libffs/Makefile diff -u src/sys/rump/fs/lib/libffs/Makefile:1.17 src/sys/rump/fs/lib/libffs/Makefile:1.18 --- src/sys/rump/fs/lib/libffs/Makefile:1.17 Sat Apr 11 20:04:45 2020 +++ src/sys/rump/fs/lib/libffs/Makefile Sat Apr 18 15:18:33 2020 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.17 2020/04/12 00:04:45 christos Exp $ +# $NetBSD: Makefile,v 1.18 2020/04/18 19:18:33 christos Exp $ # .PATH: ${.CURDIR}/../../../../ufs/ffs ${.CURDIR}/../../../../ufs/ufs @@ -8,7 +8,7 @@ COMMENT=Berkeley Fast File System SRCS= ffs_alloc.c ffs_appleufs.c ffs_balloc.c ffs_bswap.c ffs_inode.c \ ffs_snapshot.c ffs_subr.c ffs_tables.c ffs_vfsops.c ffs_vnops.c \ - ffs_wapbl.c ffs_quota2.c + ffs_wapbl.c ffs_quota2.c ffs_extattr.c SRCS+= ufs_bmap.c ufs_dirhash.c ufs_extattr.c ufs_inode.c \ ufs_lookup.c ufs_rename.c ufs_vfsops.c ufs_vnops.c \ Index: src/sys/ufs/files.ufs diff -u src/sys/ufs/files.ufs:1.46 src/sys/ufs/files.ufs:1.47 --- src/sys/ufs/files.ufs:1.46 Sat Apr 11 13:43:54 2020 +++ src/sys/ufs/files.ufs Sat Apr 18 15:18:33 2020 @@ -1,4 +1,4 @@ -# $NetBSD: files.ufs,v 1.46 2020/04/11 17:43:54 jdolecek Exp $ +# $NetBSD: files.ufs,v 1.47 2020/04/18 19:18:33 christos Exp $ deffs FFS deffs EXT2FS @@ -52,6 +52,7 @@ define ffs: vfs, ufs file ufs/ffs/ffs_alloc.c ffs file ufs/ffs/ffs_balloc.c ffs file ufs/ffs/ffs_bswap.c (ffs | mfs) & ffs_ei +file ufs/ffs/ffs_extattr.c ffs & ufs_extattr file ufs/ffs/ffs_inode.c ffs file ufs/ffs/ffs_snapshot.c ffs file ufs/ffs/ffs_subr.c ffs Index: src/sys/ufs/ffs/ffs_alloc.c diff -u src/sys/ufs/ffs/ffs_alloc.c:1.166 src/sys/ufs/ffs/ffs_alloc.c:1.167 --- src/sys/ufs/ffs/ffs_alloc.c:1.166 Sun Feb 23 10:46:42 2020 +++ src/sys/ufs/ffs/ffs_alloc.c Sat Apr 18 15:18:34 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: ffs_alloc.c,v 1.166 2020/02/23 15:46:42 ad Exp $ */ +/* $NetBSD: ffs_alloc.c,v 1.167 2020/04/18 19:18:34 christos Exp $ */ /*- * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. @@ -70,7 +70,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ffs_alloc.c,v 1.166 2020/02/23 15:46:42 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ffs_alloc.c,v 1.167 2020/04/18 19:18:34 christos Exp $"); #if defined(_KERNEL_OPT) #include "opt_ffs.h" @@ -257,7 +257,10 @@ ffs_alloc(struct inode *ip, daddr_t lbn, bno = ffs_hashalloc(ip, cg, bpref, size, 0, flags, ffs_alloccg); if (bno > 0) { DIP_ADD(ip, blocks, btodb(size)); - ip->i_flag |= IN_CHANGE | IN_UPDATE; + if (flags & IO_EXT) + ip->i_flag |= IN_CHANGE; + else + ip->i_flag |= IN_CHANGE | IN_UPDATE; *bnp = bno; return (0); } @@ -300,14 +303,15 @@ nospace: * => return with um_lock released */ int -ffs_realloccg(struct inode *ip, daddr_t lbprev, daddr_t bpref, int osize, - int nsize, kauth_cred_t cred, struct buf **bpp, daddr_t *blknop) +ffs_realloccg(struct inode *ip, daddr_t lbprev, daddr_t bprev, daddr_t bpref, + int osize, int nsize, int flags, kauth_cred_t cred, struct buf **bpp, + daddr_t *blknop) { struct ufsmount *ump; struct fs *fs; struct buf *bp; int cg, request, error; - daddr_t bprev, bno; + daddr_t bno; fs = ip->i_fs; ump = ip->i_ump; @@ -368,10 +372,6 @@ ffs_realloccg(struct inode *ip, daddr_t mutex_exit(&ump->um_lock); goto nospace; } - if (fs->fs_magic == FS_UFS2_MAGIC) - bprev = ufs_rw64(ip->i_ffs2_db[lbprev], UFS_FSNEEDSWAP(fs)); - else - bprev = ufs_rw32(ip->i_ffs1_db[lbprev], UFS_FSNEEDSWAP(fs)); if (bprev == 0) { panic("%s: bad bprev: dev = 0x%llx, bsize = %d, bprev = %" @@ -403,7 +403,10 @@ ffs_realloccg(struct inode *ip, daddr_t mutex_enter(&ump->um_lock); if ((bno = ffs_fragextend(ip, cg, bprev, osize, nsize)) != 0) { DIP_ADD(ip, blocks, btodb(nsize - osize)); - ip->i_flag |= IN_CHANGE | IN_UPDATE; + if (flags & IO_EXT) + ip->i_flag |= IN_CHANGE; + else + ip->i_flag |= IN_CHANGE | IN_UPDATE; if (bpp != NULL) { if (bp->b_blkno != FFS_FSBTODB(fs, bno)) { @@ -503,7 +506,10 @@ ffs_realloccg(struct inode *ip, daddr_t ip->i_number); } DIP_ADD(ip, blocks, btodb(nsize - osize)); - ip->i_flag |= IN_CHANGE | IN_UPDATE; + if (flags & IO_EXT) + ip->i_flag |= IN_CHANGE; + else + ip->i_flag |= IN_CHANGE | IN_UPDATE; if (bpp != NULL) { bp->b_blkno = FFS_FSBTODB(fs, bno); allocbuf(bp, nsize, 1); Index: src/sys/ufs/ffs/ffs_balloc.c diff -u src/sys/ufs/ffs/ffs_balloc.c:1.63 src/sys/ufs/ffs/ffs_balloc.c:1.64 --- src/sys/ufs/ffs/ffs_balloc.c:1.63 Fri Oct 27 20:37:13 2017 +++ src/sys/ufs/ffs/ffs_balloc.c Sat Apr 18 15:18:34 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: ffs_balloc.c,v 1.63 2017/10/28 00:37:13 pgoyette Exp $ */ +/* $NetBSD: ffs_balloc.c,v 1.64 2020/04/18 19:18:34 christos Exp $ */ /* * Copyright (c) 2002 Networks Associates Technology, Inc. @@ -41,7 +41,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ffs_balloc.c,v 1.63 2017/10/28 00:37:13 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ffs_balloc.c,v 1.64 2020/04/18 19:18:34 christos Exp $"); #if defined(_KERNEL_OPT) #include "opt_quota.h" @@ -72,6 +72,12 @@ static int ffs_balloc_ufs1(struct vnode static int ffs_balloc_ufs2(struct vnode *, off_t, int, kauth_cred_t, int, struct buf **); +static daddr_t +ffs_extb(struct fs *fs, struct ufs2_dinode *dp, daddr_t nb) +{ + return ufs_rw64(dp->di_extb[nb], UFS_FSNEEDSWAP(fs)); +} + /* * Balloc defines the structure of file system storage * by allocating the physical blocks on a device given @@ -139,10 +145,11 @@ ffs_balloc_ufs1(struct vnode *vp, off_t osize = ffs_blksize(fs, ip, nb); if (osize < fs->fs_bsize && osize > 0) { mutex_enter(&ump->um_lock); - error = ffs_realloccg(ip, nb, + error = ffs_realloccg(ip, nb, ffs_getdb(fs, ip, nb), ffs_blkpref_ufs1(ip, lastlbn, nb, flags, &ip->i_ffs1_db[0]), - osize, (int)fs->fs_bsize, cred, bpp, &newb); + osize, (int)fs->fs_bsize, flags, cred, bpp, + &newb); if (error) return (error); ip->i_size = ffs_lblktosize(fs, nb + 1); @@ -215,9 +222,10 @@ ffs_balloc_ufs1(struct vnode *vp, off_t */ mutex_enter(&ump->um_lock); error = ffs_realloccg(ip, lbn, + ffs_getdb(fs, ip, lbn), ffs_blkpref_ufs1(ip, lbn, (int)lbn, flags, &ip->i_ffs1_db[0]), - osize, nsize, cred, bpp, &newb); + osize, nsize, flags, cred, bpp, &newb); if (error) return (error); } @@ -543,11 +551,11 @@ ffs_balloc_ufs2(struct vnode *vp, off_t if (lbn < 0) return (EFBIG); -#ifdef notyet /* * Check for allocating external data. */ if (flags & IO_EXT) { + struct ufs2_dinode *dp = ip->i_din.ffs2_din; if (lbn >= UFS_NXADDR) return (EFBIG); /* @@ -562,16 +570,15 @@ ffs_balloc_ufs2(struct vnode *vp, off_t if (osize < fs->fs_bsize && osize > 0) { mutex_enter(&ump->um_lock); error = ffs_realloccg(ip, -1 - nb, - dp->di_extb[nb], + ffs_extb(fs, dp, nb), ffs_blkpref_ufs2(ip, lastlbn, (int)nb, flags, &dp->di_extb[0]), - osize, - (int)fs->fs_bsize, cred, &bp); + osize, (int)fs->fs_bsize, flags, cred, + &bp, &newb); if (error) return (error); - dp->di_extsize = smalllblktosize(fs, nb + 1); + dp->di_extsize = ffs_lblktosize(fs, nb + 1); dp->di_extb[nb] = FFS_DBTOFSB(fs, bp->b_blkno); - bp->b_xflags |= BX_ALTDATA; ip->i_flag |= IN_CHANGE | IN_UPDATE; if (flags & IO_SYNC) bwrite(bp); @@ -582,19 +589,16 @@ ffs_balloc_ufs2(struct vnode *vp, off_t /* * All blocks are direct blocks */ - if (flags & BA_METAONLY) - panic("ffs_balloc_ufs2: BA_METAONLY for ext block"); nb = dp->di_extb[lbn]; - if (nb != 0 && dp->di_extsize >= smalllblktosize(fs, lbn + 1)) { + if (nb != 0 && dp->di_extsize >= ffs_lblktosize(fs, lbn + 1)) { error = bread(vp, -1 - lbn, fs->fs_bsize, 0, &bp); if (error) { return (error); } - mutex_enter(&bp->b_interlock); + mutex_enter(bp->b_objlock); bp->b_blkno = FFS_FSBTODB(fs, nb); - bp->b_xflags |= BX_ALTDATA; - mutex_exit(&bp->b_interlock); + mutex_exit(bp->b_objlock); *bpp = bp; return (0); } @@ -610,23 +614,21 @@ ffs_balloc_ufs2(struct vnode *vp, off_t if (error) { return (error); } - mutex_enter(&bp->b_interlock); + mutex_enter(bp->b_objlock); bp->b_blkno = FFS_FSBTODB(fs, nb); - bp->b_xflags |= BX_ALTDATA; - mutex_exit(&bp->b_interlock); + mutex_exit(bp->b_objlock); } else { mutex_enter(&ump->um_lock); error = ffs_realloccg(ip, -1 - lbn, - dp->di_extb[lbn], + ffs_extb(fs, dp, lbn), ffs_blkpref_ufs2(ip, lbn, (int)lbn, flags, &dp->di_extb[0]), - osize, nsize, cred, &bp); + osize, nsize, flags, cred, &bp, &newb); if (error) return (error); - bp->b_xflags |= BX_ALTDATA; } } else { - if (dp->di_extsize < smalllblktosize(fs, lbn + 1)) + if (dp->di_extsize < ffs_lblktosize(fs, lbn + 1)) nsize = ffs_fragroundup(fs, size); else nsize = fs->fs_bsize; @@ -641,14 +643,12 @@ ffs_balloc_ufs2(struct vnode *vp, off_t nsize, (flags & B_CLRBUF) != 0, &bp); if (error) return error; - bp->b_xflags |= BX_ALTDATA; } dp->di_extb[lbn] = FFS_DBTOFSB(fs, bp->b_blkno); ip->i_flag |= IN_CHANGE | IN_UPDATE; *bpp = bp; return (0); } -#endif /* * If the next write will extend the file into a new block, * and the file is currently composed of a fragment @@ -661,10 +661,11 @@ ffs_balloc_ufs2(struct vnode *vp, off_t osize = ffs_blksize(fs, ip, nb); if (osize < fs->fs_bsize && osize > 0) { mutex_enter(&ump->um_lock); - error = ffs_realloccg(ip, nb, + error = ffs_realloccg(ip, nb, ffs_getdb(fs, ip, lbn), ffs_blkpref_ufs2(ip, lastlbn, nb, flags, &ip->i_ffs2_db[0]), - osize, (int)fs->fs_bsize, cred, bpp, &newb); + osize, (int)fs->fs_bsize, flags, cred, bpp, + &newb); if (error) return (error); ip->i_size = ffs_lblktosize(fs, nb + 1); @@ -737,9 +738,10 @@ ffs_balloc_ufs2(struct vnode *vp, off_t */ mutex_enter(&ump->um_lock); error = ffs_realloccg(ip, lbn, + ffs_getdb(fs, ip, lbn), ffs_blkpref_ufs2(ip, lbn, (int)lbn, flags, &ip->i_ffs2_db[0]), - osize, nsize, cred, bpp, &newb); + osize, nsize, flags, cred, bpp, &newb); if (error) return (error); } Index: src/sys/ufs/ffs/ffs_extern.h diff -u src/sys/ufs/ffs/ffs_extern.h:1.85 src/sys/ufs/ffs/ffs_extern.h:1.86 --- src/sys/ufs/ffs/ffs_extern.h:1.85 Tue Aug 21 21:05:24 2018 +++ src/sys/ufs/ffs/ffs_extern.h Sat Apr 18 15:18:34 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: ffs_extern.h,v 1.85 2018/08/22 01:05:24 msaitoh Exp $ */ +/* $NetBSD: ffs_extern.h,v 1.86 2020/04/18 19:18:34 christos Exp $ */ /*- * Copyright (c) 1991, 1993, 1994 @@ -89,8 +89,8 @@ __BEGIN_DECLS /* ffs_alloc.c */ int ffs_alloc(struct inode *, daddr_t, daddr_t , int, int, kauth_cred_t, daddr_t *); -int ffs_realloccg(struct inode *, daddr_t, daddr_t, int, int , - kauth_cred_t, struct buf **, daddr_t *); +int ffs_realloccg(struct inode *, daddr_t, daddr_t, daddr_t, int, int, + int, kauth_cred_t, struct buf **, daddr_t *); int ffs_valloc(struct vnode *, int, kauth_cred_t, ino_t *); daddr_t ffs_blkpref_ufs1(struct inode *, daddr_t, int, int, int32_t *); daddr_t ffs_blkpref_ufs2(struct inode *, daddr_t, int, int, int64_t *); @@ -135,16 +135,29 @@ int ffs_spec_fsync(void *); int ffs_reclaim(void *); int ffs_getpages(void *); void ffs_gop_size(struct vnode *, off_t, off_t *, int); +int ffs_lock(void *); +int ffs_unlock(void *); +int ffs_islocked(void *); +int ffs_full_fsync(struct vnode *, int); + +/* ffs_extattr.c */ +#ifdef UFS_EXTATTR int ffs_openextattr(void *); int ffs_closeextattr(void *); int ffs_getextattr(void *); int ffs_setextattr(void *); int ffs_listextattr(void *); int ffs_deleteextattr(void *); -int ffs_lock(void *); -int ffs_unlock(void *); -int ffs_islocked(void *); -int ffs_full_fsync(struct vnode *, int); +int ffsext_strategy(void *); +#else +#define ffs_openextattr genfs_eopnotsupp +#define ffs_closeextattr genfs_eopnotsupp +#define ffs_getextattr genfs_eopnotsupp +#define ffs_setextattr genfs_eopnotsupp +#define ffs_listextattr genfs_eopnotsupp +#define ffs_deleteextattr genfs_eopnotsupp +#define ffsext_strategy vn_fifo_bypass +#endif /* * Snapshot function prototypes. Index: src/sys/ufs/ffs/ffs_inode.c diff -u src/sys/ufs/ffs/ffs_inode.c:1.126 src/sys/ufs/ffs/ffs_inode.c:1.127 --- src/sys/ufs/ffs/ffs_inode.c:1.126 Sun Feb 23 10:46:42 2020 +++ src/sys/ufs/ffs/ffs_inode.c Sat Apr 18 15:18:34 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: ffs_inode.c,v 1.126 2020/02/23 15:46:42 ad Exp $ */ +/* $NetBSD: ffs_inode.c,v 1.127 2020/04/18 19:18:34 christos Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -61,7 +61,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ffs_inode.c,v 1.126 2020/02/23 15:46:42 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ffs_inode.c,v 1.127 2020/04/18 19:18:34 christos Exp $"); #if defined(_KERNEL_OPT) #include "opt_ffs.h" @@ -209,10 +209,11 @@ ffs_truncate(struct vnode *ovp, off_t le daddr_t lastblock; struct inode *oip = VTOI(ovp); daddr_t bn, lastiblock[UFS_NIADDR], indir_lbn[UFS_NIADDR]; - daddr_t blks[UFS_NDADDR + UFS_NIADDR]; + daddr_t blks[UFS_NDADDR + UFS_NIADDR], oldblks[UFS_NDADDR + UFS_NIADDR]; struct fs *fs; + int extblocks; int offset, pgoffset, level; - int64_t blocksreleased = 0; + int64_t blocksreleased = 0, datablocks; int i, aflag, nblocks; int error, allerror = 0; off_t osize; @@ -231,9 +232,44 @@ ffs_truncate(struct vnode *ovp, off_t le if (length < 0) return (EINVAL); + fs = oip->i_fs; +#define i_din2 i_din.ffs2_din + extblocks = 0; + datablocks = DIP(oip, blocks); + if (fs->fs_magic == FS_UFS2_MAGIC && oip->i_din2->di_extsize > 0) { + extblocks = btodb(ffs_fragroundup(fs, oip->i_din2->di_extsize)); + datablocks -= extblocks; + } + if ((ioflag & IO_EXT) && extblocks > 0) { + if (length != 0) + panic("ffs_truncate: partial trunc of extdata"); + { +#ifdef QUOTA + (void) chkdq(oip, -extblocks, NOCRED, FORCE); +#endif + vinvalbuf(ovp, 0, cred, curlwp, 0, 0); + osize = oip->i_din2->di_extsize; + oip->i_din2->di_blocks -= extblocks; + oip->i_din2->di_extsize = 0; + for (i = 0; i < UFS_NXADDR; i++) { + oldblks[i] = oip->i_din2->di_extb[i]; + oip->i_din2->di_extb[i] = 0; + } + oip->i_flag |= IN_CHANGE; + if ((error = ffs_update(ovp, NULL, NULL, 0))) + return (error); + for (i = 0; i < UFS_NXADDR; i++) { + if (oldblks[i] == 0) + continue; + ffs_blkfree(fs, oip->i_devvp, oldblks[i], + ffs_sblksize(fs, osize, i), oip->i_number); + } + extblocks = 0; + } + } if (ovp->v_type == VLNK && (oip->i_size < ump->um_maxsymlinklen || - (ump->um_maxsymlinklen == 0 && DIP(oip, blocks) == 0))) { + (ump->um_maxsymlinklen == 0 && datablocks == 0))) { KDASSERT(length == 0); memset(SHORTLINK(oip), 0, (size_t)oip->i_size); oip->i_size = 0; @@ -247,7 +283,6 @@ ffs_truncate(struct vnode *ovp, off_t le oip->i_flag |= IN_CHANGE | IN_UPDATE; return (ffs_update(ovp, NULL, NULL, 0)); } - fs = oip->i_fs; if (length > ump->um_maxfilesize) return (EFBIG); @@ -415,10 +450,7 @@ ffs_truncate(struct vnode *ovp, off_t le indir_lbn[DOUBLE] = indir_lbn[SINGLE] - FFS_NINDIR(fs) - 1; indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - FFS_NINDIR(fs) * FFS_NINDIR(fs) - 1; for (level = TRIPLE; level >= SINGLE; level--) { - if (oip->i_ump->um_fstype == UFS1) - bn = ufs_rw32(oip->i_ffs1_ib[level],UFS_FSNEEDSWAP(fs)); - else - bn = ufs_rw64(oip->i_ffs2_ib[level],UFS_FSNEEDSWAP(fs)); + bn = ffs_getib(fs, oip, level); if (bn != 0) { if (lastiblock[level] < 0 && oip->i_ump->um_mountp->mnt_wapbl) { @@ -461,10 +493,7 @@ ffs_truncate(struct vnode *ovp, off_t le for (i = UFS_NDADDR - 1; i > lastblock; i--) { long bsize; - if (oip->i_ump->um_fstype == UFS1) - bn = ufs_rw32(oip->i_ffs1_db[i], UFS_FSNEEDSWAP(fs)); - else - bn = ufs_rw64(oip->i_ffs2_db[i], UFS_FSNEEDSWAP(fs)); + bn = ffs_getdb(fs, oip, i); if (bn == 0) continue; @@ -488,10 +517,7 @@ ffs_truncate(struct vnode *ovp, off_t le * Finally, look for a change in size of the * last direct block; release any frags. */ - if (oip->i_ump->um_fstype == UFS1) - bn = ufs_rw32(oip->i_ffs1_db[lastblock], UFS_FSNEEDSWAP(fs)); - else - bn = ufs_rw64(oip->i_ffs2_db[lastblock], UFS_FSNEEDSWAP(fs)); + bn = ffs_getdb(fs, oip, lastblock); if (bn != 0) { long oldspace, newspace; @@ -536,9 +562,9 @@ done: KASSERTMSG((blks[i] == DIP(oip, db[i])), "itrunc2 blk mismatch: %jx != %jx", (uintmax_t)blks[i], (uintmax_t)DIP(oip, db[i])); - KASSERTMSG((length != 0 || LIST_EMPTY(&ovp->v_cleanblkhd)), + KASSERTMSG((length != 0 || extblocks || LIST_EMPTY(&ovp->v_cleanblkhd)), "itrunc3: zero length and nonempty cleanblkhd"); - KASSERTMSG((length != 0 || LIST_EMPTY(&ovp->v_dirtyblkhd)), + KASSERTMSG((length != 0 || extblocks || LIST_EMPTY(&ovp->v_dirtyblkhd)), "itrunc3: zero length and nonempty dirtyblkhd"); out: Index: src/sys/ufs/ffs/ffs_snapshot.c diff -u src/sys/ufs/ffs/ffs_snapshot.c:1.151 src/sys/ufs/ffs/ffs_snapshot.c:1.152 --- src/sys/ufs/ffs/ffs_snapshot.c:1.151 Sun Feb 23 10:46:42 2020 +++ src/sys/ufs/ffs/ffs_snapshot.c Sat Apr 18 15:18:34 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: ffs_snapshot.c,v 1.151 2020/02/23 15:46:42 ad Exp $ */ +/* $NetBSD: ffs_snapshot.c,v 1.152 2020/04/18 19:18:34 christos Exp $ */ /* * Copyright 2000 Marshall Kirk McKusick. All Rights Reserved. @@ -38,7 +38,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ffs_snapshot.c,v 1.151 2020/02/23 15:46:42 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ffs_snapshot.c,v 1.152 2020/04/18 19:18:34 christos Exp $"); #if defined(_KERNEL_OPT) #include "opt_ffs.h" @@ -443,16 +443,14 @@ snapshot_setup(struct mount *mp, struct if (error) return EACCES; - if (vp->v_size != 0) { - /* - * Must completely truncate the file here. Allocated - * blocks on a snapshot mean that block has been copied - * on write, see ffs_copyonwrite() testing "blkno != 0" - */ - error = ufs_truncate_retry(vp, 0, NOCRED); - if (error) - return error; - } + /* + * Must completely truncate the file here. Allocated + * blocks on a snapshot mean that block has been copied + * on write, see ffs_copyonwrite() testing "blkno != 0" + */ + error = ufs_truncate_all(vp); + if (error) + return error; /* Change inode to snapshot type file. */ error = UFS_WAPBL_BEGIN(mp); Index: src/sys/ufs/ffs/ffs_vnops.c diff -u src/sys/ufs/ffs/ffs_vnops.c:1.130 src/sys/ufs/ffs/ffs_vnops.c:1.131 --- src/sys/ufs/ffs/ffs_vnops.c:1.130 Sun Feb 23 10:46:42 2020 +++ src/sys/ufs/ffs/ffs_vnops.c Sat Apr 18 15:18:34 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: ffs_vnops.c,v 1.130 2020/02/23 15:46:42 ad Exp $ */ +/* $NetBSD: ffs_vnops.c,v 1.131 2020/04/18 19:18:34 christos Exp $ */ /*- * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. @@ -61,7 +61,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ffs_vnops.c,v 1.130 2020/02/23 15:46:42 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ffs_vnops.c,v 1.131 2020/04/18 19:18:34 christos Exp $"); #if defined(_KERNEL_OPT) #include "opt_ffs.h" @@ -612,160 +612,3 @@ ffs_gop_size(struct vnode *vp, off_t siz *eobp = ffs_blkroundup(fs, size); } } - -int -ffs_openextattr(void *v) -{ - struct vop_openextattr_args /* { - struct vnode *a_vp; - kauth_cred_t a_cred; - struct proc *a_p; - } */ *ap = v; - struct inode *ip = VTOI(ap->a_vp); - struct fs *fs = ip->i_fs; - - /* Not supported for UFS1 file systems. */ - if (fs->fs_magic == FS_UFS1_MAGIC) - return (EOPNOTSUPP); - - /* XXX Not implemented for UFS2 file systems. */ - return (EOPNOTSUPP); -} - -int -ffs_closeextattr(void *v) -{ - struct vop_closeextattr_args /* { - struct vnode *a_vp; - int a_commit; - kauth_cred_t a_cred; - struct proc *a_p; - } */ *ap = v; - struct inode *ip = VTOI(ap->a_vp); - struct fs *fs = ip->i_fs; - - /* Not supported for UFS1 file systems. */ - if (fs->fs_magic == FS_UFS1_MAGIC) - return (EOPNOTSUPP); - - /* XXX Not implemented for UFS2 file systems. */ - return (EOPNOTSUPP); -} - -int -ffs_getextattr(void *v) -{ - struct vop_getextattr_args /* { - struct vnode *a_vp; - int a_attrnamespace; - const char *a_name; - struct uio *a_uio; - size_t *a_size; - kauth_cred_t a_cred; - struct proc *a_p; - } */ *ap = v; - struct vnode *vp = ap->a_vp; - struct inode *ip = VTOI(vp); - struct fs *fs = ip->i_fs; - - if (fs->fs_magic == FS_UFS1_MAGIC) { -#ifdef UFS_EXTATTR - int error; - - error = ufs_getextattr(ap); - return error; -#else - return (EOPNOTSUPP); -#endif - } - - /* XXX Not implemented for UFS2 file systems. */ - return (EOPNOTSUPP); -} - -int -ffs_setextattr(void *v) -{ - struct vop_setextattr_args /* { - struct vnode *a_vp; - int a_attrnamespace; - const char *a_name; - struct uio *a_uio; - kauth_cred_t a_cred; - struct proc *a_p; - } */ *ap = v; - struct vnode *vp = ap->a_vp; - struct inode *ip = VTOI(vp); - struct fs *fs = ip->i_fs; - - if (fs->fs_magic == FS_UFS1_MAGIC) { -#ifdef UFS_EXTATTR - int error; - - error = ufs_setextattr(ap); - return error; -#else - return (EOPNOTSUPP); -#endif - } - - /* XXX Not implemented for UFS2 file systems. */ - return (EOPNOTSUPP); -} - -int -ffs_listextattr(void *v) -{ - struct vop_listextattr_args /* { - struct vnode *a_vp; - int a_attrnamespace; - struct uio *a_uio; - size_t *a_size; - kauth_cred_t a_cred; - struct proc *a_p; - } */ *ap = v; - struct inode *ip = VTOI(ap->a_vp); - struct fs *fs = ip->i_fs; - - if (fs->fs_magic == FS_UFS1_MAGIC) { -#ifdef UFS_EXTATTR - int error; - - error = ufs_listextattr(ap); - return error; -#else - return (EOPNOTSUPP); -#endif - } - - /* XXX Not implemented for UFS2 file systems. */ - return (EOPNOTSUPP); -} - -int -ffs_deleteextattr(void *v) -{ - struct vop_deleteextattr_args /* { - struct vnode *a_vp; - int a_attrnamespace; - kauth_cred_t a_cred; - struct proc *a_p; - } */ *ap = v; - struct vnode *vp = ap->a_vp; - struct inode *ip = VTOI(vp); - struct fs *fs = ip->i_fs; - - if (fs->fs_magic == FS_UFS1_MAGIC) { -#ifdef UFS_EXTATTR - int error; - - error = ufs_deleteextattr(ap); - return error; -#else - return (EOPNOTSUPP); -#endif - } - - /* XXX Not implemented for UFS2 file systems. */ - return (EOPNOTSUPP); -} Index: src/sys/ufs/ffs/fs.h diff -u src/sys/ufs/ffs/fs.h:1.66 src/sys/ufs/ffs/fs.h:1.67 --- src/sys/ufs/ffs/fs.h:1.66 Sat Feb 14 04:06:11 2015 +++ src/sys/ufs/ffs/fs.h Sat Apr 18 15:18:34 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: fs.h,v 1.66 2015/02/14 09:06:11 maxv Exp $ */ +/* $NetBSD: fs.h,v 1.67 2020/04/18 19:18:34 christos Exp $ */ /* * Copyright (c) 1982, 1986, 1993 @@ -692,6 +692,14 @@ struct ocg { ((fsb) & ((fs)->fs_frag - 1)) #define ffs_blknum(fs, fsb) /* calculates rounddown(fsb, fs->fs_frag) */ \ ((fsb) &~ ((fs)->fs_frag - 1)) +#define ffs_getdb(fs, ip, lb) \ + ((fs)->fs_magic == FS_UFS2_MAGIC ? \ + (daddr_t)ufs_rw64((ip)->i_ffs2_db[lb], UFS_FSNEEDSWAP(fs)) : \ + (daddr_t)ufs_rw32((ip)->i_ffs1_db[lb], UFS_FSNEEDSWAP(fs))) +#define ffs_getib(fs, ip, lb) \ + ((fs)->fs_magic == FS_UFS2_MAGIC ? \ + (daddr_t)ufs_rw64((ip)->i_ffs2_ib[lb], UFS_FSNEEDSWAP(fs)) : \ + (daddr_t)ufs_rw32((ip)->i_ffs1_ib[lb], UFS_FSNEEDSWAP(fs))) /* * Determine the number of available frags given a Index: src/sys/ufs/ufs/extattr.h diff -u src/sys/ufs/ufs/extattr.h:1.11 src/sys/ufs/ufs/extattr.h:1.12 --- src/sys/ufs/ufs/extattr.h:1.11 Fri Dec 19 05:59:21 2014 +++ src/sys/ufs/ufs/extattr.h Sat Apr 18 15:18:34 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: extattr.h,v 1.11 2014/12/19 10:59:21 manu Exp $ */ +/* $NetBSD: extattr.h,v 1.12 2020/04/18 19:18:34 christos Exp $ */ /*- * Copyright (c) 1999-2001 Robert N. M. Watson @@ -72,6 +72,39 @@ struct ufs_extattr_header { /* data follows the header */ }; +/* + * This structure defines the required fields of an extended-attribute header. + */ +struct extattr { + uint32_t ea_length; /* length of this attribute */ + uint8_t ea_namespace; /* name space of this attribute */ + uint8_t ea_contentpadlen; /* bytes of padding at end of attribute */ + uint8_t ea_namelength; /* length of attribute name */ + char ea_name[1]; /* attribute name (NOT nul-terminated) */ + /* padding, if any, to align attribute content to 8 byte boundary */ + /* extended attribute content follows */ +}; + +/* + * These macros are used to access and manipulate an extended attribute: + * + * EXTATTR_NEXT(eap) returns a pointer to the next extended attribute + * following eap. + * EXTATTR_CONTENT(eap) returns a pointer to the extended attribute + * content referenced by eap. + * EXTATTR_CONTENT_SIZE(eap) returns the size of the extended attribute + * content referenced by eap. + */ +#define EXTATTR_NEXT(eap) \ + ((struct extattr *)(((u_char *)(eap)) + (eap)->ea_length)) +#define EXTATTR_CONTENT(eap) \ + (void *)(((u_char *)(eap)) + EXTATTR_BASE_LENGTH(eap)) +#define EXTATTR_CONTENT_SIZE(eap) \ + ((eap)->ea_length - EXTATTR_BASE_LENGTH(eap) - (eap)->ea_contentpadlen) +/* -1 below compensates for ea_name[1] */ +#define EXTATTR_BASE_LENGTH(eap) \ + roundup2((sizeof(struct extattr) - 1 + (eap)->ea_namelength), 8) + #ifdef _KERNEL #ifdef MALLOC_DECLARE Index: src/sys/ufs/ufs/inode.h diff -u src/sys/ufs/ufs/inode.h:1.76 src/sys/ufs/ufs/inode.h:1.77 --- src/sys/ufs/ufs/inode.h:1.76 Sun Aug 20 08:09:06 2017 +++ src/sys/ufs/ufs/inode.h Sat Apr 18 15:18:34 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: inode.h,v 1.76 2017/08/20 12:09:06 maya Exp $ */ +/* $NetBSD: inode.h,v 1.77 2020/04/18 19:18:34 christos Exp $ */ /* * Copyright (c) 1982, 1989, 1993 @@ -156,6 +156,14 @@ struct inode { struct dirhash *i_dirhash; /* Hashing for large directories */ /* + * Data for extended attribute modification. + */ + u_char *i_ea_area; /* Pointer to malloced copy of EA area */ + unsigned i_ea_len; /* Length of i_ea_area */ + int i_ea_error; /* First errno in transaction */ + int i_ea_refs; /* Number of users of EA area */ + + /* * The on-disk dinode itself. */ union { @@ -216,16 +224,16 @@ struct inode { #define IN_UPDATE 0x0004 /* Inode written to; update mtime. */ #define IN_MODIFIED 0x0008 /* Inode has been modified. */ #define IN_ACCESSED 0x0010 /* Inode has been accessed. */ -/* unused 0x0020 */ /* was IN_RENAME */ +/* unused 0x0020 */ /* was IN_RENAME */ #define IN_SHLOCK 0x0040 /* File has shared lock. */ #define IN_EXLOCK 0x0080 /* File has exclusive lock. */ -/* unused 0x0100 */ /* was LFS-only IN_CLEANING */ -/* unused 0x0200 */ /* was LFS-only IN_ADIROP */ +/* unused 0x0100 */ /* was LFS-only IN_CLEANING */ +/* unused 0x0200 */ /* was LFS-only IN_ADIROP */ #define IN_SPACECOUNTED 0x0400 /* Blocks to be freed in free count. */ -/* unused 0x0800 */ /* what was that? */ -/* unused 0x1000 */ /* was LFS-only IN_PAGING */ +/* unused 0x0800 */ /* what was that? */ +/* unused 0x1000 */ /* was LFS-only IN_PAGING */ #define IN_MODIFY 0x2000 /* Modification time update request. */ -/* unused 0x4000 */ /* was LFS-only IN_CDIROP */ +/* unused 0x4000 */ /* was LFS-only IN_CDIROP */ #if defined(_KERNEL) Index: src/sys/ufs/ufs/ufs_extern.h diff -u src/sys/ufs/ufs/ufs_extern.h:1.84 src/sys/ufs/ufs/ufs_extern.h:1.85 --- src/sys/ufs/ufs/ufs_extern.h:1.84 Fri Jan 17 15:08:10 2020 +++ src/sys/ufs/ufs/ufs_extern.h Sat Apr 18 15:18:34 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: ufs_extern.h,v 1.84 2020/01/17 20:08:10 ad Exp $ */ +/* $NetBSD: ufs_extern.h,v 1.85 2020/04/18 19:18:34 christos Exp $ */ /*- * Copyright (c) 1991, 1993, 1994 @@ -111,7 +111,8 @@ int ufs_getlbns(struct vnode *, daddr_t, /* ufs_inode.c */ int ufs_reclaim(struct vnode *); int ufs_balloc_range(struct vnode *, off_t, off_t, kauth_cred_t, int); -int ufs_truncate_retry(struct vnode *, uint64_t, kauth_cred_t); +int ufs_truncate_all(struct vnode *); +int ufs_truncate_retry(struct vnode *, int, uint64_t, kauth_cred_t); /* ufs_lookup.c */ void ufs_dirbad(struct inode *, doff_t, const char *); Index: src/sys/ufs/ufs/ufs_inode.c diff -u src/sys/ufs/ufs/ufs_inode.c:1.109 src/sys/ufs/ufs/ufs_inode.c:1.110 --- src/sys/ufs/ufs/ufs_inode.c:1.109 Sun Feb 23 10:46:43 2020 +++ src/sys/ufs/ufs/ufs_inode.c Sat Apr 18 15:18:34 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: ufs_inode.c,v 1.109 2020/02/23 15:46:43 ad Exp $ */ +/* $NetBSD: ufs_inode.c,v 1.110 2020/04/18 19:18:34 christos Exp $ */ /* * Copyright (c) 1991, 1993 @@ -37,7 +37,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ufs_inode.c,v 1.109 2020/02/23 15:46:43 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ufs_inode.c,v 1.110 2020/04/18 19:18:34 christos Exp $"); #if defined(_KERNEL_OPT) #include "opt_ffs.h" @@ -98,16 +98,11 @@ ufs_inactive(void *v) #ifdef UFS_EXTATTR ufs_extattr_vnode_inactive(vp, curlwp); #endif - /* * All file blocks must be freed before we can let the vnode * be reclaimed, so can't postpone full truncating any further. */ - if (ip->i_size != 0) { - allerror = ufs_truncate_retry(vp, 0, NOCRED); - if (allerror) - goto out; - } + ufs_truncate_all(vp); #if defined(QUOTA) || defined(QUOTA2) error = UFS_WAPBL_BEGIN(mp); @@ -292,7 +287,8 @@ ufs_balloc_range(struct vnode *vp, off_t } int -ufs_truncate_retry(struct vnode *vp, uint64_t newsize, kauth_cred_t cred) +ufs_truncate_retry(struct vnode *vp, int ioflag, uint64_t newsize, + kauth_cred_t cred) { struct inode *ip = VTOI(vp); struct mount *mp = vp->v_mount; @@ -308,7 +304,7 @@ ufs_truncate_retry(struct vnode *vp, uin if (error) goto out; - error = UFS_TRUNCATE(vp, newsize, 0, cred); + error = UFS_TRUNCATE(vp, newsize, ioflag, cred); UFS_WAPBL_END(mp); if (error != 0 && error != EAGAIN) @@ -318,3 +314,18 @@ ufs_truncate_retry(struct vnode *vp, uin out: return error; } + +/* truncate all the data of the inode including extended attributes */ +int +ufs_truncate_all(struct vnode *vp) +{ + struct inode *ip = VTOI(vp); + off_t isize = ip->i_size; + + if (ip->i_ump->um_fstype == UFS2) + isize += ip->i_ffs2_extsize; + + if (isize == 0) + return 0; + return ufs_truncate_retry(vp, IO_EXT, 0, NOCRED); +} Index: src/sys/ufs/ufs/ufs_vnops.c diff -u src/sys/ufs/ufs/ufs_vnops.c:1.251 src/sys/ufs/ufs/ufs_vnops.c:1.252 --- src/sys/ufs/ufs/ufs_vnops.c:1.251 Mon Apr 13 15:23:20 2020 +++ src/sys/ufs/ufs/ufs_vnops.c Sat Apr 18 15:18:34 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: ufs_vnops.c,v 1.251 2020/04/13 19:23:20 ad Exp $ */ +/* $NetBSD: ufs_vnops.c,v 1.252 2020/04/18 19:18:34 christos Exp $ */ /*- * Copyright (c) 2008, 2020 The NetBSD Foundation, Inc. @@ -66,7 +66,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ufs_vnops.c,v 1.251 2020/04/13 19:23:20 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ufs_vnops.c,v 1.252 2020/04/18 19:18:34 christos Exp $"); #if defined(_KERNEL_OPT) #include "opt_ffs.h" @@ -556,7 +556,7 @@ ufs_setattr(void *v) error = EPERM; goto out; } - error = ufs_truncate_retry(vp, vap->va_size, cred); + error = ufs_truncate_retry(vp, 0, vap->va_size, cred); if (error) goto out; break; Added files: Index: src/sys/ufs/ffs/ffs_extattr.c diff -u /dev/null src/sys/ufs/ffs/ffs_extattr.c:1.1 --- /dev/null Sat Apr 18 15:18:34 2020 +++ src/sys/ufs/ffs/ffs_extattr.c Sat Apr 18 15:18:34 2020 @@ -0,0 +1,996 @@ +/* $NetBSD: ffs_extattr.c,v 1.1 2020/04/18 19:18:34 christos Exp $ */ + +/*- + * SPDX-License-Identifier: (BSD-2-Clause-FreeBSD AND BSD-3-Clause) + * + * Copyright (c) 2002, 2003 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by Marshall + * Kirk McKusick and Network Associates Laboratories, the Security + * Research Division of Network Associates, Inc. under DARPA/SPAWAR + * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS + * research program + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)ufs_readwrite.c 8.11 (Berkeley) 5/8/95 + * from: $FreeBSD: .../ufs/ufs_readwrite.c,v 1.96 2002/08/12 09:22:11 phk ... + * @(#)ffs_vnops.c 8.15 (Berkeley) 5/14/95 + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: ffs_extattr.c,v 1.1 2020/04/18 19:18:34 christos Exp $"); + +#if defined(_KERNEL_OPT) +#include "opt_ffs.h" +#include "opt_wapbl.h" +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/resourcevar.h> +#include <sys/kernel.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/buf.h> +#include <sys/event.h> +#include <sys/extattr.h> +#include <sys/kauth.h> +#include <sys/proc.h> +#include <sys/mount.h> +#include <sys/vnode.h> +#include <sys/malloc.h> +#include <sys/pool.h> +#include <sys/signalvar.h> +#include <sys/kauth.h> +#include <sys/wapbl.h> + +#include <miscfs/fifofs/fifo.h> +#include <miscfs/genfs/genfs.h> +#include <miscfs/specfs/specdev.h> + +#include <ufs/ufs/inode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ufs/ufs_extern.h> +#include <ufs/ufs/ufsmount.h> +#include <ufs/ufs/ufs_wapbl.h> + +#include <ufs/ffs/fs.h> +#include <ufs/ffs/ffs_extern.h> + +#include <uvm/uvm.h> + +#define ALIGNED_TO(ptr, s) \ + (((uintptr_t)(ptr) & (_Alignof(s) - 1)) == 0) +#define uoff_t uintmax_t +#define ITOFS(ip) (ip)->i_fs +#define i_din2 i_din.ffs2_din +#define VI_LOCK(vp) mutex_enter((vp)->v_interlock) +#define VI_UNLOCK(vp) mutex_exit((vp)->v_interlock) +#define UFS_INODE_SET_FLAG(ip, f) ((ip)->i_flag |= (f)) +#define ASSERT_VOP_ELOCKED(vp, m) KASSERT(VOP_ISLOCKED(vp)) +#define I_IS_UFS2(ip) (ITOFS(ip)->fs_magic == FS_UFS2_MAGIC) +#define lblktosize(fs, o) ffs_lblktosize(fs, o) +#define lblkno(fs, o) ffs_lblkno(fs, o) +#define blkoff(fs, o) ffs_blkoff(fs, o) +#define sblksize(fs, o, lbn) ffs_sblksize(fs, o, lbn) +typedef mode_t accmode_t; /* so that it breaks soon */ +typedef daddr_t ufs_lbn_t; +#define msleep(chan, mtx, pri, wmesg, timeo) \ + mtsleep((chan), (pri), (wmesg), (timeo), *(mtx)) +#define vm_page_count_severe() 0 +#define buf_dirty_count_severe() 0 +#define BA_CLRBUF B_CLRBUF +#define IO_ASYNC 0 +#define vfs_bio_brelse(bp, ioflag) brelse(bp, 0) +#define vfs_bio_clrbuf(bp) clrbuf(bp) +#define vfs_bio_set_flags(bp, ioflag) __nothing + +/* + * Credential check based on process requesting service, and per-attribute + * permissions. + */ +static int +ffs_extattr_check_cred(struct vnode *vp, int attrnamespace, kauth_cred_t cred, + accmode_t accmode) +{ + /* + * Kernel-invoked always succeeds. + */ + if (cred == NOCRED) + return 0; + + /* + * Do not allow privileged processes in jail to directly manipulate + * system attributes. + */ + switch (attrnamespace) { + case EXTATTR_NAMESPACE_SYSTEM: + /* Potentially with privs */ + return EPERM; + case EXTATTR_NAMESPACE_USER: + return VOP_ACCESS(vp, accmode, cred); + default: + return EPERM; + } +} +/* + * Extended attribute area reading. + */ +static int +ffs_extread(struct vnode *vp, struct uio *uio, int ioflag) +{ + struct inode *ip; + struct ufs2_dinode *dp; + struct fs *fs; + struct buf *bp; + ufs_lbn_t lbn, nextlbn; + off_t bytesinfile; + long size, xfersize, blkoffset; + ssize_t orig_resid; + int error; + + ip = VTOI(vp); + fs = ITOFS(ip); + dp = ip->i_din2; + +#ifdef INVARIANTS + if (uio->uio_rw != UIO_READ || fs->fs_magic != FS_UFS2_MAGIC) + panic("ffs_extread: mode"); + +#endif + orig_resid = uio->uio_resid; + KASSERT(orig_resid >= 0); + if (orig_resid == 0) + return (0); + KASSERT(uio->uio_offset >= 0); + + for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { + if ((bytesinfile = dp->di_extsize - uio->uio_offset) <= 0) + break; + lbn = lblkno(fs, uio->uio_offset); + nextlbn = lbn + 1; + + /* + * size of buffer. The buffer representing the + * end of the file is rounded up to the size of + * the block type ( fragment or full block, + * depending ). + */ + size = sblksize(fs, dp->di_extsize, lbn); + blkoffset = blkoff(fs, uio->uio_offset); + + /* + * The amount we want to transfer in this iteration is + * one FS block less the amount of the data before + * our startpoint (duh!) + */ + xfersize = fs->fs_bsize - blkoffset; + + /* + * But if we actually want less than the block, + * or the file doesn't have a whole block more of data, + * then use the lesser number. + */ + if (uio->uio_resid < xfersize) + xfersize = uio->uio_resid; + if (bytesinfile < xfersize) + xfersize = bytesinfile; + + if (lblktosize(fs, nextlbn) >= dp->di_extsize) { + /* + * Don't do readahead if this is the end of the info. + */ + error = bread(vp, -1 - lbn, size, 0, &bp); + } else { + /* + * If we have a second block, then + * fire off a request for a readahead + * as well as a read. Note that the 4th and 5th + * arguments point to arrays of the size specified in + * the 6th argument. + */ + u_int nextsize = sblksize(fs, dp->di_extsize, nextlbn); + + nextlbn = -1 - nextlbn; + error = breadn(vp, -1 - lbn, + size, &nextlbn, &nextsize, 1, 0, &bp); + } + if (error) { + brelse(bp, 0); + bp = NULL; + break; + } + + /* + * We should only get non-zero b_resid when an I/O error + * has occurred, which should cause us to break above. + * However, if the short read did not cause an error, + * then we want to ensure that we do not uiomove bad + * or uninitialized data. + */ + size -= bp->b_resid; + if (size < xfersize) { + if (size == 0) + break; + xfersize = size; + } + + error = uiomove((char *)bp->b_data + blkoffset, + (int)xfersize, uio); + if (error) + break; + vfs_bio_brelse(bp, ioflag); + } + + /* + * This can only happen in the case of an error + * because the loop above resets bp to NULL on each iteration + * and on normal completion has not set a new value into it. + * so it must have come from a 'break' statement + */ + if (bp != NULL) + vfs_bio_brelse(bp, ioflag); + return (error); +} +/* + * Extended attribute area writing. + */ +static int +ffs_extwrite(struct vnode *vp, struct uio *uio, int ioflag, kauth_cred_t ucred) +{ + struct inode *ip; + struct ufs2_dinode *dp; + struct fs *fs; + struct buf *bp; + ufs_lbn_t lbn; + off_t osize; + ssize_t resid; + int blkoffset, error, flags, size, xfersize; + + ip = VTOI(vp); + fs = ITOFS(ip); + dp = ip->i_din2; + +#ifdef INVARIANTS + if (uio->uio_rw != UIO_WRITE || fs->fs_magic != FS_UFS2_MAGIC) + panic("ffs_extwrite: mode"); +#endif + + if (ioflag & IO_APPEND) + uio->uio_offset = dp->di_extsize; + KASSERT(uio->uio_offset >= 0); + if ((uoff_t)uio->uio_offset + uio->uio_resid > + UFS_NXADDR * fs->fs_bsize) + return (EFBIG); + + resid = uio->uio_resid; + osize = dp->di_extsize; + flags = IO_EXT; + if (ioflag & IO_SYNC) + flags |= IO_SYNC; + + if ((error = UFS_WAPBL_BEGIN(vp->v_mount)) != 0) + return error; + + for (error = 0; uio->uio_resid > 0;) { + lbn = lblkno(fs, uio->uio_offset); + blkoffset = blkoff(fs, uio->uio_offset); + xfersize = fs->fs_bsize - blkoffset; + if (uio->uio_resid < xfersize) + xfersize = uio->uio_resid; + + /* + * We must perform a read-before-write if the transfer size + * does not cover the entire buffer. + */ + if (fs->fs_bsize > xfersize) + flags |= BA_CLRBUF; + else + flags &= ~BA_CLRBUF; + error = UFS_BALLOC(vp, uio->uio_offset, xfersize, + ucred, flags, &bp); + if (error != 0) + break; + /* + * If the buffer is not valid we have to clear out any + * garbage data from the pages instantiated for the buffer. + * If we do not, a failed uiomove() during a write can leave + * the prior contents of the pages exposed to a userland + * mmap(). XXX deal with uiomove() errors a better way. + */ + if ((bp->b_flags & BC_NOCACHE) && fs->fs_bsize <= xfersize) + vfs_bio_clrbuf(bp); + + if (uio->uio_offset + xfersize > dp->di_extsize) + dp->di_extsize = uio->uio_offset + xfersize; + + size = sblksize(fs, dp->di_extsize, lbn) - bp->b_resid; + if (size < xfersize) + xfersize = size; + + error = + uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio); + + vfs_bio_set_flags(bp, ioflag); + + /* + * If IO_SYNC each buffer is written synchronously. Otherwise + * if we have a severe page deficiency write the buffer + * asynchronously. Otherwise try to cluster, and if that + * doesn't do it then either do an async write (if O_DIRECT), + * or a delayed write (if not). + */ + if (ioflag & IO_SYNC) { + (void)bwrite(bp); + } else if (vm_page_count_severe() || + buf_dirty_count_severe() || + xfersize + blkoffset == fs->fs_bsize || + (ioflag & (IO_ASYNC | IO_DIRECT))) + bawrite(bp); + else + bdwrite(bp); + if (error || xfersize == 0) + break; + UFS_INODE_SET_FLAG(ip, IN_CHANGE); + } + /* + * If we successfully wrote any data, and we are not the superuser + * we clear the setuid and setgid bits as a precaution against + * tampering. + */ + if ((ip->i_mode & (ISUID | ISGID)) && resid > uio->uio_resid && ucred) { + ip->i_mode &= ~(ISUID | ISGID); + dp->di_mode = ip->i_mode; + } + if (error) { + if (ioflag & IO_UNIT) { + (void)ffs_truncate(vp, osize, + IO_EXT | (ioflag&IO_SYNC), ucred); + uio->uio_offset -= resid - uio->uio_resid; + uio->uio_resid = resid; + } + } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) + error = ffs_update(vp, NULL, NULL, UPDATE_WAIT); + UFS_WAPBL_END(vp->v_mount); + return (error); +} + +/* + * Vnode operating to retrieve a named extended attribute. + * + * Locate a particular EA (nspace:name) in the area (ptr:length), and return + * the length of the EA, and possibly the pointer to the entry and to the data. + */ +static int +ffs_findextattr(u_char *ptr, u_int length, int nspace, const char *name, + struct extattr **eapp, u_char **eac) +{ + struct extattr *eap, *eaend; + size_t nlen; + + nlen = strlen(name); + KASSERT(ALIGNED_TO(ptr, struct extattr)); + eap = (struct extattr *)ptr; + eaend = (struct extattr *)(ptr + length); + for (; eap < eaend; eap = EXTATTR_NEXT(eap)) { + /* make sure this entry is complete */ + if (EXTATTR_NEXT(eap) > eaend) + break; + if (eap->ea_namespace != nspace || eap->ea_namelength != nlen + || memcmp(eap->ea_name, name, nlen) != 0) + continue; + if (eapp != NULL) + *eapp = eap; + if (eac != NULL) + *eac = EXTATTR_CONTENT(eap); + return (EXTATTR_CONTENT_SIZE(eap)); + } + return (-1); +} + +static int +ffs_rdextattr(u_char **p, struct vnode *vp, int extra) +{ + struct inode *ip; + struct ufs2_dinode *dp; + struct fs *fs; + struct uio luio; + struct iovec liovec; + u_int easize; + int error; + u_char *eae; + + ip = VTOI(vp); + fs = ITOFS(ip); + dp = ip->i_din2; + easize = dp->di_extsize; + if ((uoff_t)easize + extra > UFS_NXADDR * fs->fs_bsize) + return (EFBIG); + + eae = malloc(easize + extra, M_TEMP, M_WAITOK); + + liovec.iov_base = eae; + liovec.iov_len = easize; + luio.uio_iov = &liovec; + luio.uio_iovcnt = 1; + luio.uio_offset = 0; + luio.uio_resid = easize; + luio.uio_vmspace = vmspace_kernel(); + luio.uio_rw = UIO_READ; + + error = ffs_extread(vp, &luio, IO_EXT | IO_SYNC); + if (error) { + free(eae, M_TEMP); + return(error); + } + *p = eae; + return (0); +} + +static void +ffs_lock_ea(struct vnode *vp) +{ +#if 0 + struct inode *ip; + + ip = VTOI(vp); + VI_LOCK(vp); + while (ip->i_flag & IN_EA_LOCKED) { + UFS_INODE_SET_FLAG(ip, IN_EA_LOCKWAIT); + msleep(&ip->i_ea_refs, &vp->v_interlock, PINOD + 2, "ufs_ea", + 0); + } + UFS_INODE_SET_FLAG(ip, IN_EA_LOCKED); + VI_UNLOCK(vp); +#endif +} + +static void +ffs_unlock_ea(struct vnode *vp) +{ +#if 0 + struct inode *ip; + + ip = VTOI(vp); + VI_LOCK(vp); + if (ip->i_flag & IN_EA_LOCKWAIT) + wakeup(&ip->i_ea_refs); + ip->i_flag &= ~(IN_EA_LOCKED | IN_EA_LOCKWAIT); + VI_UNLOCK(vp); +#endif +} + +static int +ffs_open_ea(struct vnode *vp, kauth_cred_t cred) +{ + struct inode *ip; + struct ufs2_dinode *dp; + int error; + + ip = VTOI(vp); + + ffs_lock_ea(vp); + if (ip->i_ea_area != NULL) { + ip->i_ea_refs++; + ffs_unlock_ea(vp); + return (0); + } + dp = ip->i_din2; + error = ffs_rdextattr(&ip->i_ea_area, vp, 0); + if (error) { + ffs_unlock_ea(vp); + return (error); + } + ip->i_ea_len = dp->di_extsize; + ip->i_ea_error = 0; + ip->i_ea_refs++; + ffs_unlock_ea(vp); + return (0); +} + +/* + * Vnode extattr transaction commit/abort + */ +static int +ffs_close_ea(struct vnode *vp, int commit, kauth_cred_t cred) +{ + struct inode *ip; + struct uio luio; + struct iovec liovec; + int error; + struct ufs2_dinode *dp; + + ip = VTOI(vp); + + if (commit) + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + else + KASSERT(VOP_ISLOCKED(vp)); + ffs_lock_ea(vp); + if (ip->i_ea_area == NULL) { + ffs_unlock_ea(vp); + return (EINVAL); + } + dp = ip->i_din2; + error = ip->i_ea_error; + if (commit && error == 0) { + ASSERT_VOP_ELOCKED(vp, "ffs_close_ea commit"); + if (cred == NOCRED) + cred = lwp0.l_cred; + liovec.iov_base = ip->i_ea_area; + liovec.iov_len = ip->i_ea_len; + luio.uio_iov = &liovec; + luio.uio_iovcnt = 1; + luio.uio_offset = 0; + luio.uio_resid = ip->i_ea_len; + luio.uio_vmspace = vmspace_kernel(); + luio.uio_rw = UIO_WRITE; + if ((error = UFS_WAPBL_BEGIN(vp->v_mount)) != 0) { + ffs_unlock_ea(vp); + return error; + } + + /* XXX: I'm not happy about truncating to zero size */ + if (ip->i_ea_len < dp->di_extsize) + error = ffs_truncate(vp, 0, IO_EXT, cred); + error = ffs_extwrite(vp, &luio, IO_EXT | IO_SYNC, cred); + UFS_WAPBL_END(vp->v_mount); + } + if (--ip->i_ea_refs == 0) { + free(ip->i_ea_area, M_TEMP); + ip->i_ea_area = NULL; + ip->i_ea_len = 0; + ip->i_ea_error = 0; + } + ffs_unlock_ea(vp); + return (error); +} + +/* + * Vnode extattr strategy routine for fifos. + * + * We need to check for a read or write of the external attributes. + * Otherwise we just fall through and do the usual thing. + */ +int +ffsext_strategy(void *v) +{ + struct vop_strategy_args /* { + struct vnodeop_desc *a_desc; + struct vnode *a_vp; + struct buf *a_bp; + } */ *ap = v; + struct vnode *vp; + daddr_t lbn; + + vp = ap->a_vp; + lbn = ap->a_bp->b_lblkno; + if (I_IS_UFS2(VTOI(vp)) && lbn < 0 && lbn >= -UFS_NXADDR) + return ufs_strategy(ap); + if (vp->v_type == VFIFO) + return vn_fifo_bypass(ap); + panic("spec nodes went here"); +} + +/* + * Vnode extattr transaction commit/abort + */ +int +ffs_openextattr(void *v) +{ + struct vop_openextattr_args /* { + struct vnode *a_vp; + kauth_cred_t a_cred; + struct proc *a_p; + } */ *ap = v; + struct inode *ip = VTOI(ap->a_vp); + struct fs *fs = ip->i_fs; + + /* Not supported for UFS1 file systems. */ + if (fs->fs_magic == FS_UFS1_MAGIC) + return (EOPNOTSUPP); + + if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) + return (EOPNOTSUPP); + + return (ffs_open_ea(ap->a_vp, ap->a_cred)); +} + +/* + * Vnode extattr transaction commit/abort + */ +int +ffs_closeextattr(void *v) +{ + struct vop_closeextattr_args /* { + struct vnode *a_vp; + int a_commit; + kauth_cred_t a_cred; + struct proc *a_p; + } */ *ap = v; + struct inode *ip = VTOI(ap->a_vp); + struct fs *fs = ip->i_fs; + + /* Not supported for UFS1 file systems. */ + if (fs->fs_magic == FS_UFS1_MAGIC) + return (EOPNOTSUPP); + + if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) + return (EOPNOTSUPP); + + if (ap->a_commit && (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)) + return (EROFS); + + return (ffs_close_ea(ap->a_vp, ap->a_commit, ap->a_cred)); +} + +/* + * Vnode operation to retrieve a named extended attribute. + */ +int +ffs_getextattr(void *v) +{ + struct vop_getextattr_args /* { + struct vnode *a_vp; + int a_attrnamespace; + const char *a_name; + struct uio *a_uio; + size_t *a_size; + kauth_cred_t a_cred; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct inode *ip = VTOI(vp); + struct fs *fs = ip->i_fs; + + KASSERT(VOP_ISLOCKED(vp)); + if (fs->fs_magic == FS_UFS1_MAGIC) { + return ufs_getextattr(ap); + } + + u_char *eae, *p; + unsigned easize; + int error, ealen; + + if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) + return (EOPNOTSUPP); + + error = ffs_extattr_check_cred(ap->a_vp, ap->a_attrnamespace, + ap->a_cred, VREAD); + if (error) + return (error); + + error = ffs_open_ea(ap->a_vp, ap->a_cred); + if (error) + return (error); + + eae = ip->i_ea_area; + easize = ip->i_ea_len; + + ealen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name, + NULL, &p); + if (ealen >= 0) { + error = 0; + if (ap->a_size != NULL) + *ap->a_size = ealen; + else if (ap->a_uio != NULL) + error = uiomove(p, ealen, ap->a_uio); + } else + error = ENOATTR; + + ffs_close_ea(ap->a_vp, 0, ap->a_cred); + return (error); +} + +/* + * Vnode operation to set a named attribute. + */ +int +ffs_setextattr(void *v) +{ + struct vop_setextattr_args /* { + struct vnode *a_vp; + int a_attrnamespace; + const char *a_name; + struct uio *a_uio; + kauth_cred_t a_cred; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct inode *ip = VTOI(vp); + struct fs *fs = ip->i_fs; + + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + if (fs->fs_magic == FS_UFS1_MAGIC) { + return ufs_setextattr(ap); + } + + struct extattr *eap; + uint32_t ealength, ul; + ssize_t ealen; + int olen, eapad1, eapad2, error, i, easize; + u_char *eae; + void *tmp; + + if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) + return (EOPNOTSUPP); + + if (strlen(ap->a_name) == 0) + return (EINVAL); + + /* XXX Now unsupported API to delete EAs using NULL uio. */ + if (ap->a_uio == NULL) + return (EOPNOTSUPP); + + if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + + ealen = ap->a_uio->uio_resid; + if (ealen < 0 || ealen > lblktosize(fs, UFS_NXADDR)) + return (EINVAL); + + error = ffs_extattr_check_cred(ap->a_vp, ap->a_attrnamespace, + ap->a_cred, VWRITE); + if (error) { + + /* + * ffs_lock_ea is not needed there, because the vnode + * must be exclusively locked. + */ + if (ip->i_ea_area != NULL && ip->i_ea_error == 0) + ip->i_ea_error = error; + return (error); + } + + error = ffs_open_ea(ap->a_vp, ap->a_cred); + if (error) + return (error); + + ealength = sizeof(uint32_t) + 3 + strlen(ap->a_name); + eapad1 = roundup2(ealength, 8) - ealength; + eapad2 = roundup2(ealen, 8) - ealen; + ealength += eapad1 + ealen + eapad2; + + /* + * CEM: rewrites of the same size or smaller could be done in-place + * instead. (We don't acquire any fine-grained locks in here either, + * so we could also do bigger writes in-place.) + */ + eae = malloc(ip->i_ea_len + ealength, M_TEMP, M_WAITOK); + bcopy(ip->i_ea_area, eae, ip->i_ea_len); + easize = ip->i_ea_len; + + olen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name, + &eap, NULL); + if (olen == -1) { + /* new, append at end */ + KASSERT(ALIGNED_TO(eae + easize, struct extattr)); + eap = (struct extattr *)(eae + easize); + easize += ealength; + } else { + ul = eap->ea_length; + i = (u_char *)EXTATTR_NEXT(eap) - eae; + if (ul != ealength) { + bcopy(EXTATTR_NEXT(eap), (u_char *)eap + ealength, + easize - i); + easize += (ealength - ul); + } + } + if (easize > lblktosize(fs, UFS_NXADDR)) { + free(eae, M_TEMP); + ffs_close_ea(ap->a_vp, 0, ap->a_cred); + if (ip->i_ea_area != NULL && ip->i_ea_error == 0) + ip->i_ea_error = ENOSPC; + return (ENOSPC); + } + eap->ea_length = ealength; + eap->ea_namespace = ap->a_attrnamespace; + eap->ea_contentpadlen = eapad2; + eap->ea_namelength = strlen(ap->a_name); + memcpy(eap->ea_name, ap->a_name, strlen(ap->a_name)); + bzero(&eap->ea_name[strlen(ap->a_name)], eapad1); + error = uiomove(EXTATTR_CONTENT(eap), ealen, ap->a_uio); + if (error) { + free(eae, M_TEMP); + ffs_close_ea(ap->a_vp, 0, ap->a_cred); + if (ip->i_ea_area != NULL && ip->i_ea_error == 0) + ip->i_ea_error = error; + return (error); + } + bzero((u_char *)EXTATTR_CONTENT(eap) + ealen, eapad2); + + tmp = ip->i_ea_area; + ip->i_ea_area = eae; + ip->i_ea_len = easize; + free(tmp, M_TEMP); + error = ffs_close_ea(ap->a_vp, 1, ap->a_cred); + return (error); +} + +/* + * Vnode operation to retrieve extended attributes on a vnode. + */ +int +ffs_listextattr(void *v) +{ + struct vop_listextattr_args /* { + struct vnode *a_vp; + int a_attrnamespace; + struct uio *a_uio; + size_t *a_size; + kauth_cred_t a_cred; + struct proc *a_p; + } */ *ap = v; + struct inode *ip = VTOI(ap->a_vp); + struct fs *fs = ip->i_fs; + + if (fs->fs_magic == FS_UFS1_MAGIC) { + return ufs_listextattr(ap); + } + + struct extattr *eap, *eaend; + int error, ealen; + + if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) + return (EOPNOTSUPP); + + error = ffs_extattr_check_cred(ap->a_vp, ap->a_attrnamespace, + ap->a_cred, VREAD); + if (error) + return (error); + + error = ffs_open_ea(ap->a_vp, ap->a_cred); + if (error) + return (error); + + error = 0; + if (ap->a_size != NULL) + *ap->a_size = 0; + + KASSERT(ALIGNED_TO(ip->i_ea_area, struct extattr)); + eap = (struct extattr *)ip->i_ea_area; + eaend = (struct extattr *)(ip->i_ea_area + ip->i_ea_len); + for (; error == 0 && eap < eaend; eap = EXTATTR_NEXT(eap)) { + /* make sure this entry is complete */ + if (EXTATTR_NEXT(eap) > eaend) + break; + if (eap->ea_namespace != ap->a_attrnamespace) + continue; + + ealen = eap->ea_namelength; + if (ap->a_size != NULL) + *ap->a_size += ealen + 1; + else if (ap->a_uio != NULL) + error = uiomove(&eap->ea_namelength, ealen + 1, + ap->a_uio); + } + + ffs_close_ea(ap->a_vp, 0, ap->a_cred); + return (error); +} + +/* + * Vnode operation to remove a named attribute. + */ +int +ffs_deleteextattr(void *v) +{ + struct vop_deleteextattr_args /* { + struct vnode *a_vp; + int a_attrnamespace; + kauth_cred_t a_cred; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct inode *ip = VTOI(vp); + struct fs *fs = ip->i_fs; + + if (fs->fs_magic == FS_UFS1_MAGIC) { + return ufs_deleteextattr(ap); + } + + struct extattr *eap; + uint32_t ul; + int olen, error, i, easize; + u_char *eae; + void *tmp; + + if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) + return (EOPNOTSUPP); + + if (strlen(ap->a_name) == 0) + return (EINVAL); + + if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + + error = ffs_extattr_check_cred(ap->a_vp, ap->a_attrnamespace, + ap->a_cred, VWRITE); + if (error) { + + /* + * ffs_lock_ea is not needed there, because the vnode + * must be exclusively locked. + */ + if (ip->i_ea_area != NULL && ip->i_ea_error == 0) + ip->i_ea_error = error; + return (error); + } + + error = ffs_open_ea(ap->a_vp, ap->a_cred); + if (error) + return (error); + + /* CEM: delete could be done in-place instead */ + eae = malloc(ip->i_ea_len, M_TEMP, M_WAITOK); + bcopy(ip->i_ea_area, eae, ip->i_ea_len); + easize = ip->i_ea_len; + + olen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name, + &eap, NULL); + if (olen == -1) { + /* delete but nonexistent */ + free(eae, M_TEMP); + ffs_close_ea(ap->a_vp, 0, ap->a_cred); + return (ENOATTR); + } + ul = eap->ea_length; + i = (u_char *)EXTATTR_NEXT(eap) - eae; + bcopy(EXTATTR_NEXT(eap), eap, easize - i); + easize -= ul; + + tmp = ip->i_ea_area; + ip->i_ea_area = eae; + ip->i_ea_len = easize; + free(tmp, M_TEMP); + error = ffs_close_ea(ap->a_vp, 1, ap->a_cred); + return error; +}