Sat Apr 18 19:18:34 2020 UTC ()
Extended attribute support for ffsv2, from FreeBSD.


(christos)
diff -r1.13 -r1.14 src/sys/modules/ffs/Makefile
diff -r1.17 -r1.18 src/sys/rump/fs/lib/libffs/Makefile
diff -r1.46 -r1.47 src/sys/ufs/files.ufs
diff -r1.166 -r1.167 src/sys/ufs/ffs/ffs_alloc.c
diff -r1.63 -r1.64 src/sys/ufs/ffs/ffs_balloc.c
diff -r0 -r1.1 src/sys/ufs/ffs/ffs_extattr.c
diff -r1.85 -r1.86 src/sys/ufs/ffs/ffs_extern.h
diff -r1.126 -r1.127 src/sys/ufs/ffs/ffs_inode.c
diff -r1.151 -r1.152 src/sys/ufs/ffs/ffs_snapshot.c
diff -r1.130 -r1.131 src/sys/ufs/ffs/ffs_vnops.c
diff -r1.66 -r1.67 src/sys/ufs/ffs/fs.h
diff -r1.11 -r1.12 src/sys/ufs/ufs/extattr.h
diff -r1.76 -r1.77 src/sys/ufs/ufs/inode.h
diff -r1.84 -r1.85 src/sys/ufs/ufs/ufs_extern.h
diff -r1.109 -r1.110 src/sys/ufs/ufs/ufs_inode.c
diff -r1.251 -r1.252 src/sys/ufs/ufs/ufs_vnops.c

cvs diff -r1.13 -r1.14 src/sys/modules/ffs/Makefile (expand / switch to context diff)
--- src/sys/modules/ffs/Makefile 2019/08/19 09:31:30 1.13
+++ src/sys/modules/ffs/Makefile 2020/04/18 19:18:33 1.14
@@ -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 @@
 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

cvs diff -r1.17 -r1.18 src/sys/rump/fs/lib/libffs/Makefile (expand / switch to context diff)
--- src/sys/rump/fs/lib/libffs/Makefile 2020/04/12 00:04:45 1.17
+++ src/sys/rump/fs/lib/libffs/Makefile 2020/04/18 19:18:33 1.18
@@ -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 @@
 
 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	\

cvs diff -r1.46 -r1.47 src/sys/ufs/files.ufs (expand / switch to context diff)
--- src/sys/ufs/files.ufs 2020/04/11 17:43:54 1.46
+++ src/sys/ufs/files.ufs 2020/04/18 19:18:33 1.47
@@ -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 @@
 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

cvs diff -r1.166 -r1.167 src/sys/ufs/ffs/ffs_alloc.c (expand / switch to context diff)
--- src/sys/ufs/ffs/ffs_alloc.c 2020/02/23 15:46:42 1.166
+++ src/sys/ufs/ffs/ffs_alloc.c 2020/04/18 19:18:34 1.167
@@ -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 @@
 	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);

cvs diff -r1.63 -r1.64 src/sys/ufs/ffs/ffs_balloc.c (expand / switch to context diff)
--- src/sys/ufs/ffs/ffs_balloc.c 2017/10/28 00:37:13 1.63
+++ src/sys/ufs/ffs/ffs_balloc.c 2020/04/18 19:18:34 1.64
@@ -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_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);
 			}

File Added: src/sys/ufs/ffs/ffs_extattr.c
/*	$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;
}

cvs diff -r1.85 -r1.86 src/sys/ufs/ffs/ffs_extern.h (expand / switch to context diff)
--- src/sys/ufs/ffs/ffs_extern.h 2018/08/22 01:05:24 1.85
+++ src/sys/ufs/ffs/ffs_extern.h 2020/04/18 19:18:34 1.86
@@ -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 @@
 /* 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_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.

cvs diff -r1.126 -r1.127 src/sys/ufs/ffs/ffs_inode.c (expand / switch to context diff)
--- src/sys/ufs/ffs/ffs_inode.c 2020/02/23 15:46:42 1.126
+++ src/sys/ufs/ffs/ffs_inode.c 2020/04/18 19:18:34 1.127
@@ -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 @@
 	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 @@
 	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 @@
 		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 @@
 	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 @@
 	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 @@
 	 * 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 @@
 		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:

cvs diff -r1.151 -r1.152 src/sys/ufs/ffs/ffs_snapshot.c (expand / switch to context diff)
--- src/sys/ufs/ffs/ffs_snapshot.c 2020/02/23 15:46:42 1.151
+++ src/sys/ufs/ffs/ffs_snapshot.c 2020/04/18 19:18:34 1.152
@@ -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 @@
 	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);

cvs diff -r1.130 -r1.131 src/sys/ufs/ffs/ffs_vnops.c (expand / switch to context diff)
--- src/sys/ufs/ffs/ffs_vnops.c 2020/02/23 15:46:42 1.130
+++ src/sys/ufs/ffs/ffs_vnops.c 2020/04/18 19:18:34 1.131
@@ -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"
@@ -611,161 +611,4 @@
 	} else {
 		*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);
 }

cvs diff -r1.66 -r1.67 src/sys/ufs/ffs/fs.h (expand / switch to context diff)
--- src/sys/ufs/ffs/fs.h 2015/02/14 09:06:11 1.66
+++ src/sys/ufs/ffs/fs.h 2020/04/18 19:18:34 1.67
@@ -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 @@
 	((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

cvs diff -r1.11 -r1.12 src/sys/ufs/ufs/extattr.h (expand / switch to context diff)
--- src/sys/ufs/ufs/extattr.h 2014/12/19 10:59:21 1.11
+++ src/sys/ufs/ufs/extattr.h 2020/04/18 19:18:34 1.12
@@ -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
@@ -71,6 +71,39 @@
 	uint32_t	ueh_i_gen;	/* generation number for sanity */
 	/* 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
 

cvs diff -r1.76 -r1.77 src/sys/ufs/ufs/inode.h (expand / switch to context diff)
--- src/sys/ufs/ufs/inode.h 2017/08/20 12:09:06 1.76
+++ src/sys/ufs/ufs/inode.h 2020/04/18 19:18:34 1.77
@@ -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 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 @@
 #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)
 

cvs diff -r1.84 -r1.85 src/sys/ufs/ufs/ufs_extern.h (expand / switch to context diff)
--- src/sys/ufs/ufs/ufs_extern.h 2020/01/17 20:08:10 1.84
+++ src/sys/ufs/ufs/ufs_extern.h 2020/04/18 19:18:34 1.85
@@ -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 @@
 /* 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 *);

cvs diff -r1.109 -r1.110 src/sys/ufs/ufs/ufs_inode.c (expand / switch to context diff)
--- src/sys/ufs/ufs/ufs_inode.c 2020/02/23 15:46:43 1.109
+++ src/sys/ufs/ufs/ufs_inode.c 2020/04/18 19:18:34 1.110
@@ -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 @@
 #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 @@
 }
 
 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 @@
 		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)
@@ -317,4 +313,19 @@
 
   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);
 }

cvs diff -r1.251 -r1.252 src/sys/ufs/ufs/ufs_vnops.c (expand / switch to context diff)
--- src/sys/ufs/ufs/ufs_vnops.c 2020/04/13 19:23:20 1.251
+++ src/sys/ufs/ufs/ufs_vnops.c 2020/04/18 19:18:34 1.252
@@ -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 @@
 				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;