]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
Add xfs_db expert mode commands for set/remove of extended attributes.
authorNathan Scott <nathans@sgi.com>
Fri, 16 Sep 2005 15:19:08 +0000 (15:19 +0000)
committerNathan Scott <nathans@sgi.com>
Fri, 16 Sep 2005 15:19:08 +0000 (15:19 +0000)
Merge of master-melb:xfs-cmds:23838a by kenmcd.

17 files changed:
db/Makefile
db/attrset.c [new file with mode: 0644]
db/attrset.h [new file with mode: 0644]
db/command.c
include/Makefile
include/libxfs.h
include/xfs_dir.h
include/xfs_dir2.h
libxfs/Makefile
libxfs/init.c
libxfs/rdwr.c
libxfs/util.c
libxfs/xfs.h
libxfs/xfs_attr.c [new file with mode: 0644]
libxfs/xfs_attr_leaf.c
libxfs/xfs_bmap.c
libxfs/xfs_da_btree.c

index 698b5489aa60a12e8881e248268e5c14f663a107..0c4810ff1197de57a8b3684c25c84813216ebb96 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2000-2003,2005 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
@@ -40,7 +40,8 @@ HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
        dbread.h debug.h dir.h dir2.h dir2sf.h dirshort.h dquot.h echo.h \
        faddr.h field.h flist.h fprint.h frag.h freesp.h hash.h help.h \
        init.h inobt.h inode.h input.h io.h malloc.h output.h \
-       print.h quit.h sb.h sig.h strvec.h text.h type.h write.h
+       print.h quit.h sb.h sig.h strvec.h text.h type.h write.h \
+       attrset.h
 CFILES = $(HFILES:.h=.c)
 LSRCFILES = xfs_admin.sh xfs_check.sh xfs_ncheck.sh
 LLDLIBS        = $(LIBXFS) $(LIBXLOG) $(LIBUUID)
diff --git a/db/attrset.c b/db/attrset.c
new file mode 100644 (file)
index 0000000..84193c2
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 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
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <xfs/libxfs.h>
+#include "command.h"
+#include "attrset.h"
+#include "io.h"
+#include "output.h"
+#include "type.h"
+#include "init.h"
+#include "inode.h"
+#include "malloc.h"
+
+static int             attr_set_f(int argc, char **argv);
+static int             attr_remove_f(int argc, char **argv);
+static void            attrset_help(void);
+
+static const cmdinfo_t attr_set_cmd =
+       { "attr_set", "aset", attr_set_f, 1, -1, 0,
+         "[-r|-s|-p|-u] [-R|-C] [-v n] name",
+         "set the named attribute on the current inode", attrset_help };
+static const cmdinfo_t attr_remove_cmd =
+       { "attr_remove", "aremove", attr_remove_f, 1, -1, 0,
+         "[-r|-s|-p|-u] name",
+         "remove the named attribute from the current inode", attrset_help };
+
+static void
+attrset_help(void)
+{
+       dbprintf(
+"\n"
+" The 'attr_set' and 'attr_remove' commands provide interfaces for debugging\n"
+" the extended attribute allocation and removal code.\n"
+" Both commands require an attribute name to be specified, and the attr_set\n"
+" command allows an optional value length (-v) to be provided as well.\n"
+" There are 4 namespace flags:\n"
+"  -r -- 'root'\n"
+"  -u -- 'user'                (default)\n"
+"  -s -- 'secure'\n"
+"\n"
+" For attr_set, these options further define the type of set:\n"
+"  -C -- 'create'    - create attribute, fail if it already exists\n"
+"  -R -- 'replace'   - replace attribute, fail if it does not exist\n"
+"\n");
+}
+
+void
+attrset_init(void)
+{
+       if (!expert_mode)
+               return;
+
+       add_command(&attr_set_cmd);
+       add_command(&attr_remove_cmd);
+}
+
+static int
+attr_set_f(
+       int             argc,
+       char            **argv)
+{
+       xfs_inode_t     *ip = NULL;
+       char            *name, *value, *sp;
+       int             c, namelen, valuelen = 0, flags = 0;
+
+       if (cur_typ == NULL) {
+               dbprintf("no current type\n");
+               return 0;
+       }
+       if (cur_typ->typnm != TYP_INODE) {
+               dbprintf("current type is not inode\n");
+               return 0;
+       }
+
+       while ((c = getopt(argc, argv, "rusCRv:")) != EOF) {
+               switch (c) {
+               /* namespaces */
+               case 'r':
+                       flags |= LIBXFS_ATTR_ROOT;
+                       flags &= ~LIBXFS_ATTR_SECURE;
+                       break;
+               case 'u':
+                       flags &= ~(LIBXFS_ATTR_ROOT | LIBXFS_ATTR_SECURE);
+                       break;
+               case 's':
+                       flags |= LIBXFS_ATTR_SECURE;
+                       flags &= ~LIBXFS_ATTR_ROOT;
+                       break;
+
+               /* modifiers */
+               case 'C':
+                       flags |= LIBXFS_ATTR_CREATE;
+                       break;
+               case 'R':
+                       flags |= LIBXFS_ATTR_REPLACE;
+                       break;
+
+               /* value length */
+               case 'v':
+                       valuelen = (int)strtol(optarg, &sp, 0);
+                       if (*sp != '\0' || valuelen < 0 || valuelen > 64*1024) {
+                               dbprintf("bad attr_set valuelen %s\n", optarg);
+                               return 0;
+                       }
+                       break;
+
+               default:
+                       dbprintf("bad option for attr_set command\n");
+                       return 0;
+               }
+       }
+
+       if (optind != argc - 1) {
+               dbprintf("too few options for attr_set (no name given)\n");
+               return 0;
+       }
+
+       name = argv[optind];
+       namelen = strlen(name);
+
+       if (valuelen) {
+               value = (char *)memalign(getpagesize(), valuelen);
+               if (!value) {
+                       dbprintf("cannot allocate buffer (%d)\n", valuelen);
+                       goto out;
+               }
+               memset(value, 0xfeedface, valuelen);
+       } else {
+               value = NULL;
+       }
+
+       if (libxfs_iget(mp, NULL, iocur_top->ino, 0, &ip, 0)) {
+               dbprintf(_("failed to iget inode %llu\n"),
+                       (unsigned long long)iocur_top->ino);
+               goto out;
+       }
+
+       if (libxfs_attr_set_int(ip, name, namelen, value, valuelen, flags)) {
+               dbprintf(_("failed to set attr %s on inode %llu\n"),
+                       name, (unsigned long long)iocur_top->ino);
+               goto out;
+       }
+
+       /* refresh with updated inode contents */
+       set_cur_inode(iocur_top->ino);
+
+out:
+       if (ip)
+               libxfs_iput(ip, 0);
+       if (value)
+               free(value);
+       return 0;
+}
+
+static int
+attr_remove_f(
+       int             argc,
+       char            **argv)
+{
+       xfs_inode_t     *ip = NULL;
+       char            *name;
+       int             c, namelen, flags = 0;
+
+       if (cur_typ == NULL) {
+               dbprintf("no current type\n");
+               return 0;
+       }
+       if (cur_typ->typnm != TYP_INODE) {
+               dbprintf("current type is not inode\n");
+               return 0;
+       }
+
+       while ((c = getopt(argc, argv, "rus")) != EOF) {
+               switch (c) {
+               /* namespaces */
+               case 'r':
+                       flags |= LIBXFS_ATTR_ROOT;
+                       flags &= ~LIBXFS_ATTR_SECURE;
+                       break;
+               case 'u':
+                       flags &= ~(LIBXFS_ATTR_ROOT | LIBXFS_ATTR_SECURE);
+                       break;
+               case 's':
+                       flags |= LIBXFS_ATTR_SECURE;
+                       flags &= ~LIBXFS_ATTR_ROOT;
+                       break;
+
+               default:
+                       dbprintf("bad option for attr_remove command\n");
+                       return 0;
+               }
+       }
+
+       if (optind != argc - 1) {
+               dbprintf("too few options for attr_remove (no name given)\n");
+               return 0;
+       }
+
+       name = argv[optind];
+       namelen = strlen(name);
+
+       if (libxfs_iget(mp, NULL, iocur_top->ino, 0, &ip, 0)) {
+               dbprintf(_("failed to iget inode %llu\n"),
+                       (unsigned long long)iocur_top->ino);
+               goto out;
+       }
+
+       if (libxfs_attr_remove_int(ip, name, namelen, flags)) {
+               dbprintf(_("failed to remove attr %s from inode %llu\n"),
+                       name, (unsigned long long)iocur_top->ino);
+               goto out;
+       }
+
+       /* refresh with updated inode contents */
+       set_cur_inode(iocur_top->ino);
+
+out:
+       if (ip)
+               libxfs_iput(ip, 0);
+       return 0;
+}
diff --git a/db/attrset.h b/db/attrset.h
new file mode 100644 (file)
index 0000000..4e45eb0
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 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
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+extern void    attrset_init(void);
index b2c9f769a3c8b598e4cc727d7bf4fa4dcf53cebf..dcfbdd61830b1c73405fa418b78e2f5ac0a50b41 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2001,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
@@ -32,6 +32,7 @@
 
 #include <xfs/libxfs.h>
 #include "addr.h"
+#include "attrset.h"
 #include "block.h"
 #include "bmap.h"
 #include "check.h"
@@ -130,6 +131,7 @@ init_commands(void)
        agf_init();
        agfl_init();
        agi_init();
+       attrset_init();
        block_init();
        bmap_init();
        check_init();
index bc3e3e9b22893b7852a69424cb78e5387cc28b21..ed764c332ea6a1f0ba9acc2b35ccde1d702e5e08 100644 (file)
@@ -37,7 +37,7 @@ HFILES = handle.h jdm.h libxfs.h libxlog.h swab.h xqm.h \
        xfs_ag.h xfs_alloc.h xfs_alloc_btree.h xfs_arch.h xfs_attr_leaf.h \
        xfs_attr_sf.h xfs_bit.h xfs_bmap.h xfs_bmap_btree.h xfs_btree.h \
        xfs_buf_item.h xfs_da_btree.h xfs_dfrag.h xfs_dinode.h \
-       xfs_dir.h xfs_dir2.h xfs_dir2_block.h xfs_dir2_data.h xfs_dir2_leaf.h \
+       xfs_dir2.h xfs_dir2_block.h xfs_dir2_data.h xfs_dir2_leaf.h \
        xfs_dir2_node.h xfs_dir2_sf.h xfs_dir_leaf.h xfs_dir_sf.h \
        xfs_extfree_item.h xfs_fs.h xfs_ialloc.h xfs_ialloc_btree.h \
        xfs_imap.h xfs_inode.h xfs_inode_item.h xfs_inum.h \
index 153cec575077c74b49b71ba6762ad36b1487c84d..41d49522f8bd5e7f51827483e0d4baefef0c4544 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -52,7 +52,6 @@
 #include <xfs/xfs_ialloc.h>
 #include <xfs/xfs_rtalloc.h>
 #include <xfs/xfs_btree.h>
-#include <xfs/xfs_dir.h>
 #include <xfs/xfs_dir_sf.h>
 #include <xfs/xfs_dir_leaf.h>
 #include <xfs/xfs_dir2.h>
