Oops I included ufs/ufs_extattr.c (the FFSv1 ones) not ffs/ffs_extattr.c
which is the new file, so let's try again:


? o
? ffs/ffs_inode.c.debug
Index: files.ufs
===================================================================
RCS file: /cvsroot/src/sys/ufs/files.ufs,v
retrieving revision 1.45
diff -u -u -r1.45 files.ufs
--- files.ufs   17 Jun 2019 03:32:58 -0000      1.45
+++ files.ufs   11 Apr 2020 00:13:05 -0000
@@ -52,6 +52,7 @@
 file   ufs/ffs/ffs_alloc.c             ffs
 file   ufs/ffs/ffs_balloc.c            ffs
 file   ufs/ffs/ffs_bswap.c             (ffs | mfs) & ffs_ei
+file   ufs/ffs/ffs_extattr.c           ffs & ufs_extattr
 file   ufs/ffs/ffs_inode.c             ffs
 file   ufs/ffs/ffs_snapshot.c          ffs
 file   ufs/ffs/ffs_subr.c              ffs
Index: ffs/ffs_alloc.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_alloc.c,v
retrieving revision 1.166
diff -u -u -r1.166 ffs_alloc.c
--- ffs/ffs_alloc.c     23 Feb 2020 15:46:42 -0000      1.166
+++ ffs/ffs_alloc.c     11 Apr 2020 00:13:05 -0000
@@ -257,7 +257,10 @@
        bno = ffs_hashalloc(ip, cg, bpref, size, 0, flags, ffs_alloccg);
        if (bno > 0) {
                DIP_ADD(ip, blocks, btodb(size));
-               ip->i_flag |= IN_CHANGE | IN_UPDATE;
+               if (flags & IO_EXT)
+                       ip->i_flag |= IN_CHANGE;
+               else
+                       ip->i_flag |= IN_CHANGE | IN_UPDATE;
                *bnp = bno;
                return (0);
        }
@@ -300,14 +303,15 @@
  * => return with um_lock released
  */
 int
-ffs_realloccg(struct inode *ip, daddr_t lbprev, daddr_t bpref, int osize,
-    int nsize, kauth_cred_t cred, struct buf **bpp, daddr_t *blknop)
+ffs_realloccg(struct inode *ip, daddr_t lbprev, daddr_t bprev, daddr_t bpref,
+    int osize, int nsize, int flags, kauth_cred_t cred, struct buf **bpp,
+    daddr_t *blknop)
 {
        struct ufsmount *ump;
        struct fs *fs;
        struct buf *bp;
        int cg, request, error;
-       daddr_t bprev, bno;
+       daddr_t bno;
 
        fs = ip->i_fs;
        ump = ip->i_ump;
@@ -368,10 +372,6 @@
                mutex_exit(&ump->um_lock);
                goto nospace;
        }
