return(error);
 }
 
-int
+STATIC int
 xfs_attr_set_int(xfs_inode_t *dp, char *name, int namelen,
                 char *value, int valuelen, int flags)
 {
        if ((error = XFS_QM_DQATTACH(mp, dp, 0)))
                return (error);
 
+       /*
+        * Determine space new attribute will use, and if it would be
+        * "local" or "remote" (note: local != inline).
+        */
+       size = xfs_attr_leaf_newentsize(namelen, valuelen,
+                                       mp->m_sb.sb_blocksize, &local);
+
        /*
         * If the inode doesn't have an attribute fork, add one.
         * (inode must not be locked when we call this routine)
         */
        if (XFS_IFORK_Q(dp) == 0) {
-               error = xfs_bmap_add_attrfork(dp, rsvd);
-               if (error)
+               if ((error = xfs_bmap_add_attrfork(dp, size, rsvd)))
                        return(error);
        }
 
        args.firstblock = &firstblock;
        args.flist = &flist;
        args.whichfork = XFS_ATTR_FORK;
+       args.addname = 1;
        args.oknoent = 1;
 
-       /* Determine space new attribute will use, and if it will be inline
-        * or out of line.
-        */
-       size = xfs_attr_leaf_newentsize(namelen, valuelen,
-                                       mp->m_sb.sb_blocksize, &local);
-
        nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK);
        if (local) {
                if (size > (mp->m_sb.sb_blocksize >> 1)) {
                 * Build initial attribute list (if required).
                 */
                if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS)
-                       (void)xfs_attr_shortform_create(&args);
+                       xfs_attr_shortform_create(&args);
 
                /*
                 * Try to add the attr to the attribute list in
  * Generic handler routine to remove a name from an attribute list.
  * Transitions attribute list from Btree to shortform as necessary.
  */
-int
+STATIC int
 xfs_attr_remove_int(xfs_inode_t *dp, char *name, int namelen, int flags)
 {
        xfs_da_args_t   args;
                                      XFS_ATTRRM_LOG_COUNT))) {
                xfs_trans_cancel(args.trans, 0);
                return(error);
-
        }
 
        xfs_ilock(dp, XFS_ILOCK_EXCL);
 STATIC int
 xfs_attr_shortform_addname(xfs_da_args_t *args)
 {
-       int newsize, retval;
+       int newsize, forkoff, retval;
 
        retval = xfs_attr_shortform_lookup(args);
        if ((args->flags & ATTR_REPLACE) && (retval == ENOATTR)) {
                ASSERT(retval == 0);
        }
 
+       if (args->namelen >= XFS_ATTR_SF_ENTSIZE_MAX ||
+           args->valuelen >= XFS_ATTR_SF_ENTSIZE_MAX)
+               return(XFS_ERROR(ENOSPC));
+
        newsize = XFS_ATTR_SF_TOTSIZE(args->dp);
        newsize += XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
-       if ((newsize <= XFS_IFORK_ASIZE(args->dp)) &&
-           (args->namelen < XFS_ATTR_SF_ENTSIZE_MAX) &&
-           (args->valuelen < XFS_ATTR_SF_ENTSIZE_MAX)) {
-               retval = xfs_attr_shortform_add(args);
-               ASSERT(retval == 0);
-       } else {
+
+       forkoff = xfs_attr_shortform_bytesfit(args->dp, newsize);
+       if (!forkoff)
                return(XFS_ERROR(ENOSPC));
-       }
+
+       xfs_attr_shortform_add(args, forkoff);
        return(0);
 }
 
 {
        xfs_inode_t *dp;
        xfs_dabuf_t *bp;
-       int retval, error, committed;
+       int retval, error, committed, forkoff;
 
        /*
         * Read the (only) block in the attribute list in.
                /*
                 * If the result is small enough, shrink it all into the inode.
                 */
-               if (xfs_attr_shortform_allfit(bp, dp)) {
+               if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
                        XFS_BMAP_INIT(args->flist, args->firstblock);
-                       error = xfs_attr_leaf_to_shortform(bp, args);
+                       error = xfs_attr_leaf_to_shortform(bp, args, forkoff);
                        /* bp is gone due to xfs_da_shrink_inode */
                        if (!error) {
                                error = xfs_bmap_finish(&args->trans,
 {
        xfs_inode_t *dp;
        xfs_dabuf_t *bp;
-       int committed;
-       int error;
+       int error, committed, forkoff;
 
        /*
         * Remove the attribute.
        /*
         * If the result is small enough, shrink it all into the inode.
         */
-       if (xfs_attr_shortform_allfit(bp, dp)) {
+       if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
                XFS_BMAP_INIT(args->flist, args->firstblock);
-               error = xfs_attr_leaf_to_shortform(bp, args);
+               error = xfs_attr_leaf_to_shortform(bp, args, forkoff);
                /* bp is gone due to xfs_da_shrink_inode */
                if (!error) {
                        error = xfs_bmap_finish(&args->trans, args->flist,
        xfs_da_state_blk_t *blk;
        xfs_inode_t *dp;
        xfs_dabuf_t *bp;
-       int retval, error, committed;
+       int retval, error, committed, forkoff;
 
        /*
         * Tie a string around our finger to remind us where we are.
                                      bp->data)->hdr.info.magic, ARCH_CONVERT)
                                                       == XFS_ATTR_LEAF_MAGIC);
 
-               if (xfs_attr_shortform_allfit(bp, dp)) {
+               if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
                        XFS_BMAP_INIT(args->flist, args->firstblock);
-                       error = xfs_attr_leaf_to_shortform(bp, args);
+                       error = xfs_attr_leaf_to_shortform(bp, args, forkoff);
                        /* bp is gone due to xfs_da_shrink_inode */
                        if (!error) {
                                error = xfs_bmap_finish(&args->trans,
 
 
 
 /*========================================================================
- * External routines when dirsize < XFS_LITINO(mp).
+ * External routines when attribute fork size < XFS_LITINO(mp).
  *========================================================================*/
 
 /*
- * Create the initial contents of a shortform attribute list.
+ * Query whether the requested number of additional bytes of extended
+ * attribute space will be able to fit inline.
+ * Returns zero if not, else the di_forkoff fork offset to be used in the
+ * literal area for attribute data once the new bytes have been added.
+ *
+ * di_forkoff must be 8 byte aligned, hence is stored as a >>3 value;
+ * special case for dev/uuid inodes, they have fixed size data forks.
  */
 int
+xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes)
+{
+       int offset;
+       int minforkoff; /* lower limit on valid forkoff locations */
+       int maxforkoff; /* upper limit on valid forkoff locations */
+       xfs_mount_t *mp = dp->i_mount;
+
+       if (unlikely(mp->m_flags & XFS_MOUNT_COMPAT_ATTR)) {
+               if (bytes <= XFS_IFORK_ASIZE(dp))
+                       return mp->m_attroffset >> 3;
+               return 0;
+       }
+
+       offset = (XFS_LITINO(mp) - bytes) >> 3; /* rounded down */
+
+       switch (dp->i_d.di_format) {
+       case XFS_DINODE_FMT_DEV:
+               minforkoff = roundup(sizeof(xfs_dev_t), 8) >> 3;
+               return (offset >= minforkoff) ? minforkoff : 0;
+       case XFS_DINODE_FMT_UUID:
+               minforkoff = roundup(sizeof(uuid_t), 8) >> 3;
+               return (offset >= minforkoff) ? minforkoff : 0;
+       }
+
+       /* data fork btree root can have at least this many key/ptr pairs */
+       minforkoff = MAX(dp->i_df.if_bytes, XFS_BMDR_SPACE_CALC(MINDBTPTRS));
+       minforkoff = roundup(minforkoff, 8) >> 3;
+
+       /* attr fork btree root can have at least this many key/ptr pairs */
+       maxforkoff = XFS_LITINO(mp) - XFS_BMDR_SPACE_CALC(MINABTPTRS);
+       maxforkoff = maxforkoff >> 3;   /* rounded down */
+
+       if (offset >= minforkoff && offset < maxforkoff)
+               return offset;
+       if (offset >= maxforkoff)
+               return maxforkoff;
+       return 0;
+}
+
+/*
+ * Switch on the ATTR2 superblock bit (implies also FEATURES2)
+ */
+STATIC void
+xfs_sbversion_add_attr2(xfs_mount_t *mp, xfs_trans_t *tp)
+{
+       unsigned long s;
+
+       if (!(mp->m_flags & XFS_MOUNT_COMPAT_ATTR) &&
+           !(XFS_SB_VERSION_HASATTR2(&mp->m_sb))) {
+               s = XFS_SB_LOCK(mp);
+               if (!XFS_SB_VERSION_HASATTR2(&mp->m_sb)) {
+                       XFS_SB_VERSION_ADDATTR2(&mp->m_sb);
+                       XFS_SB_UNLOCK(mp, s);
+                       xfs_mod_sb(tp, XFS_SB_VERSIONNUM | XFS_SB_FEATURES2);
+               } else
+                       XFS_SB_UNLOCK(mp, s);
+       }
+}
+
+/*
+ * Create the initial contents of a shortform attribute list.
+ */
+void
 xfs_attr_shortform_create(xfs_da_args_t *args)
 {
        xfs_attr_sf_hdr_t *hdr;
        hdr->count = 0;
        INT_SET(hdr->totsize, ARCH_CONVERT, sizeof(*hdr));
        xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
-       return(0);
 }
 
 /*
  * Add a name/value pair to the shortform attribute list.
  * Overflow from the inode has already been checked for.
  */
-int
-xfs_attr_shortform_add(xfs_da_args_t *args)
+void
+xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
 {
        xfs_attr_shortform_t *sf;
        xfs_attr_sf_entry_t *sfe;
        int i, offset, size;
+       xfs_mount_t *mp;
        xfs_inode_t *dp;
        xfs_ifork_t *ifp;
 
        dp = args->dp;
+       mp = dp->i_mount;
+       dp->i_d.di_forkoff = forkoff;
+       dp->i_df.if_ext_max =
+               XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+       dp->i_afp->if_ext_max =
+               XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+
        ifp = dp->i_afp;
        ASSERT(ifp->if_flags & XFS_IFINLINE);
        sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
        sfe = &sf->list[0];
        for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT);
                                sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
+#ifdef DEBUG
                if (sfe->namelen != args->namelen)
                        continue;
                if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
                if (((args->flags & ATTR_ROOT) != 0) !=
                    ((sfe->flags & XFS_ATTR_ROOT) != 0))
                        continue;
-               return(XFS_ERROR(EEXIST));
+               ASSERT(0);
+#endif
        }
 
        offset = (char *)sfe - (char *)sf;
        INT_MOD(sf->hdr.totsize, ARCH_CONVERT, size);
        xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
 
-       return(0);
+       xfs_sbversion_add_attr2(mp, args->trans);
 }
 
 /*
- * Remove a name from the shortform attribute list structure.
+ * Remove an attribute from the shortform attribute list structure.
  */
 int
 xfs_attr_shortform_remove(xfs_da_args_t *args)
        xfs_attr_shortform_t *sf;
        xfs_attr_sf_entry_t *sfe;
        int base, size=0, end, totsize, i;
+       xfs_mount_t *mp;
        xfs_inode_t *dp;
 
-       /*
-        * Remove the attribute.
-        */
        dp = args->dp;
+       mp = dp->i_mount;
        base = sizeof(xfs_attr_sf_hdr_t);
        sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
        sfe = &sf->list[0];
-       for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT);
-                               sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
+       end = INT_GET(sf->hdr.count, ARCH_CONVERT);
+       for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
                                        base += size, i++) {
                size = XFS_ATTR_SF_ENTSIZE(sfe);
                if (sfe->namelen != args->namelen)
                        continue;
                break;
        }
-       if (i == INT_GET(sf->hdr.count, ARCH_CONVERT))
+       if (i == end)
                return(XFS_ERROR(ENOATTR));
 
+       /*
+        * Fix up the attribute fork data, covering the hole
+        */
        end = base + size;
        totsize = INT_GET(sf->hdr.totsize, ARCH_CONVERT);
-       if (end != totsize) {
-               memmove(&((char *)sf)[base], &((char *)sf)[end],
-                                                       totsize - end);
-       }
+       if (end != totsize)
+               memmove(&((char *)sf)[base], &((char *)sf)[end], totsize - end);
        INT_MOD(sf->hdr.count, ARCH_CONVERT, -1);
        INT_MOD(sf->hdr.totsize, ARCH_CONVERT, -size);
-       xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
-       xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
+
+       /*
+        * Fix up the start offset of the attribute fork
+        */
+       totsize -= size;
+       if (totsize == sizeof(xfs_attr_sf_hdr_t) && !args->addname) {
+               /*
+                * Last attribute now removed, revert to original
+                * inode format making all literal area available
+                * to the data fork once more.
+                */
+               xfs_idestroy_fork(dp, XFS_ATTR_FORK);
+               dp->i_d.di_forkoff = 0;
+               dp->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+               ASSERT(dp->i_d.di_anextents == 0);
+               ASSERT(dp->i_afp == NULL);
+               dp->i_df.if_ext_max =
+                       XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+               xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
+       } else {
+               xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
+               dp->i_d.di_forkoff = xfs_attr_shortform_bytesfit(dp, totsize);
+               ASSERT(dp->i_d.di_forkoff);
+               ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) || args->addname);
+               dp->i_afp->if_ext_max =
+                       XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+               dp->i_df.if_ext_max =
+                       XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+               xfs_trans_log_inode(args->trans, dp,
+                                       XFS_ILOG_CORE | XFS_ILOG_ADATA);
+       }
+
+       xfs_sbversion_add_attr2(mp, args->trans);
 
        return(0);
 }
                                + name_loc->namelen
                                + INT_GET(name_loc->valuelen, ARCH_CONVERT);
        }
-       return( bytes < XFS_IFORK_ASIZE(dp) );
+       if (bytes == sizeof(struct xfs_attr_sf_hdr))
+               return(-1);
+       return(xfs_attr_shortform_bytesfit(dp, bytes));
 }
 
 /*
  * Convert a leaf attribute list to shortform attribute list
  */
 int
-xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args)
+xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args, int forkoff)
 {
        xfs_attr_leafblock_t *leaf;
        xfs_attr_leaf_entry_t *entry;
        error = xfs_da_shrink_inode(args, 0, bp);
        if (error)
                goto out;
-       error = xfs_attr_shortform_create(args);
-       if (error)
+
+       if (forkoff == -1) {
+               /*
+                * Last attribute was removed, revert to original
+                * inode format making all literal area available
+                * to the data fork once more.
+                */
+               xfs_idestroy_fork(dp, XFS_ATTR_FORK);
+               dp->i_d.di_forkoff = 0;
+               dp->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+               ASSERT(dp->i_d.di_anextents == 0);
+               ASSERT(dp->i_afp == NULL);
+               dp->i_df.if_ext_max =
+                       XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+               xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
                goto out;
+       }
+
+       xfs_attr_shortform_create(args);
 
        /*
         * Copy the attributes
                nargs.hashval = INT_GET(entry->hashval, ARCH_CONVERT);
                nargs.flags = (entry->flags & XFS_ATTR_SECURE) ? ATTR_SECURE :
                              ((entry->flags & XFS_ATTR_ROOT) ? ATTR_ROOT : 0);
-               xfs_attr_shortform_add(&nargs);
+               xfs_attr_shortform_add(&nargs, forkoff);
        }
        error = 0;
 
 
  *========================================================================*/
 
 /*
- * Internal routines when dirsize < XFS_LITINO(mp).
+ * Internal routines when attribute fork size < XFS_LITINO(mp).
  */
-int    xfs_attr_shortform_create(struct xfs_da_args *args);
-int    xfs_attr_shortform_add(struct xfs_da_args *add);
+void   xfs_attr_shortform_create(struct xfs_da_args *args);
+void   xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff);
 int    xfs_attr_shortform_lookup(struct xfs_da_args *args);
 int    xfs_attr_shortform_getvalue(struct xfs_da_args *args);
 int    xfs_attr_shortform_to_leaf(struct xfs_da_args *args);