@@ -192,6 +191,7 @@ typedef struct xfs_mount {
        int                     m_dalign;       /* stripe unit */
        int                     m_swidth;       /* stripe width */
        int                     m_sinoalign;    /* stripe unit inode alignmnt */
+       int                     m_attr_magicpct;/* 37% of the blocksize */
        int                     m_dir_magicpct; /* 37% of the dir blocksize */
        __uint8_t               m_dirversion;   /* 1 or 2 */
        int                     m_dirblksize;   /* directory block sz--bytes */
@@ -200,6 +200,7 @@ typedef struct xfs_mount {
        xfs_dablk_t             m_dirleafblk;   /* blockno of dir non-data v2 */
        xfs_dablk_t             m_dirfreeblk;   /* blockno of dirfreeindex v2 */
 } xfs_mount_t;
+#define        XFS_DIR_IS_V1(mp)       ((mp)->m_dirversion == 1)
 
 #define LIBXFS_MOUNT_ROOTINOS  0x0001
 #define LIBXFS_MOUNT_DEBUGGER  0x0002
@@ -232,6 +233,7 @@ typedef struct xfs_buf {
 #define XFS_BUF_PTR(bp)                        ((bp)->b_addr)
 #define xfs_buf_offset(bp, offset)     (XFS_BUF_PTR(bp) + (offset))
 #define XFS_BUF_ADDR(bp)               ((bp)->b_blkno)
+#define XFS_BUF_SIZE(bp)               ((bp)->b_bcount)
 #define XFS_BUF_COUNT(bp)              ((bp)->b_bcount)
 #define XFS_BUF_TARGET(bp)             ((bp)->b_dev)
 #define XFS_BUF_SET_PTR(bp,p,cnt)      ((bp)->b_addr = (char *)(p)); \
@@ -254,6 +256,12 @@ extern int libxfs_writebuf (xfs_buf_t *, int);
 extern int     libxfs_writebuf_int (xfs_buf_t *, int);
 extern void    libxfs_putbuf (xfs_buf_t *);
 
+#define LIBXFS_BREAD   0x1
+#define LIBXFS_BWRITE  0x2
+#define LIBXFS_BZERO   0x4
+
+extern void    libxfs_iomove (xfs_buf_t *, uint, int, void *, int);
+
 
 /*
  * Transaction interface
@@ -358,6 +366,11 @@ typedef struct xfs_inode {
        xfs_dinode_core_t       i_d;            /* most of ondisk inode */
 } xfs_inode_t;
 
+#define LIBXFS_ATTR_ROOT       0x0002  /* use attrs in root namespace */
+#define LIBXFS_ATTR_SECURE     0x0008  /* use attrs in security namespace */
+#define LIBXFS_ATTR_CREATE     0x0010  /* create, but fail if attr exists */
+#define LIBXFS_ATTR_REPLACE    0x0020  /* set, but fail if attr not exists */
+
 typedef struct {
        uid_t   cr_uid;
        gid_t   cr_gid;
@@ -453,6 +466,9 @@ extern int  libxfs_alloc_file_space (xfs_inode_t *, xfs_off_t,
 
 extern xfs_dahash_t    libxfs_da_hashname (uchar_t *, int);
 extern int     libxfs_attr_leaf_newentsize (xfs_da_args_t *, int, int *);
+extern int     libxfs_attr_set_int (xfs_inode_t*, char*, int, char*, int, int);
+extern int     libxfs_attr_remove_int (xfs_inode_t *, char *, int, int);
+
 
 extern void    libxfs_bmbt_get_all (xfs_bmbt_rec_t *, xfs_bmbt_irec_t *);
 #if __BYTE_ORDER != __BIG_ENDIAN
index 4dbc9f54cca5b1fefbfddf75b40f5535b79cab9b..665de8006711776adcaa41c285b1d537b40b190f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000 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
@@ -43,6 +43,8 @@
  * as possible so as to fit into the literal area of the inode.
  */
 
+#ifdef __KERNEL__
+
 /*========================================================================
  * Function prototypes for the kernel.
  *========================================================================*/
@@ -148,7 +150,10 @@ void       xfs_dir_startup(void);  /* called exactly once */
 #define        XFS_DIR_SHORTFORM_TO_SINGLE(mp,args)    \
        ((mp)->m_dirops.xd_shortform_to_single(args))
 
-#define        XFS_DIR_IS_V1(mp)       ((mp)->m_dirversion == 1)
 extern xfs_dirops_t xfsv1_dirops;
 
+#endif /* __KERNEL__*/
+
+#define        XFS_DIR_IS_V1(mp)       ((mp)->m_dirversion == 1)
+
 #endif /* __XFS_DIR_H__ */
index 1215f2af4160bd08bb5dbfd417652e92f7337353..19892e51681ff8717633aa6e671ee5fdf5d826ec 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2001,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
@@ -87,7 +87,10 @@ typedef struct xfs_dir2_put_args {
 } xfs_dir2_put_args_t;
 
 #define        XFS_DIR_IS_V2(mp)       ((mp)->m_dirversion == 2)
+
+#ifdef __KERNEL__
 extern xfs_dirops_t    xfsv2_dirops;
+#endif /* __KERNEL__ */
 
 /*
  * Other interfaces used by the rest of the dir v2 code.
index 689fd6fa0484ba842b710c803eba092cae4eb7dc..326a406c18a86d401ed850bc04c707cee6888de0 100644 (file)
@@ -45,7 +45,7 @@ CFILES = bit.c init.c logitem.c rdwr.c trans.c util.c \
        xfs_bmap_btree.c xfs_da_btree.c xfs_dir.c xfs_dir_leaf.c \
        xfs_dir2.c xfs_dir2_leaf.c xfs_attr_leaf.c xfs_dir2_block.c \
        xfs_dir2_node.c xfs_dir2_data.c xfs_dir2_sf.c xfs_bmap.c \
-       xfs_mount.c xfs_trans.c
+       xfs_mount.c xfs_trans.c xfs_attr.c
 
 CFILES += $(PKG_PLATFORM).c
 PCFILES = darwin.c freebsd.c irix.c linux.c
index 79f75449a852f78fb50420bcfbac62513c16f83b..9759e165a153cd70fec8619c40f9a0964107fb85 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -636,6 +636,9 @@ libxfs_mount(
        else
                libxfs_dir_mount(mp);
 
+       /* Initialize cached values for the attribute manager */
+       mp->m_attr_magicpct = (mp->m_sb.sb_blocksize * 37) / 100;
+
        /* Initialize the precomputed transaction reservations values */
        libxfs_trans_init(mp);
 
index 67a8edcb79ccbff83c7ffe52048d76889bda5b23..fd39a4450929a229b0de67d8dd12241eca78ec28 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -311,6 +311,25 @@ libxfs_writebuf(xfs_buf_t *buf, int flags)
        return error;
 }
 
+void
+libxfs_iomove(xfs_buf_t *buf, uint boff, int len, void *data, int flags)
+{
+       if (boff + len > buf->b_bcount)
+               abort();
+
+       switch (flags) {
+       case LIBXFS_BZERO:
+               memset(buf->b_addr + boff, 0, len);
+               break;
+       case LIBXFS_BREAD:
+               memcpy(data, buf->b_addr + boff, len);
+               break;
+       case LIBXFS_BWRITE:
+               memcpy(buf->b_addr + boff, data, len);
+               break;
+       }
+}
+
 void
 libxfs_putbuf(xfs_buf_t *buf)
 {
index f39ba1b3e3e2cf237306cb0ac45a254527964e88..96db71887474b7ae27f8b55942bba905470df090 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -594,6 +594,7 @@ libxfs_bmap_finish(
                        return error;
                xfs_bmap_del_free(flist, NULL, free);
        }
+       *committed = 0;
        return 0;
 }
 
index 78d6add80d09c5bec25b26aba41d91fc5ebd4a10..5d4844a181cd1931b6d78c6e40e1cc271cf20019 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -74,7 +74,6 @@
 #define xfs_da_log2_roundup            libxfs_da_log2_roundup
 #define xfs_highbit32                  libxfs_highbit32
 #define xfs_highbit64                  libxfs_highbit64
-#define xfs_attr_leaf_newentsize       libxfs_attr_leaf_newentsize
 #define xfs_alloc_compute_maxlevels    libxfs_alloc_compute_maxlevels
 #define xfs_bmap_compute_maxlevels     libxfs_bmap_compute_maxlevels
 #define xfs_ialloc_compute_maxlevels   libxfs_ialloc_compute_maxlevels
 #define xfs_dir2_removename            libxfs_dir2_removename
 #define xfs_dir_bogus_removename       libxfs_dir_bogus_removename
 #define xfs_dir2_bogus_removename      libxfs_dir2_bogus_removename
+#define xfs_dir_shortform_to_leaf      libxfs_dir_shortform_to_leaf
+#define xfs_dir2_sf_to_block           libxfs_dir2_sf_to_block
+#define XFS_DIR_SHORTFORM_TO_SINGLE(mp, daargs)        XFS_DIR_IS_V2(mp) ? \
+                                       libxfs_dir2_sf_to_block(daargs) : \
+                                       libxfs_dir_shortform_to_leaf(daargs);
 
 #define xfs_mount_common               libxfs_mount_common
 #define xfs_initialize_perag           libxfs_initialize_perag
 #define xfs_trans_bhold                        libxfs_trans_bhold
 #define xfs_trans_alloc                        libxfs_trans_alloc
 #define xfs_trans_commit               libxfs_trans_commit
+#define xfs_trans_cancel               libxfs_trans_cancel
 #define xfs_trans_mod_sb               libxfs_trans_mod_sb
 #define xfs_trans_reserve              libxfs_trans_reserve
 #define xfs_trans_get_buf              libxfs_trans_get_buf
 #define xfs_dir2_data_freescan         libxfs_dir2_data_freescan
 #define xfs_dir2_free_log_bests                libxfs_dir2_free_log_bests
 
+#define xfs_attr_leaf_newentsize       libxfs_attr_leaf_newentsize
+#define xfs_attr_set_int               libxfs_attr_set_int
+#define xfs_attr_remove_int            libxfs_attr_remove_int
+
 
 /*
  * Infrastructure to support building kernel XFS code in user space
 #define XFS_BUF_ISDONE(bp)             0
 #define XFS_BUF_GETERROR(bp)           0
 #define XFS_BUF_DONE(bp)               ((void) 0)
+#define XFS_BUF_STALE(bp)              ((void) 0)
+#define XFS_BUF_UNDELAYWRITE(bp)       ((void) 0)
 #define XFS_BUF_SET_REF(a,b)           ((void) 0)
 #define XFS_BUF_SET_VTYPE(a,b)         ((void) 0)
 #define XFS_BUF_SET_VTYPE_REF(a,b,c)   ((void) 0)
 #define XFS_BUF_SET_BDSTRAT_FUNC(a,b)  ((void) 0)
+#define xfs_incore(bt,blkno,len,lockit)        0
 #define xfs_baread(a,b,c)              ((void) 0)      /* no readahead */
 #define xfs_buftrace(x,y)              ((void) 0)      /* debug only */
 #define xfs_buf_item_log_debug(bip,a,b)        ((void) 0)      /* debug only */
 #define xfs_buf_relse(bp)              libxfs_putbuf(bp)
 #define xfs_read_buf(mp,devp,blkno,len,f,bpp)  \
        ( *(bpp) = libxfs_readbuf( *(dev_t*)devp, (blkno), (len), 1), 0 )
+#define xfs_buf_get_flags(devp,blkno,len,f)    \
+       ( libxfs_getbuf( devp, (blkno), (len) ) )
+#define xfs_bwrite(mp,bp)              libxfs_writebuf((bp), 0)
+
+#define XFS_B_READ                     LIBXFS_BREAD
+#define XFS_B_WRITE                    LIBXFS_BWRITE
+#define xfs_biomove(bp,off,len,data,f) libxfs_iomove(bp,off,len,data,f)
+#define xfs_biozero(bp,off,len)                libxfs_iomove(bp,off,len,0,LIBXFS_BZERO)
 
 /* transaction management */
 #define xfs_trans_set_sync(tp)                 ((void) 0)
 #define xfs_alloc_mark_busy(tp,ag,b,len)               ((void) 0)
 #define xfs_rotorstep                                  1
 
+#define xfs_ilock(ip, mode)            ((void)0)
+#define xfs_iunlock(ip, mode)          ((void)0)
+
 /* anything else */
 #if !defined(__sgi__)
 typedef __uint32_t uint_t;
@@ -242,8 +265,12 @@ typedef struct { dev_t dev; } xfs_buftarg_t;
 #undef MASK
 #define NBPP           getpagesize()
 #define STATIC
-#define ATTR_ROOT      0x0002  /* use attrs in root namespace */
-#define ATTR_SECURE    0x0008  /* use attrs in security namespace */
+#define VN_HOLD(vp)
+#define ATTR_ROOT      LIBXFS_ATTR_ROOT
+#define ATTR_SECURE    LIBXFS_ATTR_SECURE
+#define ATTR_CREATE    LIBXFS_ATTR_CREATE
+#define ATTR_REPLACE   LIBXFS_ATTR_REPLACE
+#define ATTR_KERNOTIME 0
 #define ktrace_t       void
 #define m_ddev_targp   m_dev
 #define unlikely(x)    (x)
@@ -279,12 +306,16 @@ typedef struct { dev_t dev; } xfs_buftarg_t;
 #define __return_address               __builtin_return_address(0)
 #define xfs_btree_reada_bufl(m,fsb,c)  ((void) 0)
 #define xfs_btree_reada_bufs(m,fsb,c,x)        ((void) 0)
+#define XFS_SB_LOCK(mp)                        0
+#define XFS_SB_UNLOCK(mp,s)            ((void) 0)
 #undef  XFS_DIR_SHORTFORM_VALIDATE_ONDISK
 #define XFS_DIR_SHORTFORM_VALIDATE_ONDISK(mp,dip) 0
-#define XFS_TRANS_MOD_DQUOT_BYINO(mp,tp,ip,field,delta)
+#define XFS_QM_DQATTACH(mp,ip,flags)   0
+#define XFS_TRANS_MOD_DQUOT_BYINO(mp,tp,ip,field,delta)        do { } while (0)
 #define XFS_TRANS_RESERVE_BLKQUOTA(mp,tp,ip,nblks)     0
-#define XFS_TRANS_UNRESERVE_BLKQUOTA(mp,tp,ip,nblks)
-#define XFS_TRANS_RESERVE_QUOTA_NBLKS(mp,tp,ip,nblks,ninos,fl)
+#define XFS_TRANS_UNRESERVE_BLKQUOTA(mp,tp,ip,nblks)   0
+#define XFS_TRANS_RESERVE_QUOTA_NBLKS(mp,tp,ip,nblks,ninos,fl) 0
+#define XFS_TRANS_UNRESERVE_QUOTA_NBLKS(mp,tp,ip,nblks,ninos,fl)       0
 
 /* These are lifted from the kernel */
 #define get_unaligned(ptr) \
@@ -414,10 +445,13 @@ void xfs_idestroy_fork (xfs_inode_t *, int);
 uint xfs_iroundup (uint);
 
 /* xfs_bmap.c */
-xfs_bmbt_rec_t *xfs_bmap_search_extents (xfs_inode_t *ip,
+int  xfs_bmap_local_to_extents (xfs_trans_t *, xfs_inode_t *,
+                       xfs_fsblock_t *, xfs_extlen_t, int *, int);
+xfs_bmbt_rec_t *xfs_bmap_search_extents (xfs_inode_t *,
                        xfs_fileoff_t, int, int *, xfs_extnum_t *,
                        xfs_bmbt_irec_t *, xfs_bmbt_irec_t *);
 int  xfs_bmap_read_extents (xfs_trans_t *, xfs_inode_t *, int);
+int  xfs_bmap_add_attrfork (xfs_inode_t *, int);
 void xfs_bmap_add_free (xfs_fsblock_t, xfs_filblks_t, xfs_bmap_free_t *,
                        xfs_mount_t *);
 int  xfs_bmap_first_unused (xfs_trans_t *, xfs_inode_t *, xfs_extlen_t,
@@ -446,6 +480,7 @@ int  xfs_bmap_extents_to_btree (xfs_trans_t *, xfs_inode_t *, xfs_fsblock_t *,
                        xfs_bmap_free_t *, xfs_btree_cur_t **, int, int *, int);
 void xfs_bmap_delete_exlist (xfs_inode_t *, xfs_extnum_t, xfs_extnum_t, int);
 xfs_filblks_t xfs_bmap_worst_indlen (xfs_inode_t *, xfs_filblks_t);
+void xfs_bmap_cancel (xfs_bmap_free_t *);
 int  xfs_bmap_isaeof (xfs_inode_t *, xfs_fileoff_t, int, char *);
 void xfs_bmap_insert_exlist (xfs_inode_t *, xfs_extnum_t, xfs_extnum_t,
                        xfs_bmbt_irec_t *, int);
@@ -472,6 +507,8 @@ void xfs_bmbt_log_block (struct xfs_btree_cur *, struct xfs_buf *, int);
 void xfs_bmbt_log_recs (struct xfs_btree_cur *, struct xfs_buf *, int, int);
 int  xfs_bmbt_lookup_eq (struct xfs_btree_cur *, xfs_fileoff_t, xfs_fsblock_t,
                        xfs_filblks_t, int *);
+int  xfs_bmbt_lookup_ge (struct xfs_btree_cur *, xfs_fileoff_t, xfs_fsblock_t,
+                       xfs_filblks_t, int *);
 xfs_fsblock_t xfs_bmbt_get_startblock (xfs_bmbt_rec_t *);
 xfs_filblks_t xfs_bmbt_get_blockcount (xfs_bmbt_rec_t *);
 xfs_fileoff_t xfs_bmbt_get_startoff (xfs_bmbt_rec_t *);
@@ -557,6 +594,7 @@ int  xfs_da_blk_link (xfs_da_state_t *, xfs_da_state_blk_t *,
 xfs_da_state_t *xfs_da_state_alloc (void);
 void xfs_da_state_free (xfs_da_state_t *);
 void xfs_da_state_kill_altpath (xfs_da_state_t *);
+xfs_daddr_t xfs_da_blkno(xfs_dabuf_t *);
 
 /* xfs_dir.c */
 int  xfs_dir_node_addname (xfs_da_args_t *);
@@ -576,6 +614,7 @@ int  xfs_dir_leaf_figure_balance (xfs_da_state_t *, xfs_da_state_blk_t *,
                        xfs_da_state_blk_t *, int *, int *);
 void xfs_dir_leaf_moveents (xfs_dir_leafblock_t *, int,
                        xfs_dir_leafblock_t *, int, int, xfs_mount_t *);
+int  xfs_dir_shortform_to_leaf (xfs_da_args_t *);
 
 /* xfs_dir2_leaf.c */
 void xfs_dir2_leaf_check (xfs_inode_t *, xfs_dabuf_t *);
@@ -592,6 +631,7 @@ void xfs_dir2_leafn_check (xfs_inode_t *, xfs_dabuf_t *);
 int  xfs_dir2_leafn_remove (xfs_da_args_t *, xfs_dabuf_t *, int,
                        xfs_da_state_blk_t *, int *);
 int  xfs_dir2_node_addname_int (xfs_da_args_t *, xfs_da_state_blk_t *);
+int  xfs_dir2_sf_to_block (xfs_da_args_t *);
 
 /* xfs_dir2_sf.c */
 void xfs_dir2_sf_check (xfs_da_args_t *);
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
new file mode 100644 (file)
index 0000000..21c3496
--- /dev/null
@@ -0,0 +1,1519 @@
+/*
+ * Copyright (c) 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
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <xfs.h>
+
+/*
+ * xfs_attr.c
+ *
+ * Provide the external interfaces to manage attribute lists.
+ */
+
+/*========================================================================
+ * Function prototypes for the kernel.
+ *========================================================================*/
+
+/*
+ * Internal routines when attribute list fits inside the inode.
+ */
+STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
+
+/*
+ * Internal routines when attribute list is one block.
+ */
+STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
+STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
+STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
+STATIC int xfs_attr_leaf_list(xfs_attr_list_context_t *context);
+
+/*
+ * Internal routines when attribute list is more than one block.
+ */
+STATIC int xfs_attr_node_get(xfs_da_args_t *args);
+STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
+STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
+STATIC int xfs_attr_node_list(xfs_attr_list_context_t *context);
+STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
+STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
+
+/*
+ * Routines to manipulate out-of-line attribute values.
+ */
+STATIC int xfs_attr_rmtval_get(xfs_da_args_t *args);
+STATIC int xfs_attr_rmtval_set(xfs_da_args_t *args);
+STATIC int xfs_attr_rmtval_remove(xfs_da_args_t *args);
+
+#define ATTR_RMTVALUE_MAPSIZE  1       /* # of map entries at once */
+
+/*========================================================================
+ * Overall external interface routines.
+ *========================================================================*/
+
+int                                                            /* error */
+xfs_attr_set_int(
+       xfs_inode_t     *dp,
+       char            *name,
+       int             namelen,
+       char            *value,
+       int             valuelen,
+       int             flags)
+{
+       xfs_da_args_t   args;
+       xfs_fsblock_t   firstblock;
+       xfs_bmap_free_t flist;
+       int             error, err2, committed;
+       int             local, size;
+       uint            nblks;
+       xfs_mount_t     *mp = dp->i_mount;
+       int             rsvd = (flags & ATTR_ROOT) != 0;
+
+       /*
+        * 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)
+                       return(error);
+       }
+
+       /*
+        * Fill in the arg structure for this request.
+        */
+       memset((char *)&args, 0, sizeof(args));
+       args.name = name;
+       args.namelen = namelen;
+       args.value = value;
+       args.valuelen = valuelen;
+       args.flags = flags;
+       args.hashval = xfs_da_hashname(args.name, args.namelen);
+       args.dp = dp;
+       args.firstblock = &firstblock;
+       args.flist = &flist;
+       args.whichfork = XFS_ATTR_FORK;
+       args.oknoent = 1;
+
+       /* Determine space new attribute will use, and if it will be inline
+        * or out of line.
+        */
+       size = xfs_attr_leaf_newentsize(&args, 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)) {
+                       /* Double split possible */
+                       nblks <<= 1;
+               }
+       } else {
+               uint    dblocks = XFS_B_TO_FSB(mp, valuelen);
+               /* Out of line attribute, cannot double split, but make
+                * room for the attribute value itself.
+                */
+               nblks += dblocks;
+               nblks += XFS_NEXTENTADD_SPACE_RES(mp, dblocks, XFS_ATTR_FORK);
+       }
+
+       /* Size is now blocks for attribute data */
+       args.total = nblks;
+
+       /*
+        * Start our first transaction of the day.
+        *
+        * All future transactions during this code must be "chained" off
+        * this one via the trans_dup() call.  All transactions will contain
+        * the inode, and the inode will always be marked with trans_ihold().
+        * Since the inode will be locked in all transactions, we must log
+        * the inode in every transaction to let it float upward through
+        * the log.
+        */
+       args.trans = xfs_trans_alloc(mp, XFS_TRANS_ATTR_SET);
+
+       /*
+        * Root fork attributes can use reserved data blocks for this
+        * operation if necessary
+        */
+
+       if (rsvd)
+               args.trans->t_flags |= XFS_TRANS_RESERVE;
+
+       if ((error = xfs_trans_reserve(args.trans, (uint) nblks,
+                                     XFS_ATTRSET_LOG_RES(mp, nblks),
+                                     0, XFS_TRANS_PERM_LOG_RES,
+                                     XFS_ATTRSET_LOG_COUNT))) {
+               xfs_trans_cancel(args.trans, 0);
+               return(error);
+       }
+       xfs_ilock(dp, XFS_ILOCK_EXCL);
+
+       error = XFS_TRANS_RESERVE_QUOTA_NBLKS(mp, args.trans, dp, nblks, 0,
+                        rsvd ? XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES :
+                               XFS_QMOPT_RES_REGBLKS);
+       if (error) {
+               xfs_iunlock(dp, XFS_ILOCK_EXCL);
+               xfs_trans_cancel(args.trans, XFS_TRANS_RELEASE_LOG_RES);
+               return (error);
+       }
+
+       xfs_trans_ijoin(args.trans, dp, XFS_ILOCK_EXCL);
+       xfs_trans_ihold(args.trans, dp);
+
+       /*
+        * If the attribute list is non-existant or a shortform list,
+        * upgrade it to a single-leaf-block attribute list.
+        */
+       if ((dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) ||
+           ((dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS) &&
+            (dp->i_d.di_anextents == 0))) {
+
+               /*
+                * Build initial attribute list (if required).
+                */
+               if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS)
+                       (void)xfs_attr_shortform_create(&args);
+
+               /*
+                * Try to add the attr to the attribute list in
+                * the inode.
+                */
+               error = xfs_attr_shortform_addname(&args);
+               if (error != ENOSPC) {
+                       /*
+                        * Commit the shortform mods, and we're done.
+                        * NOTE: this is also the error path (EEXIST, etc).
+                        */
+                       ASSERT(args.trans != NULL);
+
+                       /*
+                        * If this is a synchronous mount, make sure that
+                        * the transaction goes to disk before returning
+                        * to the user.
+                        */
+                       if (mp->m_flags & XFS_MOUNT_WSYNC) {
+                               xfs_trans_set_sync(args.trans);
+                       }
+                       err2 = xfs_trans_commit(args.trans,
+                                                XFS_TRANS_RELEASE_LOG_RES,
+                                                NULL);
+                       xfs_iunlock(dp, XFS_ILOCK_EXCL);
+
+                       /*
+                        * Hit the inode change time.
+                        */
+                       if (!error && (flags & ATTR_KERNOTIME) == 0) {
+                               xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
+                       }
+                       return(error == 0 ? err2 : error);
+               }
+
+               /*
+                * It won't fit in the shortform, transform to a leaf block.
+                * GROT: another possible req'mt for a double-split btree op.
+                */
+               XFS_BMAP_INIT(args.flist, args.firstblock);
+               error = xfs_attr_shortform_to_leaf(&args);
+               if (!error) {
+                       error = xfs_bmap_finish(&args.trans, args.flist,
+                                               *args.firstblock, &committed);
+               }
+               if (error) {
+                       ASSERT(committed);
+                       args.trans = NULL;
+                       xfs_bmap_cancel(&flist);
+                       goto out;
+               }
+
+               /*
+                * bmap_finish() may have committed the last trans and started
+                * a new one.  We need the inode to be in all transactions.
+                */
+               if (committed) {
+                       xfs_trans_ijoin(args.trans, dp, XFS_ILOCK_EXCL);
+                       xfs_trans_ihold(args.trans, dp);
+               }
+
+               /*
+                * Commit the leaf transformation.  We'll need another (linked)
+                * transaction to add the new attribute to the leaf.
+                */
+               if ((error = xfs_attr_rolltrans(&args.trans, dp)))
+                       goto out;
+
+       }
+
+       if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
+               error = xfs_attr_leaf_addname(&args);
+       } else {
+               error = xfs_attr_node_addname(&args);
+       }
+       if (error) {
+               goto out;
+       }
+
+       /*
+        * If this is a synchronous mount, make sure that the
+        * transaction goes to disk before returning to the user.
+        */
+       if (mp->m_flags & XFS_MOUNT_WSYNC) {
+               xfs_trans_set_sync(args.trans);
+       }
+
+       /*
+        * Commit the last in the sequence of transactions.
+        */
+       xfs_trans_log_inode(args.trans, dp, XFS_ILOG_CORE);
+       error = xfs_trans_commit(args.trans, XFS_TRANS_RELEASE_LOG_RES,
+                                NULL);
+       xfs_iunlock(dp, XFS_ILOCK_EXCL);
+
+       /*
+        * Hit the inode change time.
+        */
+       if (!error && (flags & ATTR_KERNOTIME) == 0) {
+               xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
+       }
+
+       return(error);
+
+out:
+       if (args.trans)
+               xfs_trans_cancel(args.trans,
+                       XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
+       xfs_iunlock(dp, XFS_ILOCK_EXCL);
+       return(error);
+}
+
+int                                                            /* error */
+xfs_attr_remove_int(
+       xfs_inode_t     *dp,
+       char            *name,
+       int             namelen,
+       int             flags)
+{
+       xfs_da_args_t   args;
+       xfs_fsblock_t   firstblock;
+       xfs_bmap_free_t flist;
+       xfs_mount_t     *mp = dp->i_mount;
+       int             error;
+
+       /*
+        * Fill in the arg structure for this request.
+        */
+       memset((char *)&args, 0, sizeof(args));
+       args.name = name;
+       args.namelen = namelen;
+       args.flags = flags;
+       args.hashval = xfs_da_hashname(args.name, args.namelen);
+       args.dp = dp;
+       args.firstblock = &firstblock;
+       args.flist = &flist;
+       args.total = 0;
+       args.whichfork = XFS_ATTR_FORK;
+
+       /*
+        * Attach the dquots to the inode.
+        */
+       if ((error = XFS_QM_DQATTACH(mp, dp, 0)))
+               return (error);
+
+       /*
+        * Start our first transaction of the day.
+        *
+        * All future transactions during this code must be "chained" off
+        * this one via the trans_dup() call.  All transactions will contain
+        * the inode, and the inode will always be marked with trans_ihold().
+        * Since the inode will be locked in all transactions, we must log
+        * the inode in every transaction to let it float upward through
+        * the log.
+        */
+       args.trans = xfs_trans_alloc(mp, XFS_TRANS_ATTR_RM);
+
+       /*
+        * Root fork attributes can use reserved data blocks for this
+        * operation if necessary
+        */
+
+       if (flags & ATTR_ROOT)
+               args.trans->t_flags |= XFS_TRANS_RESERVE;
+
+       if ((error = xfs_trans_reserve(args.trans,
+                                     XFS_ATTRRM_SPACE_RES(mp),
+                                     XFS_ATTRRM_LOG_RES(mp),
+                                     0, XFS_TRANS_PERM_LOG_RES,
+                                     XFS_ATTRRM_LOG_COUNT))) {
+               xfs_trans_cancel(args.trans, 0);
+               return(error);
+
+       }
+
+       xfs_ilock(dp, XFS_ILOCK_EXCL);
+       /*
+        * No need to make quota reservations here. We expect to release some
+        * blocks not allocate in the common case.
+        */
+       xfs_trans_ijoin(args.trans, dp, XFS_ILOCK_EXCL);
+       xfs_trans_ihold(args.trans, dp);
+
+       /*
+        * Decide on what work routines to call based on the inode size.
+        */
+       if (XFS_IFORK_Q(dp) == 0 ||
+           (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
+            dp->i_d.di_anextents == 0)) {
+               error = XFS_ERROR(ENOATTR);
+               goto out;
+       }
+       if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
+               ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
+               error = xfs_attr_shortform_remove(&args);
+               if (error) {
+                       goto out;
+               }
+       } else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
+               error = xfs_attr_leaf_removename(&args);
+       } else {
+               error = xfs_attr_node_removename(&args);
+       }
+       if (error) {
+               goto out;
+       }
+
+       /*
+        * If this is a synchronous mount, make sure that the
+        * transaction goes to disk before returning to the user.
+        */
+       if (mp->m_flags & XFS_MOUNT_WSYNC) {
+               xfs_trans_set_sync(args.trans);
+       }
+
+       /*
+        * Commit the last in the sequence of transactions.
+        */
+       xfs_trans_log_inode(args.trans, dp, XFS_ILOG_CORE);
+       error = xfs_trans_commit(args.trans, XFS_TRANS_RELEASE_LOG_RES,
+                                NULL);
+       xfs_iunlock(dp, XFS_ILOCK_EXCL);
+
+       /*
+        * Hit the inode change time.
+        */
+       if (!error && (flags & ATTR_KERNOTIME) == 0) {
+               xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
+       }
+
+       return(error);
+
+out:
+       if (args.trans)
+               xfs_trans_cancel(args.trans,
+                       XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
+       xfs_iunlock(dp, XFS_ILOCK_EXCL);
+       return(error);
+}
+
+
+/*========================================================================
+ * External routines when attribute list is inside the inode
+ *========================================================================*/
+
+/*
+ * Add a name to the shortform attribute list structure
+ * This is the external routine.
+ */
+STATIC int
+xfs_attr_shortform_addname(xfs_da_args_t *args)
+{
+       int newsize, retval;
+
+       retval = xfs_attr_shortform_lookup(args);
+       if ((args->flags & ATTR_REPLACE) && (retval == ENOATTR)) {
+               return(retval);
+       } else if (retval == EEXIST) {
+               if (args->flags & ATTR_CREATE)
+                       return(retval);
+               retval = xfs_attr_shortform_remove(args);
+               ASSERT(retval == 0);
+       }
+
+       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 {
+               return(XFS_ERROR(ENOSPC));
+       }
+       return(0);
+}
+
+
+/*========================================================================
+ * External routines when attribute list is one block
+ *========================================================================*/
+
+/*
+ * Add a name to the leaf attribute list structure
+ *
+ * This leaf block cannot have a "remote" value, we only call this routine
+ * if bmap_one_block() says there is only one block (ie: no remote blks).
+ */
+int
+xfs_attr_leaf_addname(xfs_da_args_t *args)
+{
+       xfs_inode_t *dp;
+       xfs_dabuf_t *bp;
+       int retval, error, committed;
+
+       /*
+        * Read the (only) block in the attribute list in.
+        */
+       dp = args->dp;
+       args->blkno = 0;
+       error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1, &bp,
+                                            XFS_ATTR_FORK);
+       if (error)
+               return(error);
+       ASSERT(bp != NULL);
+
+       /*
+        * Look up the given attribute in the leaf block.  Figure out if
+        * the given flags produce an error or call for an atomic rename.
+        */
+       retval = xfs_attr_leaf_lookup_int(bp, args);
+       if ((args->flags & ATTR_REPLACE) && (retval == ENOATTR)) {
+               xfs_da_brelse(args->trans, bp);
+               return(retval);
+       } else if (retval == EEXIST) {
+               if (args->flags & ATTR_CREATE) {        /* pure create op */
+                       xfs_da_brelse(args->trans, bp);
+                       return(retval);
+               }
+               args->rename = 1;                       /* an atomic rename */
+               args->blkno2 = args->blkno;             /* set 2nd entry info*/
+               args->index2 = args->index;
+               args->rmtblkno2 = args->rmtblkno;
+               args->rmtblkcnt2 = args->rmtblkcnt;
+       }
+
+       /*
+        * Add the attribute to the leaf block, transitioning to a Btree
+        * if required.
+        */
+       retval = xfs_attr_leaf_add(bp, args);
+       xfs_da_buf_done(bp);
+       if (retval == ENOSPC) {
+               /*
+                * Promote the attribute list to the Btree format, then
+                * Commit that transaction so that the node_addname() call
+                * can manage its own transactions.
+                */
+               XFS_BMAP_INIT(args->flist, args->firstblock);
+               error = xfs_attr_leaf_to_node(args);
+               if (!error) {
+                       error = xfs_bmap_finish(&args->trans, args->flist,
+                                               *args->firstblock, &committed);
+               }
+               if (error) {
+                       ASSERT(committed);
+                       args->trans = NULL;
+                       xfs_bmap_cancel(args->flist);
+                       return(error);
+               }
+
+               /*
+                * bmap_finish() may have committed the last trans and started
+                * a new one.  We need the inode to be in all transactions.
+                */
+               if (committed) {
+                       xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+                       xfs_trans_ihold(args->trans, dp);
+               }
+
+               /*
+                * Commit the current trans (including the inode) and start
+                * a new one.
+                */
+               if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+                       return (error);
+
+               /*
+                * Fob the whole rest of the problem off on the Btree code.
+                */
+               error = xfs_attr_node_addname(args);
+               return(error);
+       }
+
+       /*
+        * Commit the transaction that added the attr name so that
+        * later routines can manage their own transactions.
+        */
+       if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+               return (error);
+
+       /*
+        * If there was an out-of-line value, allocate the blocks we
+        * identified for its storage and copy the value.  This is done
+        * after we create the attribute so that we don't overflow the
+        * maximum size of a transaction and/or hit a deadlock.
+        */
+       if (args->rmtblkno > 0) {
+               error = xfs_attr_rmtval_set(args);
+               if (error)
+                       return(error);
+       }
+
+       /*
+        * If this is an atomic rename operation, we must "flip" the
+        * incomplete flags on the "new" and "old" attribute/value pairs
+        * so that one disappears and one appears atomically.  Then we
+        * must remove the "old" attribute/value pair.
+        */
+       if (args->rename) {
+               /*
+                * In a separate transaction, set the incomplete flag on the
+                * "old" attr and clear the incomplete flag on the "new" attr.
+                */
+               error = xfs_attr_leaf_flipflags(args);
+               if (error)
+                       return(error);
+
+               /*
+                * Dismantle the "old" attribute/value pair by removing
+                * a "remote" value (if it exists).
+                */
+               args->index = args->index2;
+               args->blkno = args->blkno2;
+               args->rmtblkno = args->rmtblkno2;
+               args->rmtblkcnt = args->rmtblkcnt2;
+               if (args->rmtblkno) {
+                       error = xfs_attr_rmtval_remove(args);
+                       if (error)
+                               return(error);
+               }
+
+               /*
+                * Read in the block containing the "old" attr, then
+                * remove the "old" attr from that block (neat, huh!)
+                */
+               error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1,
+                                                    &bp, XFS_ATTR_FORK);
+               if (error)
+                       return(error);
+               ASSERT(bp != NULL);
+               (void)xfs_attr_leaf_remove(bp, args);
+
+               /*
+                * If the result is small enough, shrink it all into the inode.
+                */
+               if (xfs_attr_shortform_allfit(bp, dp)) {
+                       XFS_BMAP_INIT(args->flist, args->firstblock);
+                       error = xfs_attr_leaf_to_shortform(bp, args);
+                       /* bp is gone due to xfs_da_shrink_inode */
+                       if (!error) {
+                               error = xfs_bmap_finish(&args->trans,
+                                                       args->flist,
+                                                       *args->firstblock,
+                                                       &committed);
+                       }
+                       if (error) {
+                               ASSERT(committed);
+                               args->trans = NULL;
+                               xfs_bmap_cancel(args->flist);
+                               return(error);
+                       }
+
+                       /*
+                        * bmap_finish() may have committed the last trans
+                        * and started a new one.  We need the inode to be
+                        * in all transactions.
+                        */
+                       if (committed) {
+                               xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+                               xfs_trans_ihold(args->trans, dp);
+                       }
+               } else
+                       xfs_da_buf_done(bp);
+
+               /*
+                * Commit the remove and start the next trans in series.
+                */
+               error = xfs_attr_rolltrans(&args->trans, dp);
+
+       } else if (args->rmtblkno > 0) {
+               /*
+                * Added a "remote" value, just clear the incomplete flag.
+                */
+               error = xfs_attr_leaf_clearflag(args);
+       }
+       return(error);
+}
+
+/*
+ * Remove a name from the leaf attribute list structure
+ *
+ * This leaf block cannot have a "remote" value, we only call this routine
+ * if bmap_one_block() says there is only one block (ie: no remote blks).
+ */
+STATIC int
+xfs_attr_leaf_removename(xfs_da_args_t *args)
+{
+       xfs_inode_t *dp;
+       xfs_dabuf_t *bp;
+       int committed;
+       int error;
+
+       /*
+        * Remove the attribute.
+        */
+       dp = args->dp;
+       args->blkno = 0;
+       error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1, &bp,
+                                            XFS_ATTR_FORK);
+       if (error) {
+               return(error);
+       }
+
+       ASSERT(bp != NULL);
+       error = xfs_attr_leaf_lookup_int(bp, args);
+       if (error == ENOATTR) {
+               xfs_da_brelse(args->trans, bp);
+               return(error);
+       }
+
+       (void)xfs_attr_leaf_remove(bp, args);
+
+       /*
+        * If the result is small enough, shrink it all into the inode.
+        */
+       if (xfs_attr_shortform_allfit(bp, dp)) {
+               XFS_BMAP_INIT(args->flist, args->firstblock);
+               error = xfs_attr_leaf_to_shortform(bp, args);
+               /* bp is gone due to xfs_da_shrink_inode */
+               if (!error) {
+                       error = xfs_bmap_finish(&args->trans, args->flist,
+                                               *args->firstblock, &committed);
+               }
+               if (error) {
+                       ASSERT(committed);
+                       args->trans = NULL;
+                       xfs_bmap_cancel(args->flist);
+                       return(error);
+               }
+
+               /*
+                * bmap_finish() may have committed the last trans and started
+                * a new one.  We need the inode to be in all transactions.
+                */
+               if (committed) {
+                       xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+                       xfs_trans_ihold(args->trans, dp);
+               }
+       } else
+               xfs_da_buf_done(bp);
+       return(0);
+}
+
+/*========================================================================
+ * External routines when attribute list size > XFS_LBSIZE(mp).
+ *========================================================================*/
+
+/*
+ * Add a name to a Btree-format attribute list.
+ *
+ * This will involve walking down the Btree, and may involve splitting
+ * leaf nodes and even splitting intermediate nodes up to and including
+ * the root node (a special case of an intermediate node).
+ *
+ * "Remote" attribute values confuse the issue and atomic rename operations
+ * add a whole extra layer of confusion on top of that.
+ */
+STATIC int
+xfs_attr_node_addname(xfs_da_args_t *args)
+{
+       xfs_da_state_t *state;
+       xfs_da_state_blk_t *blk;
+       xfs_inode_t *dp;
+       xfs_mount_t *mp;
+       int committed, retval, error;
+
+       /*
+        * Fill in bucket of arguments/results/context to carry around.
+        */
+       dp = args->dp;
+       mp = dp->i_mount;
+restart:
+       state = xfs_da_state_alloc();
+       state->args = args;
+       state->mp = mp;
+       state->blocksize = state->mp->m_sb.sb_blocksize;
+       state->node_ents = state->mp->m_attr_node_ents;
+
+       /*
+        * Search to see if name already exists, and get back a pointer
+        * to where it should go.
+        */
+       error = xfs_da_node_lookup_int(state, &retval);
+       if (error)
+               goto out;
+       blk = &state->path.blk[ state->path.active-1 ];
+       ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
+       if ((args->flags & ATTR_REPLACE) && (retval == ENOATTR)) {
+               goto out;
+       } else if (retval == EEXIST) {
+               if (args->flags & ATTR_CREATE)
+                       goto out;
+               args->rename = 1;                       /* atomic rename op */
+               args->blkno2 = args->blkno;             /* set 2nd entry info*/
+               args->index2 = args->index;
+               args->rmtblkno2 = args->rmtblkno;
+               args->rmtblkcnt2 = args->rmtblkcnt;
+               args->rmtblkno = 0;
+               args->rmtblkcnt = 0;
+       }
+
+       retval = xfs_attr_leaf_add(blk->bp, state->args);
+       if (retval == ENOSPC) {
+               if (state->path.active == 1) {
+                       /*
+                        * Its really a single leaf node, but it had
+                        * out-of-line values so it looked like it *might*
+                        * have been a b-tree.
+                        */
+                       xfs_da_state_free(state);
+                       XFS_BMAP_INIT(args->flist, args->firstblock);
+                       error = xfs_attr_leaf_to_node(args);
+                       if (!error) {
+                               error = xfs_bmap_finish(&args->trans,
+                                                       args->flist,
+                                                       *args->firstblock,
+                                                       &committed);
+                       }
+                       if (error) {
+                               ASSERT(committed);
+                               args->trans = NULL;
+                               xfs_bmap_cancel(args->flist);
+                               goto out;
+                       }
+
+                       /*
+                        * bmap_finish() may have committed the last trans
+                        * and started a new one.  We need the inode to be
+                        * in all transactions.
+                        */
+                       if (committed) {
+                               xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+                               xfs_trans_ihold(args->trans, dp);
+                       }
+
+                       /*
+                        * Commit the node conversion and start the next
+                        * trans in the chain.
+                        */
+                       if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+                               goto out;
+
+                       goto restart;
+               }
+
+               /*
+                * Split as many Btree elements as required.
+                * This code tracks the new and old attr's location
+                * in the index/blkno/rmtblkno/rmtblkcnt fields and
+                * in the index2/blkno2/rmtblkno2/rmtblkcnt2 fields.
+                */
+               XFS_BMAP_INIT(args->flist, args->firstblock);
+               error = xfs_da_split(state);
+               if (!error) {
+                       error = xfs_bmap_finish(&args->trans, args->flist,
+                                               *args->firstblock, &committed);
+               }
+               if (error) {
+                       ASSERT(committed);
+                       args->trans = NULL;
+                       xfs_bmap_cancel(args->flist);
+                       goto out;
+               }
+
+               /*
+                * bmap_finish() may have committed the last trans and started
+                * a new one.  We need the inode to be in all transactions.
+                */
+               if (committed) {
+                       xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+                       xfs_trans_ihold(args->trans, dp);
+               }
+       } else {
+               /*
+                * Addition succeeded, update Btree hashvals.
+                */
+               xfs_da_fixhashpath(state, &state->path);
+       }
+
+       /*
+        * Kill the state structure, we're done with it and need to
+        * allow the buffers to come back later.
+        */
+       xfs_da_state_free(state);
+       state = NULL;
+
+       /*
+        * Commit the leaf addition or btree split and start the next
+        * trans in the chain.
+        */
+       if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+               goto out;
+
+       /*
+        * If there was an out-of-line value, allocate the blocks we
+        * identified for its storage and copy the value.  This is done
+        * after we create the attribute so that we don't overflow the
+        * maximum size of a transaction and/or hit a deadlock.
+        */
+       if (args->rmtblkno > 0) {
+               error = xfs_attr_rmtval_set(args);
+               if (error)
+                       return(error);
+       }
+
+       /*
+        * If this is an atomic rename operation, we must "flip" the
+        * incomplete flags on the "new" and "old" attribute/value pairs
+        * so that one disappears and one appears atomically.  Then we
+        * must remove the "old" attribute/value pair.
+        */
+       if (args->rename) {
+               /*
+                * In a separate transaction, set the incomplete flag on the
+                * "old" attr and clear the incomplete flag on the "new" attr.
+                */
+               error = xfs_attr_leaf_flipflags(args);
+               if (error)
+                       goto out;
+
+               /*
+                * Dismantle the "old" attribute/value pair by removing
+                * a "remote" value (if it exists).
+                */
+               args->index = args->index2;
+               args->blkno = args->blkno2;
+               args->rmtblkno = args->rmtblkno2;
+               args->rmtblkcnt = args->rmtblkcnt2;
+               if (args->rmtblkno) {
+                       error = xfs_attr_rmtval_remove(args);
+                       if (error)
+                               return(error);
+               }
+
+               /*
+                * Re-find the "old" attribute entry after any split ops.
+                * The INCOMPLETE flag means that we will find the "old"
+                * attr, not the "new" one.
+                */
+               args->flags |= XFS_ATTR_INCOMPLETE;
+               state = xfs_da_state_alloc();
+               state->args = args;
+               state->mp = mp;
+               state->blocksize = state->mp->m_sb.sb_blocksize;
+               state->node_ents = state->mp->m_attr_node_ents;
+               state->inleaf = 0;
+               error = xfs_da_node_lookup_int(state, &retval);
+               if (error)
+                       goto out;
+
+               /*
+                * Remove the name and update the hashvals in the tree.
+                */
+               blk = &state->path.blk[ state->path.active-1 ];
+               ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
+               error = xfs_attr_leaf_remove(blk->bp, args);
+               xfs_da_fixhashpath(state, &state->path);
+
+               /*
+                * Check to see if the tree needs to be collapsed.
+                */
+               if (retval && (state->path.active > 1)) {
+                       XFS_BMAP_INIT(args->flist, args->firstblock);
+                       error = xfs_da_join(state);
+                       if (!error) {
+                               error = xfs_bmap_finish(&args->trans,
+                                                       args->flist,
+                                                       *args->firstblock,
+                                                       &committed);
+                       }
+                       if (error) {
+                               ASSERT(committed);
+                               args->trans = NULL;
+                               xfs_bmap_cancel(args->flist);
+                               goto out;
+                       }
+
+                       /*
+                        * bmap_finish() may have committed the last trans
+                        * and started a new one.  We need the inode to be
+                        * in all transactions.
+                        */
+                       if (committed) {
+                               xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+                               xfs_trans_ihold(args->trans, dp);
+                       }
+               }
+
+               /*
+                * Commit and start the next trans in the chain.
+                */
+               if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+                       goto out;
+
+       } else if (args->rmtblkno > 0) {
+               /*
+                * Added a "remote" value, just clear the incomplete flag.
+                */
+               error = xfs_attr_leaf_clearflag(args);
+               if (error)
+                       goto out;
+       }
+       retval = error = 0;
+
+out:
+       if (state)
+               xfs_da_state_free(state);
+       if (error)
+               return(error);
+       return(retval);
+}
+
+/*
+ * Remove a name from a B-tree attribute list.
+ *
+ * This will involve walking down the Btree, and may involve joining
+ * leaf nodes and even joining intermediate nodes up to and including
+ * the root node (a special case of an intermediate node).
+ */
+STATIC int
+xfs_attr_node_removename(xfs_da_args_t *args)
+{
+       xfs_da_state_t *state;
+       xfs_da_state_blk_t *blk;
+       xfs_inode_t *dp;
+       xfs_dabuf_t *bp;
+       int retval, error, committed;
+
+       /*
+        * Tie a string around our finger to remind us where we are.
+        */
+       dp = args->dp;
+       state = xfs_da_state_alloc();
+       state->args = args;
+       state->mp = dp->i_mount;
+       state->blocksize = state->mp->m_sb.sb_blocksize;
+       state->node_ents = state->mp->m_attr_node_ents;
+
+       /*
+        * Search to see if name exists, and get back a pointer to it.
+        */
+       error = xfs_da_node_lookup_int(state, &retval);
+       if (error || (retval != EEXIST)) {
+               if (error == 0)
+                       error = retval;
+               goto out;
+       }
+
+       /*
+        * If there is an out-of-line value, de-allocate the blocks.
+        * This is done before we remove the attribute so that we don't
+        * overflow the maximum size of a transaction and/or hit a deadlock.
+        */
+       blk = &state->path.blk[ state->path.active-1 ];
+       ASSERT(blk->bp != NULL);
+       ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
+       if (args->rmtblkno > 0) {
+               /*
+                * Fill in disk block numbers in the state structure
+                * so that we can get the buffers back after we commit
+                * several transactions in the following calls.
+                */
+               error = xfs_attr_fillstate(state);
+               if (error)
+                       goto out;
+
+               /*
+                * Mark the attribute as INCOMPLETE, then bunmapi() the
+                * remote value.
+                */
+               error = xfs_attr_leaf_setflag(args);
+               if (error)
+                       goto out;
+               error = xfs_attr_rmtval_remove(args);
+               if (error)
+                       goto out;
+
+               /*
+                * Refill the state structure with buffers, the prior calls
+                * released our buffers.
+                */
+               error = xfs_attr_refillstate(state);
+               if (error)
+                       goto out;
+       }
+
+       /*
+        * Remove the name and update the hashvals in the tree.
+        */
+       blk = &state->path.blk[ state->path.active-1 ];
+       ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
+       retval = xfs_attr_leaf_remove(blk->bp, args);
+       xfs_da_fixhashpath(state, &state->path);
+
+       /*
+        * Check to see if the tree needs to be collapsed.
+        */
+       if (retval && (state->path.active > 1)) {
+               XFS_BMAP_INIT(args->flist, args->firstblock);
+               error = xfs_da_join(state);
+               if (!error) {
+                       error = xfs_bmap_finish(&args->trans, args->flist,
+                                               *args->firstblock, &committed);
+               }
+               if (error) {
+                       ASSERT(committed);
+                       args->trans = NULL;
+                       xfs_bmap_cancel(args->flist);
+                       goto out;
+               }
+
+               /*
+                * bmap_finish() may have committed the last trans and started
+                * a new one.  We need the inode to be in all transactions.
+                */
+               if (committed) {
+                       xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+                       xfs_trans_ihold(args->trans, dp);
+               }
+
+               /*
+                * Commit the Btree join operation and start a new trans.
+                */
+               if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+                       goto out;
+       }
+
+       /*
+        * If the result is small enough, push it all into the inode.
+        */
+       if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
+               /*
+                * Have to get rid of the copy of this dabuf in the state.
+                */
+               ASSERT(state->path.active == 1);
+               ASSERT(state->path.blk[0].bp);
+               xfs_da_buf_done(state->path.blk[0].bp);
+               state->path.blk[0].bp = NULL;
+
+               error = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
+                                                    XFS_ATTR_FORK);
+               if (error)
+                       goto out;
+               ASSERT(INT_GET(((xfs_attr_leafblock_t *)
+                                     bp->data)->hdr.info.magic, ARCH_CONVERT)
+                                                      == XFS_ATTR_LEAF_MAGIC);
+
+               if (xfs_attr_shortform_allfit(bp, dp)) {
+                       XFS_BMAP_INIT(args->flist, args->firstblock);
+                       error = xfs_attr_leaf_to_shortform(bp, args);
+                       /* bp is gone due to xfs_da_shrink_inode */
+                       if (!error) {
+                               error = xfs_bmap_finish(&args->trans,
+                                                       args->flist,
+                                                       *args->firstblock,
+                                                       &committed);
+                       }
+                       if (error) {
+                               ASSERT(committed);
+                               args->trans = NULL;
+                               xfs_bmap_cancel(args->flist);
+                               goto out;
+                       }
+
+                       /*
+                        * bmap_finish() may have committed the last trans
+                        * and started a new one.  We need the inode to be
+                        * in all transactions.
+                        */
+                       if (committed) {
+                               xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+                               xfs_trans_ihold(args->trans, dp);
+                       }
+               } else
+                       xfs_da_brelse(args->trans, bp);
+       }
+       error = 0;
+
+out:
+       xfs_da_state_free(state);
+       return(error);
+}
+
+/*
+ * Fill in the disk block numbers in the state structure for the buffers
+ * that are attached to the state structure.
+ * This is done so that we can quickly reattach ourselves to those buffers
+ * after some set of transaction commit's has released these buffers.
+ */
+STATIC int
+xfs_attr_fillstate(xfs_da_state_t *state)
+{
+       xfs_da_state_path_t *path;
+       xfs_da_state_blk_t *blk;
+       int level;
+
+       /*
+        * Roll down the "path" in the state structure, storing the on-disk
+        * block number for those buffers in the "path".
+        */
+       path = &state->path;
+       ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
+       for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
+               if (blk->bp) {
+                       blk->disk_blkno = xfs_da_blkno(blk->bp);
+                       xfs_da_buf_done(blk->bp);
+                       blk->bp = NULL;
+               } else {
+                       blk->disk_blkno = 0;
+               }
+       }
+
+       /*
+        * Roll down the "altpath" in the state structure, storing the on-disk
+        * block number for those buffers in the "altpath".
+        */
+       path = &state->altpath;
+       ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
+       for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
+               if (blk->bp) {
+                       blk->disk_blkno = xfs_da_blkno(blk->bp);
+                       xfs_da_buf_done(blk->bp);
+                       blk->bp = NULL;
+               } else {
+                       blk->disk_blkno = 0;
+               }
+       }
+
+       return(0);
+}
+
+/*
+ * Reattach the buffers to the state structure based on the disk block
+ * numbers stored in the state structure.
+ * This is done after some set of transaction commit's has released those
+ * buffers from our grip.
+ */
+STATIC int
+xfs_attr_refillstate(xfs_da_state_t *state)
+{
+       xfs_da_state_path_t *path;
+       xfs_da_state_blk_t *blk;
+       int level, error;
+
+       /*
+        * Roll down the "path" in the state structure, storing the on-disk
+        * block number for those buffers in the "path".
+        */
+       path = &state->path;
+       ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
+       for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
+               if (blk->disk_blkno) {
+                       error = xfs_da_read_buf(state->args->trans,
+                                               state->args->dp,
+                                               blk->blkno, blk->disk_blkno,
+                                               &blk->bp, XFS_ATTR_FORK);
+                       if (error)
+                               return(error);
+               } else {
+                       blk->bp = NULL;
+               }
+       }
+
+       /*
+        * Roll down the "altpath" in the state structure, storing the on-disk
+        * block number for those buffers in the "altpath".
+        */
+       path = &state->altpath;
+       ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
+       for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
+               if (blk->disk_blkno) {
+                       error = xfs_da_read_buf(state->args->trans,
+                                               state->args->dp,
+                                               blk->blkno, blk->disk_blkno,
+                                               &blk->bp, XFS_ATTR_FORK);
+                       if (error)
+                               return(error);
+               } else {
+                       blk->bp = NULL;
+               }
+       }
+
+       return(0);
+}
+
+/*
+ * Write the value associated with an attribute into the out-of-line buffer
+ * that we have defined for it.
+ */
+STATIC int
+xfs_attr_rmtval_set(xfs_da_args_t *args)
+{
+       xfs_mount_t *mp;
+       xfs_fileoff_t lfileoff;
+       xfs_inode_t *dp;
+       xfs_bmbt_irec_t map;
+       xfs_daddr_t dblkno;
+       xfs_caddr_t src;
+       xfs_buf_t *bp;
+       xfs_dablk_t lblkno;
+       int blkcnt, valuelen, nmap, error, tmp, committed;
+
+       dp = args->dp;
+       mp = dp->i_mount;
+       src = args->value;
+
+       /*
+        * Find a "hole" in the attribute address space large enough for
+        * us to drop the new attribute's value into.
+        */
+       blkcnt = XFS_B_TO_FSB(mp, args->valuelen);
+       lfileoff = 0;
+       error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff,
+                                                  XFS_ATTR_FORK);
+       if (error) {
+               return(error);
+       }
+       args->rmtblkno = lblkno = (xfs_dablk_t)lfileoff;
+       args->rmtblkcnt = blkcnt;
+
+       /*
+        * Roll through the "value", allocating blocks on disk as required.
+        */
+       while (blkcnt > 0) {
+               /*
+                * Allocate a single extent, up to the size of the value.
+                */
+               XFS_BMAP_INIT(args->flist, args->firstblock);
+               nmap = 1;
+               error = xfs_bmapi(args->trans, dp, (xfs_fileoff_t)lblkno,
+                                 blkcnt,
+                                 XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA |
+                                                       XFS_BMAPI_WRITE,
+                                 args->firstblock, args->total, &map, &nmap,
+                                 args->flist);
+               if (!error) {
+                       error = xfs_bmap_finish(&args->trans, args->flist,
+                                               *args->firstblock, &committed);
+               }
+               if (error) {
+                       ASSERT(committed);
+                       args->trans = NULL;
+                       xfs_bmap_cancel(args->flist);
+                       return(error);
+               }
+
+               /*
+                * bmap_finish() may have committed the last trans and started
+                * a new one.  We need the inode to be in all transactions.
+                */
+               if (committed) {
+                       xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+                       xfs_trans_ihold(args->trans, dp);
+               }
+
+               ASSERT(nmap == 1);
+               ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
+                      (map.br_startblock != HOLESTARTBLOCK));
+               lblkno += map.br_blockcount;
+               blkcnt -= map.br_blockcount;
+
+               /*
+                * Start the next trans in the chain.
+                */
+               if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+                       return (error);
+       }
+
+       /*
+        * Roll through the "value", copying the attribute value to the
+        * already-allocated blocks.  Blocks are written synchronously
+        * so that we can know they are all on disk before we turn off
+        * the INCOMPLETE flag.
+        */
+       lblkno = args->rmtblkno;
+       valuelen = args->valuelen;
+       while (valuelen > 0) {
+               /*
+                * Try to remember where we decided to put the value.
+                */
+               XFS_BMAP_INIT(args->flist, args->firstblock);
+               nmap = 1;
+               error = xfs_bmapi(NULL, dp, (xfs_fileoff_t)lblkno,
+                                 args->rmtblkcnt,
+                                 XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
+                                 args->firstblock, 0, &map, &nmap, NULL);
+               if (error) {
+                       return(error);
+               }
+               ASSERT(nmap == 1);
+               ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
+                      (map.br_startblock != HOLESTARTBLOCK));
+
+               dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
+               blkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
+
+               bp = xfs_buf_get_flags(mp->m_ddev_targp, dblkno,
+                                                       blkcnt, XFS_BUF_LOCK);
+               ASSERT(bp);
+               ASSERT(!XFS_BUF_GETERROR(bp));
+
+               tmp = (valuelen < XFS_BUF_SIZE(bp)) ? valuelen :
+                                                       XFS_BUF_SIZE(bp);
+               xfs_biomove(bp, 0, tmp, src, XFS_B_WRITE);
+               if (tmp < XFS_BUF_SIZE(bp))
+                       xfs_biozero(bp, tmp, XFS_BUF_SIZE(bp) - tmp);
+               if ((error = xfs_bwrite(mp, bp))) {/* GROT: NOTE: synchronous write */
+                       return (error);
+               }
+               src += tmp;
+               valuelen -= tmp;
+
+               lblkno += map.br_blockcount;
+       }
+       ASSERT(valuelen == 0);
+       return(0);
+}
+
+/*
+ * Remove the value associated with an attribute by deleting the
+ * out-of-line buffer that it is stored on.
+ */
+STATIC int
+xfs_attr_rmtval_remove(xfs_da_args_t *args)
+{
+       xfs_mount_t *mp;
+       xfs_bmbt_irec_t map;
+       xfs_buf_t *bp;
+       xfs_daddr_t dblkno;
+       xfs_dablk_t lblkno;
+       int valuelen, blkcnt, nmap, error, done, committed;
+
+       mp = args->dp->i_mount;
+
+       /*
+        * Roll through the "value", invalidating the attribute value's
+        * blocks.
+        */
+       lblkno = args->rmtblkno;
+       valuelen = args->rmtblkcnt;
+       while (valuelen > 0) {
+               /*
+                * Try to remember where we decided to put the value.
+                */
+               XFS_BMAP_INIT(args->flist, args->firstblock);
+               nmap = 1;
+               error = xfs_bmapi(NULL, args->dp, (xfs_fileoff_t)lblkno,
+                                       args->rmtblkcnt,
+                                       XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
+                                       args->firstblock, 0, &map, &nmap,
+                                       args->flist);
+               if (error) {
+                       return(error);
+               }
+               ASSERT(nmap == 1);
+               ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
+                      (map.br_startblock != HOLESTARTBLOCK));
+
+               dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
+               blkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
+
+               /*
+                * If the "remote" value is in the cache, remove it.
+                */
+               bp = xfs_incore(mp->m_ddev_targp, dblkno, blkcnt,
+                               XFS_INCORE_TRYLOCK);
+               if (bp) {
+                       XFS_BUF_STALE(bp);
+                       XFS_BUF_UNDELAYWRITE(bp);
+                       xfs_buf_relse(bp);
+                       bp = NULL;
+               }
+
+               valuelen -= map.br_blockcount;
+
+               lblkno += map.br_blockcount;
+       }
+
+       /*
+        * Keep de-allocating extents until the remote-value region is gone.
+        */
+       lblkno = args->rmtblkno;
+       blkcnt = args->rmtblkcnt;
+       done = 0;
+       while (!done) {
+               XFS_BMAP_INIT(args->flist, args->firstblock);
+               error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
+                                   XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
+                                   1, args->firstblock, args->flist, &done);
+               if (!error) {
+                       error = xfs_bmap_finish(&args->trans, args->flist,
+                                               *args->firstblock, &committed);
+               }
+               if (error) {
+                       ASSERT(committed);
+                       args->trans = NULL;
+                       xfs_bmap_cancel(args->flist);
+                       return(error);
+               }
+
+               /*
+                * bmap_finish() may have committed the last trans and started
+                * a new one.  We need the inode to be in all transactions.
+                */
+               if (committed) {
+                       xfs_trans_ijoin(args->trans, args->dp, XFS_ILOCK_EXCL);
+                       xfs_trans_ihold(args->trans, args->dp);
+               }
+
+               /*
+                * Close out trans and start the next one in the chain.
+                */
+               if ((error = xfs_attr_rolltrans(&args->trans, args->dp)))
+                       return (error);
+       }
+       return(0);
+}
index 29d12de9778842cf22d89d78f2ac1ef6b0f30f49..7140bcd56c52842d84949e52e0c63d3af4919397 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
  * Routines to implement leaf blocks of attributes as Btrees of hashed names.
  */
 