-       if (fs->fs_magic == FS_UFS2_MAGIC)
-               bprev = ufs_rw64(ip->i_ffs2_db[lbprev], UFS_FSNEEDSWAP(fs));
-       else
-               bprev = ufs_rw32(ip->i_ffs1_db[lbprev], UFS_FSNEEDSWAP(fs));
 
        if (bprev == 0) {
                panic("%s: bad bprev: dev = 0x%llx, bsize = %d, bprev = %"
@@ -403,7 +403,10 @@
        mutex_enter(&ump->um_lock);
        if ((bno = ffs_fragextend(ip, cg, bprev, osize, nsize)) != 0) {
                DIP_ADD(ip, blocks, btodb(nsize - osize));
-               ip->i_flag |= IN_CHANGE | IN_UPDATE;
+               if (flags & IO_EXT)
+                       ip->i_flag |= IN_CHANGE;
+               else
+                       ip->i_flag |= IN_CHANGE | IN_UPDATE;
 
                if (bpp != NULL) {
                        if (bp->b_blkno != FFS_FSBTODB(fs, bno)) {
@@ -503,7 +506,10 @@
                            ip->i_number);
                }
                DIP_ADD(ip, blocks, btodb(nsize - osize));
-               ip->i_flag |= IN_CHANGE | IN_UPDATE;
+               if (flags & IO_EXT)
+                       ip->i_flag |= IN_CHANGE;
+               else
+                       ip->i_flag |= IN_CHANGE | IN_UPDATE;
                if (bpp != NULL) {
                        bp->b_blkno = FFS_FSBTODB(fs, bno);
                        allocbuf(bp, nsize, 1);
Index: ffs/ffs_balloc.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_balloc.c,v
retrieving revision 1.63
diff -u -u -r1.63 ffs_balloc.c
--- ffs/ffs_balloc.c    28 Oct 2017 00:37:13 -0000      1.63
+++ ffs/ffs_balloc.c    11 Apr 2020 00:13:05 -0000
@@ -72,6 +72,12 @@
 static int ffs_balloc_ufs2(struct vnode *, off_t, int, kauth_cred_t, int,
     struct buf **);
 
+static daddr_t
+ffs_extb(struct fs *fs, struct ufs2_dinode *dp, daddr_t nb)
+{
+       return ufs_rw64(dp->di_extb[nb], UFS_FSNEEDSWAP(fs));
+}
+   
 /*
  * Balloc defines the structure of file system storage
  * by allocating the physical blocks on a device given
@@ -139,10 +145,11 @@
                osize = ffs_blksize(fs, ip, nb);
                if (osize < fs->fs_bsize && osize > 0) {
                        mutex_enter(&ump->um_lock);
-                       error = ffs_realloccg(ip, nb,
+                       error = ffs_realloccg(ip, nb, ffs_getdb(fs, ip, nb),
                                    ffs_blkpref_ufs1(ip, lastlbn, nb, flags,
                                        &ip->i_ffs1_db[0]),
-                                   osize, (int)fs->fs_bsize, cred, bpp, &newb);
+                                   osize, (int)fs->fs_bsize, flags, cred, bpp,
+                                   &newb);
                        if (error)
                                return (error);
                        ip->i_size = ffs_lblktosize(fs, nb + 1);
@@ -215,9 +222,10 @@
                                 */
                                mutex_enter(&ump->um_lock);
                                error = ffs_realloccg(ip, lbn,
+                                   ffs_getdb(fs, ip, lbn),
                                    ffs_blkpref_ufs1(ip, lbn, (int)lbn, flags,
                                        &ip->i_ffs1_db[0]),
-                                   osize, nsize, cred, bpp, &newb);
+                                   osize, nsize, flags, cred, bpp, &newb);
                                if (error)
                                        return (error);
                        }
@@ -543,11 +551,11 @@
        if (lbn < 0)
                return (EFBIG);
 
-#ifdef notyet
        /*
         * Check for allocating external data.
         */
        if (flags & IO_EXT) {
+               struct ufs2_dinode *dp = ip->i_din.ffs2_din;
                if (lbn >= UFS_NXADDR)
                        return (EFBIG);
                /*
@@ -562,16 +570,15 @@
                        if (osize < fs->fs_bsize && osize > 0) {
                                mutex_enter(&ump->um_lock);
                                error = ffs_realloccg(ip, -1 - nb,
-                                   dp->di_extb[nb],
+                                   ffs_extb(fs, dp, nb),
                                    ffs_blkpref_ufs2(ip, lastlbn, (int)nb,
                                        flags, &dp->di_extb[0]),
-                                   osize,
-                                   (int)fs->fs_bsize, cred, &bp);
+                                   osize, (int)fs->fs_bsize, flags, cred,
+                                   &bp, &newb);
                                if (error)
                                        return (error);
-                               dp->di_extsize = smalllblktosize(fs, nb + 1);
+                               dp->di_extsize = ffs_lblktosize(fs, nb + 1);
                                dp->di_extb[nb] = FFS_DBTOFSB(fs, bp->b_blkno);
-                               bp->b_xflags |= BX_ALTDATA;
                                ip->i_flag |= IN_CHANGE | IN_UPDATE;
                                if (flags & IO_SYNC)
                                        bwrite(bp);
@@ -582,19 +589,16 @@
                /*
                 * All blocks are direct blocks
                 */
-               if (flags & BA_METAONLY)
-                       panic("ffs_balloc_ufs2: BA_METAONLY for ext block");
                nb = dp->di_extb[lbn];
-               if (nb != 0 && dp->di_extsize >= smalllblktosize(fs, lbn + 1)) {
+               if (nb != 0 && dp->di_extsize >= ffs_lblktosize(fs, lbn + 1)) {
                        error = bread(vp, -1 - lbn, fs->fs_bsize,
                            0, &bp);
                        if (error) {
                                return (error);
                        }
-                       mutex_enter(&bp->b_interlock);
+                       mutex_enter(bp->b_objlock);
                        bp->b_blkno = FFS_FSBTODB(fs, nb);
-                       bp->b_xflags |= BX_ALTDATA;
-                       mutex_exit(&bp->b_interlock);
+                       mutex_exit(bp->b_objlock);
                        *bpp = bp;
                        return (0);
                }
@@ -610,23 +614,21 @@
                                if (error) {
                                        return (error);
                                }
-                               mutex_enter(&bp->b_interlock);
+                               mutex_enter(bp->b_objlock);
                                bp->b_blkno = FFS_FSBTODB(fs, nb);
-                               bp->b_xflags |= BX_ALTDATA;
-                               mutex_exit(&bp->b_interlock);
+                               mutex_exit(bp->b_objlock);
                        } else {
                                mutex_enter(&ump->um_lock);
                                error = ffs_realloccg(ip, -1 - lbn,
-                                   dp->di_extb[lbn],
+                                   ffs_extb(fs, dp, lbn),
                                    ffs_blkpref_ufs2(ip, lbn, (int)lbn, flags,
                                        &dp->di_extb[0]),
-                                   osize, nsize, cred, &bp);
+                                   osize, nsize, flags, cred, &bp, &newb);
                                if (error)
                                        return (error);
-                               bp->b_xflags |= BX_ALTDATA;
                        }
                } else {
-                       if (dp->di_extsize < smalllblktosize(fs, lbn + 1))
+                       if (dp->di_extsize < ffs_lblktosize(fs, lbn + 1))
                                nsize = ffs_fragroundup(fs, size);
                        else
                                nsize = fs->fs_bsize;
@@ -641,14 +643,12 @@
                            nsize, (flags & B_CLRBUF) != 0, &bp);
                        if (error)
                                return error;
-                       bp->b_xflags |= BX_ALTDATA;
                }
                dp->di_extb[lbn] = FFS_DBTOFSB(fs, bp->b_blkno);
                ip->i_flag |= IN_CHANGE | IN_UPDATE;
                *bpp = bp;
                return (0);
        }