-int    xfs_attr_shortform_remove(struct xfs_da_args *remove);
+int    xfs_attr_shortform_remove(struct xfs_da_args *args);
 int    xfs_attr_shortform_list(struct xfs_attr_list_context *context);
 int    xfs_attr_shortform_allfit(struct xfs_dabuf *bp, struct xfs_inode *dp);
+int    xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes);
+
 
 /*
- * Internal routines when dirsize == XFS_LBSIZE(mp).
+ * Internal routines when attribute fork size == XFS_LBSIZE(mp).
  */
 int    xfs_attr_leaf_to_node(struct xfs_da_args *args);
 int    xfs_attr_leaf_to_shortform(struct xfs_dabuf *bp,
-                                         struct xfs_da_args *args);
+                                  struct xfs_da_args *args, int forkoff);
 int    xfs_attr_leaf_clearflag(struct xfs_da_args *args);
 int    xfs_attr_leaf_setflag(struct xfs_da_args *args);
 int    xfs_attr_leaf_flipflags(xfs_da_args_t *args);
 
 /*
- * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
 #include "xfs_error.h"
 #include "xfs_da_btree.h"
 #include "xfs_dir_leaf.h"
+#include "xfs_attr_leaf.h"
 #include "xfs_bit.h"
 #include "xfs_rw.h"
 #include "xfs_quota.h"
                xfs_bmbt_set_all(&base[to], new);
 }
 
+/*
+ * Helper routine to reset inode di_forkoff field when switching
+ * attribute fork from local to extent format - we reset it where
+ * possible to make space available for inline data fork extents.
+ */
+STATIC void
+xfs_bmap_forkoff_reset(
+       xfs_mount_t     *mp,
+       xfs_inode_t     *ip,
+       int             whichfork)
+{
+       if (whichfork == XFS_ATTR_FORK &&
+           (ip->i_d.di_format != XFS_DINODE_FMT_DEV) &&
+           (ip->i_d.di_format != XFS_DINODE_FMT_UUID) &&
+           ((mp->m_attroffset >> 3) > ip->i_d.di_forkoff)) {
+               ip->i_d.di_forkoff = mp->m_attroffset >> 3;
+               ip->i_df.if_ext_max = XFS_IFORK_DSIZE(ip) /
+                                       (uint)sizeof(xfs_bmbt_rec_t);
+               ip->i_afp->if_ext_max = XFS_IFORK_ASIZE(ip) /
+                                       (uint)sizeof(xfs_bmbt_rec_t);
+       }
+}
+
 /*
  * Convert a local file to an extents file.
  * This code is out of bounds for data forks of regular files,
                memcpy((char *)XFS_BUF_PTR(bp), ifp->if_u1.if_data,
                        ifp->if_bytes);
                xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1);
+               xfs_bmap_forkoff_reset(args.mp, ip, whichfork);
                xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
                xfs_iext_realloc(ip, 1, whichfork);
                ep = ifp->if_u1.if_extents;
                XFS_TRANS_MOD_DQUOT_BYINO(args.mp, tp, ip,
                        XFS_TRANS_DQ_BCOUNT, 1L);
                flags |= XFS_ILOG_FEXT(whichfork);
-       } else
+       } else {
                ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) == 0);
+               xfs_bmap_forkoff_reset(ip->i_mount, ip, whichfork);
+       }
        ifp->if_flags &= ~XFS_IFINLINE;
        ifp->if_flags |= XFS_IFEXTENTS;
        XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
 int                                            /* error code */
 xfs_bmap_add_attrfork(
        xfs_inode_t             *ip,            /* incore inode pointer */
-       int                     rsvd)           /* OK to allocated reserved blocks in trans */
+       int                     size,           /* space new attribute needs */
+       int                     rsvd)           /* xact may use reserved blks */
 {
-       int                     blks;           /* space reservation */
-       int                     committed;      /* xaction was committed */
-       int                     error;          /* error return value */
        xfs_fsblock_t           firstblock;     /* 1st block/ag allocated */
        xfs_bmap_free_t         flist;          /* freed extent list */
-       int                     logflags;       /* logging flags */
        xfs_mount_t             *mp;            /* mount structure */
-       unsigned long           s;              /* spinlock spl value */
        xfs_trans_t             *tp;            /* transaction pointer */
+       unsigned long           s;              /* spinlock spl value */
+       int                     blks;           /* space reservation */
+       int                     version = 1;    /* superblock attr version */
+       int                     committed;      /* xaction was committed */
+       int                     logflags;       /* logging flags */
+       int                     error;          /* error return value */
 
+       ASSERT(XFS_IFORK_Q(ip) == 0);
        ASSERT(ip->i_df.if_ext_max ==
               XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t));