+/*========================================================================
+ * External routines when dirsize < XFS_LITINO(mp).
+ *========================================================================*/
+
+/*
+ * Create the initial contents of a shortform attribute list.
+ */
+int
+xfs_attr_shortform_create(xfs_da_args_t *args)
+{
+       xfs_attr_sf_hdr_t *hdr;
+       xfs_inode_t *dp;
+       xfs_ifork_t *ifp;
+
+       dp = args->dp;
+       ASSERT(dp != NULL);
+       ifp = dp->i_afp;
+       ASSERT(ifp != NULL);
+       ASSERT(ifp->if_bytes == 0);
+       if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS) {
+               ifp->if_flags &= ~XFS_IFEXTENTS;        /* just in case */
+               dp->i_d.di_aformat = XFS_DINODE_FMT_LOCAL;
+               ifp->if_flags |= XFS_IFINLINE;
+       } else {
+               ASSERT(ifp->if_flags & XFS_IFINLINE);
+       }
+       xfs_idata_realloc(dp, sizeof(*hdr), XFS_ATTR_FORK);
+       hdr = (xfs_attr_sf_hdr_t *)ifp->if_u1.if_data;
+       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)
+{
+       xfs_attr_shortform_t *sf;
+       xfs_attr_sf_entry_t *sfe;
+       int i, offset, size;
+       xfs_inode_t *dp;
+       xfs_ifork_t *ifp;
+
+       dp = args->dp;
+       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++) {
+               if (sfe->namelen != args->namelen)
+                       continue;
+               if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
+                       continue;
+               if (((args->flags & ATTR_SECURE) != 0) !=
+                   ((sfe->flags & XFS_ATTR_SECURE) != 0))
+                       continue;
+               if (((args->flags & ATTR_ROOT) != 0) !=
+                   ((sfe->flags & XFS_ATTR_ROOT) != 0))
+                       continue;
+               return(XFS_ERROR(EEXIST));
+       }
+
+       offset = (char *)sfe - (char *)sf;
+       size = XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
+       xfs_idata_realloc(dp, size, XFS_ATTR_FORK);
+       sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
+       sfe = (xfs_attr_sf_entry_t *)((char *)sf + offset);
+
+       sfe->namelen = args->namelen;
+       INT_SET(sfe->valuelen, ARCH_CONVERT, args->valuelen);
+       sfe->flags = (args->flags & ATTR_SECURE) ? XFS_ATTR_SECURE :
+                       ((args->flags & ATTR_ROOT) ? XFS_ATTR_ROOT : 0);
+       memcpy(sfe->nameval, args->name, args->namelen);
+       memcpy(&sfe->nameval[args->namelen], args->value, args->valuelen);
+       INT_MOD(sf->hdr.count, ARCH_CONVERT, 1);
+       INT_MOD(sf->hdr.totsize, ARCH_CONVERT, size);
+       xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
+
+       return(0);
+}
+
+/*
+ * Remove a name 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_inode_t *dp;
+
+       /*
+        * Remove the attribute.
+        */
+       dp = args->dp;
+       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),
+                                       base += size, i++) {
+               size = XFS_ATTR_SF_ENTSIZE(sfe);
+               if (sfe->namelen != args->namelen)
+                       continue;
+               if (memcmp(sfe->nameval, args->name, args->namelen) != 0)
+                       continue;
+               if (((args->flags & ATTR_SECURE) != 0) !=
+                   ((sfe->flags & XFS_ATTR_SECURE) != 0))
+                       continue;
+               if (((args->flags & ATTR_ROOT) != 0) !=
+                   ((sfe->flags & XFS_ATTR_ROOT) != 0))
+                       continue;
+               break;
+       }
+       if (i == INT_GET(sf->hdr.count, ARCH_CONVERT))
+               return(XFS_ERROR(ENOATTR));
+
+       end = base + size;
+       totsize = INT_GET(sf->hdr.totsize, ARCH_CONVERT);
+       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);
+
+       return(0);
+}
+
+/*
+ * Look up a name in a shortform attribute list structure.
+ */
+/*ARGSUSED*/
+int
+xfs_attr_shortform_lookup(xfs_da_args_t *args)
+{
+       xfs_attr_shortform_t *sf;
+       xfs_attr_sf_entry_t *sfe;
+       int i;
+       xfs_ifork_t *ifp;
+
+       ifp = args->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++) {
+               if (sfe->namelen != args->namelen)
+                       continue;
+               if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
+                       continue;
+               if (((args->flags & ATTR_SECURE) != 0) !=
+                   ((sfe->flags & XFS_ATTR_SECURE) != 0))
+                       continue;
+               if (((args->flags & ATTR_ROOT) != 0) !=
+                   ((sfe->flags & XFS_ATTR_ROOT) != 0))
+                       continue;
+               return(XFS_ERROR(EEXIST));
+       }
+       return(XFS_ERROR(ENOATTR));
+}
+
+/*
+ * Convert from using the shortform to the leaf.
+ */
+int
+xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
+{
+       xfs_inode_t *dp;
+       xfs_attr_shortform_t *sf;
+       xfs_attr_sf_entry_t *sfe;
+       xfs_da_args_t nargs;
+       char *tmpbuffer;
+       int error, i, size;
+       xfs_dablk_t blkno;
+       xfs_dabuf_t *bp;
+       xfs_ifork_t *ifp;
+
+       dp = args->dp;
+       ifp = dp->i_afp;
+       sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
+       size = INT_GET(sf->hdr.totsize, ARCH_CONVERT);
+       tmpbuffer = kmem_alloc(size, KM_SLEEP);
+       ASSERT(tmpbuffer != NULL);
+       memcpy(tmpbuffer, ifp->if_u1.if_data, size);
+       sf = (xfs_attr_shortform_t *)tmpbuffer;
+
+       xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
+       bp = NULL;
+       error = xfs_da_grow_inode(args, &blkno);
+       if (error) {
+               /*
+                * If we hit an IO error middle of the transaction inside
+                * grow_inode(), we may have inconsistent data. Bail out.
+                */
+               if (error == EIO)
+                       goto out;
+               xfs_idata_realloc(dp, size, XFS_ATTR_FORK);     /* try to put */
+               memcpy(ifp->if_u1.if_data, tmpbuffer, size);    /* it back */
+               goto out;
+       }
+
+       ASSERT(blkno == 0);
+       error = xfs_attr_leaf_create(args, blkno, &bp);
+       if (error) {
+               error = xfs_da_shrink_inode(args, 0, bp);
+               bp = NULL;
+               if (error)
+                       goto out;
+               xfs_idata_realloc(dp, size, XFS_ATTR_FORK);     /* try to put */
+               memcpy(ifp->if_u1.if_data, tmpbuffer, size);    /* it back */
+               goto out;
+       }
+
+       memset((char *)&nargs, 0, sizeof(nargs));
+       nargs.dp = dp;
+       nargs.firstblock = args->firstblock;
+       nargs.flist = args->flist;
+       nargs.total = args->total;
+       nargs.whichfork = XFS_ATTR_FORK;
+       nargs.trans = args->trans;
+       nargs.oknoent = 1;
+
+       sfe = &sf->list[0];
+       for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT); i++) {
+               nargs.name = (char *)sfe->nameval;
+               nargs.namelen = sfe->namelen;
+               nargs.value = (char *)&sfe->nameval[nargs.namelen];
+               nargs.valuelen = INT_GET(sfe->valuelen, ARCH_CONVERT);
+               nargs.hashval = xfs_da_hashname((char *)sfe->nameval,
+                                               sfe->namelen);
+               nargs.flags = (sfe->flags & XFS_ATTR_SECURE) ? ATTR_SECURE :
+                               ((sfe->flags & XFS_ATTR_ROOT) ? ATTR_ROOT : 0);
+               error = xfs_attr_leaf_lookup_int(bp, &nargs); /* set a->index */
+               ASSERT(error == ENOATTR);
+               error = xfs_attr_leaf_add(bp, &nargs);
+               ASSERT(error != ENOSPC);
+               if (error)
+                       goto out;
+               sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
+       }
+       error = 0;
+
+out:
+       if(bp)
+               xfs_da_buf_done(bp);
+       kmem_free(tmpbuffer, size);
+       return(error);
+}
+
+/*
+ * Check a leaf attribute block to see if all the entries would fit into
+ * a shortform attribute list.
+ */
+int
+xfs_attr_shortform_allfit(xfs_dabuf_t *bp, xfs_inode_t *dp)
+{
+       xfs_attr_leafblock_t *leaf;
+       xfs_attr_leaf_entry_t *entry;
+       xfs_attr_leaf_name_local_t *name_loc;
+       int bytes, i;
+
+       leaf = bp->data;
+       ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+                                               == XFS_ATTR_LEAF_MAGIC);
+
+       entry = &leaf->entries[0];
+       bytes = sizeof(struct xfs_attr_sf_hdr);
+       for (i = 0; i < INT_GET(leaf->hdr.count, ARCH_CONVERT); entry++, i++) {
+               if (entry->flags & XFS_ATTR_INCOMPLETE)
+                       continue;               /* don't copy partial entries */
+               if (!(entry->flags & XFS_ATTR_LOCAL))
+                       return(0);
+               name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, i);
+               if (name_loc->namelen >= XFS_ATTR_SF_ENTSIZE_MAX)
+                       return(0);
+               if (INT_GET(name_loc->valuelen, ARCH_CONVERT) >= XFS_ATTR_SF_ENTSIZE_MAX)
+                       return(0);
+               bytes += sizeof(struct xfs_attr_sf_entry)-1
+                               + name_loc->namelen
+                               + INT_GET(name_loc->valuelen, ARCH_CONVERT);
+       }
+       return( bytes < XFS_IFORK_ASIZE(dp) );
+}
+
+/*
+ * 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_leafblock_t *leaf;
+       xfs_attr_leaf_entry_t *entry;
+       xfs_attr_leaf_name_local_t *name_loc;
+       xfs_da_args_t nargs;
+       xfs_inode_t *dp;
+       char *tmpbuffer;
+       int error, i;
+
+       dp = args->dp;
+       tmpbuffer = kmem_alloc(XFS_LBSIZE(dp->i_mount), KM_SLEEP);
+       ASSERT(tmpbuffer != NULL);
+
+       ASSERT(bp != NULL);
+       memcpy(tmpbuffer, bp->data, XFS_LBSIZE(dp->i_mount));
+       leaf = (xfs_attr_leafblock_t *)tmpbuffer;
+       ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+                                               == XFS_ATTR_LEAF_MAGIC);
+       memset(bp->data, 0, XFS_LBSIZE(dp->i_mount));
+
+       /*
+        * Clean out the prior contents of the attribute list.
+        */
+       error = xfs_da_shrink_inode(args, 0, bp);
+       if (error)
+               goto out;
+       error = xfs_attr_shortform_create(args);
+       if (error)
+               goto out;
+
+       /*
+        * Copy the attributes
+        */
+       memset((char *)&nargs, 0, sizeof(nargs));
+       nargs.dp = dp;
+       nargs.firstblock = args->firstblock;
+       nargs.flist = args->flist;
+       nargs.total = args->total;
+       nargs.whichfork = XFS_ATTR_FORK;
+       nargs.trans = args->trans;
+       nargs.oknoent = 1;
+       entry = &leaf->entries[0];
+       for (i = 0; i < INT_GET(leaf->hdr.count, ARCH_CONVERT); entry++, i++) {
+               if (entry->flags & XFS_ATTR_INCOMPLETE)
+                       continue;       /* don't copy partial entries */
+               if (!entry->nameidx)
+                       continue;
+               ASSERT(entry->flags & XFS_ATTR_LOCAL);
+               name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, i);
+               nargs.name = (char *)name_loc->nameval;
+               nargs.namelen = name_loc->namelen;
+               nargs.value = (char *)&name_loc->nameval[nargs.namelen];
+               nargs.valuelen = INT_GET(name_loc->valuelen, ARCH_CONVERT);
+               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);
+       }
+       error = 0;
+
+out:
+       kmem_free(tmpbuffer, XFS_LBSIZE(dp->i_mount));
+       return(error);
+}
+
+/*
+ * Convert from using a single leaf to a root node and a leaf.
+ */
+int
+xfs_attr_leaf_to_node(xfs_da_args_t *args)
+{
+       xfs_attr_leafblock_t *leaf;
+       xfs_da_intnode_t *node;
+       xfs_inode_t *dp;
+       xfs_dabuf_t *bp1, *bp2;
+       xfs_dablk_t blkno;
+       int error;
+
+       dp = args->dp;
+       bp1 = bp2 = NULL;
+       error = xfs_da_grow_inode(args, &blkno);
+       if (error)
+               goto out;
+       error = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp1,
+                                            XFS_ATTR_FORK);
+       if (error)
+               goto out;
+       ASSERT(bp1 != NULL);
+       bp2 = NULL;
+       error = xfs_da_get_buf(args->trans, args->dp, blkno, -1, &bp2,
+                                           XFS_ATTR_FORK);
+       if (error)
+               goto out;
+       ASSERT(bp2 != NULL);
+       memcpy(bp2->data, bp1->data, XFS_LBSIZE(dp->i_mount));
+       xfs_da_buf_done(bp1);
+       bp1 = NULL;
+       xfs_da_log_buf(args->trans, bp2, 0, XFS_LBSIZE(dp->i_mount) - 1);
+
+       /*
+        * Set up the new root node.
+        */
+       error = xfs_da_node_create(args, 0, 1, &bp1, XFS_ATTR_FORK);
+       if (error)
+               goto out;
+       node = bp1->data;
+       leaf = bp2->data;
+       ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+                                               == XFS_ATTR_LEAF_MAGIC);
+       /* both on-disk, don't endian-flip twice */
+       node->btree[0].hashval =
+               leaf->entries[INT_GET(leaf->hdr.count, ARCH_CONVERT)-1 ].hashval;
+       INT_SET(node->btree[0].before, ARCH_CONVERT, blkno);
+       INT_SET(node->hdr.count, ARCH_CONVERT, 1);
+       xfs_da_log_buf(args->trans, bp1, 0, XFS_LBSIZE(dp->i_mount) - 1);
+       error = 0;
+out:
+       if (bp1)
+               xfs_da_buf_done(bp1);
+       if (bp2)
+               xfs_da_buf_done(bp2);
+       return(error);
+}
+
 /*========================================================================
  * Routines used for growing the Btree.
  *========================================================================*/