-#endif
        /*
         * If the next write will extend the file into a new block,
         * and the file is currently composed of a fragment
@@ -661,10 +661,11 @@
                osize = ffs_blksize(fs, ip, nb);
                if (osize < fs->fs_bsize && osize > 0) {
                        mutex_enter(&ump->um_lock);
-                       error = ffs_realloccg(ip, nb,
+                       error = ffs_realloccg(ip, nb, ffs_getdb(fs, ip, lbn),
                                    ffs_blkpref_ufs2(ip, lastlbn, nb, flags,
                                        &ip->i_ffs2_db[0]),
-                                   osize, (int)fs->fs_bsize, cred, bpp, &newb);
+                                   osize, (int)fs->fs_bsize, flags, cred, bpp,
+                                   &newb);
                        if (error)
                                return (error);
                        ip->i_size = ffs_lblktosize(fs, nb + 1);
@@ -737,9 +738,10 @@
                                 */
                                mutex_enter(&ump->um_lock);
                                error = ffs_realloccg(ip, lbn,
+                                   ffs_getdb(fs, ip, lbn),
                                    ffs_blkpref_ufs2(ip, lbn, (int)lbn, flags,
                                        &ip->i_ffs2_db[0]),
-                                   osize, nsize, cred, bpp, &newb);
+                                   osize, nsize, flags, cred, bpp, &newb);
                                if (error)
                                        return (error);
                        }