-       if (XFS_IFORK_Q(ip))
-               return 0;
+
        mp = ip->i_mount;
        ASSERT(!XFS_NOT_DQATTACHED(mp, ip));
        tp = xfs_trans_alloc(mp, XFS_TRANS_ADDAFORK);
        case XFS_DINODE_FMT_LOCAL:
        case XFS_DINODE_FMT_EXTENTS:
        case XFS_DINODE_FMT_BTREE:
-               ip->i_d.di_forkoff = mp->m_attroffset >> 3;
+               ip->i_d.di_forkoff = xfs_attr_shortform_bytesfit(ip, size);
+               if (!ip->i_d.di_forkoff)
+                       ip->i_d.di_forkoff = mp->m_attroffset >> 3;
+               else if (!(mp->m_flags & XFS_MOUNT_COMPAT_ATTR))
+                       version = 2;
                break;
        default:
                ASSERT(0);
                xfs_trans_log_inode(tp, ip, logflags);
        if (error)
                goto error2;
-       if (!XFS_SB_VERSION_HASATTR(&mp->m_sb)) {
+       if (!XFS_SB_VERSION_HASATTR(&mp->m_sb) ||
+          (!XFS_SB_VERSION_HASATTR2(&mp->m_sb) && version == 2)) {
+               logflags = 0;
                s = XFS_SB_LOCK(mp);
                if (!XFS_SB_VERSION_HASATTR(&mp->m_sb)) {
                        XFS_SB_VERSION_ADDATTR(&mp->m_sb);
+                       logflags |= XFS_SB_VERSIONNUM;
+               }
+               if (!XFS_SB_VERSION_HASATTR2(&mp->m_sb) && version == 2) {
+                       XFS_SB_VERSION_ADDATTR2(&mp->m_sb);
+                       logflags |= (XFS_SB_VERSIONNUM | XFS_SB_FEATURES2);
+               }
+               if (logflags) {
                        XFS_SB_UNLOCK(mp, s);
-                       xfs_mod_sb(tp, XFS_SB_VERSIONNUM);
+                       xfs_mod_sb(tp, logflags);
                } else
                        XFS_SB_UNLOCK(mp, s);
        }
         * (a signed 32-bit number, xfs_extnum_t), or by di_anextents
         * (a signed 16-bit number, xfs_aextnum_t).
         */
-       maxleafents = (whichfork == XFS_DATA_FORK) ? MAXEXTNUM : MAXAEXTNUM;
+       if (whichfork == XFS_DATA_FORK) {
+               maxleafents = MAXEXTNUM;
+               sz = (mp->m_flags & XFS_MOUNT_COMPAT_ATTR) ?
+                       mp->m_attroffset : XFS_BMDR_SPACE_CALC(MINDBTPTRS);
+       } else {
+               maxleafents = MAXAEXTNUM;
+               sz = (mp->m_flags & XFS_MOUNT_COMPAT_ATTR) ?
+                       mp->m_sb.sb_inodesize - mp->m_attroffset :
+                       XFS_BMDR_SPACE_CALC(MINABTPTRS);
+       }
+       maxrootrecs = (int)XFS_BTREE_BLOCK_MAXRECS(sz, xfs_bmdr, 0);
        minleafrecs = mp->m_bmap_dmnr[0];
        minnoderecs = mp->m_bmap_dmnr[1];
-       sz = (whichfork == XFS_DATA_FORK) ?
-               mp->m_attroffset :
-               mp->m_sb.sb_inodesize - mp->m_attroffset;
-       maxrootrecs = (int)XFS_BTREE_BLOCK_MAXRECS(sz, xfs_bmdr, 0);
        maxblocks = (maxleafents + minleafrecs - 1) / minleafrecs;
        for (level = 1; maxblocks > 1; level++) {
                if (maxblocks <= maxrootrecs)
 
 /*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
 int                                    /* error code */
 xfs_bmap_add_attrfork(
        struct xfs_inode        *ip,    /* incore inode pointer */
-       int                                     rsvd);  /* flag for reserved block allocation */
+       int                     size,   /* space needed for new attribute */
+       int                     rsvd);  /* flag for reserved block allocation */
 
 /*
  * Add the extent to the list of extents to be free at transaction end.
 
 /*
- * Copyright (c) 2000-2001 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
        uint shortcount, leafcount, count;
 
        mp->m_dirversion = 1;
-       shortcount = (mp->m_attroffset - (uint)sizeof(xfs_dir_sf_hdr_t)) /
-                    (uint)sizeof(xfs_dir_sf_entry_t);
-       leafcount = (XFS_LBSIZE(mp) - (uint)sizeof(xfs_dir_leaf_hdr_t)) /
-                   ((uint)sizeof(xfs_dir_leaf_entry_t) +
-                    (uint)sizeof(xfs_dir_leaf_name_t));
+       if (mp->m_flags & XFS_MOUNT_COMPAT_ATTR) {
+               shortcount = (mp->m_attroffset -
+                               (uint)sizeof(xfs_dir_sf_hdr_t)) /
+                                (uint)sizeof(xfs_dir_sf_entry_t);
+               leafcount = (XFS_LBSIZE(mp) -
+                               (uint)sizeof(xfs_dir_leaf_hdr_t)) /
+                                ((uint)sizeof(xfs_dir_leaf_entry_t) +
+                                 (uint)sizeof(xfs_dir_leaf_name_t));
+       } else {
+               shortcount = (XFS_BMDR_SPACE_CALC(MINABTPTRS) -
+                             (uint)sizeof(xfs_dir_sf_hdr_t)) /
+                              (uint)sizeof(xfs_dir_sf_entry_t);
+               leafcount = (XFS_LBSIZE(mp) -
+                           (uint)sizeof(xfs_dir_leaf_hdr_t)) /
+                            ((uint)sizeof(xfs_dir_leaf_entry_t) +
+                             (uint)sizeof(xfs_dir_leaf_name_t));
+       }
        count = shortcount > leafcount ? shortcount : leafcount;
        mp->m_dircook_elog = xfs_da_log2_roundup(count + 1);
        ASSERT(mp->m_dircook_elog <= mp->m_sb.sb_blocklog);
 
 /*
- * Copyright (c) 1995-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 1995-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2.1 of the GNU Lesser General Public License
 #define XFS_FSOP_GEOM_FLAGS_DIRV2      0x0080  /* directory version 2  */
 #define XFS_FSOP_GEOM_FLAGS_LOGV2      0x0100  /* log format version 2 */
 #define XFS_FSOP_GEOM_FLAGS_SECTOR     0x0200  /* sector sizes >1BB    */
+#define XFS_FSOP_GEOM_FLAGS_ATTR2      0x0400  /* inline attributes rework */
 
 
 /*
 
 /*
- * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
                        (XFS_SB_VERSION_HASDIRV2(&mp->m_sb) ?
                                XFS_FSOP_GEOM_FLAGS_DIRV2 : 0) |
                        (XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ?
-                               XFS_FSOP_GEOM_FLAGS_SECTOR : 0);
+                               XFS_FSOP_GEOM_FLAGS_SECTOR : 0) |
+                       (XFS_SB_VERSION_HASATTR2(&mp->m_sb) ?
+                               XFS_FSOP_GEOM_FLAGS_ATTR2 : 0);
                geo->logsectsize = XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ?
                                mp->m_sb.sb_logsectsize : BBSIZE;
                geo->rtsectsize = mp->m_sb.sb_blocksize;
 
 /*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
 }
 #endif
 
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_ADDATTR2)
+void
+xfs_sb_version_addattr2(xfs_sb_t *sbp)
+{
+       XFS_SB_VERSION_ADDATTR2(sbp);
+}
+#endif
+
 #if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASALIGN)
 int
 xfs_sb_version_hasalign(xfs_sb_t *sbp)
 }
 #endif
 
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASATTR2)
+int
+xfs_sb_version_hasattr2(xfs_sb_t *sbp)
+{
+       return XFS_SB_VERSION_HASATTR2(sbp);
+}
+#endif
 
 /*
- * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
        ASSERT(sbp->sb_inodesize >= 256 && sbp->sb_inodesize <= 2048);
        switch (sbp->sb_inodesize) {
        case 256:
-               mp->m_attroffset = XFS_LITINO(mp) - XFS_BMDR_SPACE_CALC(2);
+               mp->m_attroffset = XFS_LITINO(mp) -
+                                  XFS_BMDR_SPACE_CALC(MINABTPTRS);
                break;
        case 512:
        case 1024:
        case 2048:
-               mp->m_attroffset = XFS_BMDR_SPACE_CALC(12);
+               mp->m_attroffset = XFS_BMDR_SPACE_CALC(6 * MINABTPTRS);
                break;
        default:
                ASSERT(0);
 
 /*
- * Copyright (c) 2000-2001 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
         XFS_SB_VERSION_DALIGNBIT | \
         XFS_SB_VERSION_SHAREDBIT | \
         XFS_SB_VERSION_LOGV2BIT | \
-        XFS_SB_VERSION_SECTORBIT)
+        XFS_SB_VERSION_SECTORBIT | \
+        XFS_SB_VERSION_MOREBITSBIT)
 #define        XFS_SB_VERSION_OKSASHBITS       \
        (XFS_SB_VERSION_NUMBITS | \
         XFS_SB_VERSION_REALFBITS | \
  */
 #define XFS_SB_VERSION2_REALFBITS      0x00ffffff      /* Mask: features */
 #define XFS_SB_VERSION2_RESERVED1BIT   0x00000001
+#define XFS_SB_VERSION2_RESERVED2BIT   0x00000002
+#define XFS_SB_VERSION2_RESERVED4BIT   0x00000004
+#define XFS_SB_VERSION2_ATTR2BIT       0x00000008      /* Inline attr rework */
 #define XFS_SB_VERSION2_SASHFBITS      0xff000000      /* Mask: features that
                                                           require changing
                                                           PROM and SASH */
 
 #define        XFS_SB_VERSION2_OKREALFBITS     \
-       (0)
+       (XFS_SB_VERSION2_ATTR2BIT)
 #define        XFS_SB_VERSION2_OKSASHFBITS     \
        (0)
 #define XFS_SB_VERSION2_OKREALBITS     \
 /*
  * mkfs macro to set up sb_features2 word
  */
-#define        XFS_SB_VERSION2_MKFS(xyz)       \
-       ((xyz) ? 0 : 0)
+#define        XFS_SB_VERSION2_MKFS(resvd1, sbcntr)    0
 
 typedef struct xfs_sb
 {
        __uint8_t       sb_logsectlog;  /* log2 of the log sector size */
        __uint16_t      sb_logsectsize; /* sector size for the log, bytes */
        __uint32_t      sb_logsunit;    /* stripe unit size for the log */
-       __uint32_t      sb_features2;   /* additonal feature bits */
+       __uint32_t      sb_features2;   /* additional feature bits */
 } xfs_sb_t;
 
 /*
 #define XFS_SB_SHARED_VN       XFS_SB_MVAL(SHARED_VN)
 #define XFS_SB_UNIT            XFS_SB_MVAL(UNIT)
 #define XFS_SB_WIDTH           XFS_SB_MVAL(WIDTH)
+#define XFS_SB_FEATURES2       XFS_SB_MVAL(FEATURES2)
 #define        XFS_SB_NUM_BITS         ((int)XFS_SBS_FIELDCOUNT)
 #define        XFS_SB_ALL_BITS         ((1LL << XFS_SB_NUM_BITS) - 1)
 #define        XFS_SB_MOD_BITS         \
        (XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \
         XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \
-        XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH)
+        XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \
+        XFS_SB_FEATURES2)
+
 
 /*
  * Misc. Flags - warning - these will be cleared by xfs_repair unless
 /*
  * sb_features2 bit version macros.
  *
- * For example, for a bit defined as XFS_SB_VERSION2_YBIT, has a macro:
+ * For example, for a bit defined as XFS_SB_VERSION2_FUNBIT, has a macro:
  *
- * SB_VERSION_HASYBIT(xfs_sb_t *sbp)
+ * SB_VERSION_HASFUNBIT(xfs_sb_t *sbp)
  *     ((XFS_SB_VERSION_HASMOREBITS(sbp) &&
- *      ((sbp)->sb_versionnum & XFS_SB_VERSION2_YBIT)
+ *      ((sbp)->sb_features2 & XFS_SB_VERSION2_FUNBIT)
  */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASATTR2)