@@ -807,6 +1229,178 @@ xfs_attr_leaf_toosmall(xfs_da_state_t *state, int *action)
        return(0);
 }
 
+/*
+ * Remove a name from the leaf attribute list structure.
+ *
+ * Return 1 if leaf is less than 37% full, 0 if >= 37% full.
+ * If two leaves are 37% full, when combined they will leave 25% free.
+ */
+int
+xfs_attr_leaf_remove(xfs_dabuf_t *bp, xfs_da_args_t *args)
+{
+       xfs_attr_leafblock_t *leaf;
+       xfs_attr_leaf_hdr_t *hdr;
+       xfs_attr_leaf_map_t *map;
+       xfs_attr_leaf_entry_t *entry;
+       int before, after, smallest, entsize;
+       int tablesize, tmp, i;
+       xfs_mount_t *mp;
+
+       leaf = bp->data;
+       ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+                                               == XFS_ATTR_LEAF_MAGIC);
+       hdr = &leaf->hdr;
+       mp = args->trans->t_mountp;
+       ASSERT((INT_GET(hdr->count, ARCH_CONVERT) > 0)
+               && (INT_GET(hdr->count, ARCH_CONVERT) < (XFS_LBSIZE(mp)/8)));
+       ASSERT((args->index >= 0)
+               && (args->index < INT_GET(hdr->count, ARCH_CONVERT)));
+       ASSERT(INT_GET(hdr->firstused, ARCH_CONVERT)
+                               >= ((INT_GET(hdr->count, ARCH_CONVERT)
+                                       * sizeof(*entry))+sizeof(*hdr)));
+       entry = &leaf->entries[args->index];
+       ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT)
+                               >= INT_GET(hdr->firstused, ARCH_CONVERT));
+       ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) < XFS_LBSIZE(mp));
+
+       /*
+        * Scan through free region table:
+        *    check for adjacency of free'd entry with an existing one,
+        *    find smallest free region in case we need to replace it,
+        *    adjust any map that borders the entry table,
+        */
+       tablesize = INT_GET(hdr->count, ARCH_CONVERT)
+                                       * sizeof(xfs_attr_leaf_entry_t)
+                                       + sizeof(xfs_attr_leaf_hdr_t);
+       map = &hdr->freemap[0];
+       tmp = INT_GET(map->size, ARCH_CONVERT);
+       before = after = -1;
+       smallest = XFS_ATTR_LEAF_MAPSIZE - 1;
+       entsize = xfs_attr_leaf_entsize(leaf, args->index);
+       for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; map++, i++) {
+               ASSERT(INT_GET(map->base, ARCH_CONVERT) < XFS_LBSIZE(mp));
+               ASSERT(INT_GET(map->size, ARCH_CONVERT) < XFS_LBSIZE(mp));
+               if (INT_GET(map->base, ARCH_CONVERT) == tablesize) {
+                       INT_MOD(map->base, ARCH_CONVERT,
+                                       -sizeof(xfs_attr_leaf_entry_t));
+                       INT_MOD(map->size, ARCH_CONVERT,
+                                       sizeof(xfs_attr_leaf_entry_t));
+               }
+
+               if ((INT_GET(map->base, ARCH_CONVERT)
+                                       + INT_GET(map->size, ARCH_CONVERT))
+                               == INT_GET(entry->nameidx, ARCH_CONVERT)) {
+                       before = i;
+               } else if (INT_GET(map->base, ARCH_CONVERT)
+                       == (INT_GET(entry->nameidx, ARCH_CONVERT) + entsize)) {
+                       after = i;
+               } else if (INT_GET(map->size, ARCH_CONVERT) < tmp) {
+                       tmp = INT_GET(map->size, ARCH_CONVERT);
+                       smallest = i;
+               }
+       }
+
+       /*
+        * Coalesce adjacent freemap regions,
+        * or replace the smallest region.
+        */
+       if ((before >= 0) || (after >= 0)) {
+               if ((before >= 0) && (after >= 0)) {
+                       map = &hdr->freemap[before];
+                       INT_MOD(map->size, ARCH_CONVERT, entsize);
+                       INT_MOD(map->size, ARCH_CONVERT,
+                               INT_GET(hdr->freemap[after].size,
+                                                       ARCH_CONVERT));
+                       hdr->freemap[after].base = 0;
+                       hdr->freemap[after].size = 0;
+               } else if (before >= 0) {
+                       map = &hdr->freemap[before];
+                       INT_MOD(map->size, ARCH_CONVERT, entsize);
+               } else {
+                       map = &hdr->freemap[after];
+                       /* both on-disk, don't endian flip twice */
+                       map->base = entry->nameidx;
+                       INT_MOD(map->size, ARCH_CONVERT, entsize);
+               }
+       } else {
+               /*
+                * Replace smallest region (if it is smaller than free'd entry)
+                */
+               map = &hdr->freemap[smallest];
+               if (INT_GET(map->size, ARCH_CONVERT) < entsize) {
+                       INT_SET(map->base, ARCH_CONVERT,
+                                       INT_GET(entry->nameidx, ARCH_CONVERT));
+                       INT_SET(map->size, ARCH_CONVERT, entsize);
+               }
+       }
+
+       /*
+        * Did we remove the first entry?
+        */
+       if (INT_GET(entry->nameidx, ARCH_CONVERT)
+                               == INT_GET(hdr->firstused, ARCH_CONVERT))
+               smallest = 1;
+       else
+               smallest = 0;
+
+       /*
+        * Compress the remaining entries and zero out the removed stuff.
+        */
+       memset(XFS_ATTR_LEAF_NAME(leaf, args->index), 0, entsize);
+       INT_MOD(hdr->usedbytes, ARCH_CONVERT, -entsize);
+       xfs_da_log_buf(args->trans, bp,
+            XFS_DA_LOGRANGE(leaf, XFS_ATTR_LEAF_NAME(leaf, args->index),
+                                  entsize));
+
+       tmp = (INT_GET(hdr->count, ARCH_CONVERT) - args->index)
+                                       * sizeof(xfs_attr_leaf_entry_t);
+       memmove((char *)entry, (char *)(entry+1), tmp);
+       INT_MOD(hdr->count, ARCH_CONVERT, -1);
+       xfs_da_log_buf(args->trans, bp,
+           XFS_DA_LOGRANGE(leaf, entry, tmp + sizeof(*entry)));
+       entry = &leaf->entries[INT_GET(hdr->count, ARCH_CONVERT)];
+       memset((char *)entry, 0, sizeof(xfs_attr_leaf_entry_t));
+
+       /*
+        * If we removed the first entry, re-find the first used byte
+        * in the name area.  Note that if the entry was the "firstused",
+        * then we don't have a "hole" in our block resulting from
+        * removing the name.
+        */
+       if (smallest) {
+               tmp = XFS_LBSIZE(mp);
+               entry = &leaf->entries[0];
+               for (i = INT_GET(hdr->count, ARCH_CONVERT)-1;
+                                               i >= 0; entry++, i--) {
+                       ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT)
+                               >= INT_GET(hdr->firstused, ARCH_CONVERT));
+                       ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT)
+                                                       < XFS_LBSIZE(mp));
+                       if (INT_GET(entry->nameidx, ARCH_CONVERT) < tmp)
+                               tmp = INT_GET(entry->nameidx, ARCH_CONVERT);
+               }
+               INT_SET(hdr->firstused, ARCH_CONVERT, tmp);
+               if (!hdr->firstused) {
+                       INT_SET(hdr->firstused, ARCH_CONVERT,
+                                       tmp - XFS_ATTR_LEAF_NAME_ALIGN);
+               }
+       } else {
+               hdr->holes = 1;         /* mark as needing compaction */
+       }
+       xfs_da_log_buf(args->trans, bp,
+                         XFS_DA_LOGRANGE(leaf, hdr, sizeof(*hdr)));
+
+       /*
+        * Check if leaf is less than 50% full, caller may want to
+        * "join" the leaf with a sibling if so.
+        */
+       tmp  = sizeof(xfs_attr_leaf_hdr_t);
+       tmp += INT_GET(leaf->hdr.count, ARCH_CONVERT)
+                                       * sizeof(xfs_attr_leaf_entry_t);
+       tmp += INT_GET(leaf->hdr.usedbytes, ARCH_CONVERT);
+       return(tmp < mp->m_attr_magicpct); /* leaf is < 37% full */
+}
+
 /*
  * Move all the attribute list entries from drop_leaf into save_leaf.
  */