Index: ffs/ffs_extattr.c
===================================================================
RCS file: ffs/ffs_extattr.c
diff -N ffs/ffs_extattr.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ffs/ffs_extattr.c   11 Apr 2020 00:13:05 -0000
@@ -0,0 +1,983 @@
+/*     $NetBSD: ffs_vnops.c,v 1.130 2020/02/23 15:46:42 ad 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.130 2020/02/23 15:46:42 ad 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) {
+               printf("%s: wapbl begin: %d\n", __func__, error);
+               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)
+{
+       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);
+}
+
+static void
+ffs_unlock_ea(struct vnode *vp)
+{
+       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);
+}
+
+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);
+
+       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;
+               /* 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);
+       }
+       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;
+
+       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;
+
+       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;
+}
Index: ffs/ffs_extern.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_extern.h,v
retrieving revision 1.85
diff -u -u -r1.85 ffs_extern.h
--- ffs/ffs_extern.h    22 Aug 2018 01:05:24 -0000      1.85
+++ ffs/ffs_extern.h    11 Apr 2020 00:13:05 -0000
@@ -89,8 +89,8 @@
 /* ffs_alloc.c */
 int    ffs_alloc(struct inode *, daddr_t, daddr_t , int, int, kauth_cred_t,
                  daddr_t *);
-int    ffs_realloccg(struct inode *, daddr_t, daddr_t, int, int ,
-                     kauth_cred_t, struct buf **, daddr_t *);
+int    ffs_realloccg(struct inode *, daddr_t, daddr_t, daddr_t, int, int,
+                     int, kauth_cred_t, struct buf **, daddr_t *);
 int    ffs_valloc(struct vnode *, int, kauth_cred_t, ino_t *);
 daddr_t        ffs_blkpref_ufs1(struct inode *, daddr_t, int, int, int32_t *);
 daddr_t        ffs_blkpref_ufs2(struct inode *, daddr_t, int, int, int64_t *);
@@ -135,16 +135,31 @@
 int    ffs_reclaim(void *);
 int    ffs_getpages(void *);
 void   ffs_gop_size(struct vnode *, off_t, off_t *, int);
+int    ffs_lock(void *);
+int    ffs_unlock(void *);
+int    ffs_islocked(void *);
+int    ffs_full_fsync(struct vnode *, int);
+
+/* ffs_extattr.c */
+#ifdef UFS_EXTATTR
 int    ffs_openextattr(void *);
 int    ffs_closeextattr(void *);
 int    ffs_getextattr(void *);
 int    ffs_setextattr(void *);
 int    ffs_listextattr(void *);
 int    ffs_deleteextattr(void *);
