tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Extended Attribute support for FFSv2
Hi,
I am planning to add ACL support to FFS. As I was porting the
FreeBSD ACL code, I noticed that we currently lack extended attribute
support for FFSv2, so I took a detour and added that support by
copying the FreeBSD code (which you can compare against; I've left
it mostly intact by adding compatibility defines). I am not sure
if I got it all right, but I've written a simple unit-test and it
passes and does not corrupt the filesystem. Here are my changes
for review. If you think they are ok, I will commit them in a day
or so.
Thanks,
christos
Index: files.ufs
===================================================================
RCS file: /cvsroot/src/sys/ufs/files.ufs,v
retrieving revision 1.45
diff -u -u -r1.45 files.ufs
--- files.ufs 17 Jun 2019 03:32:58 -0000 1.45
+++ files.ufs 10 Apr 2020 22:15:21 -0000
@@ -52,6 +52,7 @@
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: ffs/ffs_alloc.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_alloc.c,v
retrieving revision 1.166
diff -u -u -r1.166 ffs_alloc.c
--- ffs/ffs_alloc.c 23 Feb 2020 15:46:42 -0000 1.166
+++ ffs/ffs_alloc.c 10 Apr 2020 22:15:22 -0000
@@ -257,7 +257,10 @@
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 @@
* => 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 @@
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 @@
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 @@
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: ffs/ffs_balloc.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_balloc.c,v
retrieving revision 1.63
diff -u -u -r1.63 ffs_balloc.c
--- ffs/ffs_balloc.c 28 Oct 2017 00:37:13 -0000 1.63
+++ ffs/ffs_balloc.c 10 Apr 2020 22:15:22 -0000
@@ -72,6 +72,12 @@
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 @@
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 @@
*/
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 @@
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 @@
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 @@
/*
* 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 @@
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 @@
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 @@
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 @@
*/
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: ffs/ffs_extern.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_extern.h,v
retrieving revision 1.85
diff -u -u -r1.85 ffs_extern.h
--- ffs/ffs_extern.h 22 Aug 2018 01:05:24 -0000 1.85
+++ ffs/ffs_extern.h 10 Apr 2020 22:15:22 -0000
@@ -89,8 +89,8 @@
/* 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,31 @@
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_notsupp ((void (*)(void *))eopnotsupp)
+#define ffs_openextattr ffsnotsupp
+#define ffs_closeextattr ffsnotsupp
+#define ffs_getextattr ffsnotsupp
+#define ffs_setextattr ffsnotsupp
+#define ffs_listextattr ffsnotsupp
+#define ffs_deleteextattr ffsnotsupp
+#define ffsext_strategy vn_fifo_bypass
+#endif
/*
* Snapshot function prototypes.
Index: ffs/ffs_inode.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_inode.c,v
retrieving revision 1.126
diff -u -u -r1.126 ffs_inode.c
--- ffs/ffs_inode.c 23 Feb 2020 15:46:42 -0000 1.126
+++ ffs/ffs_inode.c 10 Apr 2020 22:15:22 -0000
@@ -209,10 +209,11 @@
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,45 @@
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];
+ printf("oldblks[i] = %d %#lx\n", i, oldblks[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 +284,6 @@
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 +451,7 @@
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 +494,7 @@
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 +518,7 @@
* 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 +563,9 @@
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: ffs/ffs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_vnops.c,v
retrieving revision 1.130
diff -u -u -r1.130 ffs_vnops.c
--- ffs/ffs_vnops.c 23 Feb 2020 15:46:42 -0000 1.130
+++ ffs/ffs_vnops.c 10 Apr 2020 22:15:22 -0000
@@ -612,160 +612,3 @@
*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: ffs/fs.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/fs.h,v
retrieving revision 1.66
diff -u -u -r1.66 fs.h
--- ffs/fs.h 14 Feb 2015 09:06:11 -0000 1.66
+++ ffs/fs.h 10 Apr 2020 22:15:22 -0000
@@ -692,6 +692,14 @@
((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: ufs/extattr.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/extattr.h,v
retrieving revision 1.11
diff -u -u -r1.11 extattr.h
--- ufs/extattr.h 19 Dec 2014 10:59:21 -0000 1.11
+++ ufs/extattr.h 10 Apr 2020 22:15:22 -0000
@@ -72,6 +72,39 @@
/* 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: ufs/inode.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/inode.h,v
retrieving revision 1.76
diff -u -u -r1.76 inode.h
--- ufs/inode.h 20 Aug 2017 12:09:06 -0000 1.76
+++ ufs/inode.h 10 Apr 2020 22:15:22 -0000
@@ -156,6 +156,14 @@
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 {
@@ -219,8 +227,8 @@
/* 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 */
+#define IN_EA_LOCKED 0x0100 /* was LFS-only IN_CLEANING */
+#define IN_EA_LOCKWAIT 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 */
Index: ufs/ufs_inode.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_inode.c,v
retrieving revision 1.109
diff -u -u -r1.109 ufs_inode.c
--- ufs/ufs_inode.c 23 Feb 2020 15:46:43 -0000 1.109
+++ ufs/ufs_inode.c 10 Apr 2020 22:15:22 -0000
@@ -308,7 +308,7 @@
if (error)
goto out;
- error = UFS_TRUNCATE(vp, newsize, 0, cred);
+ error = UFS_TRUNCATE(vp, newsize, IO_EXT, cred);
UFS_WAPBL_END(mp);
if (error != 0 && error != EAGAIN)
--- /dev/null 2020-04-10 18:17:09.933701211 -0400
+++ ufs/ufs_extattr.c 2020-01-17 15:08:10.000000000 -0500
@@ -0,0 +1,1633 @@
+/* $NetBSD: ufs_extattr.c,v 1.51 2020/01/17 20:08:10 ad Exp $ */
+
+/*-
+ * Copyright (c) 1999-2002 Robert N. M. Watson
+ * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed by Robert Watson for the TrustedBSD Project.
+ *
+ * This software was developed for the FreeBSD Project in part by 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.
+ *
+ */
+
+/*
+ * Support for file system extended attributes on the UFS1 file system.
+ *
+ * Extended attributes are defined in the form name=value, where name is
+ * a nul-terminated string in the style of a file name, and value is a
+ * binary blob of zero or more bytes. The UFS1 extended attribute service
+ * layers support for extended attributes onto a backing file, in the style
+ * of the quota implementation, meaning that it requires no underlying format
+ * changes to the file system. This design choice exchanges simplicity,
+ * usability, and easy deployment for performance.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: ufs_extattr.c,v 1.51 2020/01/17 20:08:10 ad Exp $");
+
+#ifdef _KERNEL_OPT
+#include "opt_ffs.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/reboot.h>
+#include <sys/kauth.h>
+#include <sys/kernel.h>
+#include <sys/namei.h>
+#include <sys/kmem.h>
+#include <sys/fcntl.h>
+#include <sys/lwp.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/lock.h>
+#include <sys/dirent.h>
+#include <sys/extattr.h>
+#include <sys/sysctl.h>
+
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/extattr.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ufs/ufs_extern.h>
+
+int ufs_extattr_sync = 1;
+int ufs_extattr_autocreate = 1024;
+
+static int ufs_extattr_valid_attrname(int attrnamespace,
+ const char *attrname);
+static int ufs_extattr_enable_with_open(struct ufsmount *ump,
+ struct vnode *vp, int attrnamespace, const char *attrname,
+ struct lwp *l);
+static int ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
+ const char *attrname, struct vnode *backing_vnode,
+ struct lwp *l);
+static int ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
+ const char *attrname, struct lwp *l);
+static int ufs_extattr_get(struct vnode *vp, int attrnamespace,
+ const char *name, struct uio *uio, size_t *size,
+ kauth_cred_t cred, struct lwp *l);
+static int ufs_extattr_list(struct vnode *vp, int attrnamespace,
+ struct uio *uio, size_t *size, int flag,
+ kauth_cred_t cred, struct lwp *l);
+static int ufs_extattr_set(struct vnode *vp, int attrnamespace,
+ const char *name, struct uio *uio, kauth_cred_t cred,
+ struct lwp *l);
+static int ufs_extattr_rm(struct vnode *vp, int attrnamespace,
+ const char *name, kauth_cred_t cred, struct lwp *l);
+static struct ufs_extattr_list_entry *ufs_extattr_find_attr(struct ufsmount *,
+ int, const char *);
+static int ufs_extattr_get_header(struct vnode *,
+ struct ufs_extattr_list_entry *,
+ struct ufs_extattr_header *, off_t *);
+
+/*
+ * Convert a FreeBSD extended attribute and namespace to a consistent string
+ * representation.
+ *
+ * The returned value, if not NULL, is guaranteed to be an allocated object
+ * of its size as returned by strlen() + 1 and must be freed by the caller.
+ */
+static char *
+from_freebsd_extattr(int attrnamespace, const char *attrname)
+{
+ const char *namespace;
+ char *attr;
+ size_t len;
+
+ if (attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
+ namespace = "system";
+ else if (attrnamespace == EXTATTR_NAMESPACE_USER)
+ namespace = "user";
+ else
+ return NULL;
+
+ /* <namespace>.<attrname>\0 */
+ len = strlen(namespace) + 1 + strlen(attrname) + 1;
+
+ attr = kmem_alloc(len, KM_SLEEP);
+
+ snprintf(attr, len, "%s.%s", namespace, attrname);
+
+ return attr;
+}
+
+/*
+ * Internal wrapper around a conversion-check-free sequence.
+ */
+static int
+internal_extattr_check_cred(vnode_t *vp, int attrnamespace, const char *name,
+ kauth_cred_t cred, int access_mode)
+{
+ char *attr;
+ int error;
+
+ attr = from_freebsd_extattr(attrnamespace, name);
+ if (attr == NULL)
+ return EINVAL;
+
+ error = extattr_check_cred(vp, attr, cred, access_mode);
+
+ kmem_free(attr, strlen(attr) + 1);
+
+ return error;
+}
+
+/*
+ * Per-FS attribute lock protecting attribute operations.
+ * XXX Right now there is a lot of lock contention due to having a single
+ * lock per-FS; really, this should be far more fine-grained.
+ */
+static void
+ufs_extattr_uepm_lock(struct ufsmount *ump)
+{
+
+ /*
+ * XXX This needs to be recursive for the following reasons:
+ * - it is taken in ufs_extattr_vnode_inactive
+ * - which is called from VOP_INACTIVE
+ * - which can be triggered by any vrele, vput, or vn_close
+ * - several of these can happen while it's held
+ */
+ if (mutex_owned(&ump->um_extattr.uepm_lock)) {
+ ump->um_extattr.uepm_lockcnt++;
+ return;
+ }
+ mutex_enter(&ump->um_extattr.uepm_lock);
+}
+
+static void
+ufs_extattr_uepm_unlock(struct ufsmount *ump)
+{
+
+ if (ump->um_extattr.uepm_lockcnt != 0) {
+ KASSERT(mutex_owned(&ump->um_extattr.uepm_lock));
+ ump->um_extattr.uepm_lockcnt--;
+ return;
+ }
+ mutex_exit(&ump->um_extattr.uepm_lock);
+}
+
+/*-
+ * Determine whether the name passed is a valid name for an actual
+ * attribute.
+ *
+ * Invalid currently consists of:
+ * NULL pointer for attrname
+ * zero-length attrname (used to retrieve application attribute list)
+ */
+static int
+ufs_extattr_valid_attrname(int attrnamespace, const char *attrname)
+{
+
+ if (attrname == NULL)
+ return 0;
+ if (strlen(attrname) == 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * Autocreate an attribute storage
+ */
+static int
+ufs_extattr_autocreate_attr(struct vnode *vp, int attrnamespace,
+ const char *attrname, struct lwp *l, struct ufs_extattr_list_entry **uelep)
+{
+ struct mount *mp = vp->v_mount;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ struct vnode *backing_vp;
+ struct nameidata nd;
+ struct pathbuf *pb;
+ char *path;
+ struct ufs_extattr_fileheader uef;
+ struct ufs_extattr_list_entry *uele;
+ int error;
+
+ path = PNBUF_GET();
+
+ /*
+ * We only support system and user namespace autocreation
+ */
+ switch (attrnamespace) {
+ case EXTATTR_NAMESPACE_SYSTEM:
+ (void)snprintf(path, PATH_MAX, "%s/%s/%s/%s",
+ mp->mnt_stat.f_mntonname, UFS_EXTATTR_FSROOTSUBDIR,
+ UFS_EXTATTR_SUBDIR_SYSTEM, attrname);
+ break;
+ case EXTATTR_NAMESPACE_USER:
+ (void)snprintf(path, PATH_MAX, "%s/%s/%s/%s",
+ mp->mnt_stat.f_mntonname, UFS_EXTATTR_FSROOTSUBDIR,
+ UFS_EXTATTR_SUBDIR_USER, attrname);
+ break;
+ default:
+ PNBUF_PUT(path);
+ *uelep = NULL;
+ return EINVAL;
+ break;
+ }
+
+ /*
+ * Release extended attribute mount lock, otherwise
+ * we can deadlock with another thread that would lock
+ * vp after we unlock it below, and call
+ * ufs_extattr_uepm_lock(ump), for instance
+ * in ufs_getextattr().
+ */
+ ufs_extattr_uepm_unlock(ump);
+
+ /*
+ * XXX unlock/lock should only be done when setting extattr
+ * on backing store or one of its parent directory
+ * including root, but we always do it for now.
+ */
+ KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+ VOP_UNLOCK(vp);
+
+ pb = pathbuf_create(path);
+ NDINIT(&nd, CREATE, LOCKPARENT, pb);
+
+ /*
+ * Since we do not hold ufs_extattr_uepm_lock anymore,
+ * another thread may race with us for backend creation,
+ * but only one can succeed here thanks to O_EXCL
+ */
+ error = vn_open(&nd, O_CREAT|O_EXCL|O_RDWR, 0600);
+
+ /*
+ * Reacquire the lock on the vnode
+ */
+ KASSERT(VOP_ISLOCKED(vp) == 0);
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+
+ ufs_extattr_uepm_lock(ump);
+
+ if (error != 0) {
+ pathbuf_destroy(pb);
+ PNBUF_PUT(path);
+ *uelep = NULL;
+ return error;
+ }
+
+ KASSERT(nd.ni_vp != NULL);
+ KASSERT(VOP_ISLOCKED(nd.ni_vp) == LK_EXCLUSIVE);
+ KASSERT(VOP_ISLOCKED(nd.ni_dvp) == 0);
+
+ /*
+ * backing_vp is the backing store.
+ */
+ backing_vp = nd.ni_vp;
+ pathbuf_destroy(pb);
+ PNBUF_PUT(path);
+
+ uef.uef_magic = UFS_EXTATTR_MAGIC;
+ uef.uef_version = UFS_EXTATTR_VERSION;
+ uef.uef_size = ufs_extattr_autocreate;
+
+ error = vn_rdwr(UIO_WRITE, backing_vp, &uef, sizeof(uef), 0,
+ UIO_SYSSPACE, IO_NODELOCKED|IO_APPEND,
+ l->l_cred, NULL, l);
+
+ VOP_UNLOCK(backing_vp);
+
+ if (error != 0) {
+ printf("%s: write uef header failed for `%s' (%d)\n",
+ __func__, attrname, error);
+ vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
+ *uelep = NULL;
+ return error;
+ }
+
+ /*
+ * Now enable attribute.
+ */
+ error = ufs_extattr_enable(ump,attrnamespace, attrname, backing_vp, l);
+ KASSERT(VOP_ISLOCKED(backing_vp) == 0);
+
+ if (error != 0) {
+ printf("%s: enable `%s' failed (%d)\n",
+ __func__, attrname, error);
+ vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
+ *uelep = NULL;
+ return error;
+ }
+
+ uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
+ if (uele == NULL) {
+ printf("%s: atttribute `%s' created but not found!\n",
+ __func__, attrname);
+ vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
+ *uelep = NULL;
+ return ESRCH; /* really internal error */
+ }
+
+ printf("%s: EA backing store autocreated for %s\n",
+ mp->mnt_stat.f_mntonname, attrname);
+
+ *uelep = uele;
+ return 0;
+}
+
+/*
+ * Locate an attribute given a name and mountpoint.
+ * Must be holding uepm lock for the mount point.
+ */
+static struct ufs_extattr_list_entry *
+ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace,
+ const char *attrname)
+{
+ struct ufs_extattr_list_entry *search_attribute;
+
+ for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list);
+ search_attribute != NULL;
+ search_attribute = LIST_NEXT(search_attribute, uele_entries)) {
+ if (!(strncmp(attrname, search_attribute->uele_attrname,
+ UFS_EXTATTR_MAXEXTATTRNAME)) &&
+ (attrnamespace == search_attribute->uele_attrnamespace)) {
+ return search_attribute;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize per-FS structures supporting extended attributes. Do not
+ * start extended attributes yet.
+ */
+void
+ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm)
+{
+
+ uepm->uepm_flags = 0;
+ uepm->uepm_lockcnt = 0;
+
+ LIST_INIT(&uepm->uepm_list);
+ mutex_init(&uepm->uepm_lock, MUTEX_DEFAULT, IPL_NONE);
+ uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
+}
+
+/*
+ * Destroy per-FS structures supporting extended attributes. Assumes
+ * that EAs have already been stopped, and will panic if not.
+ */
+void
+ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm)
+{
+
+ if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
+ panic("ufs_extattr_uepm_destroy: not initialized");
+
+ if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED))
+ panic("ufs_extattr_uepm_destroy: called while still started");
+
+ /*
+ * It's not clear that either order for the next three lines is
+ * ideal, and it should never be a problem if this is only called
+ * during unmount, and with vfs_busy().
+ */
+ uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
+ uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED;
+ mutex_destroy(&uepm->uepm_lock);
+}
+
+/*
+ * Start extended attribute support on an FS.
+ */
+int
+ufs_extattr_start(struct mount *mp, struct lwp *l)
+{
+ struct ufsmount *ump;
+ int error = 0;
+
+ ump = VFSTOUFS(mp);
+
+ if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
+ ufs_extattr_uepm_init(&ump->um_extattr);
+
+ ufs_extattr_uepm_lock(ump);
+
+ if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) {
+ error = EOPNOTSUPP;
+ goto unlock;
+ }
+ if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) {
+ error = EBUSY;
+ goto unlock;
+ }
+
+ ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
+
+ ump->um_extattr.uepm_ucred = l->l_cred;
+ kauth_cred_hold(ump->um_extattr.uepm_ucred);
+
+ unlock:
+ ufs_extattr_uepm_unlock(ump);
+ return error;
+}
+
+/*
+ * Helper routine: given a locked parent directory and filename, return
+ * the locked vnode of the inode associated with the name. Will not
+ * follow symlinks, may return any type of vnode. Lock on parent will
+ * be released even in the event of a failure. In the event that the
+ * target is the parent (i.e., "."), there will be two references and
+ * one lock, requiring the caller to possibly special-case.
+ */
+static int
+ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, const char *dirname,
+ struct vnode **vp, struct lwp *l)
+{
+ struct vop_lookup_v2_args vargs;
+ struct componentname cnp;
+ struct vnode *target_vp;
+ char *pnbuf;
+ int error;
+
+ KASSERT(VOP_ISLOCKED(start_dvp) == LK_EXCLUSIVE);
+
+ pnbuf = PNBUF_GET();
+
+ memset(&cnp, 0, sizeof(cnp));
+ cnp.cn_nameiop = LOOKUP;
+ cnp.cn_flags = ISLASTCN | lockparent;
+ cnp.cn_cred = l->l_cred;
+ cnp.cn_nameptr = pnbuf;
+ error = copystr(dirname, pnbuf, MAXPATHLEN, &cnp.cn_namelen);
+ if (error) {
+ if (lockparent == 0) {
+ VOP_UNLOCK(start_dvp);
+ }
+ PNBUF_PUT(pnbuf);
+ printf("%s: copystr failed (%d)\n", __func__, error);
+ return error;
+ }
+ cnp.cn_namelen--; /* trim nul termination */
+ vargs.a_desc = NULL;
+ vargs.a_dvp = start_dvp;
+ vargs.a_vpp = &target_vp;
+ vargs.a_cnp = &cnp;
+ error = ufs_lookup(&vargs);
+ PNBUF_PUT(pnbuf);
+ if (error) {
+ if (lockparent == 0) {
+ VOP_UNLOCK(start_dvp);
+ }
+ return error;
+ }
+#if 0
+ if (target_vp == start_dvp)
+ panic("%s: target_vp == start_dvp", __func__);
+#endif
+
+ if (target_vp != start_dvp) {
+ error = vn_lock(target_vp, LK_EXCLUSIVE);
+ if (lockparent == 0)
+ VOP_UNLOCK(start_dvp);
+ if (error) {
+ vrele(target_vp);
+ return error;
+ }
+ }
+
+ KASSERT(VOP_ISLOCKED(target_vp) == LK_EXCLUSIVE);
+ *vp = target_vp;
+ return 0;
+}
+
+/*
+ * Enable an EA using the passed filesystem, backing vnode, attribute name,
+ * namespace, and proc. Will perform a VOP_OPEN() on the vp, so expects vp
+ * to be locked when passed in. The vnode will be returned unlocked,
+ * regardless of success/failure of the function. As a result, the caller
+ * will always need to vrele(), but not vput().
+ */
+static int
+ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp,
+ int attrnamespace, const char *attrname, struct lwp *l)
+{
+ int error;
+
+ error = VOP_OPEN(vp, FREAD|FWRITE, l->l_cred);
+ if (error) {
+ printf("%s: VOP_OPEN(): failed (%d)\n", __func__, error);
+ VOP_UNLOCK(vp);
+ return error;
+ }
+
+ mutex_enter(vp->v_interlock);
+ vp->v_writecount++;
+ mutex_exit(vp->v_interlock);
+
+ vref(vp);
+
+ VOP_UNLOCK(vp);
+
+ error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, l);
+ if (error != 0)
+ vn_close(vp, FREAD|FWRITE, l->l_cred);
+ return error;
+}
+
+/*
+ * Given a locked directory vnode, iterate over the names in the directory
+ * and use ufs_extattr_lookup() to retrieve locked vnodes of potential
+ * attribute files. Then invoke ufs_extattr_enable_with_open() on each
+ * to attempt to start the attribute. Leaves the directory locked on
+ * exit.
+ */
+static int
+ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp,
+ int attrnamespace, struct lwp *l)
+{
+ struct vop_readdir_args vargs;
+ struct statvfs *sbp = &ump->um_mountp->mnt_stat;
+ struct dirent *dp, *edp;
+ struct vnode *attr_vp;
+ struct uio auio;
+ struct iovec aiov;
+ char *dirbuf;
+ int error, eofflag = 0;
+
+ if (dvp->v_type != VDIR)
+ return ENOTDIR;
+
+ dirbuf = kmem_alloc(UFS_DIRBLKSIZ, KM_SLEEP);
+
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_rw = UIO_READ;
+ auio.uio_offset = 0;
+ UIO_SETUP_SYSSPACE(&auio);
+
+ vargs.a_desc = NULL;
+ vargs.a_vp = dvp;
+ vargs.a_uio = &auio;
+ vargs.a_cred = l->l_cred;
+ vargs.a_eofflag = &eofflag;
+ vargs.a_ncookies = NULL;
+ vargs.a_cookies = NULL;
+
+ while (!eofflag) {
+ auio.uio_resid = UFS_DIRBLKSIZ;
+ aiov.iov_base = dirbuf;
+ aiov.iov_len = UFS_DIRBLKSIZ;
+ error = ufs_readdir(&vargs);
+ if (error) {
+ printf("%s: ufs_readdir (%d)\n", __func__, error);
+ return error;
+ }
+
+ /*
+ * XXXRW: While in UFS, we always get UFS_DIRBLKSIZ returns from
+ * the directory code on success, on other file systems this
+ * may not be the case. For portability, we should check the
+ * read length on return from ufs_readdir().
+ */
+ edp = (struct dirent *)&dirbuf[UFS_DIRBLKSIZ];
+ for (dp = (struct dirent *)dirbuf; dp < edp; ) {
+ if (dp->d_reclen == 0)
+ break;
+ /* Skip "." and ".." */
+ if (dp->d_name[0] == '.' &&
+ (dp->d_name[1] == '\0' ||
+ (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
+ goto next;
+ error = ufs_extattr_lookup(dvp, LOCKPARENT,
+ dp->d_name, &attr_vp, l);
+ if (error == ENOENT) {
+ goto next; /* keep silent */
+ } else if (error) {
+ printf("%s: lookup `%s' (%d)\n", __func__,
+ dp->d_name, error);
+ } else if (attr_vp == dvp) {
+ vrele(attr_vp);
+ } else if (attr_vp->v_type != VREG) {
+ vput(attr_vp);
+ } else {
+ error = ufs_extattr_enable_with_open(ump,
+ attr_vp, attrnamespace, dp->d_name, l);
+ vrele(attr_vp);
+ if (error) {
+ printf("%s: enable `%s' (%d)\n",
+ __func__, dp->d_name, error);
+ } else if (bootverbose) {
+ printf("%s: EA %s loaded\n",
+ sbp->f_mntonname, dp->d_name);
+ }
+ }
+ next:
+ dp = (struct dirent *) ((char *)dp + dp->d_reclen);
+ if (dp >= edp)
+ break;
+ }
+ }
+ kmem_free(dirbuf, UFS_DIRBLKSIZ);
+
+ return 0;
+}
+
+static int
+ufs_extattr_subdir(struct lwp *l, struct mount *mp, struct vnode *attr_dvp,
+ const char *subdir, int namespace)
+{
+ int error;
+ struct vnode *attr_sub;
+ error = ufs_extattr_lookup(attr_dvp, LOCKPARENT, subdir, &attr_sub, l);
+ KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
+ if (error) {
+ printf("%s: Can't find `%s/%s/%s' (%d)\n",
+ __func__, mp->mnt_stat.f_mntonname,
+ UFS_EXTATTR_FSROOTSUBDIR, subdir, error);
+ return error;
+ }
+ KASSERT(VOP_ISLOCKED(attr_sub) == LK_EXCLUSIVE);
+ error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
+ attr_sub, namespace, l);
+ if (error) {
+ printf("%s: ufs_extattr_iterate_directory `%s/%s/%s' (%d)\n",
+ __func__, mp->mnt_stat.f_mntonname,
+ UFS_EXTATTR_FSROOTSUBDIR, subdir, error);
+ }
+ KASSERT(VOP_ISLOCKED(attr_sub) == LK_EXCLUSIVE);
+ vput(attr_sub);
+ return error;
+}
+
+/*
+ * Auto-start of extended attributes, to be executed (optionally) at
+ * mount-time.
+ */
+int
+ufs_extattr_autostart(struct mount *mp, struct lwp *l)
+{
+ struct vnode *rvp, *attr_dvp;
+ int error;
+
+ /*
+ * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root?
+ * If so, automatically start EA's.
+ */
+ error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp);
+ if (error) {
+ printf("%s: VFS_ROOT() (%d)\n", __func__, error);
+ return error;
+ }
+
+ KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
+
+ error = ufs_extattr_lookup(rvp, 0,
+ UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, l);
+ if (error) {
+ /* rvp ref'd but now unlocked */
+ KASSERT(VOP_ISLOCKED(rvp) == 0);
+ vrele(rvp);
+ printf("%s: lookup `%s/%s' (%d)\n", __func__,
+ mp->mnt_stat.f_mntonname, UFS_EXTATTR_FSROOTSUBDIR, error);
+ return error;
+ }
+ if (rvp == attr_dvp) {
+ /* Should never happen. */
+ KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
+ vrele(attr_dvp);
+ vput(rvp);
+ printf("%s: `/' == `%s/%s' (%d)\n", __func__,
+ mp->mnt_stat.f_mntonname, UFS_EXTATTR_FSROOTSUBDIR, EINVAL);
+ return EINVAL;
+ }
+ KASSERT(VOP_ISLOCKED(rvp) == 0);
+ vrele(rvp);
+
+ KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
+
+ if (attr_dvp->v_type != VDIR) {
+ printf("%s: `%s/%s' is not a directory\n",
+ __func__, mp->mnt_stat.f_mntonname,
+ UFS_EXTATTR_FSROOTSUBDIR);
+ goto return_vput_attr_dvp;
+ }
+
+ error = ufs_extattr_start(mp, l);
+ if (error) {
+ printf("%s: ufs_extattr_start failed (%d)\n", __func__,
+ error);
+ goto return_vput_attr_dvp;
+ }
+
+ /*
+ * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM,
+ * UFS_EXTATTR_SUBDIR_USER. For each, iterate over the sub-directory,
+ * and start with appropriate type. Failures in either don't
+ * result in an over-all failure. attr_dvp is left locked to
+ * be cleaned up on exit.
+ */
+ error = ufs_extattr_subdir(l, mp, attr_dvp, UFS_EXTATTR_SUBDIR_SYSTEM,
+ EXTATTR_NAMESPACE_SYSTEM);
+ error = ufs_extattr_subdir(l, mp, attr_dvp, UFS_EXTATTR_SUBDIR_USER,
+ EXTATTR_NAMESPACE_USER);
+
+ /* Mask startup failures in sub-directories. */
+ error = 0;
+
+ return_vput_attr_dvp:
+ KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
+ vput(attr_dvp);
+
+ return error;
+}
+
+/*
+ * Stop extended attribute support on an FS.
+ */
+void
+ufs_extattr_stop(struct mount *mp, struct lwp *l)
+{
+ struct ufs_extattr_list_entry *uele;
+ struct ufsmount *ump = VFSTOUFS(mp);
+
+ ufs_extattr_uepm_lock(ump);
+
+ /*
+ * If we haven't been started, no big deal. Just short-circuit
+ * the processing work.
+ */
+ if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
+ goto unlock;
+ }
+
+ while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) {
+ uele = LIST_FIRST(&ump->um_extattr.uepm_list);
+ ufs_extattr_disable(ump, uele->uele_attrnamespace,
+ uele->uele_attrname, l);
+ }
+
+ ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
+
+ kauth_cred_free(ump->um_extattr.uepm_ucred);
+ ump->um_extattr.uepm_ucred = NULL;
+
+ unlock:
+ ufs_extattr_uepm_unlock(ump);
+}
+
+/*
+ * Enable a named attribute on the specified filesystem; provide an
+ * unlocked backing vnode to hold the attribute data.
+ */
+static int
+ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
+ const char *attrname, struct vnode *backing_vnode, struct lwp *l)
+{
+ struct ufs_extattr_list_entry *attribute;
+ struct iovec aiov;
+ struct uio auio;
+ int error = 0;
+
+ if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
+ return EINVAL;
+ if (backing_vnode->v_type != VREG)
+ return EINVAL;
+
+ attribute = kmem_zalloc(sizeof(*attribute), KM_SLEEP);
+
+ if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
+ error = EOPNOTSUPP;
+ goto free_exit;
+ }
+
+ if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) {
+ error = EEXIST;
+ goto free_exit;
+ }
+
+ strncpy(attribute->uele_attrname, attrname,
+ UFS_EXTATTR_MAXEXTATTRNAME);
+ attribute->uele_attrnamespace = attrnamespace;
+ memset(&attribute->uele_fileheader, 0,
+ sizeof(struct ufs_extattr_fileheader));
+
+ attribute->uele_backing_vnode = backing_vnode;
+
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ aiov.iov_base = (void *) &attribute->uele_fileheader;
+ aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
+ auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
+ auio.uio_offset = (off_t) 0;
+ auio.uio_rw = UIO_READ;
+ UIO_SETUP_SYSSPACE(&auio);
+
+ vn_lock(backing_vnode, LK_SHARED | LK_RETRY);
+ error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
+ ump->um_extattr.uepm_ucred);
+
+ if (error)
+ goto unlock_free_exit;
+
+ if (auio.uio_resid != 0) {
+ printf("%s: malformed attribute header\n", __func__);
+ error = EINVAL;
+ goto unlock_free_exit;
+ }
+
+ /*
+ * Try to determine the byte order of the attribute file.
+ */
+ if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
+ attribute->uele_flags |= UELE_F_NEEDSWAP;
+ attribute->uele_fileheader.uef_magic =
+ ufs_rw32(attribute->uele_fileheader.uef_magic,
+ UELE_NEEDSWAP(attribute));
+ if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
+ printf("%s: invalid attribute header magic\n",
+ __func__);
+ error = EINVAL;
+ goto unlock_free_exit;
+ }
+ }
+ attribute->uele_fileheader.uef_version =
+ ufs_rw32(attribute->uele_fileheader.uef_version,
+ UELE_NEEDSWAP(attribute));
+ attribute->uele_fileheader.uef_size =
+ ufs_rw32(attribute->uele_fileheader.uef_size,
+ UELE_NEEDSWAP(attribute));
+
+ if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
+ printf("%s: incorrect attribute header version %d != %d\n",
+ __func__, attribute->uele_fileheader.uef_version,
+ UFS_EXTATTR_VERSION);
+ error = EINVAL;
+ goto unlock_free_exit;
+ }
+
+ LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute,
+ uele_entries);
+
+ VOP_UNLOCK(backing_vnode);
+ return 0;
+
+ unlock_free_exit:
+ VOP_UNLOCK(backing_vnode);
+
+ free_exit:
+ kmem_free(attribute, sizeof(*attribute));
+ return error;
+}
+
+/*
+ * Disable extended attribute support on an FS.
+ */
+static int
+ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
+ const char *attrname, struct lwp *l)
+{
+ struct ufs_extattr_list_entry *uele;
+ int error = 0;
+
+ if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
+ return EINVAL;
+
+ uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
+ if (!uele)
+ return ENODATA;
+
+ LIST_REMOVE(uele, uele_entries);
+
+ error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
+ l->l_cred);
+
+ kmem_free(uele, sizeof(*uele));
+
+ return error;
+}
+
+/*
+ * VFS call to manage extended attributes in UFS. If filename_vp is
+ * non-NULL, it must be passed in locked, and regardless of errors in
+ * processing, will be unlocked.
+ */
+int
+ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
+ int attrnamespace, const char *attrname)
+{
+ struct lwp *l = curlwp;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ int error;
+
+ /*
+ * Only privileged processes can configure extended attributes.
+ */
+ error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_EXTATTR,
+ 0, mp, NULL, NULL);
+ if (error) {
+ if (filename_vp != NULL)
+ VOP_UNLOCK(filename_vp);
+ return error;
+ }
+
+ switch(cmd) {
+ case UFS_EXTATTR_CMD_START:
+ case UFS_EXTATTR_CMD_STOP:
+ case UFS_EXTATTR_CMD_ENABLE:
+ case UFS_EXTATTR_CMD_DISABLE:
+ if (filename_vp != NULL) {
+ VOP_UNLOCK(filename_vp);
+ return EINVAL;
+ }
+ if (attrname != NULL)
+ return EINVAL;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ switch(cmd) {
+ case UFS_EXTATTR_CMD_START:
+ error = ufs_extattr_autostart(mp, l);
+ return error;
+
+ case UFS_EXTATTR_CMD_STOP:
+ ufs_extattr_stop(mp, l);
+ return 0;
+
+ case UFS_EXTATTR_CMD_ENABLE:
+ /*
+ * ufs_extattr_enable_with_open() will always unlock the
+ * vnode, regardless of failure.
+ */
+ ufs_extattr_uepm_lock(ump);
+ error = ufs_extattr_enable_with_open(ump, filename_vp,
+ attrnamespace, attrname, l);
+ ufs_extattr_uepm_unlock(ump);
+ return error;
+
+ case UFS_EXTATTR_CMD_DISABLE:
+ ufs_extattr_uepm_lock(ump);
+ error = ufs_extattr_disable(ump, attrnamespace, attrname, l);
+ ufs_extattr_uepm_unlock(ump);
+ return error;
+
+ default:
+ return EINVAL;
+ }
+}
+
+/*
+ * Read extended attribute header for a given vnode and attribute.
+ * Backing vnode should be locked and unlocked by caller.
+ */
+static int
+ufs_extattr_get_header(struct vnode *vp, struct ufs_extattr_list_entry *uele,
+ struct ufs_extattr_header *ueh, off_t *bap)
+{
+ struct mount *mp = vp->v_mount;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ struct inode *ip = VTOI(vp);
+ off_t base_offset;
+ struct iovec aiov;
+ struct uio aio;
+ int error;
+
+ /*
+ * Find base offset of header in file based on file header size, and
+ * data header size + maximum data size, indexed by inode number.
+ */
+ base_offset = sizeof(struct ufs_extattr_fileheader) +
+ ip->i_number * (sizeof(struct ufs_extattr_header) +
+ uele->uele_fileheader.uef_size);
+
+ /*
+ * Read in the data header to see if the data is defined, and if so
+ * how much.
+ */
+ memset(ueh, 0, sizeof(struct ufs_extattr_header));
+ aiov.iov_base = ueh;
+ aiov.iov_len = sizeof(struct ufs_extattr_header);
+ aio.uio_iov = &aiov;
+ aio.uio_iovcnt = 1;
+ aio.uio_rw = UIO_READ;
+ aio.uio_offset = base_offset;
+ aio.uio_resid = sizeof(struct ufs_extattr_header);
+ UIO_SETUP_SYSSPACE(&aio);
+
+ error = VOP_READ(uele->uele_backing_vnode, &aio,
+ IO_NODELOCKED, ump->um_extattr.uepm_ucred);
+ if (error)
+ return error;
+
+ /*
+ * Attribute headers are kept in file system byte order.
+ * XXX What about the blob of data?
+ */
+ ueh->ueh_flags = ufs_rw32(ueh->ueh_flags, UELE_NEEDSWAP(uele));
+ ueh->ueh_len = ufs_rw32(ueh->ueh_len, UELE_NEEDSWAP(uele));
+ ueh->ueh_i_gen = ufs_rw32(ueh->ueh_i_gen, UELE_NEEDSWAP(uele));
+
+ /* Defined? */
+ if ((ueh->ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0)
+ return ENODATA;
+
+ /* Valid for the current inode generation? */
+ if (ueh->ueh_i_gen != ip->i_gen) {
+ /*
+ * The inode itself has a different generation number
+ * than the uele data. For now, the best solution
+ * is to coerce this to undefined, and let it get cleaned
+ * up by the next write or extattrctl clean.
+ */
+ printf("%s: %s: inode gen inconsistency (%u, %jd)\n",
+ __func__, mp->mnt_stat.f_mntonname, ueh->ueh_i_gen,
+ (intmax_t)ip->i_gen);
+ return ENODATA;
+ }
+
+ /* Local size consistency check. */
+ if (ueh->ueh_len > uele->uele_fileheader.uef_size)
+ return ENXIO;
+
+ /* Return base offset */
+ if (bap != NULL)
+ *bap = base_offset;
+
+ return 0;
+}
+
+/*
+ * Vnode operation to retrieve a named extended attribute.
+ */
+int
+ufs_getextattr(struct vop_getextattr_args *ap)
+/*
+vop_getextattr {
+ IN struct vnode *a_vp;
+ IN int a_attrnamespace;
+ IN const char *a_name;
+ INOUT struct uio *a_uio;
+ OUT size_t *a_size;
+ IN kauth_cred_t a_cred;
+};
+*/
+{
+ struct mount *mp = ap->a_vp->v_mount;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ int error;
+
+ if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
+ return EOPNOTSUPP;
+
+ ufs_extattr_uepm_lock(ump);
+
+ error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
+ ap->a_uio, ap->a_size, ap->a_cred, curlwp);
+
+ ufs_extattr_uepm_unlock(ump);
+
+ return error;
+}
+
+/*
+ * Real work associated with retrieving a named attribute--assumes that
+ * the attribute lock has already been grabbed.
+ */
+static int
+ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
+ struct uio *uio, size_t *size, kauth_cred_t cred, struct lwp *l)
+{
+ struct ufs_extattr_list_entry *attribute;
+ struct ufs_extattr_header ueh;
+ struct mount *mp = vp->v_mount;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ off_t base_offset;
+ size_t len, old_len;
+ int error = 0;
+
+ if (strlen(name) == 0)
+ return EINVAL;
+
+ error = internal_extattr_check_cred(vp, attrnamespace, name, cred,
+ VREAD);
+ if (error)
+ return error;
+
+ attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
+ if (!attribute)
+ return ENODATA;
+
+ /*
+ * Allow only offsets of zero to encourage the read/replace
+ * extended attribute semantic. Otherwise we can't guarantee
+ * atomicity, as we don't provide locks for extended attributes.
+ */
+ if (uio != NULL && uio->uio_offset != 0)
+ return ENXIO;
+
+ /*
+ * Don't need to get a lock on the backing file if the getattr is
+ * being applied to the backing file, as the lock is already held.
+ */
+ if (attribute->uele_backing_vnode != vp)
+ vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_RETRY);
+
+ error = ufs_extattr_get_header(vp, attribute, &ueh, &base_offset);
+ if (error)
+ goto vopunlock_exit;
+
+ /* Return full data size if caller requested it. */
+ if (size != NULL)
+ *size = ueh.ueh_len;
+
+ /* Return data if the caller requested it. */
+ if (uio != NULL) {
+ /* Allow for offset into the attribute data. */
+ uio->uio_offset = base_offset + sizeof(struct
+ ufs_extattr_header);
+
+ /*
+ * Figure out maximum to transfer -- use buffer size and
+ * local data limit.
+ */
+ len = MIN(uio->uio_resid, ueh.ueh_len);
+ old_len = uio->uio_resid;
+ uio->uio_resid = len;
+
+ error = VOP_READ(attribute->uele_backing_vnode, uio,
+ IO_NODELOCKED, ump->um_extattr.uepm_ucred);
+ if (error)
+ goto vopunlock_exit;
+
+ uio->uio_resid = old_len - (len - uio->uio_resid);
+ }
+
+ vopunlock_exit:
+
+ if (uio != NULL)
+ uio->uio_offset = 0;
+
+ if (attribute->uele_backing_vnode != vp)
+ VOP_UNLOCK(attribute->uele_backing_vnode);
+
+ return error;
+}
+
+/*
+ * Vnode operation to list extended attribute for a vnode
+ */
+int
+ufs_listextattr(struct vop_listextattr_args *ap)
+/*
+vop_listextattr {
+ IN struct vnode *a_vp;
+ IN int a_attrnamespace;
+ INOUT struct uio *a_uio;
+ OUT size_t *a_size;
+ IN int flag;
+ IN kauth_cred_t a_cred;
+ struct proc *a_p;
+};
+*/
+{
+ struct mount *mp = ap->a_vp->v_mount;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ int error;
+
+ if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
+ return EOPNOTSUPP;
+
+ ufs_extattr_uepm_lock(ump);
+
+ error = ufs_extattr_list(ap->a_vp, ap->a_attrnamespace,
+ ap->a_uio, ap->a_size, ap->a_flag, ap->a_cred, curlwp);
+
+ ufs_extattr_uepm_unlock(ump);
+
+ return error;
+}
+
+/*
+ * Real work associated with retrieving list of attributes--assumes that
+ * the attribute lock has already been grabbed.
+ */
+static int
+ufs_extattr_list(struct vnode *vp, int attrnamespace,
+ struct uio *uio, size_t *size, int flag,
+ kauth_cred_t cred, struct lwp *l)
+{
+ struct ufs_extattr_list_entry *uele;
+ struct ufs_extattr_header ueh;
+ struct mount *mp = vp->v_mount;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ size_t listsize = 0;
+ int error = 0;
+
+ /*
+ * XXX: We can move this inside the loop and iterate on individual
+ * attributes.
+ */
+ error = internal_extattr_check_cred(vp, attrnamespace, "", cred,
+ VREAD);
+ if (error)
+ return error;
+
+ LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) {
+ unsigned char attrnamelen;
+
+ if (uele->uele_attrnamespace != attrnamespace)
+ continue;
+
+ error = ufs_extattr_get_header(vp, uele, &ueh, NULL);
+ if (error == ENODATA)
+ continue;
+ if (error != 0)
+ return error;
+
+ /*
+ * Don't need to get a lock on the backing file if
+ * the listattr is being applied to the backing file,
+ * as the lock is already held.
+ */
+ if (uele->uele_backing_vnode != vp)
+ vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY);
+
+ /*
+ * +1 for trailing NUL (listxattr flavor)
+ * or leading name length (extattr_list_file flavor)
+ */
+ attrnamelen = strlen(uele->uele_attrname);
+ listsize += attrnamelen + 1;
+
+ /* Return data if the caller requested it. */
+ if (uio != NULL) {
+ /*
+ * We support two flavors. Either NUL-terminated
+ * strings (a la listxattr), or non NUL-terminated,
+ * one byte length prefixed strings (for
+ * extattr_list_file). EXTATTR_LIST_LENPREFIX switches
+ * that second behavior.
+ */
+ if (flag & EXTATTR_LIST_LENPREFIX) {
+ uint8_t len = (uint8_t)attrnamelen;
+
+ /* Copy leading name length */
+ error = uiomove(&len, sizeof(len), uio);
+ if (error != 0)
+ break;
+ } else {
+ /* Include trailing NULL */
+ attrnamelen++;
+ }
+
+ error = uiomove(uele->uele_attrname,
+ (size_t)attrnamelen, uio);
+ if (error != 0)
+ break;
+ }
+
+ if (uele->uele_backing_vnode != vp)
+ VOP_UNLOCK(uele->uele_backing_vnode);
+
+ if (error != 0)
+ return error;
+ }
+
+ if (uio != NULL)
+ uio->uio_offset = 0;
+
+ /* Return full data size if caller requested it. */
+ if (size != NULL)
+ *size = listsize;
+
+ return 0;
+}
+
+/*
+ * Vnode operation to remove a named attribute.
+ */
+int
+ufs_deleteextattr(struct vop_deleteextattr_args *ap)
+/*
+vop_deleteextattr {
+ IN struct vnode *a_vp;
+ IN int a_attrnamespace;
+ IN const char *a_name;
+ IN kauth_cred_t a_cred;
+};
+*/
+{
+ struct mount *mp = ap->a_vp->v_mount;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ int error;
+
+ if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
+ return EOPNOTSUPP;
+
+ ufs_extattr_uepm_lock(ump);
+
+ error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name,
+ ap->a_cred, curlwp);
+
+ ufs_extattr_uepm_unlock(ump);
+
+ return error;
+}
+
+/*
+ * Vnode operation to set a named attribute.
+ */
+int
+ufs_setextattr(struct vop_setextattr_args *ap)
+/*
+vop_setextattr {
+ IN struct vnode *a_vp;
+ IN int a_attrnamespace;
+ IN const char *a_name;
+ INOUT struct uio *a_uio;
+ IN kauth_cred_t a_cred;
+};
+*/
+{
+ struct mount *mp = ap->a_vp->v_mount;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ int error;
+
+ if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
+ return EOPNOTSUPP;
+
+ ufs_extattr_uepm_lock(ump);
+
+ /*
+ * XXX: No longer a supported way to delete extended attributes.
+ */
+ if (ap->a_uio == NULL) {
+ ufs_extattr_uepm_unlock(ump);
+ return EINVAL;
+ }
+
+ error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name,
+ ap->a_uio, ap->a_cred, curlwp);
+
+ ufs_extattr_uepm_unlock(ump);
+
+ return error;
+}
+
+/*
+ * Real work associated with setting a vnode's extended attributes;
+ * assumes that the attribute lock has already been grabbed.
+ */
+static int
+ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
+ struct uio *uio, kauth_cred_t cred, struct lwp *l)
+{
+ struct ufs_extattr_list_entry *attribute;
+ struct ufs_extattr_header ueh;
+ struct iovec local_aiov;
+ struct uio local_aio;
+ struct mount *mp = vp->v_mount;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ struct inode *ip = VTOI(vp);
+ off_t base_offset;
+ int error = 0, ioflag;
+
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return EROFS;
+
+ if (!ufs_extattr_valid_attrname(attrnamespace, name))
+ return EINVAL;
+
+ error = internal_extattr_check_cred(vp, attrnamespace, name, cred,
+ VWRITE);
+ if (error)
+ return error;
+
+ attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
+ if (!attribute) {
+ error = ufs_extattr_autocreate_attr(vp, attrnamespace,
+ name, l, &attribute);
+ if (error == EEXIST) {
+ /* Another thread raced us for backend creation */
+ error = 0;
+ attribute =
+ ufs_extattr_find_attr(ump, attrnamespace, name);
+ }
+
+ if (error || !attribute)
+ return ENODATA;
+ }
+
+ /*
+ * Early rejection of invalid offsets/length.
+ * Reject: any offset but 0 (replace)
+ * Any size greater than attribute size limit
+ */
+ if (uio->uio_offset != 0 ||
+ uio->uio_resid > attribute->uele_fileheader.uef_size)
+ return ENXIO;
+
+ /*
+ * Find base offset of header in file based on file header size, and
+ * data header size + maximum data size, indexed by inode number.
+ */
+ base_offset = sizeof(struct ufs_extattr_fileheader) +
+ ip->i_number * (sizeof(struct ufs_extattr_header) +
+ attribute->uele_fileheader.uef_size);
+
+ /*
+ * Write out a data header for the data.
+ */
+ ueh.ueh_len = ufs_rw32((uint32_t) uio->uio_resid,
+ UELE_NEEDSWAP(attribute));
+ ueh.ueh_flags = ufs_rw32(UFS_EXTATTR_ATTR_FLAG_INUSE,
+ UELE_NEEDSWAP(attribute));
+ ueh.ueh_i_gen = ufs_rw32(ip->i_gen, UELE_NEEDSWAP(attribute));
+ local_aiov.iov_base = &ueh;
+ local_aiov.iov_len = sizeof(struct ufs_extattr_header);
+ local_aio.uio_iov = &local_aiov;
+ local_aio.uio_iovcnt = 1;
+ local_aio.uio_rw = UIO_WRITE;
+ local_aio.uio_offset = base_offset;
+ local_aio.uio_resid = sizeof(struct ufs_extattr_header);
+ UIO_SETUP_SYSSPACE(&local_aio);
+
+ /*
+ * Don't need to get a lock on the backing file if the setattr is
+ * being applied to the backing file, as the lock is already held.
+ */
+ if (attribute->uele_backing_vnode != vp)
+ vn_lock(attribute->uele_backing_vnode,
+ LK_EXCLUSIVE | LK_RETRY);
+
+ ioflag = IO_NODELOCKED;
+ if (ufs_extattr_sync)
+ ioflag |= IO_SYNC;
+ error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
+ ump->um_extattr.uepm_ucred);
+ if (error)
+ goto vopunlock_exit;
+
+ if (local_aio.uio_resid != 0) {
+ error = ENXIO;
+ goto vopunlock_exit;
+ }
+
+ /*
+ * Write out user data.
+ * XXX NOT ATOMIC WITH RESPECT TO THE HEADER.
+ */
+ uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
+
+ ioflag = IO_NODELOCKED;
+ if (ufs_extattr_sync)
+ ioflag |= IO_SYNC;
+ error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
+ ump->um_extattr.uepm_ucred);
+
+ vopunlock_exit:
+ uio->uio_offset = 0;
+
+ if (attribute->uele_backing_vnode != vp)
+ VOP_UNLOCK(attribute->uele_backing_vnode);
+
+ return error;
+}
+
+/*
+ * Real work associated with removing an extended attribute from a vnode.
+ * Assumes the attribute lock has already been grabbed.
+ */
+static int
+ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
+ kauth_cred_t cred, struct lwp *l)
+{
+ struct ufs_extattr_list_entry *attribute;
+ struct ufs_extattr_header ueh;
+ struct mount *mp = vp->v_mount;
+ struct ufsmount *ump = VFSTOUFS(mp);
+ struct iovec local_aiov;
+ struct uio local_aio;
+ off_t base_offset;
+ int error = 0, ioflag;
+
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return EROFS;
+
+ if (!ufs_extattr_valid_attrname(attrnamespace, name))
+ return EINVAL;
+
+ error = internal_extattr_check_cred(vp, attrnamespace, name, cred,
+ VWRITE);
+ if (error)
+ return error;
+
+ attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
+ if (!attribute)
+ return ENODATA;
+
+ /*
+ * Don't need to get a lock on the backing file if the getattr is
+ * being applied to the backing file, as the lock is already held.
+ */
+ if (attribute->uele_backing_vnode != vp)
+ vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY);
+
+ error = ufs_extattr_get_header(vp, attribute, &ueh, &base_offset);
+ if (error)
+ goto vopunlock_exit;
+
+ /* Flag it as not in use. */
+ ueh.ueh_flags = 0; /* No need to byte swap 0 */
+ ueh.ueh_len = 0; /* ...ditto... */
+
+ local_aiov.iov_base = &ueh;
+ local_aiov.iov_len = sizeof(struct ufs_extattr_header);
+ local_aio.uio_iov = &local_aiov;
+ local_aio.uio_iovcnt = 1;
+ local_aio.uio_rw = UIO_WRITE;
+ local_aio.uio_offset = base_offset;
+ local_aio.uio_resid = sizeof(struct ufs_extattr_header);
+ UIO_SETUP_SYSSPACE(&local_aio);
+
+ ioflag = IO_NODELOCKED;
+ if (ufs_extattr_sync)
+ ioflag |= IO_SYNC;
+ error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
+ ump->um_extattr.uepm_ucred);
+ if (error)
+ goto vopunlock_exit;
+
+ if (local_aio.uio_resid != 0)
+ error = ENXIO;
+
+ vopunlock_exit:
+ VOP_UNLOCK(attribute->uele_backing_vnode);
+
+ return error;
+}
+
+/*
+ * Called by UFS when an inode is no longer active and should have its
+ * attributes stripped.
+ */
+void
+ufs_extattr_vnode_inactive(struct vnode *vp, struct lwp *l)
+{
+ struct ufs_extattr_list_entry *uele;
+ struct mount *mp = vp->v_mount;
+ struct ufsmount *ump = VFSTOUFS(mp);
+
+ /*
+ * In that case, we cannot lock. We should not have any active vnodes
+ * on the fs if this is not yet initialized but is going to be, so
+ * this can go unlocked.
+ */
+ if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
+ return;
+
+ if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
+ return;
+
+ ufs_extattr_uepm_lock(ump);
+
+ LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
+ ufs_extattr_rm(vp, uele->uele_attrnamespace,
+ uele->uele_attrname, lwp0.l_cred, l);
+
+ ufs_extattr_uepm_unlock(ump);
+}
+
+void
+ufs_extattr_init(void)
+{
+
+}
+
+void
+ufs_extattr_done(void)
+{
+
+}
Home |
Main Index |
Thread Index |
Old Index