@@ -912,6 +1506,138 @@ xfs_attr_leaf_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
                                                                ARCH_CONVERT);
 }
 
+/*========================================================================
+ * Routines used for finding things in the Btree.
+ *========================================================================*/
+
+/*
+ * Look up a name in a leaf attribute list structure.
+ * This is the internal routine, it uses the caller's buffer.
+ *
+ * Note that duplicate keys are allowed, but only check within the
+ * current leaf node.  The Btree code must check in adjacent leaf nodes.
+ *
+ * Return in args->index the index into the entry[] array of either
+ * the found entry, or where the entry should have been (insert before
+ * that entry).
+ *
+ * Don't change the args->value unless we find the attribute.
+ */
+int
+xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp, xfs_da_args_t *args)
+{
+       xfs_attr_leafblock_t *leaf;
+       xfs_attr_leaf_entry_t *entry;
+       xfs_attr_leaf_name_local_t *name_loc;
+       xfs_attr_leaf_name_remote_t *name_rmt;
+       int probe, span;
+       xfs_dahash_t hashval;
+
+       leaf = bp->data;
+       ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+                                               == XFS_ATTR_LEAF_MAGIC);
+       ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT)
+                                       < (XFS_LBSIZE(args->dp->i_mount)/8));
+
+       /*
+        * Binary search.  (note: small blocks will skip this loop)
+        */
+       hashval = args->hashval;
+       probe = span = INT_GET(leaf->hdr.count, ARCH_CONVERT) / 2;
+       for (entry = &leaf->entries[probe]; span > 4;
+                  entry = &leaf->entries[probe]) {
+               span /= 2;
+               if (INT_GET(entry->hashval, ARCH_CONVERT) < hashval)
+                       probe += span;
+               else if (INT_GET(entry->hashval, ARCH_CONVERT) > hashval)
+                       probe -= span;
+               else
+                       break;
+       }
+       ASSERT((probe >= 0) && 
+              (!leaf->hdr.count
+              || (probe < INT_GET(leaf->hdr.count, ARCH_CONVERT))));
+       ASSERT((span <= 4) || (INT_GET(entry->hashval, ARCH_CONVERT)
+                                                       == hashval));
+
+       /*
+        * Since we may have duplicate hashval's, find the first matching
+        * hashval in the leaf.
+        */
+       while ((probe > 0) && (INT_GET(entry->hashval, ARCH_CONVERT)
+                                                       >= hashval)) {
+               entry--;
+               probe--;
+       }
+       while ((probe < INT_GET(leaf->hdr.count, ARCH_CONVERT))
+               && (INT_GET(entry->hashval, ARCH_CONVERT) < hashval)) {
+               entry++;
+               probe++;
+       }
+       if ((probe == INT_GET(leaf->hdr.count, ARCH_CONVERT))
+                   || (INT_GET(entry->hashval, ARCH_CONVERT) != hashval)) {
+               args->index = probe;
+               return(XFS_ERROR(ENOATTR));
+       }
+
+       /*
+        * Duplicate keys may be present, so search all of them for a match.
+        */
+       for (  ; (probe < INT_GET(leaf->hdr.count, ARCH_CONVERT))
+                       && (INT_GET(entry->hashval, ARCH_CONVERT) == hashval);
+                       entry++, probe++) {
+/*
+ * GROT: Add code to remove incomplete entries.
+ */
+               /*
+                * If we are looking for INCOMPLETE entries, show only those.
+                * If we are looking for complete entries, show only those.
+                */
+               if ((args->flags & XFS_ATTR_INCOMPLETE) !=
+                   (entry->flags & XFS_ATTR_INCOMPLETE)) {
+                       continue;
+               }
+               if (entry->flags & XFS_ATTR_LOCAL) {
+                       name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, probe);
+                       if (name_loc->namelen != args->namelen)
+                               continue;
+                       if (memcmp(args->name, (char *)name_loc->nameval,
+                                            args->namelen) != 0)
+                               continue;
+                       if (((args->flags & ATTR_SECURE) != 0) !=
+                           ((entry->flags & XFS_ATTR_SECURE) != 0))
+                               continue;
+                       if (((args->flags & ATTR_ROOT) != 0) !=
+                           ((entry->flags & XFS_ATTR_ROOT) != 0))
+                               continue;
+                       args->index = probe;
+                       return(XFS_ERROR(EEXIST));
+               } else {
+                       name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, probe);
+                       if (name_rmt->namelen != args->namelen)
+                               continue;
+                       if (memcmp(args->name, (char *)name_rmt->name,
+                                            args->namelen) != 0)
+                               continue;
+                       if (((args->flags & ATTR_SECURE) != 0) !=
+                           ((entry->flags & XFS_ATTR_SECURE) != 0))
+                               continue;
+                       if (((args->flags & ATTR_ROOT) != 0) !=
+                           ((entry->flags & XFS_ATTR_ROOT) != 0))
+                               continue;
+                       args->index = probe;
+                       args->rmtblkno
+                                 = INT_GET(name_rmt->valueblk, ARCH_CONVERT);
+                       args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount,
+                                                  INT_GET(name_rmt->valuelen,
+                                                               ARCH_CONVERT));
+                       return(XFS_ERROR(EEXIST));
+               }
+       }
+       args->index = probe;
+       return(XFS_ERROR(ENOATTR));
+}
+
 
 /*========================================================================
  * Utility routines.
@@ -1167,3 +1893,374 @@ xfs_attr_leaf_newentsize(xfs_da_args_t *args, int blocksize, int *local)
        }
        return(size);
 }
+
+/*========================================================================
+ * Manage the INCOMPLETE flag in a leaf entry
+ *========================================================================*/
+
+/*
+ * Clear the INCOMPLETE flag on an entry in a leaf block.
+ */
+int
+xfs_attr_leaf_clearflag(xfs_da_args_t *args)
+{
+       xfs_attr_leafblock_t *leaf;
+       xfs_attr_leaf_entry_t *entry;
+       xfs_attr_leaf_name_remote_t *name_rmt;
+       xfs_dabuf_t *bp;
+       int error;
+#ifdef DEBUG
+       xfs_attr_leaf_name_local_t *name_loc;
+       int namelen;
+       char *name;
+#endif /* DEBUG */
+
+       /*
+        * Set up the operation.
+        */
+       error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1, &bp,
+                                            XFS_ATTR_FORK);
+       if (error) {
+               return(error);
+       }
+       ASSERT(bp != NULL);
+
+       leaf = bp->data;
+       ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+                                               == XFS_ATTR_LEAF_MAGIC);
+       ASSERT(args->index < INT_GET(leaf->hdr.count, ARCH_CONVERT));
+       ASSERT(args->index >= 0);
+       entry = &leaf->entries[ args->index ];
+       ASSERT(entry->flags & XFS_ATTR_INCOMPLETE);
+
+#ifdef DEBUG
+       if (entry->flags & XFS_ATTR_LOCAL) {
+               name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, args->index);
+               namelen = name_loc->namelen;
+               name = (char *)name_loc->nameval;
+       } else {
+               name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, args->index);
+               namelen = name_rmt->namelen;
+               name = (char *)name_rmt->name;
+       }
+       ASSERT(INT_GET(entry->hashval, ARCH_CONVERT) == args->hashval);
+       ASSERT(namelen == args->namelen);
+       ASSERT(memcmp(name, args->name, namelen) == 0);
+#endif /* DEBUG */
+
+       entry->flags &= ~XFS_ATTR_INCOMPLETE;
+       xfs_da_log_buf(args->trans, bp,
+                        XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));
+
+       if (args->rmtblkno) {
+               ASSERT((entry->flags & XFS_ATTR_LOCAL) == 0);
+               name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, args->index);
+               INT_SET(name_rmt->valueblk, ARCH_CONVERT, args->rmtblkno);
+               INT_SET(name_rmt->valuelen, ARCH_CONVERT, args->valuelen);
+               xfs_da_log_buf(args->trans, bp,
+                        XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt)));
+       }
+       xfs_da_buf_done(bp);
+
+       /*
+        * Commit the flag value change and start the next trans in series.
+        */
+       error = xfs_attr_rolltrans(&args->trans, args->dp);
+
+       return(error);
+}
+
+/*
+ * Set the INCOMPLETE flag on an entry in a leaf block.
+ */
+int
+xfs_attr_leaf_setflag(xfs_da_args_t *args)
+{
+       xfs_attr_leafblock_t *leaf;
+       xfs_attr_leaf_entry_t *entry;
+       xfs_attr_leaf_name_remote_t *name_rmt;
+       xfs_dabuf_t *bp;
+       int error;
+
+       /*
+        * Set up the operation.
+        */
+       error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1, &bp,
+                                            XFS_ATTR_FORK);
+       if (error) {
+               return(error);
+       }
+       ASSERT(bp != NULL);
+
+       leaf = bp->data;
+       ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+                                               == XFS_ATTR_LEAF_MAGIC);
+       ASSERT(args->index < INT_GET(leaf->hdr.count, ARCH_CONVERT));
+       ASSERT(args->index >= 0);
+       entry = &leaf->entries[ args->index ];
+
+       ASSERT((entry->flags & XFS_ATTR_INCOMPLETE) == 0);
+       entry->flags |= XFS_ATTR_INCOMPLETE;
+       xfs_da_log_buf(args->trans, bp,
+                       XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));
+       if ((entry->flags & XFS_ATTR_LOCAL) == 0) {
+               name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, args->index);
+               name_rmt->valueblk = 0;
+               name_rmt->valuelen = 0;
+               xfs_da_log_buf(args->trans, bp,
+                        XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt)));
+       }
+       xfs_da_buf_done(bp);
+
+       /*
+        * Commit the flag value change and start the next trans in series.
+        */
+       error = xfs_attr_rolltrans(&args->trans, args->dp);
+
+       return(error);
+}
+
+/*
+ * In a single transaction, clear the INCOMPLETE flag on the leaf entry
+ * given by args->blkno/index and set the INCOMPLETE flag on the leaf
+ * entry given by args->blkno2/index2.
+ *
+ * Note that they could be in different blocks, or in the same block.
+ */
+int
+xfs_attr_leaf_flipflags(xfs_da_args_t *args)
+{
+       xfs_attr_leafblock_t *leaf1, *leaf2;
+       xfs_attr_leaf_entry_t *entry1, *entry2;
+       xfs_attr_leaf_name_remote_t *name_rmt;
+       xfs_dabuf_t *bp1, *bp2;
+       int error;
+#ifdef DEBUG
+       xfs_attr_leaf_name_local_t *name_loc;
+       int namelen1, namelen2;
+       char *name1, *name2;
+#endif /* DEBUG */
+
+       /*
+        * Read the block containing the "old" attr
+        */
+       error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1, &bp1,
+                                            XFS_ATTR_FORK);
+       if (error) {
+               return(error);
+       }
+       ASSERT(bp1 != NULL);
+
+       /*
+        * Read the block containing the "new" attr, if it is different
+        */
+       if (args->blkno2 != args->blkno) {
+               error = xfs_da_read_buf(args->trans, args->dp, args->blkno2,
+                                       -1, &bp2, XFS_ATTR_FORK);
+               if (error) {
+                       return(error);
+               }
+               ASSERT(bp2 != NULL);
+       } else {
+               bp2 = bp1;
+       }
+
+       leaf1 = bp1->data;
+       ASSERT(INT_GET(leaf1->hdr.info.magic, ARCH_CONVERT)
+                                               == XFS_ATTR_LEAF_MAGIC);
+       ASSERT(args->index < INT_GET(leaf1->hdr.count, ARCH_CONVERT));
+       ASSERT(args->index >= 0);
+       entry1 = &leaf1->entries[ args->index ];
+
+       leaf2 = bp2->data;
+       ASSERT(INT_GET(leaf2->hdr.info.magic, ARCH_CONVERT)
+                                               == XFS_ATTR_LEAF_MAGIC);
+       ASSERT(args->index2 < INT_GET(leaf2->hdr.count, ARCH_CONVERT));
+       ASSERT(args->index2 >= 0);
+       entry2 = &leaf2->entries[ args->index2 ];
+
+#ifdef DEBUG
+       if (entry1->flags & XFS_ATTR_LOCAL) {
+               name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf1, args->index);
+               namelen1 = name_loc->namelen;
+               name1 = (char *)name_loc->nameval;
+       } else {
+               name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf1, args->index);
+               namelen1 = name_rmt->namelen;
+               name1 = (char *)name_rmt->name;
+       }
+       if (entry2->flags & XFS_ATTR_LOCAL) {
+               name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf2, args->index2);
+               namelen2 = name_loc->namelen;
+               name2 = (char *)name_loc->nameval;
+       } else {
+               name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf2, args->index2);
+               namelen2 = name_rmt->namelen;
+               name2 = (char *)name_rmt->name;
+       }
+       ASSERT(INT_GET(entry1->hashval, ARCH_CONVERT) == INT_GET(entry2->hashval, ARCH_CONVERT));
+       ASSERT(namelen1 == namelen2);
+       ASSERT(memcmp(name1, name2, namelen1) == 0);
+#endif /* DEBUG */
+
+       ASSERT(entry1->flags & XFS_ATTR_INCOMPLETE);
+       ASSERT((entry2->flags & XFS_ATTR_INCOMPLETE) == 0);
+
+       entry1->flags &= ~XFS_ATTR_INCOMPLETE;
+       xfs_da_log_buf(args->trans, bp1,
+                         XFS_DA_LOGRANGE(leaf1, entry1, sizeof(*entry1)));
+       if (args->rmtblkno) {
+               ASSERT((entry1->flags & XFS_ATTR_LOCAL) == 0);
+               name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf1, args->index);
+               INT_SET(name_rmt->valueblk, ARCH_CONVERT, args->rmtblkno);
+               INT_SET(name_rmt->valuelen, ARCH_CONVERT, args->valuelen);
+               xfs_da_log_buf(args->trans, bp1,
+                        XFS_DA_LOGRANGE(leaf1, name_rmt, sizeof(*name_rmt)));
+       }
+
+       entry2->flags |= XFS_ATTR_INCOMPLETE;
+       xfs_da_log_buf(args->trans, bp2,
+                         XFS_DA_LOGRANGE(leaf2, entry2, sizeof(*entry2)));
+       if ((entry2->flags & XFS_ATTR_LOCAL) == 0) {
+               name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf2, args->index2);
+               name_rmt->valueblk = 0;
+               name_rmt->valuelen = 0;
+               xfs_da_log_buf(args->trans, bp2,
+                        XFS_DA_LOGRANGE(leaf2, name_rmt, sizeof(*name_rmt)));
+       }
+       xfs_da_buf_done(bp1);
+       if (bp1 != bp2)
+               xfs_da_buf_done(bp2);
+
+       /*
+        * Commit the flag value change and start the next trans in series.
+        */
+       error = xfs_attr_rolltrans(&args->trans, args->dp);
+
+       return(error);
+}
+
+#if 0
+/*
+ * Look at all the extents for this logical region,
+ * invalidate any buffers that are incore/in transactions.
+ */
+STATIC int
+xfs_attr_leaf_freextent(xfs_trans_t **trans, xfs_inode_t *dp,
+                                   xfs_dablk_t blkno, int blkcnt)
+{
+       xfs_bmbt_irec_t map;
+       xfs_dablk_t tblkno;
+       int tblkcnt, dblkcnt, nmap, error;
+       xfs_daddr_t dblkno;
+       xfs_buf_t *bp;
+
+       /*
+        * Roll through the "value", invalidating the attribute value's
+        * blocks.
+        */
+       tblkno = blkno;
+       tblkcnt = blkcnt;
+       while (tblkcnt > 0) {
+               /*
+                * Try to remember where we decided to put the value.
+                */
+               nmap = 1;
+               error = xfs_bmapi(*trans, dp, (xfs_fileoff_t)tblkno, tblkcnt,
+                                       XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
+                                       NULL, 0, &map, &nmap, NULL);
+               if (error) {
+                       return(error);
+               }
+               ASSERT(nmap == 1);
+               ASSERT(map.br_startblock != DELAYSTARTBLOCK);
+
+               /*
+                * If it's a hole, these are already unmapped
+                * so there's nothing to invalidate.
+                */
+               if (map.br_startblock != HOLESTARTBLOCK) {
+
+                       dblkno = XFS_FSB_TO_DADDR(dp->i_mount,
+                                                 map.br_startblock);
+                       dblkcnt = XFS_FSB_TO_BB(dp->i_mount,
+                                               map.br_blockcount);
+                       bp = xfs_trans_get_buf(*trans,
+                                       dp->i_mount->m_ddev_targp,
+                                       dblkno, dblkcnt, XFS_BUF_LOCK);
+                       xfs_trans_binval(*trans, bp);
+                       /*
+                        * Roll to next transaction.
+                        */
+                       if ((error = xfs_attr_rolltrans(trans, dp)))
+                               return (error);
+               }
+
+               tblkno += map.br_blockcount;
+               tblkcnt -= map.br_blockcount;
+       }
+
+       return(0);
+}
+#endif
+
+
+/*
+ * Roll from one trans in the sequence of PERMANENT transactions to the next.
+ */
+int
+xfs_attr_rolltrans(xfs_trans_t **transp, xfs_inode_t *dp)
+{
+       xfs_trans_t *trans;
+       unsigned int logres, count;
+       int     error;
+
+       /*
+        * Ensure that the inode is always logged.
+        */
+       trans = *transp;
+       xfs_trans_log_inode(trans, dp, XFS_ILOG_CORE);
+
+       /*
+        * Copy the critical parameters from one trans to the next.
+        */
+#ifdef __KERNEL__
+       logres = trans->t_log_res;
+       count = trans->t_log_count;
+#else
+       logres = count = 0;
+#endif
+       *transp = xfs_trans_dup(trans);
+
+       /*
+        * Commit the current transaction.
+        * If this commit failed, then it'd just unlock those items that
+        * are not marked ihold. That also means that a filesystem shutdown
+        * is in progress. The caller takes the responsibility to cancel
+        * the duplicate transaction that gets returned.
+        */
+       if ((error = xfs_trans_commit(trans, 0, NULL)))
+               return (error);
+
+       trans = *transp;
+
+       /*
+        * Reserve space in the log for th next transaction.
+        * This also pushes items in the "AIL", the list of logged items,
+        * out to disk if they are taking up space at the tail of the log
+        * that we want to use.  This requires that either nothing be locked
+        * across this call, or that anything that is locked be logged in
+        * the prior and the next transactions.
+        */
+       error = xfs_trans_reserve(trans, 0, logres, 0,
+                                 XFS_TRANS_PERM_LOG_RES, count);
+       /*
+        *  Ensure that the inode is in the new transaction and locked.
+        */
+       if (!error) {
+               xfs_trans_ijoin(trans, dp, XFS_ILOCK_EXCL);
+               xfs_trans_ihold(trans, dp);
+       }
+       return (error);
+
+}
index cb5cca094213b37c61d2f548d0f4f2f5a14cfb12..00b9ce1a3a4c37a95cb2bae81818dba9d8a656a9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
 
 #include <xfs.h>
 