-int    ffs_lock(void *);
-int    ffs_unlock(void *);
-int    ffs_islocked(void *);
-int    ffs_full_fsync(struct vnode *, int);
+int    ffsext_strategy(void *);
+#else
+
+#define        ffs_notsupp ((void (*)(void *))eopnotsupp)
+#define        ffs_openextattr         ffsnotsupp
+#define        ffs_closeextattr        ffsnotsupp
+#define        ffs_getextattr          ffsnotsupp
+#define        ffs_setextattr          ffsnotsupp
+#define        ffs_listextattr         ffsnotsupp
+#define        ffs_deleteextattr       ffsnotsupp
+#define        ffsext_strategy         vn_fifo_bypass
+#endif
 
 /*
  * Snapshot function prototypes.
Index: ffs/ffs_inode.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_inode.c,v
retrieving revision 1.126
diff -u -u -r1.126 ffs_inode.c
--- ffs/ffs_inode.c     23 Feb 2020 15:46:42 -0000      1.126
+++ ffs/ffs_inode.c     11 Apr 2020 00:13:05 -0000
@@ -209,10 +209,11 @@
        daddr_t lastblock;
        struct inode *oip = VTOI(ovp);
        daddr_t bn, lastiblock[UFS_NIADDR], indir_lbn[UFS_NIADDR];
-       daddr_t blks[UFS_NDADDR + UFS_NIADDR];
+       daddr_t blks[UFS_NDADDR + UFS_NIADDR], oldblks[UFS_NDADDR + UFS_NIADDR];
        struct fs *fs;
+       int extblocks;
        int offset, pgoffset, level;
-       int64_t blocksreleased = 0;
+       int64_t blocksreleased = 0, datablocks;
        int i, aflag, nblocks;
        int error, allerror = 0;
        off_t osize;
@@ -231,9 +232,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:
Index: ffs/ffs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_vnops.c,v
retrieving revision 1.130
diff -u -u -r1.130 ffs_vnops.c
--- ffs/ffs_vnops.c     23 Feb 2020 15:46:42 -0000      1.130
+++ ffs/ffs_vnops.c     11 Apr 2020 00:13:05 -0000
@@ -612,160 +612,3 @@
                *eobp = ffs_blkroundup(fs, size);
        }
 }
-
-int
-ffs_openextattr(void *v)
-{
-       struct vop_openextattr_args /* {
-               struct vnode *a_vp;
-               kauth_cred_t a_cred;
-               struct proc *a_p;
-       } */ *ap = v;
-       struct inode *ip = VTOI(ap->a_vp);
-       struct fs *fs = ip->i_fs;
-
-       /* Not supported for UFS1 file systems. */
-       if (fs->fs_magic == FS_UFS1_MAGIC)
-               return (EOPNOTSUPP);
-
-       /* XXX Not implemented for UFS2 file systems. */
-       return (EOPNOTSUPP);
-}
-
-int
-ffs_closeextattr(void *v)
-{
-       struct vop_closeextattr_args /* {
-               struct vnode *a_vp;
-               int a_commit;
-               kauth_cred_t a_cred;
-               struct proc *a_p;
-       } */ *ap = v;
-       struct inode *ip = VTOI(ap->a_vp);
-       struct fs *fs = ip->i_fs;
-
-       /* Not supported for UFS1 file systems. */
-       if (fs->fs_magic == FS_UFS1_MAGIC)
-               return (EOPNOTSUPP);
-
-       /* XXX Not implemented for UFS2 file systems. */
-       return (EOPNOTSUPP);
-}
-
-int
-ffs_getextattr(void *v)
-{
-       struct vop_getextattr_args /* {
-               struct vnode *a_vp;
-               int a_attrnamespace;
-               const char *a_name;
-               struct uio *a_uio;
-               size_t *a_size;
-               kauth_cred_t a_cred;
-               struct proc *a_p;
-       } */ *ap = v;
-       struct vnode *vp = ap->a_vp;
-       struct inode *ip = VTOI(vp);
-       struct fs *fs = ip->i_fs;
-
-       if (fs->fs_magic == FS_UFS1_MAGIC) {
-#ifdef UFS_EXTATTR
-               int error;
-
-               error = ufs_getextattr(ap);
-               return error;
-#else
-               return (EOPNOTSUPP);
-#endif
-       }
-
-       /* XXX Not implemented for UFS2 file systems. */
-       return (EOPNOTSUPP);
-}
-
-int
-ffs_setextattr(void *v)
-{
-       struct vop_setextattr_args /* {
-               struct vnode *a_vp;
-               int a_attrnamespace;
-               const char *a_name;
-               struct uio *a_uio;
-               kauth_cred_t a_cred;
-               struct proc *a_p;
-       } */ *ap = v;
-       struct vnode *vp = ap->a_vp;
-       struct inode *ip = VTOI(vp);
-       struct fs *fs = ip->i_fs;
-
-       if (fs->fs_magic == FS_UFS1_MAGIC) {
-#ifdef UFS_EXTATTR
-               int error;
-
-               error = ufs_setextattr(ap);
-               return error;
-#else
-               return (EOPNOTSUPP);
-#endif
-       }
-
-       /* XXX Not implemented for UFS2 file systems. */
-       return (EOPNOTSUPP);
-}
-
-int
-ffs_listextattr(void *v)
-{
-       struct vop_listextattr_args /* {
-               struct vnode *a_vp;
-               int a_attrnamespace;
-               struct uio *a_uio;
-               size_t *a_size;
-               kauth_cred_t a_cred;
-               struct proc *a_p;
-       } */ *ap = v;
-       struct inode *ip = VTOI(ap->a_vp);
-       struct fs *fs = ip->i_fs;
-
-       if (fs->fs_magic == FS_UFS1_MAGIC) {
-#ifdef UFS_EXTATTR
-               int error;
-
-               error = ufs_listextattr(ap);
-               return error;
-#else
-               return (EOPNOTSUPP);
-#endif
-       }
-
-       /* XXX Not implemented for UFS2 file systems. */
-       return (EOPNOTSUPP);
-}
-
-int
-ffs_deleteextattr(void *v)
-{
-       struct vop_deleteextattr_args /* {
-               struct vnode *a_vp;
-               int a_attrnamespace;
-               kauth_cred_t a_cred;
-               struct proc *a_p;
-       } */ *ap = v;
-       struct vnode *vp = ap->a_vp;
-       struct inode *ip = VTOI(vp);
-       struct fs *fs = ip->i_fs;
-
-       if (fs->fs_magic == FS_UFS1_MAGIC) {
-#ifdef UFS_EXTATTR
-               int error;
-
-               error = ufs_deleteextattr(ap);
-               return error;
-#else
-               return (EOPNOTSUPP);
-#endif
-       }
-
-       /* XXX Not implemented for UFS2 file systems. */
-       return (EOPNOTSUPP);
-}
Index: ffs/fs.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/fs.h,v
retrieving revision 1.66
diff -u -u -r1.66 fs.h
--- ffs/fs.h    14 Feb 2015 09:06:11 -0000      1.66
+++ ffs/fs.h    11 Apr 2020 00:13:05 -0000
@@ -692,6 +692,14 @@
        ((fsb) & ((fs)->fs_frag - 1))
 #define        ffs_blknum(fs, fsb)     /* calculates rounddown(fsb, 