+int xfs_sb_version_hasattr2(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASATTR2(sbp)   xfs_sb_version_hasattr2(sbp)
+#else
+#define XFS_SB_VERSION_HASATTR2(sbp)   \
+       ((XFS_SB_VERSION_HASMOREBITS(sbp)) &&   \
+        ((sbp)->sb_features2 & XFS_SB_VERSION2_ATTR2BIT))
+#endif
 
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDATTR2)
+void xfs_sb_version_addattr2(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_ADDATTR2(sbp)   xfs_sb_version_addattr2(sbp)
+#else
+#define XFS_SB_VERSION_ADDATTR2(sbp)   \
+       ((sbp)->sb_versionnum = \
+               ((sbp)->sb_versionnum | XFS_SB_VERSION_MOREBITSBIT),    \
+       ((sbp)->sb_features2 =  \
+               ((sbp)->sb_features2 | XFS_SB_VERSION2_ATTR2BIT)))
+#endif
 /*
  * end of superblock version macros
  */
 
 /*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
 #define        MAXEXTNUM       ((xfs_extnum_t)0x7fffffff)      /* signed int */
 #define        MAXAEXTNUM      ((xfs_aextnum_t)0x7fff)         /* signed short */
 
+/*
+ * Min numbers of data/attr fork btree root pointers.
+ */
+#define MINDBTPTRS     3
+#define MINABTPTRS     2
+
 /*
  * MAXNAMELEN is the length (including the terminating null) of
  * the longest permissible file (component) name.
 
 #endif
        if (ap->flags & XFSMNT_NOATIME)
                mp->m_flags |= XFS_MOUNT_NOATIME;
-
        if (ap->flags & XFSMNT_RETERR)
                mp->m_flags |= XFS_MOUNT_RETERR;
-
        if (ap->flags & XFSMNT_NOALIGN)
                mp->m_flags |= XFS_MOUNT_NOALIGN;
-
        if (ap->flags & XFSMNT_SWALLOC)
                mp->m_flags |= XFS_MOUNT_SWALLOC;
-
        if (ap->flags & XFSMNT_OSYNCISOSYNC)
                mp->m_flags |= XFS_MOUNT_OSYNCISOSYNC;
-
        if (ap->flags & XFSMNT_32BITINODES)
                mp->m_flags |= (XFS_MOUNT_32BITINODES | XFS_MOUNT_32BITINOOPT);
 
 
        if (ap->flags & XFSMNT_IHASHSIZE)
                mp->m_flags |= XFS_MOUNT_IHASHSIZE;
-
        if (ap->flags & XFSMNT_IDELETE)
                mp->m_flags |= XFS_MOUNT_IDELETE;
-
        if (ap->flags & XFSMNT_DIRSYNC)
                mp->m_flags |= XFS_MOUNT_DIRSYNC;
-
        if (ap->flags & XFSMNT_COMPAT_IOSIZE)
                mp->m_flags |= XFS_MOUNT_COMPAT_IOSIZE;
+       if (ap->flags & XFSMNT_COMPAT_ATTR)
+               mp->m_flags |= XFS_MOUNT_COMPAT_ATTR;
 
        /*
         * no recovery flag requires a read-only mount
 #define MNTOPT_IHASHSIZE    "ihashsize"    /* size of inode hash table */
 #define MNTOPT_NORECOVERY   "norecovery"   /* don't run XFS recovery */
 #define MNTOPT_BARRIER "barrier"       /* use writer barriers for log write and
-                                          unwritten extent conversion */
+                                        * unwritten extent conversion */
 #define MNTOPT_OSYNCISOSYNC "osyncisosync" /* o_sync is REALLY o_sync */
 #define MNTOPT_64BITINODE   "inode64"  /* inodes can be allocated anywhere */
 #define MNTOPT_IKEEP   "ikeep"         /* do not free empty inode clusters */
 #define MNTOPT_LARGEIO    "largeio"    /* report large I/O sizes in stat() */
 #define MNTOPT_NOLARGEIO   "nolargeio" /* do not report large I/O sizes
                                         * in stat(). */
+#define MNTOPT_ATTR2   "attr2"         /* do use attr2 attribute format */
+#define MNTOPT_NOATTR2 "noattr2"       /* do not use attr2 attribute format */
 
 STATIC unsigned long
 suffix_strtoul(const char *cp, char **endp, unsigned int base)
                        args->flags &= ~XFSMNT_COMPAT_IOSIZE;
                } else if (!strcmp(this_char, MNTOPT_NOLARGEIO)) {
                        args->flags |= XFSMNT_COMPAT_IOSIZE;
+               } else if (!strcmp(this_char, MNTOPT_ATTR2)) {
+                       args->flags &= ~XFSMNT_COMPAT_ATTR;
+               } else if (!strcmp(this_char, MNTOPT_NOATTR2)) {
+                       args->flags |= XFSMNT_COMPAT_ATTR;
                } else if (!strcmp(this_char, "osyncisdsync")) {
                        /* no-op, this is now the default */
 printk("XFS: osyncisdsync is now the default, option is deprecated.\n");