+extern xfs_zone_t      *xfs_ifork_zone;
 xfs_zone_t             *xfs_bmap_free_item_zone;
 
+STATIC int                                     /* error */
+xfs_bmap_add_attrfork_btree(
+       xfs_trans_t             *tp,            /* transaction pointer */
+       xfs_inode_t             *ip,            /* incore inode pointer */
+       xfs_fsblock_t           *firstblock,    /* first block allocated */
+       xfs_bmap_free_t         *flist,         /* blocks to free at commit */
+       int                     *flags)         /* inode logging flags */
+{
+       xfs_btree_cur_t         *cur;           /* btree cursor */
+       int                     error;          /* error return value */
+       xfs_mount_t             *mp;            /* file system mount struct */
+       int                     stat;           /* newroot status */
+
+       mp = ip->i_mount;
+       if (ip->i_df.if_broot_bytes <= XFS_IFORK_DSIZE(ip))
+               *flags |= XFS_ILOG_DBROOT;
+       else {
+               cur = xfs_btree_init_cursor(mp, tp, NULL, 0, XFS_BTNUM_BMAP, ip,
+                       XFS_DATA_FORK);
+               cur->bc_private.b.flist = flist;
+               cur->bc_private.b.firstblock = *firstblock;
+               if ((error = xfs_bmbt_lookup_ge(cur, 0, 0, 0, &stat)))
+                       goto error0;
+               ASSERT(stat == 1);      /* must be at least one entry */
+               if ((error = xfs_bmbt_newroot(cur, flags, &stat)))
+                       goto error0;
+               if (stat == 0) {
+                       xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+                       return XFS_ERROR(ENOSPC);
+               }
+               *firstblock = cur->bc_private.b.firstblock;
+               cur->bc_private.b.allocated = 0;
+               xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+       }
+       return 0;
+error0:
+       xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+       return error;
+}
+
+/*
+ * Called from xfs_bmap_add_attrfork to handle extents format files.
+ */
+STATIC int                                     /* error */
+xfs_bmap_add_attrfork_extents(
+       xfs_trans_t             *tp,            /* transaction pointer */
+       xfs_inode_t             *ip,            /* incore inode pointer */
+       xfs_fsblock_t           *firstblock,    /* first block allocated */
+       xfs_bmap_free_t         *flist,         /* blocks to free at commit */
+       int                     *flags)         /* inode logging flags */
+{
+       xfs_btree_cur_t         *cur;           /* bmap btree cursor */
+       int                     error;          /* error return value */
+
+       if (ip->i_d.di_nextents * sizeof(xfs_bmbt_rec_t) <= XFS_IFORK_DSIZE(ip))
+               return 0;
+       cur = NULL;
+       error = xfs_bmap_extents_to_btree(tp, ip, firstblock, flist, &cur, 0,
+               flags, XFS_DATA_FORK);
+       if (cur) {
+               cur->bc_private.b.allocated = 0;
+               xfs_btree_del_cursor(cur,
+                       error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+       }
+       return error;
+}
+
+/*
+ * Called from xfs_bmap_add_attrfork to handle local format files.
+ */
+STATIC int                                     /* error */
+xfs_bmap_add_attrfork_local(
+       xfs_trans_t             *tp,            /* transaction pointer */
+       xfs_inode_t             *ip,            /* incore inode pointer */
+       xfs_fsblock_t           *firstblock,    /* first block allocated */
+       xfs_bmap_free_t         *flist,         /* blocks to free at commit */
+       int                     *flags)         /* inode logging flags */
+{
+       xfs_da_args_t           dargs;          /* args for dir/attr code */
+       int                     error;          /* error return value */
+       xfs_mount_t             *mp;            /* mount structure pointer */
+
+       if (ip->i_df.if_bytes <= XFS_IFORK_DSIZE(ip))
+               return 0;
+       if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
+               mp = ip->i_mount;
+               memset(&dargs, 0, sizeof(dargs));
+               dargs.dp = ip;
+               dargs.firstblock = firstblock;
+               dargs.flist = flist;
+               dargs.total = mp->m_dirblkfsbs;
+               dargs.whichfork = XFS_DATA_FORK;
+               dargs.trans = tp;
+               error = XFS_DIR_SHORTFORM_TO_SINGLE(mp, &dargs);
+       } else
+               error = xfs_bmap_local_to_extents(tp, ip, firstblock, 1, flags,
+                       XFS_DATA_FORK);
+       return error;
+}
+
 /*
  * Called by xfs_bmapi to update extent list structure and the btree
  * after allocating space (or doing a delayed allocation).
@@ -3095,6 +3196,134 @@ xfs_bmap_worst_indlen(
        return rval;
 }
 
+/*
+ * Convert inode from non-attributed to attributed.
+ * Must not be in a transaction, ip must not be locked.
+ */
+int                                            /* error code */
+xfs_bmap_add_attrfork(
+       xfs_inode_t             *ip,            /* incore inode pointer */
+       int                     rsvd)           /* OK to allocated reserved blocks in trans */
+{
+       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 */
+
+       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);
+       blks = XFS_ADDAFORK_SPACE_RES(mp);
+       if (rsvd)
+               tp->t_flags |= XFS_TRANS_RESERVE;
+       if ((error = xfs_trans_reserve(tp, blks, XFS_ADDAFORK_LOG_RES(mp), 0,
+                       XFS_TRANS_PERM_LOG_RES, XFS_ADDAFORK_LOG_COUNT)))
+               goto error0;
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       error = XFS_TRANS_RESERVE_QUOTA_NBLKS(mp, tp, ip, blks, 0, rsvd ?
+                       XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES :
+                       XFS_QMOPT_RES_REGBLKS);
+       if (error) {
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+               xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES);
+               return error;
+       }
+       if (XFS_IFORK_Q(ip))
+               goto error1;
+       if (ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS) {
+               /*
+                * For inodes coming from pre-6.2 filesystems.
+                */
+               ASSERT(ip->i_d.di_aformat == 0);
+               ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+       }
+       ASSERT(ip->i_d.di_anextents == 0);
+       VN_HOLD(XFS_ITOV(ip));
+       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+       switch (ip->i_d.di_format) {
+       case XFS_DINODE_FMT_DEV:
+               ip->i_d.di_forkoff = roundup(sizeof(xfs_dev_t), 8) >> 3;
+               break;
+       case XFS_DINODE_FMT_UUID:
+               ip->i_d.di_forkoff = roundup(sizeof(uuid_t), 8) >> 3;
+               break;
+       case XFS_DINODE_FMT_LOCAL:
+       case XFS_DINODE_FMT_EXTENTS:
+       case XFS_DINODE_FMT_BTREE:
+               ip->i_d.di_forkoff = mp->m_attroffset >> 3;
+               break;
+       default:
+               ASSERT(0);
+               error = XFS_ERROR(EINVAL);
+               goto error1;
+       }
+       ip->i_df.if_ext_max =
+               XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
+       ASSERT(ip->i_afp == NULL);
+       ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP);
+       ip->i_afp->if_ext_max =
+               XFS_IFORK_ASIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
+       ip->i_afp->if_flags = XFS_IFEXTENTS;
+       logflags = 0;
+       XFS_BMAP_INIT(&flist, &firstblock);
+       switch (ip->i_d.di_format) {
+       case XFS_DINODE_FMT_LOCAL:
+               error = xfs_bmap_add_attrfork_local(tp, ip, &firstblock, &flist,
+                       &logflags);
+               break;
+       case XFS_DINODE_FMT_EXTENTS:
+               error = xfs_bmap_add_attrfork_extents(tp, ip, &firstblock,
+                       &flist, &logflags);
+               break;
+       case XFS_DINODE_FMT_BTREE:
+               error = xfs_bmap_add_attrfork_btree(tp, ip, &firstblock, &flist,
+                       &logflags);
+               break;
+       default:
+               error = 0;
+               break;
+       }
+       if (logflags)
+               xfs_trans_log_inode(tp, ip, logflags);
+       if (error)
+               goto error2;
+       if (!XFS_SB_VERSION_HASATTR(&mp->m_sb)) {
+               s = XFS_SB_LOCK(mp);
+               if (!XFS_SB_VERSION_HASATTR(&mp->m_sb)) {
+                       XFS_SB_VERSION_ADDATTR(&mp->m_sb);
+                       XFS_SB_UNLOCK(mp, s);
+                       xfs_mod_sb(tp, XFS_SB_VERSIONNUM);
+               } else
+                       XFS_SB_UNLOCK(mp, s);
+       }
+       if ((error = xfs_bmap_finish(&tp, &flist, firstblock, &committed)))
+               goto error2;
+       error = xfs_trans_commit(tp, XFS_TRANS_PERM_LOG_RES, NULL);
+       ASSERT(ip->i_df.if_ext_max ==
+              XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t));
+       return error;
+error2:
+       xfs_bmap_cancel(&flist);
+error1:
+       ASSERT(ismrlocked(&ip->i_lock,MR_UPDATE));
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+error0:
+       xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
+       ASSERT(ip->i_df.if_ext_max ==
+              XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t));
+       return error;
+}
+
 /*
  * Add the extent to the list of extents to be free at transaction end.
  * The list is maintained sorted (by block number).
@@ -3183,6 +3412,26 @@ xfs_bmap_compute_maxlevels(
        mp->m_bm_maxlevels[whichfork] = level;
 }
 
+/*
+ * Free up any items left in the list.
+ */
+void
+xfs_bmap_cancel(
+       xfs_bmap_free_t         *flist) /* list of bmap_free_items */
+{
+       xfs_bmap_free_item_t    *free;  /* free list item */
+       xfs_bmap_free_item_t    *next;
+
+       if (flist->xbf_count == 0)
+               return;
+       ASSERT(flist->xbf_first != NULL);
+       for (free = flist->xbf_first; free; free = next) {
+               next = free->xbfi_next;
+               xfs_bmap_del_free(flist, NULL, free);
+       }
+       ASSERT(flist->xbf_count == 0);
+}
+
 /*
  * Returns the file-relative block number of the first unused block(s)
  * in the file with at least "len" logically contiguous blocks free.
@@ -3739,7 +3988,7 @@ xfs_bmapi(
                                                XFS_SBS_FREXTENTS,
                                                -(ralen), rsvd)) {
                                                if (XFS_IS_QUOTA_ON(ip->i_mount))
-                                                       XFS_TRANS_UNRESERVE_BLKQUOTA(
+                                                       (void)XFS_TRANS_UNRESERVE_BLKQUOTA(
                                                                mp, NULL, ip,
                                                                (long)alen);
                                                break;
@@ -3749,7 +3998,7 @@ xfs_bmapi(
                                                              XFS_SBS_FDBLOCKS,
                                                              -(alen), rsvd)) {
                                                if (XFS_IS_QUOTA_ON(ip->i_mount))
-                                                       XFS_TRANS_UNRESERVE_BLKQUOTA(
+                                                       (void)XFS_TRANS_UNRESERVE_BLKQUOTA(
                                                                mp, NULL, ip,
                                                                (long)alen);
                                                break;
@@ -3758,7 +4007,7 @@ xfs_bmapi(
 
                                if (xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS,
                                                -(indlen), rsvd)) {
-                                       XFS_TRANS_UNRESERVE_BLKQUOTA(
+                                       (void)XFS_TRANS_UNRESERVE_BLKQUOTA(
                                                mp, NULL, ip, (long)alen);
                                        break;
                                }
@@ -4389,7 +4638,7 @@ xfs_bunmapi(
                        xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS,
                                (int)del.br_blockcount, rsvd);
                        /* Unreserve our quota space */