fs->fs_frag) */ \
        ((fsb) &~ ((fs)->fs_frag - 1))
+#define ffs_getdb(fs, ip, lb) \
+    ((fs)->fs_magic == FS_UFS2_MAGIC ? \
+       (daddr_t)ufs_rw64((ip)->i_ffs2_db[lb], UFS_FSNEEDSWAP(fs)) : \
+       (daddr_t)ufs_rw32((ip)->i_ffs1_db[lb], UFS_FSNEEDSWAP(fs)))
+#define ffs_getib(fs, ip, lb) \
+    ((fs)->fs_magic == FS_UFS2_MAGIC ? \
+       (daddr_t)ufs_rw64((ip)->i_ffs2_ib[lb], UFS_FSNEEDSWAP(fs)) : \
+       (daddr_t)ufs_rw32((ip)->i_ffs1_ib[lb], UFS_FSNEEDSWAP(fs)))
 
 /*
  * Determine the number of available frags given a
Index: ufs/extattr.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/extattr.h,v
retrieving revision 1.11
diff -u -u -r1.11 extattr.h
--- ufs/extattr.h       19 Dec 2014 10:59:21 -0000      1.11
+++ ufs/extattr.h       11 Apr 2020 00:13:05 -0000
@@ -72,6 +72,39 @@
        /* data follows the header */
 };
 
