tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

buffer cache & ufs changes (preliminary ffsv2 extattr support)



Hello,
I'm working on porting the FreeBSD FFSv2 extended attributes support.
What we have right now only works for ffsv1 (it's a restriction in our
sources but it could be extended to ffsv2), and uses a file hierarchy
to store attributes. This has several issues, one being that it doesn't
integrate with WAPBL and is very slow (glusterfs shows this very well).

FFSv2 has native extended attributes support, in the form of 2 direct
blocks reserved for this purpose in the on-disk inode. This was commented out
in our kernel when FFSv2 support was imported. It should be possible to
integrate this with WAPBL and handle it as other metadata, so it should
be fast. fsck will also be able to check it.

I don't think I'll be able to have this ready for netbsd-6, but I now know
this requires 2 changes that will require a kernel version bump, so theses
changes needs to go in before netbsd-6 is branched so that full
extended attributes support can be pulled up later.

The fisrt change is to the buffer cache. Right now the buffer cache is
indexed by the couple <vnode pointer, block number>, block number being
a block offset in the file being pointed to by vnode pointer. 
But we'll have 2 kinds of blocks: data blocks (what we have now) and
extended attributes blocks, so block number is not enough to identify
blocks from a vnode. FreeBSD use negative block numbers for extattrs,
but I find it unclean, I'm not sure it won't cause problems with our
buffer cache (at last block -1 is used already for vtruncbuf()), and negative
blocks numbers are already used in ufs for indirect blocks.
I see 2 ways to fix this:
1) Add a new bflag, B_ALTDATA. When the buffer refers to a extended attr
  block (and not a data block), this flag is set. This flag can also be
  passed to bread() and breadn() (not the same namespace, but the same
  B_ prefix and so the same name, this part of the buffer cache API could also
  be improved). When looking up a buffer in the cache we also check for
  this flag. For consumers to be able to specify we're looking up a
  B_ALTDATA buffer, incore(), getblk() and vtruncbuf() gains a new flag
  argument. To avoid touching a all buffer cache users, I choose to
  introduce incore2(), getblk2() and vtruncbuf2() with the extra argument,
  and the origical functions just call the *2 version with flag set to 0.
  This is implemented in buffer.diff, and has been tested to not
  introduce new problems with existing code.

2) instead of using a new flag, add a new 'int type' member to struct buf,
   which is opaque to the buffer cache itself (the meaning of type > 0 is
   fs-dependant) but is checked when looking up a buffer.
   Type 0 would still mean regular vnode data, so that existing users
   won't have to be changed, other values could be used by filesystems
   for their internal data usage (for example, ufs could use 1 for
   first indirect blocks, 2, for second indirect blocks, 3 for thrird
   indirect blocks, and some other values for extended attributes. Another
   filesystem with e.g. blocks to store ACLs could also use its own
   type to have its ACL blocks entered in the bufcache).
   In addition to new incore2(), getblk2() and vtruncbuf2() functions
   with a type argument, we'd also need a bread2() and breadn2() with
   a type argument.
   I've not implemented this yet.

Althrough I've done 1 as a POC, I prefer solution 2 (the patch is mostly the
same, with bflag remplaced by b_type). What do other think ?

The second change needed outside of sys/ufs/ffs/ is:
- new members in struct inode. This is strait from FreeBSD, and this
  affects modules so require a kernel version bump
- ufs_inode.c:ufs_inactive() truncate extended data to 0 as well when
  freeing the inode. This require changes to ffs and lfs to ignore
  IO_EXT (for now).
- ufs_vnops.c:ufs_strategy(): when requesting extended attribute data
  (B_ALTDATA actually), get extdata bn instead of regular bn via VOP_BMAP().
This is in ufs.diff attached.

The complete diff of actual code (where extended attributes are not working
yet, there's locking issues, as well as more code needed for reverse endian
handling, WAPBL and fsck) is also included, so that you can see how
all of this goes in together.

I'd really like to be able to get this in netbsd-6 later, which would
means (given my own schedule) I have to commit the kern and ufs/ufs parts
before next friday if we want to avoid kernel API changes in the branch.

-- 
Manuel Bouyer <bouyer%antioche.eu.org@localhost>
     NetBSD: 26 ans d'experience feront toujours la difference