-                       XFS_TRANS_RESERVE_QUOTA_NBLKS(
+                       (void)XFS_TRANS_RESERVE_QUOTA_NBLKS(
                                mp, NULL, ip, -((long)del.br_blockcount), 0,
                                isrt ?  XFS_QMOPT_RES_RTBLKS :
                                        XFS_QMOPT_RES_REGBLKS);
index 16a5075cf35701f7510bdb939d7ab68f5cb48e4f..e236b3d3e5dba943b4774ac77abc4a95757f8542 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -112,9 +112,6 @@ xfs_da_split(xfs_da_state_t *state)
                 */
                switch (oldblk->magic) {
                case XFS_ATTR_LEAF_MAGIC:
-#ifndef __KERNEL__
-                       return(ENOTTY);
-#else
                        error = xfs_attr_leaf_split(state, oldblk, newblk);
                        if ((error != 0) && (error != ENOSPC)) {
                                return(error);  /* GROT: attr is inconsistent */
@@ -140,7 +137,6 @@ xfs_da_split(xfs_da_state_t *state)
                                return(error);  /* GROT: attr inconsistent */
                        addblk = newblk;
                        break;
-#endif
                case XFS_DIR_LEAF_MAGIC:
                        ASSERT(XFS_DIR_IS_V1(state->mp));
                        error = xfs_dir_leaf_split(state, oldblk, newblk);
@@ -628,18 +624,12 @@ xfs_da_join(xfs_da_state_t *state)
                 */
                switch (drop_blk->magic) {
                case XFS_ATTR_LEAF_MAGIC:
-#ifndef __KERNEL__
-                       error = ENOTTY;
-#else
                        error = xfs_attr_leaf_toosmall(state, &action);
-#endif
                        if (error)
                                return(error);
                        if (action == 0)
                                return(0);
-#ifdef __KERNEL__
                        xfs_attr_leaf_unbalance(state, drop_blk, save_blk);
-#endif
                        break;
                case XFS_DIR_LEAF_MAGIC:
                        ASSERT(XFS_DIR_IS_V1(state->mp));
@@ -895,13 +885,11 @@ xfs_da_fixhashpath(xfs_da_state_t *state, xfs_da_state_path_t *path)
        level = path->active-1;
        blk = &path->blk[ level ];
        switch (blk->magic) {
-#ifdef __KERNEL__
        case XFS_ATTR_LEAF_MAGIC:
                lasthash = xfs_attr_leaf_lasthash(blk->bp, &count);
                if (count == 0)
                        return;
                break;
-#endif
        case XFS_DIR_LEAF_MAGIC:
                ASSERT(XFS_DIR_IS_V1(state->mp));
                lasthash = xfs_dir_leaf_lasthash(blk->bp, &count);
@@ -1142,12 +1130,10 @@ xfs_da_node_lookup_int(xfs_da_state_t *state, int *result)
                                blkno = INT_GET(btree->before, ARCH_CONVERT);
                        }
                }
-#ifdef __KERNEL__
                else if (INT_GET(curr->magic, ARCH_CONVERT) == XFS_ATTR_LEAF_MAGIC) {
                        blk->hashval = xfs_attr_leaf_lasthash(blk->bp, NULL);
                        break;
                }
-#endif
                else if (INT_GET(curr->magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC) {
                        blk->hashval = xfs_dir_leaf_lasthash(blk->bp, NULL);
                        break;
@@ -1174,13 +1160,11 @@ xfs_da_node_lookup_int(xfs_da_state_t *state, int *result)
                        retval = xfs_dir2_leafn_lookup_int(blk->bp, args,
                                                        &blk->index, state);
                }
-#ifdef __KERNEL__
                else if (blk->magic == XFS_ATTR_LEAF_MAGIC) {
                        retval = xfs_attr_leaf_lookup_int(blk->bp, args);
                        blk->index = args->index;
                        args->blkno = blk->blkno;
                }
-#endif
                if (((retval == ENOENT) || (retval == ENOATTR)) &&
                    (blk->hashval == args->hashval)) {
                        error = xfs_da_path_shift(state, &state->path, 1, 1,
@@ -1190,12 +1174,10 @@ xfs_da_node_lookup_int(xfs_da_state_t *state, int *result)
                        if (retval == 0) {
                                continue;
                        }
-#ifdef __KERNEL__
                        else if (blk->magic == XFS_ATTR_LEAF_MAGIC) {
                                /* path_shift() gives ENOENT */
                                retval = XFS_ERROR(ENOATTR);
                        }
-#endif
                }
                break;
        }
@@ -1234,11 +1216,9 @@ xfs_da_blk_link(xfs_da_state_t *state, xfs_da_state_blk_t *old_blk,
        ASSERT(old_blk->magic == new_blk->magic);
 
        switch (old_blk->magic) {
-#ifdef __KERNEL__
        case XFS_ATTR_LEAF_MAGIC:
                before = xfs_attr_leaf_order(old_blk->bp, new_blk->bp);
                break;
-#endif
        case XFS_DIR_LEAF_MAGIC:
                ASSERT(XFS_DIR_IS_V1(state->mp));
                before = xfs_dir_leaf_order(old_blk->bp, new_blk->bp);
@@ -1509,12 +1489,10 @@ xfs_da_path_shift(xfs_da_state_t *state, xfs_da_state_path_t *path,
                        ASSERT(level == path->active-1);
                        blk->index = 0;
                        switch(blk->magic) {
-#ifdef __KERNEL__
                        case XFS_ATTR_LEAF_MAGIC:
                                blk->hashval = xfs_attr_leaf_lasthash(blk->bp,
                                                                      NULL);
                                break;
-#endif
                        case XFS_DIR_LEAF_MAGIC:
                                ASSERT(XFS_DIR_IS_V1(state->mp));
                                blk->hashval = xfs_dir_leaf_lasthash(blk->bp,
@@ -2124,20 +2102,16 @@ xfs_da_do_buf(
                        error = bp ? XFS_BUF_GETERROR(bp) : XFS_ERROR(EIO);
                        break;
                case 1:
-#ifndef __KERNEL__
                case 2:
-#endif
                        bp = NULL;
                        error = xfs_trans_read_buf(mp, trans, mp->m_ddev_targp,
                                mappedbno, nmapped, 0, &bp);
                        break;
-#ifdef __KERNEL__
                case 3:
                        xfs_baread(mp->m_ddev_targp, mappedbno, nmapped);
                        error = 0;
                        bp = NULL;
                        break;
-#endif
                }
                if (error) {
                        if (bp)
@@ -2542,3 +2516,14 @@ xfs_da_binval(xfs_trans_t *tp, xfs_dabuf_t *dabuf)
        if (bplist != &bp)
                kmem_free(bplist, nbuf * sizeof(*bplist));
 }
+
+/*
+ * Get the first daddr from a dabuf.
+ */
+xfs_daddr_t
+xfs_da_blkno(xfs_dabuf_t *dabuf)
+{
+       ASSERT(dabuf->nbuf);
+       ASSERT(dabuf->data);
+       return XFS_BUF_ADDR(dabuf->bps[0]);
+}