+/*
+ * This structure defines the required fields of an extended-attribute header.
+ */
+struct extattr {
+       uint32_t ea_length;         /* length of this attribute */
+       uint8_t ea_namespace;       /* name space of this attribute */
+       uint8_t ea_contentpadlen;   /* bytes of padding at end of attribute */
+       uint8_t ea_namelength;      /* length of attribute name */
+       char    ea_name[1];         /* attribute name (NOT nul-terminated) */
+       /* padding, if any, to align attribute content to 8 byte boundary */
+       /* extended attribute content follows */
+};
+
+/*
+ * These macros are used to access and manipulate an extended attribute:
+ *
+ * EXTATTR_NEXT(eap) returns a pointer to the next extended attribute
+ *     following eap.
+ * EXTATTR_CONTENT(eap) returns a pointer to the extended attribute
+ *     content referenced by eap.
+ * EXTATTR_CONTENT_SIZE(eap) returns the size of the extended attribute
+ *     content referenced by eap.
+ */
+#define        EXTATTR_NEXT(eap) \
+       ((struct extattr *)(((u_char *)(eap)) + (eap)->ea_length))
+#define        EXTATTR_CONTENT(eap) \
+       (void *)(((u_char *)(eap)) + EXTATTR_BASE_LENGTH(eap))
+#define        EXTATTR_CONTENT_SIZE(eap) \
+       ((eap)->ea_length - EXTATTR_BASE_LENGTH(eap) - (eap)->ea_contentpadlen)
+/* -1 below compensates for ea_name[1] */
+#define        EXTATTR_BASE_LENGTH(eap) \
+       roundup2((sizeof(struct extattr) - 1 + (eap)->ea_namelength), 8)
+
 #ifdef _KERNEL
 
 #ifdef MALLOC_DECLARE
Index: ufs/inode.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/inode.h,v
retrieving revision 1.76
diff -u -u -r1.76 inode.h
--- ufs/inode.h 20 Aug 2017 12:09:06 -0000      1.76
+++ ufs/inode.h 11 Apr 2020 00:13:05 -0000
@@ -156,6 +156,14 @@
        struct dirhash *i_dirhash;      /* Hashing for large directories */
 
        /*
+        * Data for extended attribute modification.
+        */
+       u_char    *i_ea_area;   /* Pointer to malloced copy of EA area */
+       unsigned  i_ea_len;     /* Length of i_ea_area */
+       int       i_ea_error;   /* First errno in transaction */
+       int       i_ea_refs;    /* Number of users of EA area */
+
+       /*
         * The on-disk dinode itself.
         */
        union {
@@ -219,8 +227,8 @@
 /*        unused       0x0020 */       /* was IN_RENAME */
 #define        IN_SHLOCK       0x0040          /* File has shared lock. */
 #define        IN_EXLOCK       0x0080          /* File has exclusive lock. */
-/*        unused       0x0100 */       /* was LFS-only IN_CLEANING */
-/*        unused       0x0200 */       /* was LFS-only IN_ADIROP */
+#define IN_EA_LOCKED   0x0100          /* was LFS-only IN_CLEANING */
+#define        IN_EA_LOCKWAIT  0x0200          /* was LFS-only IN_ADIROP */
 #define        IN_SPACECOUNTED 0x0400          /* Blocks to be freed in free 
count. */
 /*        unused       0x0800 */       /* what was that? */
 /*        unused       0x1000 */       /* was LFS-only IN_PAGING */
Index: ufs/ufs_inode.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_inode.c,v
retrieving revision 1.109
diff -u -u -r1.109 ufs_inode.c
--- ufs/ufs_inode.c     23 Feb 2020 15:46:43 -0000      1.109
+++ ufs/ufs_inode.c     11 Apr 2020 00:13:05 -0000
@@ -308,7 +308,7 @@
                if (error)
                        goto out;
 
-               error = UFS_TRUNCATE(vp, newsize, 0, cred);
+               error = UFS_TRUNCATE(vp, newsize, IO_EXT, cred);
                UFS_WAPBL_END(mp);
 
                if (error != 0 && error != EAGAIN)


Reply via email to