--
Index: sys/kern/vfs_bio.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_bio.c,v
retrieving revision 1.232
diff -u -r1.232 vfs_bio.c
--- sys/kern/vfs_bio.c  5 Oct 2011 01:53:03 -0000       1.232
+++ sys/kern/vfs_bio.c  15 Jan 2012 19:26:16 -0000
@@ -656,12 +656,13 @@
  */
 static buf_t *
 bio_doread(struct vnode *vp, daddr_t blkno, int size, kauth_cred_t cred,
-    int async)
+    int flags)
 {
        buf_t *bp;
        struct mount *mp;
+       const bool async = ((flags & B_ASYNC) != 0);
 
-       bp = getblk(vp, blkno, size, 0, 0);
+       bp = getblk2(vp, blkno, size, 0, 0, (flags & B_ALTDATA));
 
 #ifdef DIAGNOSTIC
        if (bp == NULL) {
@@ -676,7 +677,7 @@
         */
        if (!ISSET(bp->b_oflags, (BO_DONE | BO_DELWRI))) {
                /* Start I/O for the buffer. */
-               SET(bp->b_flags, B_READ | async);
+               SET(bp->b_flags, B_READ | flags);
                if (async)
                        BIO_SETPRIO(bp, BPRIO_TIMELIMITED);
                else
@@ -699,7 +700,7 @@
         * filesystem (if any).
         */
        if (mp != NULL) {
-               if (async == 0)
+               if (async == false)
                        mp->mnt_stat.f_syncreads++;
                else
                        mp->mnt_stat.f_asyncreads++;
@@ -720,7 +721,7 @@
        int error;
 
        /* Get buffer for block. */
-       bp = *bpp = bio_doread(vp, blkno, size, cred, 0);
+       bp = *bpp = bio_doread(vp, blkno, size, cred, flags & B_ALTDATA);
 
        /* Wait for the read to complete, and return result. */
        error = biowait(bp);
@@ -741,7 +742,7 @@
        buf_t *bp;
        int error, i;
 
-       bp = *bpp = bio_doread(vp, blkno, size, cred, 0);
+       bp = *bpp = bio_doread(vp, blkno, size, cred, flags & B_ALTDATA);
 
        /*
         * For each of the read-ahead blocks, start a read, if necessary.
@@ -749,12 +750,13 @@
        mutex_enter(&bufcache_lock);
        for (i = 0; i < nrablks; i++) {
                /* If it's in the cache, just go on to next one. */
-               if (incore(vp, rablks[i]))
+               if (incore2(vp, rablks[i], flags))
                        continue;
 
                /* Get a buffer for the read-ahead block */
                mutex_exit(&bufcache_lock);
-               (void) bio_doread(vp, rablks[i], rasizes[i], cred, B_ASYNC);
+               (void) bio_doread(vp, rablks[i], rasizes[i], cred,
+                   B_ASYNC | (flags & B_ALTDATA));
                mutex_enter(&bufcache_lock);
        }
        mutex_exit(&bufcache_lock);
@@ -1099,6 +1101,12 @@
 buf_t *
 incore(struct vnode *vp, daddr_t blkno)
 {
+       return incore2(vp, blkno, 0);
+}
+
+buf_t *
+incore2(struct vnode *vp, daddr_t blkno, int bflags)
+{
        buf_t *bp;
 
        KASSERT(mutex_owned(&bufcache_lock));
@@ -1106,6 +1114,7 @@
        /* Search hash chain */
        LIST_FOREACH(bp, BUFHASH(vp, blkno), b_hash) {
                if (bp->b_lblkno == blkno && bp->b_vp == vp &&
+                   (bp->b_flags & bflags) ==  bflags &&
                    !ISSET(bp->b_cflags, BC_INVAL)) {
                        KASSERT(bp->b_objlock == vp->v_interlock);
                        return (bp);
@@ -1126,12 +1135,19 @@
 buf_t *
 getblk(struct vnode *vp, daddr_t blkno, int size, int slpflag, int slptimeo)
 {
+       return getblk2(vp, blkno, size, slpflag, slptimeo, 0);
+}
+
+buf_t *
+getblk2(struct vnode *vp, daddr_t blkno, int size, int slpflag, int slptimeo,
+    int bflags)
+{
        int err, preserve;
        buf_t *bp;
 
        mutex_enter(&bufcache_lock);
  loop:
-       bp = incore(vp, blkno);
+       bp = incore2(vp, blkno, bflags);
        if (bp != NULL) {
                err = bbusy(bp, ((slpflag & PCATCH) != 0), slptimeo, NULL);
                if (err != 0) {
@@ -1152,7 +1168,7 @@
                if ((bp = getnewbuf(slpflag, slptimeo, 0)) == NULL)
                        goto loop;
 
-               if (incore(vp, blkno) != NULL) {
+               if (incore2(vp, blkno, bflags) != NULL) {
                        /* The block has come into memory in the meantime. */
                        brelsel(bp, 0);
                        goto loop;
@@ -1160,6 +1176,7 @@
 
                LIST_INSERT_HEAD(BUFHASH(vp, blkno), bp, b_hash);
                bp->b_blkno = bp->b_lblkno = bp->b_rawblkno = blkno;
+               bp->b_flags |= bflags;
                mutex_enter(vp->v_interlock);
                bgetvp(vp, bp);
                mutex_exit(vp->v_interlock);
Index: sys/kern/vfs_subr.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_subr.c,v
retrieving revision 1.426
diff -u -r1.426 vfs_subr.c
--- sys/kern/vfs_subr.c 2 Dec 2011 12:32:38 -0000       1.426
+++ sys/kern/vfs_subr.c 15 Jan 2012 19:26:16 -0000
@@ -224,6 +224,12 @@
 int
 vtruncbuf(struct vnode *vp, daddr_t lbn, bool catch, int slptimeo)
 {
+       return vtruncbuf2(vp, lbn, catch, slptimeo, 0);
+}
+
+int
+vtruncbuf2(struct vnode *vp, daddr_t lbn, bool catch, int slptimeo, int bflags)
+{
        struct buf *bp, *nbp;
        int error;
        voff_t off;
@@ -240,6 +246,8 @@
        for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
                KASSERT(bp->b_vp == vp);
                nbp = LIST_NEXT(bp, b_vnbufs);
+               if ((bp->b_flags & bflags) != bflags)
+                       continue;
                if (bp->b_lblkno < lbn)
                        continue;
                error = bbusy(bp, catch, slptimeo, NULL);
@@ -255,7 +263,7 @@
        for (bp = LIST_FIRST(&vp->v_cleanblkhd); bp; bp = nbp) {
                KASSERT(bp->b_vp == vp);
                nbp = LIST_NEXT(bp, b_vnbufs);
-               if (bp->b_lblkno < lbn)
+               if (bp->b_lblkno < lbn && (bp->b_flags & bflags) == bflags)
                        continue;
                error = bbusy(bp, catch, slptimeo, NULL);
                if (error != 0) {
Index: sys/sys/buf.h
===================================================================
RCS file: /cvsroot/src/sys/sys/buf.h,v
retrieving revision 1.118
diff -u -r1.118 buf.h
--- sys/sys/buf.h       21 Nov 2011 04:36:05 -0000      1.118
+++ sys/sys/buf.h       15 Jan 2012 19:26:23 -0000
@@ -185,11 +185,12 @@
 #define        B_RAW           0x00080000      /* Set by physio for raw 
transfers. */
 #define        B_READ          0x00100000      /* Read buffer. */
 #define        B_DEVPRIVATE    0x02000000      /* Device driver private flag. 
*/
+#define        B_ALTDATA       0x04000000      /* alternate FS data */
 
 #define BUF_FLAGBITS \
     "\20\1AGE\3ASYNC\4BAD\5BUSY\10DELWRI" \
     "\12DONE\13COWDONE\15GATHERED\16INVAL\17LOCKED\20NOCACHE" \
-    "\23PHYS\24RAW\25READ\32DEVPRIVATE\33VFLUSH"
+    "\23PHYS\24RAW\25READ\32DEVPRIVATE\33VFLUSH\34ALTDATA"
 
 /* Avoid weird code due to B_WRITE being a "pseudo flag" */
 #define BUF_ISREAD(bp) (((bp)->b_flags & B_READ) == B_READ)
@@ -223,9 +224,11 @@
 #define B_SYNC         0x02    /* Do all allocations synchronously. */
 #define B_METAONLY     0x04    /* Return indirect block buffer. */
 #define B_CONTIG       0x08    /* Allocate file contiguously. */
+/* #define     B_ALTDATA       0x04000000 already defined */
 
 /* Flags to bread() and breadn(). */
 #define B_MODIFY       0x01    /* Hint: caller might modify buffer */
+/* #define     B_ALTDATA       0x04000000 already defined */
 
 #ifdef _KERNEL
 
@@ -275,8 +278,10 @@
 void   bufinit2(void);
 int    bwrite(buf_t *);
 buf_t  *getblk(struct vnode *, daddr_t, int, int, int);
+buf_t  *getblk2(struct vnode *, daddr_t, int, int, int, int);
 buf_t  *geteblk(int);
 buf_t  *incore(struct vnode *, daddr_t);
+buf_t  *incore2(struct vnode *, daddr_t, int);
 
 void   minphys(buf_t *);
 int    physio(void (*)(buf_t *), buf_t *, dev_t, int,
Index: sys/sys/vnode.h
===================================================================
RCS file: /cvsroot/src/sys/sys/vnode.h,v
retrieving revision 1.236
diff -u -r1.236 vnode.h
--- sys/sys/vnode.h     24 Nov 2011 15:51:30 -0000      1.236
+++ sys/sys/vnode.h     15 Jan 2012 19:26:23 -0000
@@ -564,6 +564,7 @@
 void   vrele_async(struct vnode *);
 void   vrele_flush(void);
 int    vtruncbuf(struct vnode *, daddr_t, bool, int);
+int    vtruncbuf2(struct vnode *, daddr_t, bool, int, int);
 void   vwakeup(struct buf *);
 void   vwait(struct vnode *, int);
 void   vclean(struct vnode *, int);
Index: sys/ufs/ufs/inode.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/inode.h,v
retrieving revision 1.59
diff -u -r1.59 inode.h
--- sys/ufs/ufs/inode.h 2 Jan 2012 22:10:45 -0000       1.59
+++ sys/ufs/ufs/inode.h 15 Jan 2012 19:26:23 -0000
@@ -70,6 +70,13 @@
        /* follow two fields are used by contiguous allocation code only. */
        daddr_t ffs_first_data_blk;     /* first data block on disk. */
        daddr_t ffs_first_indir_blk;    /* first indirect block on disk. */
+       /*
+        * Data for extended attribute modification.
+        */
+       u_char    *ffs_ea_area;   /* Pointer to malloced copy of EA area */
+       unsigned  ffs_ea_len;     /* Length of i_ea_area */
+       int       ffs_ea_error;   /* First errno in transaction */
+       int       ffs_ea_refs;    /* Number of users of EA area */
 };
 
 struct ext2fs_inode_ext {
@@ -132,6 +139,10 @@
 #define        i_snapblklist           inode_ext.ffs.ffs_snapblklist
 #define        i_ffs_first_data_blk    inode_ext.ffs.ffs_first_data_blk
 #define        i_ffs_first_indir_blk   inode_ext.ffs.ffs_first_indir_blk
+#define i_ffs_ea_area          inode_ext.ffs.ffs_ea_area
+#define i_ffs_ea_len           inode_ext.ffs.ffs_ea_len
+#define i_ffs_ea_error         inode_ext.ffs.ffs_ea_error
+#define i_ffs_ea_refs          inode_ext.ffs.ffs_ea_refs
 #define        i_e2fs_last_lblk        inode_ext.e2fs.ext2fs_last_lblk
 #define        i_e2fs_last_blk         inode_ext.e2fs.ext2fs_last_blk
        /*
@@ -242,7 +253,7 @@
 #define        IN_ADIROP       0x0200          /* LFS: dirop in progress */
 #define        IN_SPACECOUNTED 0x0400          /* Blocks to be freed in free 
count. */
 #define        IN_PAGING       0x1000          /* LFS: file is on paging queue 
*/
-#define IN_CDIROP       0x4000          /* LFS: dirop completed pending i/o */
+#define IN_CDIROP       0x4000         /* LFS: dirop completed pending i/o */
 #if defined(_KERNEL)
 
 /*
Index: sys/ufs/ufs/ufs_bmap.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_bmap.c,v
retrieving revision 1.49
diff -u -r1.49 ufs_bmap.c
--- sys/ufs/ufs/ufs_bmap.c      6 Mar 2011 17:08:39 -0000       1.49
+++ sys/ufs/ufs/ufs_bmap.c      15 Jan 2012 19:26:23 -0000
@@ -403,3 +403,21 @@
                *nump = numlevels;
        return (0);
 }
+
+int
+ufs_extbmap(struct vnode *vp, daddr_t bn, daddr_t *bnp)
+{
+       struct inode *ip = VTOI(vp);
+       struct ufsmount *ump = ip->i_ump;
+       daddr_t daddr;
+
+       KASSERT(ump->um_fstype == UFS2);
+       KASSERT(bnp != NULL);
+       if (bn >= 0 && bn < NXADDR) {
+               daddr = ufs_rw64(ip->i_ffs2_extb[bn], UFS_MPNEEDSWAP(ump));
+               *bnp = blkptrtodb(ump, daddr);
+       } else {
+               *bnp = -1;
+       }
+       return 0;
+}
Index: sys/ufs/ufs/ufs_extern.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_extern.h,v
retrieving revision 1.66
diff -u -r1.66 ufs_extern.h
--- sys/ufs/ufs/ufs_extern.h    17 Jul 2011 22:07:59 -0000      1.66
+++ sys/ufs/ufs/ufs_extern.h    15 Jan 2012 19:26:23 -0000
@@ -107,6 +107,7 @@
 int    ufs_bmaparray(struct vnode *, daddr_t, daddr_t *, struct indir *,
                      int *, int *, ufs_issequential_callback_t);
 int    ufs_getlbns(struct vnode *, daddr_t, struct indir *, int *);
+int    ufs_extbmap(struct vnode *, daddr_t, daddr_t *);
 
 /* ufs_ihash.c */
 void   ufs_ihashinit(void);
Index: sys/ufs/ufs/ufs_inode.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_inode.c,v
retrieving revision 1.88
diff -u -r1.88 ufs_inode.c
--- sys/ufs/ufs/ufs_inode.c     20 Sep 2011 14:01:33 -0000      1.88
+++ sys/ufs/ufs/ufs_inode.c     15 Jan 2012 19:26:23 -0000
@@ -106,6 +106,10 @@
                if (error)
                        goto out;
                logged = 1;
+               /* trucate extended data if any */
+               error = UFS_TRUNCATE(vp, (off_t)0, IO_EXT, NOCRED);
+               if (error)
+                       goto out;
                if (ip->i_size != 0) {
                        /*
                         * When journaling, only truncate one indirect block
Index: sys/ufs/ufs/ufs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_vnops.c,v
retrieving revision 1.206
diff -u -r1.206 ufs_vnops.c
--- sys/ufs/ufs/ufs_vnops.c     18 Nov 2011 21:18:52 -0000      1.206
+++ sys/ufs/ufs/ufs_vnops.c     15 Jan 2012 19:26:23 -0000
@@ -2500,8 +2500,12 @@
                panic("ufs_strategy: spec");
        KASSERT(bp->b_bcount != 0);
        if (bp->b_blkno == bp->b_lblkno) {
-               error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno,
-                                NULL);
+               if (bp->b_flags & B_ALTDATA) {
+                       error = ufs_extbmap(vp, bp->b_lblkno, &bp->b_blkno);
+               } else {
+                       error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno,
+                                        NULL);
+               }
                if (error) {
                        bp->b_error = error;
                        biodone(bp);
Index: sys/ufs/lfs/lfs_inode.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/lfs/lfs_inode.c,v
retrieving revision 1.126
diff -u -r1.126 lfs_inode.c
--- sys/ufs/lfs/lfs_inode.c     23 Nov 2011 19:42:10 -0000      1.126
+++ sys/ufs/lfs/lfs_inode.c     15 Jan 2012 19:29:26 -0000
@@ -214,6 +214,9 @@
        int usepc;
        struct ufsmount *ump = oip->i_ump;
 
+       if (ioflag & IO_EXT)
+               return 0;
+
        if (ovp->v_type == VCHR || ovp->v_type == VBLK ||
            ovp->v_type == VFIFO || ovp->v_type == VSOCK) {
                KASSERT(oip->i_size == 0);
Index: lib/librumphijack/hijack.c
===================================================================
RCS file: /cvsroot/src/lib/librumphijack/hijack.c,v
retrieving revision 1.90
diff -u -r1.90 hijack.c
--- lib/librumphijack/hijack.c  21 Apr 2011 13:38:14 -0000      1.90
+++ lib/librumphijack/hijack.c  15 Jan 2012 19:25:43 -0000
@@ -40,6 +40,7 @@
 #include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/statvfs.h>
+#include <sys/extattr.h>
 
 #include <rump/rumpclient.h>
 #include <rump/rump_syscalls.h>
@@ -101,6 +102,23 @@
 #if __NetBSD_Prereq__(5,99,48)
        DUALCALL_QUOTACTL,
 #endif
+       DUALCALL_EXTATTRCTL,
+       DUALCALL_EXTATTR_SET_FILE,
+       DUALCALL_EXTATTR_GET_FILE,
+       DUALCALL_EXTATTR_DELETE_FILE,
+#ifdef notyet
+       DUALCALL_EXTATTR_SET_FD,
+       DUALCALL_EXTATTR_GET_FD,
+       DUALCALL_EXTATTR_DELETE_FD,
+#endif
+       DUALCALL_EXTATTR_SET_LINK,
+       DUALCALL_EXTATTR_GET_LINK,
+       DUALCALL_EXTATTR_DELETE_LINK,
+#ifdef notyet
+       DUALCALL_EXTATTR_LIST_FD,
+#endif
+       DUALCALL_EXTATTR_LIST_FILE,
+       DUALCALL_EXTATTR_LIST_LINK,
        DUALCALL__NUM
 };
 
@@ -251,6 +269,36 @@
 #if __NetBSD_Prereq__(5,99,48)
        { DUALCALL_QUOTACTL,    S(REALQUOTACTL),RSYS_NAME(QUOTACTL)     },
 #endif
+       { DUALCALL_EXTATTRCTL,          "extattrctl",
+               RSYS_NAME(EXTATTRCTL)           },
+       { DUALCALL_EXTATTR_SET_FILE,     "extattr_set_file",
+               RSYS_NAME(EXTATTR_SET_FILE)     },
+       { DUALCALL_EXTATTR_GET_FILE,    "extattr_get_file",
+               RSYS_NAME(EXTATTR_GET_FILE)     },
+       { DUALCALL_EXTATTR_DELETE_FILE, "extattr_delete_file",
+               RSYS_NAME(EXTATTR_DELETE_FILE)  },
+#ifdef notyet
+       { DUALCALL_EXTATTR_SET_FD,      "extattr_set_fd",
+               RSYS_NAME(EXTATTR_SET_FD)       },
+       { DUALCALL_EXTATTR_GET_FD,      "extattr_get_fd",
+               RSYS_NAME(EXTATTR_GET_FD)       },
+       { DUALCALL_EXTATTR_DELETE_FD,   "extattr_delete_fd",
+               RSYS_NAME(EXTATTR_DELETE_FD)    },
+#endif
+       { DUALCALL_EXTATTR_SET_LINK,    "extattr_set_link",
+               RSYS_NAME(EXTATTR_SET_LINK)     },
+       { DUALCALL_EXTATTR_GET_LINK,    "extattr_get_link",
+               RSYS_NAME(EXTATTR_GET_LINK)     },
+       { DUALCALL_EXTATTR_DELETE_LINK, "extattr_delete_link",
+               RSYS_NAME(EXTATTR_DELETE_LINK)  },
+#ifdef notyet
+       { DUALCALL_EXTATTR_LIST_FD,     "extattr_list_fd",
+               RSYS_NAME(EXTATTR_LIST_FD)      },
+#endif
+       { DUALCALL_EXTATTR_LIST_FILE,   "extattr_list_file",
+               RSYS_NAME(EXTATTR_LIST_FILE)    },
+       { DUALCALL_EXTATTR_LIST_LINK,   "extattr_list_link",
+               RSYS_NAME(EXTATTR_LIST_LINK)    },
 };
 #undef S
 
@@ -2190,6 +2238,74 @@
        (const char *, void *, size_t *),                               \
        (path, fhp, fh_size))
 
+PATHCALL(int, extattrctl, DUALCALL_EXTATTRCTL,  \
+       (const char *path, int cmd, const char *filename, int attrnamespace, 
const char *attrname), \
+       (const char *, int, const char *, int, const char *), \
+       (path, cmd, filename, attrnamespace, attrname))
+
+PATHCALL(int, extattr_set_file, DUALCALL_EXTATTR_SET_FILE,      \
+       (const char *path, int attrnamespace, const char *attrname, const void 
*data, size_t nbytes), \
+       (const char *, int , const char *, const void *, size_t), \
+       (path, attrnamespace, attrname, data, nbytes))
+
+PATHCALL(ssize_t, extattr_get_file, DUALCALL_EXTATTR_GET_FILE,      \
+       (const char *path, int attrnamespace, const char *attrname, void *data, 
size_t nbytes), \
+       (const char *, int, const char *, void *, size_t), \
+       (path, attrnamespace, attrname, data, nbytes))
+
+PATHCALL(int, extattr_delete_file, DUALCALL_EXTATTR_DELETE_FILE,        \
+       (const char *path, int attrnamespace, const char *attrname), \
+       (const char *, int, const char *), \
+       (path, attrnamespace, attrname))
+
+#ifdef notyet
+PATHCALL(int, extattr_set_fd, DUALCALL_EXTATTR_SET_FD,  \
+       (int fd, int attrnamespace, const char *attrname, const void *data, 
size_t nbytes), \
+       (int, int, const char *, const void *, size_t), \
+       (fd, attrnamespace, attrname, data, nbytes))
+
+PATHCALL(ssize_t, extattr_get_fd, DUALCALL_EXTATTR_GET_FD,  \
+       (int fd, int attrnamespace, const char *attrname, void *data, size_t 
nbytes), \
+       (int, int , const char *, void *, size_t), \
+       (fd, attrnamespace, attrname, data, nbytes))
+
+PATHCALL(int, extattr_delete_fd, DUALCALL_EXTATTR_DELETE_FD,    \
+       (int fd, int attrnamespace, const char *attrname), \
+       (int, int, const char *), \
+       (fd, attrnamespace, attrname))
+#endif
+
+PATHCALL(int, extattr_set_link, DUALCALL_EXTATTR_SET_LINK,      \
+       (const char *path, int attrnamespace, const char *attrname, const void 
*data, size_t nbytes), \
+       (const char *, int, const char *, const void *, size_t), \
+       (path, attrnamespace, attrname, data, nbytes))
+
+PATHCALL(ssize_t, extattr_get_link, DUALCALL_EXTATTR_GET_LINK,      \
+       (const char *path, int attrnamespace, const char *attrname, void *data, 
size_t nbytes), \
+       (const char *, int, const char *, void *, size_t), \
+       (path, attrnamespace, attrname, data, nbytes))
+
+PATHCALL(int, extattr_delete_link, DUALCALL_EXTATTR_DELETE_LINK,        \
+       (const char *path, int attrnamespace, const char *attrname), \
+       (const char *, int, const char *), \
+       (path, attrnamespace, attrname))
+
+#ifdef notyet
+PATHCALL(ssize_t, extattr_list_fd, DUALCALL_EXTATTR_LIST_FD,        \
+       (int fd, int attrnamespace, void *data, size_t nbytes), \
+       (int, int, void *, size_t), \
+       (fd, attrnamespace, data, nbytes))
+#endif
+
+PATHCALL(ssize_t, extattr_list_file, DUALCALL_EXTATTR_LIST_FILE,    \
+       (const char *path, int attrnamespace, void *data, size_t nbytes), \
+       (const char *, int, void *, size_t), \
+       (path, attrnamespace, data, nbytes))
+
+PATHCALL(ssize_t, extattr_list_link, DUALCALL_EXTATTR_LIST_LINK,    \
+       (const char *path, int attrnamespace, void *data, size_t nbytes), \
+       (const char *, int, void *, size_t), \
+       (path, attrnamespace, data, nbytes))
 /*
  * These act different on a per-process vfs configuration
  */
Index: sys/kern/vfs_bio.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_bio.c,v
retrieving revision 1.232
diff -u -r1.232 vfs_bio.c
--- sys/kern/vfs_bio.c  5 Oct 2011 01:53:03 -0000       1.232
+++ sys/kern/vfs_bio.c  15 Jan 2012 19:26:16 -0000
@@ -656,12 +656,13 @@
  */
 static buf_t *
 bio_doread(struct vnode *vp, daddr_t blkno, int size, kauth_cred_t cred,
-    int async)
+    int flags)
 {
        buf_t *bp;
        struct mount *mp;
+       const bool async = ((flags & B_ASYNC) != 0);
 
-       bp = getblk(vp, blkno, size, 0, 0);
+       bp = getblk2(vp, blkno, size, 0, 0, (flags & B_ALTDATA));
 
 #ifdef DIAGNOSTIC
        if (bp == NULL) {
@@ -676,7 +677,7 @@
         */
        if (!ISSET(bp->b_oflags, (BO_DONE | BO_DELWRI))) {
                /* Start I/O for the buffer. */
-               SET(bp->b_flags, B_READ | async);
+               SET(bp->b_flags, B_READ | flags);
                if (async)
                        BIO_SETPRIO(bp, BPRIO_TIMELIMITED);
                else
@@ -699,7 +700,7 @@
         * filesystem (if any).
         */
        if (mp != NULL) {
-               if (async == 0)
+               if (async == false)
                        mp->mnt_stat.f_syncreads++;
                else
                        mp->mnt_stat.f_asyncreads++;
@@ -720,7 +721,7 @@
        int error;
 
        /* Get buffer for block. */
-       bp = *bpp = bio_doread(vp, blkno, size, cred, 0);
+       bp = *bpp = bio_doread(vp, blkno, size, cred, flags & B_ALTDATA);
 
        /* Wait for the read to complete, and return result. */
        error = biowait(bp);
@@ -741,7 +742,7 @@
        buf_t *bp;
        int error, i;
 
-       bp = *bpp = bio_doread(vp, blkno, size, cred, 0);
+       bp = *bpp = bio_doread(vp, blkno, size, cred, flags & B_ALTDATA);
 
        /*
         * For each of the read-ahead blocks, start a read, if necessary.
@@ -749,12 +750,13 @@
        mutex_enter(&bufcache_lock);
        for (i = 0; i < nrablks; i++) {
                /* If it's in the cache, just go on to next one. */
-               if (incore(vp, rablks[i]))
+               if (incore2(vp, rablks[i], flags))
                        continue;
 
                /* Get a buffer for the read-ahead block */
                mutex_exit(&bufcache_lock);
-               (void) bio_doread(vp, rablks[i], rasizes[i], cred, B_ASYNC);
+               (void) bio_doread(vp, rablks[i], rasizes[i], cred,
+                   B_ASYNC | (flags & B_ALTDATA));
                mutex_enter(&bufcache_lock);
        }
        mutex_exit(&bufcache_lock);
@@ -1099,6 +1101,12 @@
 buf_t *
 incore(struct vnode *vp, daddr_t blkno)
 {
+       return incore2(vp, blkno, 0);
+}
+
+buf_t *
+incore2(struct vnode *vp, daddr_t blkno, int bflags)
+{
        buf_t *bp;
 
        KASSERT(mutex_owned(&bufcache_lock));
@@ -1106,6 +1114,7 @@
        /* Search hash chain */
        LIST_FOREACH(bp, BUFHASH(vp, blkno), b_hash) {
                if (bp->b_lblkno == blkno && bp->b_vp == vp &&
+                   (bp->b_flags & bflags) ==  bflags &&
                    !ISSET(bp->b_cflags, BC_INVAL)) {
                        KASSERT(bp->b_objlock == vp->v_interlock);
                        return (bp);
@@ -1126,12 +1135,19 @@
 buf_t *
 getblk(struct vnode *vp, daddr_t blkno, int size, int slpflag, int slptimeo)
 {
+       return getblk2(vp, blkno, size, slpflag, slptimeo, 0);
+}
+
+buf_t *
+getblk2(struct vnode *vp, daddr_t blkno, int size, int slpflag, int slptimeo,
+    int bflags)
+{
        int err, preserve;
        buf_t *bp;
 
        mutex_enter(&bufcache_lock);
  loop:
-       bp = incore(vp, blkno);
+       bp = incore2(vp, blkno, bflags);
        if (bp != NULL) {
                err = bbusy(bp, ((slpflag & PCATCH) != 0), slptimeo, NULL);
                if (err != 0) {
@@ -1152,7 +1168,7 @@
                if ((bp = getnewbuf(slpflag, slptimeo, 0)) == NULL)
                        goto loop;
 
-               if (incore(vp, blkno) != NULL) {
+               if (incore2(vp, blkno, bflags) != NULL) {
                        /* The block has come into memory in the meantime. */
                        brelsel(bp, 0);
                        goto loop;
@@ -1160,6 +1176,7 @@
 
                LIST_INSERT_HEAD(BUFHASH(vp, blkno), bp, b_hash);
                bp->b_blkno = bp->b_lblkno = bp->b_rawblkno = blkno;
+               bp->b_flags |= bflags;
                mutex_enter(vp->v_interlock);
                bgetvp(vp, bp);
                mutex_exit(vp->v_interlock);
Index: sys/kern/vfs_subr.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_subr.c,v
retrieving revision 1.426
diff -u -r1.426 vfs_subr.c
--- sys/kern/vfs_subr.c 2 Dec 2011 12:32:38 -0000       1.426
+++ sys/kern/vfs_subr.c 15 Jan 2012 19:26:16 -0000
@@ -224,6 +224,12 @@
 int
 vtruncbuf(struct vnode *vp, daddr_t lbn, bool catch, int slptimeo)
 {
+       return vtruncbuf2(vp, lbn, catch, slptimeo, 0);
+}
+
+int
+vtruncbuf2(struct vnode *vp, daddr_t lbn, bool catch, int slptimeo, int bflags)
+{
        struct buf *bp, *nbp;
        int error;
        voff_t off;
@@ -240,6 +246,8 @@
        for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
                KASSERT(bp->b_vp == vp);
                nbp = LIST_NEXT(bp, b_vnbufs);
+               if ((bp->b_flags & bflags) != bflags)
+                       continue;
                if (bp->b_lblkno < lbn)
                        continue;
                error = bbusy(bp, catch, slptimeo, NULL);
@@ -255,7 +263,7 @@
        for (bp = LIST_FIRST(&vp->v_cleanblkhd); bp; bp = nbp) {
                KASSERT(bp->b_vp == vp);
                nbp = LIST_NEXT(bp, b_vnbufs);
-               if (bp->b_lblkno < lbn)
+               if (bp->b_lblkno < lbn && (bp->b_flags & bflags) == bflags)
                        continue;
                error = bbusy(bp, catch, slptimeo, NULL);
                if (error != 0) {
Index: sys/sys/buf.h
===================================================================
RCS file: /cvsroot/src/sys/sys/buf.h,v
retrieving revision 1.118
diff -u -r1.118 buf.h
--- sys/sys/buf.h       21 Nov 2011 04:36:05 -0000      1.118
+++ sys/sys/buf.h       15 Jan 2012 19:26:23 -0000
@@ -185,11 +185,12 @@
 #define        B_RAW           0x00080000      /* Set by physio for raw 
transfers. */
 #define        B_READ          0x00100000      /* Read buffer. */
 #define        B_DEVPRIVATE    0x02000000      /* Device driver private flag. 
*/
+#define        B_ALTDATA       0x04000000      /* alternate FS data */
 
 #define BUF_FLAGBITS \
     "\20\1AGE\3ASYNC\4BAD\5BUSY\10DELWRI" \
     "\12DONE\13COWDONE\15GATHERED\16INVAL\17LOCKED\20NOCACHE" \
-    "\23PHYS\24RAW\25READ\32DEVPRIVATE\33VFLUSH"
+    "\23PHYS\24RAW\25READ\32DEVPRIVATE\33VFLUSH\34ALTDATA"
 
 /* Avoid weird code due to B_WRITE being a "pseudo flag" */
 #define BUF_ISREAD(bp) (((bp)->b_flags & B_READ) == B_READ)
@@ -223,9 +224,11 @@
 #define B_SYNC         0x02    /* Do all allocations synchronously. */
 #define B_METAONLY     0x04    /* Return indirect block buffer. */
 #define B_CONTIG       0x08    /* Allocate file contiguously. */
+/* #define     B_ALTDATA       0x04000000 already defined */
 
 /* Flags to bread() and breadn(). */
 #define B_MODIFY       0x01    /* Hint: caller might modify buffer */
+/* #define     B_ALTDATA       0x04000000 already defined */
 
 #ifdef _KERNEL
 
@@ -275,8 +278,10 @@
 void   bufinit2(void);
 int    bwrite(buf_t *);
 buf_t  *getblk(struct vnode *, daddr_t, int, int, int);
+buf_t  *getblk2(struct vnode *, daddr_t, int, int, int, int);
 buf_t  *geteblk(int);
 buf_t  *incore(struct vnode *, daddr_t);
+buf_t  *incore2(struct vnode *, daddr_t, int);
 
 void   minphys(buf_t *);
 int    physio(void (*)(buf_t *), buf_t *, dev_t, int,
Index: sys/sys/vnode.h
===================================================================
RCS file: /cvsroot/src/sys/sys/vnode.h,v
retrieving revision 1.236
diff -u -r1.236 vnode.h
--- sys/sys/vnode.h     24 Nov 2011 15:51:30 -0000      1.236
+++ sys/sys/vnode.h     15 Jan 2012 19:26:23 -0000
@@ -564,6 +564,7 @@
 void   vrele_async(struct vnode *);
 void   vrele_flush(void);
 int    vtruncbuf(struct vnode *, daddr_t, bool, int);
+int    vtruncbuf2(struct vnode *, daddr_t, bool, int, int);
 void   vwakeup(struct buf *);
 void   vwait(struct vnode *, int);
 void   vclean(struct vnode *, int);
Index: sys/ufs/ffs/ffs_alloc.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_alloc.c,v
retrieving revision 1.130
diff -u -r1.130 ffs_alloc.c
--- sys/ufs/ffs/ffs_alloc.c     28 Nov 2011 08:05:07 -0000      1.130
+++ sys/ufs/ffs/ffs_alloc.c     15 Jan 2012 19:26:23 -0000
@@ -303,7 +303,7 @@
  */
 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)
+    int nsize, kauth_cred_t cred, int flags, struct buf **bpp, daddr_t *blknop)
 {
        struct ufsmount *ump;
        struct fs *fs;
@@ -364,10 +364,22 @@
                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 (flags & B_ALTDATA) {
+               if (fs->fs_magic == FS_UFS2_MAGIC)  {
+                       bprev = ufs_rw64(ip->i_ffs2_extb[lbprev],
+                           UFS_FSNEEDSWAP(fs));
+               } else {
+                       panic("ffs_realloccg: B_ALTDATA");
+               }
+       } else {
+               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) {
                printf("dev = 0x%llx, bsize = %d, bprev = %" PRId64 ", fs = 
%s\n",
@@ -381,7 +393,8 @@
         * Allocate the extra space in the buffer.
         */
        if (bpp != NULL &&
-           (error = bread(ITOV(ip), lbprev, osize, NOCRED, 0, &bp)) != 0) {
+           (error = bread(ITOV(ip), lbprev, osize, NOCRED,
+               (flags & B_ALTDATA), &bp)) != 0) {
                brelse(bp, 0);
                return (error);
        }
@@ -479,7 +492,7 @@
        bno = ffs_hashalloc(ip, cg, bpref, request, 0, ffs_alloccg);
        if (bno > 0) {
                if ((ip->i_ump->um_mountp->mnt_wapbl) &&
-                   (ITOV(ip)->v_type != VREG)) {
+                   ((ITOV(ip)->v_type != VREG) || (flags & B_ALTDATA))) {
                        UFS_WAPBL_REGISTER_DEALLOCATION(
                            ip->i_ump->um_mountp, fsbtodb(fs, bprev),
                            osize);
@@ -489,7 +502,7 @@
                }
                if (nsize < request) {
                        if ((ip->i_ump->um_mountp->mnt_wapbl) &&
-                           (ITOV(ip)->v_type != VREG)) {
+                           ((ITOV(ip)->v_type != VREG) || (flags & 
B_ALTDATA))) {
                                UFS_WAPBL_REGISTER_DEALLOCATION(
                                    ip->i_ump->um_mountp,
                                    fsbtodb(fs, (bno + numfrags(fs, nsize))),
@@ -1333,7 +1346,7 @@
                        bp = NULL;
                        error = ffs_getblk(ip->i_devvp, fsbtodb(fs,
                            ino_to_fsba(fs, cg * fs->fs_ipg + initediblk)),
-                           FFS_NOBLK, fs->fs_bsize, false, &ibp);
+                           FFS_NOBLK, fs->fs_bsize, 0, &ibp);
                        if (error)
                                goto fail;
                        goto retry;
Index: sys/ufs/ffs/ffs_balloc.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_balloc.c,v
retrieving revision 1.54
diff -u -r1.54 ffs_balloc.c
--- sys/ufs/ffs/ffs_balloc.c    23 Apr 2011 07:36:02 -0000      1.54
+++ sys/ufs/ffs/ffs_balloc.c    15 Jan 2012 19:26:23 -0000
@@ -125,6 +125,9 @@
        }
        UVMHIST_LOG(ubchist, "vp %p lbn 0x%x size 0x%x", vp, lbn, size,0);
 
+       if (flags & B_ALTDATA)
+               return (EOPNOTSUPP);
+
        if (lbn < 0)
                return (EFBIG);
 
@@ -143,7 +146,8 @@
                        error = ffs_realloccg(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, cred,
+                                   flags, bpp, &newb);
                        if (error)
                                return (error);
                        ip->i_size = lblktosize(fs, nb + 1);
@@ -220,7 +224,8 @@
                                error = ffs_realloccg(ip, lbn,
                                    ffs_blkpref_ufs1(ip, lbn, (int)lbn, flags,
                                        &ip->i_ffs1_db[0]),
-                                   osize, nsize, cred, bpp, &newb);
+                                   osize, nsize, cred,
+                                   flags, bpp, &newb);
                                if (error)
                                        return (error);
                        }
@@ -244,7 +249,7 @@
                                return (error);
                        if (bpp != NULL) {
                                error = ffs_getblk(vp, lbn, fsbtodb(fs, newb),
-                                   nsize, (flags & B_CLRBUF) != 0, bpp);
+                                   nsize, flags, bpp);
                                if (error)
                                        return error;
                        }
@@ -280,7 +285,7 @@
                nb = newb;
                *allocblk++ = nb;
                error = ffs_getblk(vp, indirs[1].in_lbn, fsbtodb(fs, nb),
-                   fs->fs_bsize, true, &bp);
+                   fs->fs_bsize, B_CLRBUF, &bp);
                if (error)
                        goto fail;
                /*
@@ -336,7 +341,7 @@
                nb = newb;
                *allocblk++ = nb;
                error = ffs_getblk(vp, indirs[i].in_lbn, fsbtodb(fs, nb),
-                   fs->fs_bsize, true, &nbp);
+                   fs->fs_bsize, B_CLRBUF, &nbp);
                if (error) {
                        brelse(bp, 0);
                        goto fail;
@@ -393,7 +398,7 @@
                *allocblk++ = nb;
                if (bpp != NULL) {
                        error = ffs_getblk(vp, lbn, fsbtodb(fs, nb),
-                           fs->fs_bsize, (flags & B_CLRBUF) != 0, bpp);
+                           fs->fs_bsize, flags, bpp);
                        if (error) {
                                brelse(bp, 0);
                                goto fail;
@@ -427,7 +432,7 @@
                        }
                } else {
                        error = ffs_getblk(vp, lbn, fsbtodb(fs, nb),
-                           fs->fs_bsize, true, &nbp);
+                           fs->fs_bsize, B_CLRBUF, &nbp);
                        if (error)
                                goto fail;
                }
@@ -455,14 +460,14 @@
                                break;
                        }
                        if (ffs_getblk(vp, indirs[i].in_lbn, FFS_NOBLK,
-                           fs->fs_bsize, false, &bp) != 0)
+                           fs->fs_bsize, 0, &bp) != 0)
                                continue;
                        if (bp->b_oflags & BO_DELWRI) {
                                nb = fsbtodb(fs, cgtod(fs, dtog(fs,
                                    dbtofsb(fs, bp->b_blkno))));
                                bwrite(bp);
                                if (ffs_getblk(ip->i_devvp, nb, FFS_NOBLK,
-                                   fs->fs_cgsize, false, &bp) != 0)
+                                   fs->fs_cgsize, 0, &bp) != 0)
                                        continue;
                                if (bp->b_oflags & BO_DELWRI) {
                                        bwrite(bp);
@@ -496,7 +501,7 @@
                }
                for (i = unwindidx + 1; i <= num; i++) {
                        if (ffs_getblk(vp, indirs[i].in_lbn, FFS_NOBLK,
-                           fs->fs_bsize, false, &bp) == 0)
+                           fs->fs_bsize, 0, &bp) == 0)
                                brelse(bp, BC_INVAL);
                }
        }
@@ -550,11 +555,10 @@
        if (lbn < 0)
                return (EFBIG);
 
-#ifdef notyet
        /*
         * Check for allocating external data.
         */
-       if (flags & IO_EXT) {
+       if (flags & B_ALTDATA) {
                if (lbn >= NXADDR)
                        return (EFBIG);
                /*
@@ -562,102 +566,105 @@
                 * and the data is currently composed of a fragment
                 * this fragment has to be extended to be a full block.
                 */
-               lastlbn = lblkno(fs, dp->di_extsize);
+               lastlbn = lblkno(fs, ip->i_ffs2_extsize);
                if (lastlbn < lbn) {
                        nb = lastlbn;
-                       osize = sblksize(fs, dp->di_extsize, nb);
+                       osize = extsize(fs, ip->i_ffs2_extsize, nb);
                        if (osize < fs->fs_bsize && osize > 0) {
                                mutex_enter(&ump->um_lock);
                                error = ffs_realloccg(ip, -1 - nb,
-                                   dp->di_extb[nb],
                                    ffs_blkpref_ufs2(ip, lastlbn, (int)nb,
-                                       flags, &dp->di_extb[0]),
+                                       flags, &ip->i_ffs2_extb[0]),
                                    osize,
-                                   (int)fs->fs_bsize, cred, &bp);
+                                   (int)fs->fs_bsize, cred,
+                                   flags, bpp, &newb);
                                if (error)
                                        return (error);
-                               dp->di_extsize = smalllblktosize(fs, nb + 1);
-                               dp->di_extb[nb] = dbtofsb(fs, bp->b_blkno);
-                               bp->b_xflags |= BX_ALTDATA;
+                               ip->i_ffs2_extsize = lblktosize(fs, nb + 1);
+                               ip->i_ffs2_extb[nb] = ufs_rw64(newb, needswap);
                                ip->i_flag |= IN_CHANGE | IN_UPDATE;
-                               if (flags & IO_SYNC)
-                                       bwrite(bp);
-                               else
-                                       bawrite(bp);
+                               if (bpp) {
+                                       if (flags & B_SYNC)
+                                               bwrite(*bpp);
+                                       else
+                                               bawrite(*bpp);
+                               }
                        }
                }
                /*
                 * All blocks are direct blocks
                 */
-               if (flags & BA_METAONLY)
+               if (flags & B_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)) {
-                       error = bread(vp, -1 - lbn, fs->fs_bsize,
-                           NOCRED, 0, &bp);
-                       if (error) {
-                               brelse(bp, 0);
-                               return (error);
+               nb = ufs_rw64(ip->i_ffs2_extb[lbn], needswap);
+               if (nb != 0 && ip->i_ffs2_extsize >= lblktosize(fs, lbn + 1)) {
+                       if (bpp != NULL) {
+                               error = bread(vp, lbn, fs->fs_bsize,
+                                   NOCRED, B_ALTDATA, bpp);
+                               if (error) {
+                                       brelse(*bpp, 0);
+                                       return (error);
+                               }
                        }
-                       mutex_enter(&bp->b_interlock);
-                       bp->b_blkno = fsbtodb(fs, nb);
-                       bp->b_xflags |= BX_ALTDATA;
-                       mutex_exit(&bp->b_interlock);
-                       *bpp = bp;
                        return (0);
                }
                if (nb != 0) {
                        /*
                         * Consider need to reallocate a fragment.
                         */
-                       osize = fragroundup(fs, blkoff(fs, dp->di_extsize));
+                       osize = fragroundup(fs, blkoff(fs, ip->i_ffs2_extsize));
                        nsize = fragroundup(fs, size);
                        if (nsize <= osize) {
-                               error = bread(vp, -1 - lbn, osize,
-                                   NOCRED, 0, &bp);
-                               if (error) {
-                                       brelse(bp, 0);
-                                       return (error);
+                               if (bpp != NULL) {
+                                       error = bread(vp, lbn, osize,
+                                           NOCRED, B_ALTDATA, bpp);
+                                       if (error) {
+                                               brelse(*bpp, 0);
+                                               return (error);
+                                       }
                                }
-                               mutex_enter(&bp->b_interlock);
-                               bp->b_blkno = fsbtodb(fs, nb);
-                               bp->b_xflags |= BX_ALTDATA;
-                               mutex_exit(&bp->b_interlock);
                        } else {
+                               /*
+                                * The existing block is smaller than we want,
+                                * grow it.
+                                */
                                mutex_enter(&ump->um_lock);
-                               error = ffs_realloccg(ip, -1 - lbn,
-                                   dp->di_extb[lbn],
+                               error = ffs_realloccg(ip, lbn,
                                    ffs_blkpref_ufs2(ip, lbn, (int)lbn, flags,
-                                       &dp->di_extb[0]),
-                                   osize, nsize, cred, &bp);
+                                       &ip->i_ffs2_extb[0]),
+                                   osize, nsize, cred,
+                                   flags, bpp, &newb);
                                if (error)
                                        return (error);
-                               bp->b_xflags |= BX_ALTDATA;
                        }
                } else {
-                       if (dp->di_extsize < smalllblktosize(fs, lbn + 1))
+                       /*
+                        * the block was not previously allocated,
+                        * allocate a new block or fragment.
+                        */
+                       if (ip->i_ffs2_extsize < lblktosize(fs, lbn + 1))
                                nsize = fragroundup(fs, size);
                        else
                                nsize = fs->fs_bsize;
                        mutex_enter(&ump->um_lock);
                        error = ffs_alloc(ip, lbn,
                           ffs_blkpref_ufs2(ip, lbn, (int)lbn, flags,
-                              &dp->di_extb[0]),
+                              &ip->i_ffs2_extb[0]),
                           nsize, flags, cred, &newb);
                        if (error)
                                return (error);
-                       error = ffs_getblk(vp, -1 - lbn, fsbtodb(fs, newb),
-                           nsize, (flags & BA_CLRBUF) != 0, &bp);
-                       if (error)
-                               return error;
-                       bp->b_xflags |= BX_ALTDATA;
+                       if (bpp != NULL) {
+                               error = ffs_getblk(vp, -1 - lbn,
+                                   fsbtodb(fs, newb),
+                                   nsize, flags, bpp);
+                               if (error)
+                                       return error;
+                       }
                }
-               dp->di_extb[lbn] = dbtofsb(fs, bp->b_blkno);
+               ip->i_ffs2_extb[lbn] = ufs_rw64(newb, needswap);
                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
@@ -673,7 +680,8 @@
                        error = ffs_realloccg(ip, nb,
                                    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, cred,
+                                   flags, bpp, &newb);
                        if (error)
                                return (error);
                        ip->i_size = lblktosize(fs, nb + 1);
@@ -750,7 +758,8 @@
                                error = ffs_realloccg(ip, lbn,
                                    ffs_blkpref_ufs2(ip, lbn, (int)lbn, flags,
                                        &ip->i_ffs2_db[0]),
-                                   osize, nsize, cred, bpp, &newb);
+                                   osize, nsize, cred,
+                                   flags, bpp, &newb);
                                if (error)
                                        return (error);
                        }
@@ -774,7 +783,7 @@
                                return (error);
                        if (bpp != NULL) {
                                error = ffs_getblk(vp, lbn, fsbtodb(fs, newb),
-                                   nsize, (flags & B_CLRBUF) != 0, bpp);
+                                   nsize, flags, bpp);
                                if (error)
                                        return error;
                        }
@@ -810,7 +819,7 @@
                nb = newb;
                *allocblk++ = nb;
                error = ffs_getblk(vp, indirs[1].in_lbn, fsbtodb(fs, nb),
-                   fs->fs_bsize, true, &bp);
+                   fs->fs_bsize, B_CLRBUF, &bp);
                if (error)
                        goto fail;
                /*
@@ -866,7 +875,7 @@
                nb = newb;
                *allocblk++ = nb;
                error = ffs_getblk(vp, indirs[i].in_lbn, fsbtodb(fs, nb),
-                   fs->fs_bsize, true, &nbp);
+                   fs->fs_bsize, B_CLRBUF, &nbp);
                if (error) {
                        brelse(bp, 0);
                        goto fail;
@@ -923,7 +932,7 @@
                *allocblk++ = nb;
                if (bpp != NULL) {
                        error = ffs_getblk(vp, lbn, fsbtodb(fs, nb),
-                           fs->fs_bsize, (flags & B_CLRBUF) != 0, bpp);
+                           fs->fs_bsize, flags, bpp);
                        if (error) {
                                brelse(bp, 0);
                                goto fail;
@@ -957,7 +966,7 @@
                        }
                } else {
                        error = ffs_getblk(vp, lbn, fsbtodb(fs, nb),
-                           fs->fs_bsize, true, &nbp);
+                           fs->fs_bsize, B_CLRBUF, &nbp);
                        if (error)
                                goto fail;
                }
@@ -985,14 +994,14 @@
                                break;
                        }
                        if (ffs_getblk(vp, indirs[i].in_lbn, FFS_NOBLK,
-                           fs->fs_bsize, false, &bp) != 0)
+                           fs->fs_bsize, 0, &bp) != 0)
                                continue;
                        if (bp->b_oflags & BO_DELWRI) {
                                nb = fsbtodb(fs, cgtod(fs, dtog(fs,
                                    dbtofsb(fs, bp->b_blkno))));
                                bwrite(bp);
                                if (ffs_getblk(ip->i_devvp, nb, FFS_NOBLK,
-                                   fs->fs_cgsize, false, &bp) != 0)
+                                   fs->fs_cgsize, 0, &bp) != 0)
                                        continue;
                                if (bp->b_oflags & BO_DELWRI) {
                                        bwrite(bp);
@@ -1028,7 +1037,7 @@
                }
                for (i = unwindidx + 1; i <= num; i++) {
                        if (ffs_getblk(vp, indirs[i].in_lbn, FFS_NOBLK,
-                           fs->fs_bsize, false, &bp) == 0)
+                           fs->fs_bsize, 0, &bp) == 0)
                                brelse(bp, BC_INVAL);
                }
        }
Index: sys/ufs/ffs/ffs_extern.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_extern.h,v
retrieving revision 1.78
diff -u -r1.78 ffs_extern.h
--- sys/ufs/ffs/ffs_extern.h    17 Jun 2011 14:23:52 -0000      1.78
+++ sys/ufs/ffs/ffs_extern.h    15 Jan 2012 19:26:23 -0000
@@ -91,7 +91,7 @@
 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 *);
+                     kauth_cred_t, int, struct buf **, daddr_t *);
 int    ffs_valloc(struct vnode *, int, kauth_cred_t, struct vnode **);
 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 *);
@@ -112,6 +112,7 @@
 int    ffs_update(struct vnode *, const struct timespec *,
     const struct timespec *, int);
 int    ffs_truncate(struct vnode *, off_t, int, kauth_cred_t);
+int    ffs_extattr_truncate(struct vnode *, off_t, int, kauth_cred_t);
 
 /* ffs_vfsops.c */
 VFS_PROTOS(ffs);
@@ -192,7 +193,7 @@
 /* ffs_subr.c */
 #if defined(_KERNEL)
 void   ffs_load_inode(struct buf *, struct inode *, struct fs *, ino_t);
-int    ffs_getblk(struct vnode *, daddr_t, daddr_t, int, bool, buf_t **);
+int    ffs_getblk(struct vnode *, daddr_t, daddr_t, int, int, buf_t **);
 #endif /* defined(_KERNEL) */
 void   ffs_fragacct(struct fs *, int, int32_t[], int, int);
 int    ffs_isblock(struct fs *, u_char *, int32_t);
Index: sys/ufs/ffs/ffs_inode.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_inode.c,v
retrieving revision 1.108
diff -u -r1.108 ffs_inode.c
--- sys/ufs/ffs/ffs_inode.c     23 Nov 2011 19:42:10 -0000      1.108
+++ sys/ufs/ffs/ffs_inode.c     15 Jan 2012 19:26:23 -0000
@@ -209,7 +209,7 @@
        struct inode *oip = VTOI(ovp);
        daddr_t bn, lastiblock[NIADDR], indir_lbn[NIADDR];
        daddr_t blks[NDADDR + NIADDR];
-       struct fs *fs;
+       struct fs *fs = oip->i_fs;
        int offset, pgoffset, level;
        int64_t count, blocksreleased = 0;
        int i, aflag, nblocks;
@@ -218,6 +218,15 @@
        int sync;
        struct ufsmount *ump = oip->i_ump;
 
+       if (ioflag & IO_EXT) {
+               if (fs->fs_magic == FS_UFS1_MAGIC)
+                       return 0;
+               if (oip->i_ffs2_extsize <= length)
+                       return 0;
+               return ffs_extattr_truncate(ovp, length, ioflag, cred);
+       }
+
+
        if (ovp->v_type == VCHR || ovp->v_type == VBLK ||
            ovp->v_type == VFIFO || ovp->v_type == VSOCK) {
                KASSERT(oip->i_size == 0);
@@ -243,7 +252,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);
 
@@ -588,7 +596,7 @@
         * explicitly instead of letting bread do everything for us.
         */
        vp = ITOV(ip);
-       error = ffs_getblk(vp, lbn, FFS_NOBLK, fs->fs_bsize, false, &bp);
+       error = ffs_getblk(vp, lbn, FFS_NOBLK, fs->fs_bsize, 0, &bp);
        if (error) {
                *countp = 0;
                return error;
@@ -685,6 +693,99 @@
        return (allerror);
 }
 
+/* truncate extended attr data */
+int
+ffs_extattr_truncate(struct vnode *ovp, off_t length,
+    int ioflag, kauth_cred_t cred)
+{
+       daddr_t lastblock, bn;
+       struct inode *oip = VTOI(ovp);
+       struct fs *fs = oip->i_fs;
+       int offset;
+       int blocksreleased = 0;
+       int i, aflag;
+       int error, allerror = 0;
+       off_t osize;
+
+       if (ovp->v_type == VCHR || ovp->v_type == VBLK)
+               return (EOPNOTSUPP);
+
+        if (length < 0)
+               return (EINVAL);
+
+       if (length > oip->i_ffs2_extsize)
+               return (EINVAL);
+
+       aflag = ioflag & IO_SYNC ? B_SYNC : 0;
+       aflag |= B_ALTDATA;
+
+       offset = blkoff(fs, length);
+       lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1;
+
+       genfs_node_wrlock(ovp);
+       osize = oip->i_ffs2_extsize;
+       oip->i_ffs2_extsize = length;
+       error = vtruncbuf2(ovp, lastblock + 1, 0, 0, B_ALTDATA);
+       if (error && !allerror)
+               allerror = error;
+
+       for (i = NXADDR - 1; i > lastblock; i--) {
+               long bsize;
+               bn = ufs_rw64(oip->i_ffs2_extb[i], UFS_FSNEEDSWAP(fs));
+               if (bn == 0)
+                       continue;
+               oip->i_ffs2_extb[i] = 0;
+               bsize = extsize(fs, length, i);
+               if (oip->i_ump->um_mountp->mnt_wapbl) {
+                       UFS_WAPBL_REGISTER_DEALLOCATION(oip->i_ump->um_mountp,
+                           fsbtodb(fs, bn), bsize);
+               } else {
+                       ffs_blkfree(fs, oip->i_devvp, bn, bsize, oip->i_number);
+               }
+               blocksreleased += btodb(bsize);
+       }
+       if (lastblock < 0)
+               goto done;
+       /*
+        * Finally, look for a change in size of the
+        * last direct block; release any frags.
+        */
+
+       bn = ufs_rw64(oip->i_ffs2_extb[lastblock], UFS_FSNEEDSWAP(fs));
+       if (bn != 0) {
+               long oldspace, newspace;
+               oldspace = extsize(fs, osize, lastblock);
+               newspace = extsize(fs, length, lastblock);
+               if (newspace == 0)
+                       panic("extitrunc: newspace");
+               if (oldspace - newspace > 0) {
+                        /*
+                         * Block number of space to be free'd is
+                         * the old block # plus the number of frags
+                         * required for the storage we're keeping.
+                         */
+                        bn += numfrags(fs, newspace);
+                        if (oip->i_ump->um_mountp->mnt_wapbl) {
+                                UFS_WAPBL_REGISTER_DEALLOCATION(
+                                    oip->i_ump->um_mountp, fsbtodb(fs, bn),
+                                    oldspace - newspace);
+                        } else
+                                ffs_blkfree(fs, oip->i_devvp, bn,
+                                    oldspace - newspace, oip->i_number);
+                        blocksreleased += btodb(oldspace - newspace);
+               }
+       }
+done:
+       DIP_ADD(oip, blocks, -blocksreleased);
+       genfs_node_unlock(ovp);
+       oip->i_flag |= IN_CHANGE;
+       UFS_WAPBL_UPDATE(ovp, NULL, NULL, 0);
+#if defined(QUOTA) || defined(QUOTA2)
+        (void) chkdq(oip, -blocksreleased, NOCRED, 0);
+#endif
+       return (allerror);
+}
+
 void
 ffs_itimes(struct inode *ip, const struct timespec *acc,
     const struct timespec *mod, const struct timespec *cre)
Index: sys/ufs/ffs/ffs_snapshot.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_snapshot.c,v
retrieving revision 1.118
diff -u -r1.118 ffs_snapshot.c
--- sys/ufs/ffs/ffs_snapshot.c  7 Oct 2011 09:35:06 -0000       1.118
+++ sys/ufs/ffs/ffs_snapshot.c  15 Jan 2012 19:26:23 -0000
@@ -1170,7 +1170,7 @@
         * up the block number for any blocks that are not in the cache.
         */
        error = ffs_getblk(cancelvp, lbn, fsbtodb(fs, blkno), fs->fs_bsize,
-           false, &bp);
+           0, &bp);
        if (error)
                return error;
        if ((bp->b_oflags & (BO_DONE | BO_DELWRI)) == 0 && (error =
Index: sys/ufs/ffs/ffs_subr.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_subr.c,v
retrieving revision 1.47
diff -u -r1.47 ffs_subr.c
--- sys/ufs/ffs/ffs_subr.c      14 Aug 2011 12:37:09 -0000      1.47
+++ sys/ufs/ffs/ffs_subr.c      15 Jan 2012 19:26:23 -0000
@@ -115,17 +115,18 @@
 
 int
 ffs_getblk(struct vnode *vp, daddr_t lblkno, daddr_t blkno, int size,
-    bool clearbuf, buf_t **bpp)
+    int flags, buf_t **bpp)
 {
        int error = 0;
 
        KASSERT(blkno >= 0 || blkno == FFS_NOBLK);
 
-       if ((*bpp = getblk(vp, lblkno, size, 0, 0)) == NULL)
+       if ((*bpp = getblk2(vp, lblkno, size, 0, 0, (flags & B_ALTDATA)))
+           == NULL)
                return ENOMEM;
        if (blkno != FFS_NOBLK)
                (*bpp)->b_blkno = blkno;
-       if (clearbuf)
+       if (flags & B_CLRBUF)
                clrbuf(*bpp);
        if ((*bpp)->b_blkno >= 0 && (error = fscow_run(*bpp, false)) != 0)
                brelse(*bpp, BC_INVAL);
Index: sys/ufs/ffs/ffs_vfsops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_vfsops.c,v
retrieving revision 1.272
diff -u -r1.272 ffs_vfsops.c
--- sys/ufs/ffs/ffs_vfsops.c    3 Jan 2012 15:44:00 -0000       1.272
+++ sys/ufs/ffs/ffs_vfsops.c    15 Jan 2012 19:26:23 -0000
@@ -1797,6 +1797,7 @@
        ip->i_fs = fs = ump->um_fs;
        ip->i_dev = dev;
        ip->i_number = ino;
+       ip->i_ffs_ea_refs = 0;
 #if defined(QUOTA) || defined(QUOTA2)
        ufsquota_init(ip);
 #endif
@@ -1969,7 +1970,7 @@
 
        error = ffs_getblk(mp->um_devvp,
            fs->fs_sblockloc / DEV_BSIZE, FFS_NOBLK,
-           fs->fs_sbsize, false, &bp);
+           fs->fs_sbsize, 0, &bp);
        if (error)
                return error;
        saveflag = fs->fs_flags & FS_INTERNAL;
@@ -2008,7 +2009,7 @@
                if (i + fs->fs_frag > blks)
                        size = (blks - i) * fs->fs_fsize;
                error = ffs_getblk(mp->um_devvp, fsbtodb(fs, fs->fs_csaddr + i),
-                   FFS_NOBLK, size, false, &bp);
+                   FFS_NOBLK, size, 0, &bp);
                if (error)
                        break;
 #ifdef FFS_EI
Index: sys/ufs/ffs/ffs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_vnops.c,v
retrieving revision 1.120
diff -u -r1.120 ffs_vnops.c
--- sys/ufs/ffs/ffs_vnops.c     27 Jun 2011 16:34:47 -0000      1.120
+++ sys/ufs/ffs/ffs_vnops.c     15 Jan 2012 19:26:23 -0000
@@ -76,6 +76,7 @@
 #include <sys/stat.h>
 #include <sys/buf.h>
 #include <sys/event.h>
+#include <sys/kauth.h>
 #include <sys/proc.h>
 #include <sys/mount.h>
 #include <sys/vnode.h>
@@ -84,6 +85,8 @@
 #include <sys/kauth.h>
 #include <sys/wapbl.h>
 #include <sys/fstrans.h>
+#include <sys/extattr.h>
+#include <sys/malloc.h>
 
 #include <miscfs/fifofs/fifo.h>
 #include <miscfs/genfs/genfs.h>
@@ -100,6 +103,19 @@
 
 #include <uvm/uvm.h>
 
+static MALLOC_JUSTDEFINE(M_FFS_EXTATTR, "ufs_extattr","ufs extended 
attribute");
+
+static int  ffs_extread(struct vnode *, struct uio *, int);
+static int  ffs_extwrite(struct vnode *, struct uio *, int ,
+                               kauth_cred_t);
+static int  ffs_findextattr(u_char *, u_int, int, const char *, u_char **,
+                               u_char **);
+static int  ffs_rdextattr(u_char **, struct vnode *, int);
+static void ffs_lock_ea(struct vnode *);
+static void ffs_unlock_ea(struct vnode *);
+static int  ffs_open_ea(struct vnode *, kauth_cred_t);
+static int  ffs_close_ea(struct vnode *, int, kauth_cred_t);
+
 /* Global vfs data structures for ufs. */
 int (**ffs_vnodeop_p)(void *);
 const struct vnodeopv_entry_desc ffs_vnodeop_entries[] = {
@@ -573,6 +589,7 @@
                fstrans_done(mp);
                return (error);
        }
+       KASSERT(ip->i_ffs_ea_refs == 0);
        if (ip->i_din.ffs1_din != NULL) {
                if (ump->um_fstype == UFS1)
                        pool_cache_put(ffs_dinode1_cache, ip->i_din.ffs1_din);
@@ -626,15 +643,18 @@
                kauth_cred_t a_cred;
                struct proc *a_p;
        } */ *ap = v;
-       struct inode *ip = VTOI(ap->a_vp);
+       struct vnode *vp = ap->a_vp;
+       struct inode *ip = VTOI(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);
+       if (vp->v_type == VCHR || vp->v_type == VBLK)
+               return (EOPNOTSUPP);
+
+       return (ffs_open_ea(vp, ap->a_cred));
 }
 
 int
@@ -646,15 +666,21 @@
                kauth_cred_t a_cred;
                struct proc *a_p;
        } */ *ap = v;
-       struct inode *ip = VTOI(ap->a_vp);
+       struct vnode *vp = ap->a_vp;
+       struct inode *ip = VTOI(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);
+       if (vp->v_type == VCHR || vp->v_type == VBLK)
+               return (EOPNOTSUPP);
+
+       if (ap->a_commit && (vp->v_mount->mnt_flag & MNT_RDONLY))
+               return (EROFS);
+
+       return (ffs_close_ea(vp, ap->a_commit, ap->a_cred));
 }
 
 int
@@ -672,11 +698,13 @@
        struct vnode *vp = ap->a_vp;
        struct inode *ip = VTOI(vp);
        struct fs *fs = ip->i_fs;
+       int error;
+       u_char *eae, *p;
+       size_t easize;
+       ssize_t ealen;
 
        if (fs->fs_magic == FS_UFS1_MAGIC) {
 #ifdef UFS_EXTATTR
-               int error;
-
                fstrans_start(vp->v_mount, FSTRANS_SHARED);
                error = ufs_getextattr(ap);
                fstrans_done(vp->v_mount);
@@ -686,8 +714,33 @@
 #endif
        }
 
-       /* XXX Not implemented for UFS2 file systems. */
-       return (EOPNOTSUPP);
+       if (vp->v_type == VCHR || vp->v_type == VBLK)
+               return (EOPNOTSUPP);
+
+       error = extattr_check_cred(vp, ap->a_attrnamespace,
+           ap->a_cred, curlwp, IREAD);
+       if (error)
+               return (error);
+
+       error = ffs_open_ea(vp, ap->a_cred);
+       if (error)
+               return (error);
+
+       eae = ip->i_ffs_ea_area;
+       easize = ip->i_ffs_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);
+       }
+               error = ENOATTR;
+       ffs_close_ea(ap->a_vp, 0, ap->a_cred);
+       return error;
 }
 
 int
@@ -704,11 +757,13 @@
        struct vnode *vp = ap->a_vp;
        struct inode *ip = VTOI(vp);
        struct fs *fs = ip->i_fs;
+       uint32_t ealength, ul;
+       int error, i;
+       ssize_t ealen, olen, eapad1, eapad2, easize;
+       u_char *eae, *p;
 
        if (fs->fs_magic == FS_UFS1_MAGIC) {
 #ifdef UFS_EXTATTR
-               int error;
-
                fstrans_start(vp->v_mount, FSTRANS_SHARED);
                error = ufs_setextattr(ap);
                fstrans_done(vp->v_mount);
@@ -718,8 +773,95 @@
 #endif
        }
 
-       /* XXX Not implemented for UFS2 file systems. */
-       return (EOPNOTSUPP);
+       if (vp->v_type == VCHR || vp->v_type == VBLK)
+               return (EOPNOTSUPP);
+
+       if (strlen(ap->a_name) == 0)
+               return (EINVAL);
+
+       if (ap->a_uio == NULL)
+               return (EOPNOTSUPP);
+
+       if (vp->v_mount->mnt_flag & MNT_RDONLY)
+               return (EROFS);
+
+       error = extattr_check_cred(vp, ap->a_attrnamespace,
+           ap->a_cred, curlwp, IWRITE);
+       if (error) {
+               /*
+                * ffs_lock_ea is not needed there, because the vnode
+                * must be exclusively locked.
+                */
+               if (ip->i_ffs_ea_area != NULL && ip->i_ffs_ea_error == 0)
+                       ip->i_ffs_ea_error = error;
+               return (error);
+       }
+       error = ffs_open_ea(vp, ap->a_cred);
+       if (error)
+               return (error);
+
+       ealen = ap->a_uio->uio_resid;
+       ealength = sizeof(uint32_t) + 3 + strlen(ap->a_name);
+       eapad1 = 8 - (ealength % 8);
+       if (eapad1 == 8)
+               eapad1 = 0;
+       eapad2 = 8 - (ealen % 8);
+       if (eapad2 == 8)
+               eapad2 = 0;
+       ealength += eapad1 + ealen + eapad2;
+
+       eae = malloc(ip->i_ffs_ea_len + ealength, M_FFS_EXTATTR, M_WAITOK);
+       memcpy(eae, ip->i_ffs_ea_area, ip->i_ffs_ea_len);
+       easize = ip->i_ffs_ea_len;
+
+       olen = ffs_findextattr(eae, easize,
+           ap->a_attrnamespace, ap->a_name, &p, NULL);
+
+       if (olen == -1) {
+               /* new, append at end */
+               p = eae + easize;
+               easize += ealength;
+       } else {
+               memcpy(&ul, p, sizeof(ul));
+               i = p - eae + ul;
+               if (ul != ealength) {
+                       memcpy(p + ealength, p + ul, easize - i);
+                       easize += (ealength - ul);
+               }
+       }
+       if (easize > NXADDR * fs->fs_bsize) {
+               free(eae, M_FFS_EXTATTR);
+               ffs_close_ea(vp, 0, ap->a_cred);
+               if (ip->i_ffs_ea_area != NULL && ip->i_ffs_ea_error == 0)
+                       ip->i_ffs_ea_error = ENOSPC;
+               return (ENOSPC);
+       }
+       memcpy(p, &ealength, sizeof(ealength));
+       p += sizeof(ealength);
+       *p++ = ap->a_attrnamespace;
+       *p++ = eapad2;
+       *p++ = strlen(ap->a_name);
+       strcpy(p, ap->a_name);
+       p += strlen(ap->a_name);
+       memset(p, 0, eapad1);
+       p += eapad1;
+       error = uiomove(p, ealen, ap->a_uio);
+       if (error) {
+               free(eae, M_FFS_EXTATTR);
+               ffs_close_ea(vp, 0, ap->a_cred);
+               if (ip->i_ffs_ea_area != NULL && ip->i_ffs_ea_error == 0)
+                       ip->i_ffs_ea_error = error;
+               return(error);
+       }
+       p += ealen;             
+       memset(p, 0, eapad2);
+
+       p = ip->i_ffs_ea_area;
+       ip->i_ffs_ea_area = eae;
+       ip->i_ffs_ea_len = easize;
+       free(p, M_FFS_EXTATTR);
+       error = ffs_close_ea(vp, 1, ap->a_cred);
+       return(error);
 }
 
 int
@@ -733,14 +875,17 @@
                kauth_cred_t a_cred;
                struct proc *a_p;
        } */ *ap = v;
-       struct inode *ip = VTOI(ap->a_vp);
+       struct vnode *vp = ap->a_vp;
+       struct inode *ip = VTOI(vp);
        struct fs *fs = ip->i_fs;
+       u_char *eae, *p, *pe, *pn;
+       size_t easize;
+       uint32_t ul;
+       ssize_t ealen;
+       int error;
 
        if (fs->fs_magic == FS_UFS1_MAGIC) {
 #ifdef UFS_EXTATTR
-               struct vnode *vp = ap->a_vp;
-               int error;
-
                fstrans_start(vp->v_mount, FSTRANS_SHARED);
                error = ufs_listextattr(ap);
                fstrans_done(vp->v_mount);
@@ -750,8 +895,44 @@
 #endif
        }
 
-       /* XXX Not implemented for UFS2 file systems. */
-       return (EOPNOTSUPP);
+       if (vp->v_type == VCHR || vp->v_type == VBLK)
+               return (EOPNOTSUPP);
+
+       error = extattr_check_cred(vp, ap->a_attrnamespace,
+           ap->a_cred, curlwp, IREAD);
+       if (error)
+               return (error);
+
+       error = ffs_open_ea(vp, ap->a_cred);
+       if (error)
+               return (error);
+
+               eae = ip->i_ffs_ea_area;
+       easize = ip->i_ffs_ea_len;      
+
+       error = 0;
+       if (ap->a_size != NULL)
+               *ap->a_size = 0;
+       pe = eae + easize;
+
+       for(p = eae; error == 0 && p < pe; p = pn) {
+               memcpy(&ul, p, sizeof(ul));
+               pn = p + ul;
+               if (pn > pe)
+                       break;
+               p += sizeof(ul);
+               if (*p++ != ap->a_attrnamespace)
+                       continue;
+               p++;    /* pad2 */
+               ealen = *p;
+               if (ap->a_size != NULL) {
+                       *ap->a_size += ealen + 1;
+               } else if (ap->a_uio != NULL) {
+                       error = uiomove(p, ealen + 1, ap->a_uio);
+               }
+       }
+       ffs_close_ea(vp, 0, ap->a_cred);
+       return(error);
 }
 
 int
@@ -766,11 +947,14 @@
        struct vnode *vp = ap->a_vp;
        struct inode *ip = VTOI(vp);
        struct fs *fs = ip->i_fs;
+       uint32_t ealength, ul;
+       ssize_t ealen, olen, eapad1, eapad2, easize;
+       int error, i;
+       u_char *eae, *p;
+
 
        if (fs->fs_magic == FS_UFS1_MAGIC) {
 #ifdef UFS_EXTATTR
-               int error;
-
                fstrans_start(vp->v_mount, FSTRANS_SHARED);
                error = ufs_deleteextattr(ap);
                fstrans_done(vp->v_mount);
@@ -779,7 +963,501 @@
                return (EOPNOTSUPP);
 #endif
        }
+       if (vp->v_type == VCHR || vp->v_type == VBLK)
+               return (EOPNOTSUPP);
+
+       if (strlen(ap->a_name) == 0)
+               return (EINVAL);
+
+       if (vp->v_mount->mnt_flag & MNT_RDONLY)
+               return (EROFS);
+
+       error = extattr_check_cred(vp, ap->a_attrnamespace,
+           ap->a_cred, curlwp, IWRITE);
+       if (error) {
+                               
+               /*
+                * ffs_lock_ea is not needed there, because the vnode
+                * must be exclusively locked.
+                */
+               if (ip->i_ffs_ea_area != NULL && ip->i_ffs_ea_error == 0)
+                       ip->i_ffs_ea_error = error;
+               return (error);
+       }
+                               
+       error = ffs_open_ea(vp, ap->a_cred);
+       if (error)
+               return (error);
+
+       ealength = eapad1 = ealen = eapad2 = 0;
+
+       eae = malloc(ip->i_ffs_ea_len, M_FFS_EXTATTR, M_WAITOK);
+       memcpy(eae, ip->i_ffs_ea_area, ip->i_ffs_ea_len);
+       easize = ip->i_ffs_ea_len;
+
+       olen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name,
+           &p, NULL);
+       if (olen == -1) {
+               /* delete but nonexistent */
+               free(eae, M_FFS_EXTATTR);
+               ffs_close_ea(vp, 0, ap->a_cred);
+               return(ENOATTR);
+       }
+       memcpy(&ul, p, sizeof ul);
+       i = p - eae + ul;
+       if (ul != ealength) {
+               memmove(p + ealength, p + ul, easize - i);
+               easize += (ealength - ul);
+       }
+       KASSERT(easize <= NXADDR * fs->fs_bsize);
+       p = ip->i_ffs_ea_area;  
+       ip->i_ffs_ea_area = eae;
+       ip->i_ffs_ea_len = easize;
+       free(p, M_FFS_EXTATTR);
+       error = ffs_close_ea(vp, 1, ap->a_cred);
+       return(error);
+}
+
+
+/*
+ * 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;
+       daddr_t lbn, nextlbn;
+       off_t bytesinfile;
+       long size, xfersize, blkoffset;
+       int error, orig_resid;
+
+       ip = VTOI(vp);
+       fs = ip->i_fs;
+       dp = ip->i_din.ffs2_din;
+
+#ifdef DIAGNOSTIC
+       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, lbn, size, NOCRED, B_ALTDATA, &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.
+                        */
+                       int nextsize = sblksize(fs, dp->di_extsize, nextlbn);
+
+                       error = breadn(vp, lbn,
+                           size, &nextlbn, &nextsize, 1, NOCRED,
+                           B_ALTDATA, &bp);
+               }
+               if (error) {
+                       brelse(bp, 0);
+                       bp = NULL;
+                       break;
+               }
+
+#ifdef notyet
+               /*
+                * If IO_DIRECT then set B_DIRECT for the buffer.  This
+                * will cause us to attempt to release the buffer later on
+                * and will cause the buffer cache to attempt to free the
+                * underlying pages.
+                */
+               if (ioflag & IO_DIRECT)
+                       bp->b_flags |= B_DIRECT;
+#endif
+
+               /*
+                * 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;
+
+#ifdef notyet
+               if ((ioflag & (IO_VMIO|IO_DIRECT)) &&
+                  (LIST_EMPTY(&bp->b_dep))) {
+                       /*
+                        * If there are no dependencies, and it's VMIO,
+                        * then we don't need the buf, mark it available
+                        * for freeing.  For non-direct VMIO reads, the VM
+                        * has the data.
+                        */
+                       bp->b_flags |= B_RELBUF;
+                       brelse(bp);
+               } else {
+                       /*
+                        * Otherwise let whoever
+                        * made the request take care of
+                        * freeing it. We just queue
+                        * it onto another list.
+                        */
+                       bqrelse(bp);
+               }
+#else
+               brelse(bp, 0);
+#endif
+       }
+
+       /*
+        * 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) {
+#ifdef notyet
+               if ((ioflag & (IO_VMIO|IO_DIRECT)) &&
+                  (LIST_EMPTY(&bp->b_dep))) {
+                       bp->b_flags |= B_RELBUF;
+                       brelse(bp);
+               } else {
+                       bqrelse(bp);
+               }
+#else
+               brelse(bp, BC_INVAL);
+#endif
+       }
+       return (error);
+}
+
+/*
+ * Extended attribute area writing.
+ */
+static int
+ffs_extwrite(struct vnode *vp, struct uio *uio, int ioflag,
+    kauth_cred_t cred)
+{
+       struct inode *ip;
+       struct ufs2_dinode *dp;
+       struct fs *fs;
+       struct buf *bp;
+       daddr_t lbn;
+       off_t osize;
+       ssize_t blkoffset, resid, size, xfersize;
+       int error, flags;
+
+       ip = VTOI(vp);
+       fs = ip->i_fs;
+       dp = ip->i_din.ffs2_din;
+
+#ifdef DIAGNOSTIC
+       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 (uio->uio_offset + uio->uio_resid > NXADDR * fs->fs_bsize)
+               return (EFBIG);
+
+       resid = uio->uio_resid;
+       osize = dp->di_extsize;
+       flags = (ioflag & IO_SYNC) ? B_SYNC : 0;
+       flags |= B_ALTDATA;
+
+       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;
 
-       /* XXX Not implemented for UFS2 file systems. */
-       return (EOPNOTSUPP);
+               /*
+                * We must perform a read-before-write if the transfer size
+                * does not cover the entire buffer.
+                 */
+               if (fs->fs_bsize > xfersize)
+                       flags |= B_CLRBUF;
+               else
+                       flags &= ~B_CLRBUF;
+               error = ffs_balloc(vp, uio->uio_offset, xfersize,
+                   cred, flags, &bp);
+               if (error != 0)
+                       break;
+#ifdef notyet
+               if (ioflag & IO_DIRECT)
+                       bp->b_flags |= B_DIRECT;
+#endif
+
+               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);
+
+               if (ioflag & IO_SYNC)
+                       (void)bwrite(bp);
+               else
+                       bdwrite(bp);
+               if (error || xfersize == 0)
+                       break;
+               ip->i_flag |= 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 && cred) {
+               if (kauth_authorize_generic(cred,
+                   KAUTH_GENERIC_ISSUSER, NULL)) {
+                       ip->i_mode &= ~(ISUID | ISGID);
+                       DIP_ASSIGN(ip, mode, ip->i_mode);
+               }
+       }
+       if (error) {
+               if (ioflag & IO_UNIT) {
+                       (void)ffs_extattr_truncate(vp, osize,
+                           (ioflag&IO_SYNC), cred);
+                       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);
+       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,
+    u_char **eap, u_char **eac)
+{
+       u_char *p, *pe, *pn, *p0;
+       ssize_t eapad1, eapad2, ealength, ealen, nlen;
+       uint32_t ul;
+
+       pe = ptr + length;
+       nlen = strlen(name);
+
+       for (p = ptr; p < pe; p = pn) {
+               p0 = p;
+               memcpy(&ul, p, sizeof(ul));
+               pn = p + ul;
+               /* make sure this entry is complete */
+               if (pn > pe)
+                       break;
+               p += sizeof(uint32_t);
+               if (*p != nspace)
+                       continue;
+               p++;
+               eapad2 = *p++;
+               if (*p != nlen)
+                       continue;
+               p++;
+               if (bcmp(p, name, nlen))
+                       continue;
+               ealength = sizeof(uint32_t) + 3 + nlen;
+               eapad1 = 8 - (ealength % 8);
+               if (eapad1 == 8)
+                       eapad1 = 0;
+               ealength += eapad1;
+               ealen = ul - ealength - eapad2;
+               p += nlen + eapad1;
+               if (eap != NULL)
+                       *eap = p0;
+               if (eac != NULL)
+                       *eac = p;
+               return (ealen);
+       }
+       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;
+       ssize_t easize;
+       int error;
+       u_char *eae;
+
+       ip = VTOI(vp);
+       fs = ip->i_fs;
+       dp = ip->i_din.ffs2_din;
+       easize = dp->di_extsize;
+       if (easize + extra > NXADDR * fs->fs_bsize)
+               return (EFBIG);
+
+       eae = malloc(easize + extra, M_FFS_EXTATTR, 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;
+       UIO_SETUP_SYSSPACE(&luio);
+       luio.uio_rw = UIO_READ;
+
+       error = ffs_extread(vp, &luio, IO_SYNC);
+       if (error) {
+               free(eae, M_FFS_EXTATTR);
+               return(error);
+       }
+       *p = eae;
+       return (0);
+}
+
+static void
+ffs_lock_ea(struct vnode *vp)
+{
+       KASSERT(VOP_ISLOCKED(vp));
+       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+}
+
+static void
+ffs_unlock_ea(struct vnode *vp)
+{
+       /* nothing */
+}
+
+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_ffs_ea_area != NULL) {
+               ip->i_ffs_ea_refs++;
+               ffs_unlock_ea(vp);
+               return (0);
+       }
+       dp = ip->i_din.ffs2_din;
+       error = ffs_rdextattr(&ip->i_ffs_ea_area, vp, 0);
+       if (error) {
+               ffs_unlock_ea(vp);
+               return (error);
+       }
+       ip->i_ffs_ea_len = dp->di_extsize;
+       ip->i_ffs_ea_error = 0;
+       ip->i_ffs_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_ffs_ea_area == NULL) {
+               ffs_unlock_ea(vp);
+               return (EINVAL);
+       }
+       dp = ip->i_din.ffs2_din;
+       error = ip->i_ffs_ea_error;
+       if (commit && error == 0) {
+               liovec.iov_base = ip->i_ffs_ea_area;
+               liovec.iov_len = ip->i_ffs_ea_len;
+               luio.uio_iov = &liovec;
+               luio.uio_iovcnt = 1;
+               luio.uio_offset = 0;
+               luio.uio_resid = ip->i_ffs_ea_len;
+               UIO_SETUP_SYSSPACE(&luio);
+               luio.uio_rw = UIO_WRITE;
+               if (ip->i_ffs_ea_len < dp->di_extsize)
+                       error = ffs_extattr_truncate(vp, ip->i_ffs_ea_len,
+                           0, cred);
+               if (error == 0)
+                       error = ffs_extwrite(vp, &luio, IO_SYNC, cred);
+       }
+       if (--ip->i_ffs_ea_refs == 0) {
+               free(ip->i_ffs_ea_area, M_TEMP);
+               ip->i_ffs_ea_area = NULL;
+               ip->i_ffs_ea_len = 0;
+               ip->i_ffs_ea_error = 0;
+       }
+       ffs_unlock_ea(vp);
+       return (error);
 }
Index: sys/ufs/ffs/fs.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/fs.h,v
retrieving revision 1.56
diff -u -r1.56 fs.h
--- sys/ufs/ffs/fs.h    6 Mar 2011 17:08:38 -0000       1.56
+++ sys/ufs/ffs/fs.h    15 Jan 2012 19:26:23 -0000
@@ -712,6 +712,10 @@
          ? (fs)->fs_bsize \
          : (fragroundup(fs, blkoff(fs, (size)))))
 
+#define extsize(fs, size, lbn) \
+       (((size) >= ((lbn) + 1) << (fs)->fs_bshift) \
+         ? (fs)->fs_bsize \
+         : (fragroundup(fs, blkoff(fs, (size)))))
 
 /*
  * Number of inodes in a secondary storage block/fragment.
Index: sys/ufs/ufs/inode.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/inode.h,v
retrieving revision 1.59
diff -u -r1.59 inode.h
--- sys/ufs/ufs/inode.h 2 Jan 2012 22:10:45 -0000       1.59
+++ sys/ufs/ufs/inode.h 15 Jan 2012 19:26:23 -0000
@@ -70,6 +70,13 @@
        /* follow two fields are used by contiguous allocation code only. */
        daddr_t ffs_first_data_blk;     /* first data block on disk. */
        daddr_t ffs_first_indir_blk;    /* first indirect block on disk. */
+       /*
+        * Data for extended attribute modification.
+        */
+       u_char    *ffs_ea_area;   /* Pointer to malloced copy of EA area */
+       unsigned  ffs_ea_len;     /* Length of i_ea_area */
+       int       ffs_ea_error;   /* First errno in transaction */
+       int       ffs_ea_refs;    /* Number of users of EA area */
 };
 
 struct ext2fs_inode_ext {
@@ -132,6 +139,10 @@
 #define        i_snapblklist           inode_ext.ffs.ffs_snapblklist
 #define        i_ffs_first_data_blk    inode_ext.ffs.ffs_first_data_blk
 #define        i_ffs_first_indir_blk   inode_ext.ffs.ffs_first_indir_blk
+#define i_ffs_ea_area          inode_ext.ffs.ffs_ea_area
+#define i_ffs_ea_len           inode_ext.ffs.ffs_ea_len
+#define i_ffs_ea_error         inode_ext.ffs.ffs_ea_error
+#define i_ffs_ea_refs          inode_ext.ffs.ffs_ea_refs
 #define        i_e2fs_last_lblk        inode_ext.e2fs.ext2fs_last_lblk
 #define        i_e2fs_last_blk         inode_ext.e2fs.ext2fs_last_blk
        /*
@@ -242,7 +253,7 @@
 #define        IN_ADIROP       0x0200          /* LFS: dirop in progress */
 #define        IN_SPACECOUNTED 0x0400          /* Blocks to be freed in free 
count. */
 #define        IN_PAGING       0x1000          /* LFS: file is on paging queue 
*/
-#define IN_CDIROP       0x4000          /* LFS: dirop completed pending i/o */
+#define IN_CDIROP       0x4000         /* LFS: dirop completed pending i/o */
 #if defined(_KERNEL)
 
 /*
Index: sys/ufs/ufs/ufs_bmap.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_bmap.c,v
retrieving revision 1.49
diff -u -r1.49 ufs_bmap.c
--- sys/ufs/ufs/ufs_bmap.c      6 Mar 2011 17:08:39 -0000       1.49
+++ sys/ufs/ufs/ufs_bmap.c      15 Jan 2012 19:26:23 -0000
@@ -403,3 +403,21 @@
                *nump = numlevels;
        return (0);
 }
+
+int
+ufs_extbmap(struct vnode *vp, daddr_t bn, daddr_t *bnp)
+{
+       struct inode *ip = VTOI(vp);
+       struct ufsmount *ump = ip->i_ump;
+       daddr_t daddr;
+
+       KASSERT(ump->um_fstype == UFS2);
+       KASSERT(bnp != NULL);
+       if (bn >= 0 && bn < NXADDR) {
+               daddr = ufs_rw64(ip->i_ffs2_extb[bn], UFS_MPNEEDSWAP(ump));
+               *bnp = blkptrtodb(ump, daddr);
+       } else {
+               *bnp = -1;
+       }
+       return 0;
+}
Index: sys/ufs/ufs/ufs_extern.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_extern.h,v
retrieving revision 1.66
diff -u -r1.66 ufs_extern.h
--- sys/ufs/ufs/ufs_extern.h    17 Jul 2011 22:07:59 -0000      1.66
+++ sys/ufs/ufs/ufs_extern.h    15 Jan 2012 19:26:23 -0000
@@ -107,6 +107,7 @@
 int    ufs_bmaparray(struct vnode *, daddr_t, daddr_t *, struct indir *,
                      int *, int *, ufs_issequential_callback_t);
 int    ufs_getlbns(struct vnode *, daddr_t, struct indir *, int *);
+int    ufs_extbmap(struct vnode *, daddr_t, daddr_t *);
 
 /* ufs_ihash.c */
 void   ufs_ihashinit(void);
Index: sys/ufs/ufs/ufs_inode.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_inode.c,v
retrieving revision 1.88
diff -u -r1.88 ufs_inode.c
--- sys/ufs/ufs/ufs_inode.c     20 Sep 2011 14:01:33 -0000      1.88
+++ sys/ufs/ufs/ufs_inode.c     15 Jan 2012 19:26:23 -0000
@@ -106,6 +106,10 @@
                if (error)
                        goto out;
                logged = 1;
+               /* trucate extended data if any */
+               error = UFS_TRUNCATE(vp, (off_t)0, IO_EXT, NOCRED);
+               if (error)
+                       goto out;
                if (ip->i_size != 0) {
                        /*
                         * When journaling, only truncate one indirect block
Index: sys/ufs/ufs/ufs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_vnops.c,v
retrieving revision 1.206
diff -u -r1.206 ufs_vnops.c
--- sys/ufs/ufs/ufs_vnops.c     18 Nov 2011 21:18:52 -0000      1.206
+++ sys/ufs/ufs/ufs_vnops.c     15 Jan 2012 19:26:23 -0000
@@ -2500,8 +2500,12 @@
                panic("ufs_strategy: spec");
        KASSERT(bp->b_bcount != 0);
        if (bp->b_blkno == bp->b_lblkno) {
-               error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno,
-                                NULL);
+               if (bp->b_flags & B_ALTDATA) {
+                       error = ufs_extbmap(vp, bp->b_lblkno, &bp->b_blkno);
+               } else {
+                       error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno,
+                                        NULL);
+               }
                if (error) {
                        bp->b_error = error;
                        biodone(bp);
Index: sys/ufs/lfs/lfs_inode.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/lfs/lfs_inode.c,v
retrieving revision 1.126
diff -u -r1.126 lfs_inode.c
--- sys/ufs/lfs/lfs_inode.c     23 Nov 2011 19:42:10 -0000      1.126
+++ sys/ufs/lfs/lfs_inode.c     15 Jan 2012 19:29:26 -0000
@@ -214,6 +214,9 @@
        int usepc;
        struct ufsmount *ump = oip->i_ump;
 
+       if (ioflag & IO_EXT)
+               return 0;
+
        if (ovp->v_type == VCHR || ovp->v_type == VBLK ||
            ovp->v_type == VFIFO || ovp->v_type == VSOCK) {
                KASSERT(oip->i_size == 0);


Home | Main Index | Thread Index | Old Index