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;
+}