All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/12] xfsprogs-4.15: common library for misc. routines
@ 2017-11-17 20:13 Darrick J. Wong
  2017-11-17 20:13 ` [PATCH 01/12] libfrog: move all the userspace support stuff into a new library Darrick J. Wong
                   ` (12 more replies)
  0 siblings, 13 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:13 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

Hi all,

This patch series refactors miscellaneous data structures and runtime
support code out of libxfs and libxcmd into a new library to contain all
of the funny random other gunk ("libfrog") that doesn't fit anywhere
else.  Initially, libfrog will contain routines for bit manipulation,
device topology, threaded workqueues, an avl tree, and file path
detection.  Existing programs are modified to pull from libfrog; the
future xfs_scrub program will also take advantage of it.

This whole mess sits atop the libxfs-4.15-sync branch; see the
djwong-devel branch in my repo[1] for something pullable.

--D

[1] https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=djwong-devel

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 01/12] libfrog: move all the userspace support stuff into a new library
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
@ 2017-11-17 20:13 ` Darrick J. Wong
  2017-11-17 20:13 ` [PATCH 02/12] libfrog: move libxfs_log2_roundup to libfrog Darrick J. Wong
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:13 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

This library is meant to contain all the Funny Random Other Gunk that
the xfsprogs utilities rely on.  Move all that stuff into this library
to reduce the pollution in the other libraries.

Ribbit!  Ribbit!

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 Makefile             |    2 +-
 debian/rules         |    2 +-
 include/builddefs.in |    1 +
 libfrog/Makefile     |   21 +++++++++++++++++++++
 4 files changed, 24 insertions(+), 2 deletions(-)
 create mode 100644 libfrog/Makefile


diff --git a/Makefile b/Makefile
index 72d0044..4146473 100644
--- a/Makefile
+++ b/Makefile
@@ -44,7 +44,7 @@ endif
 # header install rules to populate include/xfs correctly
 HDR_SUBDIRS = include libxfs
 
-DLIB_SUBDIRS = libxlog libxcmd libhandle
+DLIB_SUBDIRS = libfrog libxlog libxcmd libhandle
 LIB_SUBDIRS = libxfs $(DLIB_SUBDIRS)
 TOOL_SUBDIRS = copy db estimate fsck growfs io logprint mkfs quota \
 		mdrestore repair rtcp m4 man doc debian spaceman
diff --git a/debian/rules b/debian/rules
index f186d79..baefdba 100755
--- a/debian/rules
+++ b/debian/rules
@@ -48,7 +48,7 @@ dibuild:
 		for dir in include libxfs; do \
 			$(MAKE) -C $$dir NODEP=1 install-headers; \
 		done; \
-		for dir in include libxfs libxcmd mkfs; do \
+		for dir in include libxfs libxcmd libfrog mkfs; do \
 			$(MAKE) -C $$dir; \
 		done; \
 		mv mkfs/mkfs.xfs mkfs/mkfs.xfs-$(bootpkg); \
diff --git a/include/builddefs.in b/include/builddefs.in
index 1d454b6..2d7b199 100644
--- a/include/builddefs.in
+++ b/include/builddefs.in
@@ -36,6 +36,7 @@ LIBEDITLINE = @libeditline@
 LIBREADLINE = @libreadline@
 LIBBLKID = @libblkid@
 LIBXFS = $(TOPDIR)/libxfs/libxfs.la
+LIBFROG = $(TOPDIR)/libfrog/libfrog.la
 LIBXCMD = $(TOPDIR)/libxcmd/libxcmd.la
 LIBXLOG = $(TOPDIR)/libxlog/libxlog.la
 LIBHANDLE = $(TOPDIR)/libhandle/libhandle.la
diff --git a/libfrog/Makefile b/libfrog/Makefile
new file mode 100644
index 0000000..231a734
--- /dev/null
+++ b/libfrog/Makefile
@@ -0,0 +1,21 @@
+#
+# Copyright (c) 2017 Oracle.  All Rights Reserved.
+#
+
+TOPDIR = ..
+include $(TOPDIR)/include/builddefs
+
+LTLIBRARY = libfrog.la
+LT_CURRENT = 0
+LT_REVISION = 0
+LT_AGE = 0
+
+CFILES =
+
+default: ltdepend $(LTLIBRARY)
+
+include $(BUILDRULES)
+
+install install-dev: default
+
+-include .ltdep


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 02/12] libfrog: move libxfs_log2_roundup to libfrog
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
  2017-11-17 20:13 ` [PATCH 01/12] libfrog: move all the userspace support stuff into a new library Darrick J. Wong
@ 2017-11-17 20:13 ` Darrick J. Wong
  2017-12-04 22:01   ` [PATCH v2 " Darrick J. Wong
  2017-11-17 20:13 ` [PATCH 03/12] libfrog: add bit manipulation functions Darrick J. Wong
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:13 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Move libxfs_log2_roundup to libfrog and remove the 'libxfs_' prefix.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 include/libfrog.h |   25 +++++++++++++++++++++++++
 include/libxfs.h  |    1 -
 libfrog/Makefile  |    3 ++-
 libfrog/util.c    |   38 ++++++++++++++++++++++++++++++++++++++
 libxfs/util.c     |   12 ------------
 mkfs/Makefile     |    5 +++--
 mkfs/maxtrres.c   |    4 ++--
 mkfs/xfs_mkfs.c   |    4 ++--
 repair/Makefile   |    6 +++---
 repair/sb.c       |    4 ++--
 10 files changed, 77 insertions(+), 25 deletions(-)
 create mode 100644 include/libfrog.h
 create mode 100644 libfrog/util.c


diff --git a/include/libfrog.h b/include/libfrog.h
new file mode 100644
index 0000000..c6a4fa7
--- /dev/null
+++ b/include/libfrog.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __LIBFROG_UTIL_H_
+#define __LIBFROG_UTIL_H_
+
+unsigned int	log2_roundup(unsigned int i);
+
+#endif /* __LIBFROG_UTIL_H_ */
diff --git a/include/libxfs.h b/include/libxfs.h
index abb01cb..e392e52 100644
--- a/include/libxfs.h
+++ b/include/libxfs.h
@@ -165,7 +165,6 @@ extern int	libxfs_log_header(char *, uuid_t *, int, int, int, xfs_lsn_t,
 
 
 /* Shared utility routines */
-extern unsigned int	libxfs_log2_roundup(unsigned int i);
 
 extern int	libxfs_alloc_file_space (struct xfs_inode *, xfs_off_t,
 				xfs_off_t, int, int);
diff --git a/libfrog/Makefile b/libfrog/Makefile
index 231a734..6d9ed07 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -10,7 +10,8 @@ LT_CURRENT = 0
 LT_REVISION = 0
 LT_AGE = 0
 
-CFILES =
+CFILES = \
+util.c
 
 default: ltdepend $(LTLIBRARY)
 
diff --git a/libfrog/util.c b/libfrog/util.c
new file mode 100644
index 0000000..be38b0b
--- /dev/null
+++ b/libfrog/util.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "platform_defs.h"
+#include "libfrog.h"
+
+/*
+ * libfrog is a collection of miscellaneous userspace utilities.
+ * It's a library of Funny Random Oddball Gunk <cough>.
+ */
+
+unsigned int
+log2_roundup(unsigned int i)
+{
+	unsigned int	rval;
+
+	for (rval = 0; rval < NBBY * sizeof(i); rval++) {
+		if ((1 << rval) >= i)
+			break;
+	}
+	return rval;
+}
diff --git a/libxfs/util.c b/libxfs/util.c
index 3b11ac4..6d8cb5e 100644
--- a/libxfs/util.c
+++ b/libxfs/util.c
@@ -631,18 +631,6 @@ libxfs_alloc_file_space(
 	return error;
 }
 
-unsigned int
-libxfs_log2_roundup(unsigned int i)
-{
-	unsigned int	rval;
-
-	for (rval = 0; rval < NBBY * sizeof(i); rval++) {
-		if ((1 << rval) >= i)
-			break;
-	}
-	return rval;
-}
-
 /*
  * Wrapper around call to libxfs_ialloc. Takes care of committing and
  * allocating a new transaction as needed.
diff --git a/mkfs/Makefile b/mkfs/Makefile
index c13b903..e2dc1d4 100644
--- a/mkfs/Makefile
+++ b/mkfs/Makefile
@@ -10,8 +10,9 @@ LTCOMMAND = mkfs.xfs
 HFILES =
 CFILES = maxtrres.c proto.c xfs_mkfs.c
 
-LLDLIBS += $(LIBXFS) $(LIBXCMD) $(LIBRT) $(LIBPTHREAD) $(LIBBLKID) $(LIBUUID)
-LTDEPENDENCIES += $(LIBXFS) $(LIBXCMD)
+LLDLIBS += $(LIBXFS) $(LIBXCMD) $(LIBFROG) $(LIBRT) $(LIBPTHREAD) $(LIBBLKID) \
+	$(LIBUUID)
+LTDEPENDENCIES += $(LIBXFS) $(LIBXCMD) $(LIBFROG)
 LLDFLAGS = -static-libtool-libs
 
 default: depend $(LTCOMMAND)
diff --git a/mkfs/maxtrres.c b/mkfs/maxtrres.c
index 04028bf..0fa18c8 100644
--- a/mkfs/maxtrres.c
+++ b/mkfs/maxtrres.c
@@ -23,7 +23,7 @@
  * of sector size, block size, inode size, directory version, and
  * directory block size.
  */
-
+#include "libfrog.h"
 #include "libxfs.h"
 #include "xfs_multidisk.h"
 
@@ -55,7 +55,7 @@ max_trans_res(
 	sbp->sb_blocklog = blocklog;
 	sbp->sb_blocksize = 1 << blocklog;
 	sbp->sb_agblocks = agsize;
-	sbp->sb_agblklog = (uint8_t)libxfs_log2_roundup((unsigned int)agsize);
+	sbp->sb_agblklog = (uint8_t)log2_roundup((unsigned int)agsize);
 	sbp->sb_inodelog = inodelog;
 	sbp->sb_inopblog = blocklog - inodelog;
 	sbp->sb_inodesize = 1 << inodelog;
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index 5bfec03..cade04c 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -15,7 +15,7 @@
  * along with this program; if not, write the Free Software Foundation,
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
-
+#include "libfrog.h"
 #include "libxfs.h"
 #include <ctype.h>
 #include "xfs_multidisk.h"
@@ -2720,7 +2720,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 	memset(mp, 0, sizeof(xfs_mount_t));
 	sbp->sb_blocklog = (uint8_t)blocklog;
 	sbp->sb_sectlog = (uint8_t)sectorlog;
-	sbp->sb_agblklog = (uint8_t)libxfs_log2_roundup((unsigned int)agsize);
+	sbp->sb_agblklog = (uint8_t)log2_roundup((unsigned int)agsize);
 	sbp->sb_agblocks = (xfs_agblock_t)agsize;
 	mp->m_blkbb_log = sbp->sb_blocklog - BBSHIFT;
 	mp->m_sectbb_log = sbp->sb_sectlog - BBSHIFT;
diff --git a/repair/Makefile b/repair/Makefile
index b7e8fd5..4184a70 100644
--- a/repair/Makefile
+++ b/repair/Makefile
@@ -20,9 +20,9 @@ CFILES = agheader.c attr_repair.c avl.c avl64.c bmap.c btree.c \
 	progress.c prefetch.c rmap.c rt.c sb.c scan.c slab.c threads.c \
 	versions.c xfs_repair.c
 
-LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBXCMD) $(LIBUUID) \
-	$(LIBRT) $(LIBPTHREAD) $(LIBBLKID)
-LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG) $(LIBXCMD)
+LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBXCMD) $(LIBFROG) $(LIBUUID) $(LIBRT) \
+	$(LIBPTHREAD) $(LIBBLKID)
+LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG) $(LIBXCMD) $(LIBFROG)
 LLDFLAGS = -static-libtool-libs
 
 default: depend $(LTCOMMAND)
diff --git a/repair/sb.c b/repair/sb.c
index acc9283..f40cdea 100644
--- a/repair/sb.c
+++ b/repair/sb.c
@@ -15,7 +15,7 @@
  * along with this program; if not, write the Free Software Foundation,
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
-
+#include "libfrog.h"
 #include "libxfs.h"
 #include "libxcmd.h"
 #include "libxlog.h"
@@ -399,7 +399,7 @@ verify_sb(char *sb_buf, xfs_sb_t *sb, int is_primary_sb)
 		sb->sb_dblocks < XFS_MIN_DBLOCKS(sb))
 		return(XR_BAD_FS_SIZE_DATA);
 
-	if (sb->sb_agblklog != (uint8_t)libxfs_log2_roundup(sb->sb_agblocks))
+	if (sb->sb_agblklog != (uint8_t)log2_roundup(sb->sb_agblocks))
 		return(XR_BAD_FS_SIZE_DATA);
 
 	if (sb->sb_inodesize < XFS_DINODE_MIN_SIZE                     ||


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 03/12] libfrog: add bit manipulation functions
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
  2017-11-17 20:13 ` [PATCH 01/12] libfrog: move all the userspace support stuff into a new library Darrick J. Wong
  2017-11-17 20:13 ` [PATCH 02/12] libfrog: move libxfs_log2_roundup to libfrog Darrick J. Wong
@ 2017-11-17 20:13 ` Darrick J. Wong
  2017-11-17 20:13 ` [PATCH 04/12] libfrog: move list_sort out of libxfs Darrick J. Wong
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:13 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Duplicate the libxfs bit manipulation functions -- this is for programs
that don't need libxfs.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 include/bitops.h |   58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)


diff --git a/include/bitops.h b/include/bitops.h
index 7774950..dd561c2 100644
--- a/include/bitops.h
+++ b/include/bitops.h
@@ -56,4 +56,62 @@ static inline unsigned fls_long(unsigned long l)
  */
 #define ffz(x)	ffs(~(x))
 
+/*
+ * XFS bit manipulation routines.  Repeated here so that some programs
+ * don't have to link in all of libxfs just to have bit manipulation.
+ */
+
+/*
+ * masks with n high/low bits set, 64-bit values
+ */
+static inline uint64_t mask64hi(int n)
+{
+	return (uint64_t)-1 << (64 - (n));
+}
+static inline uint32_t mask32lo(int n)
+{
+	return ((uint32_t)1 << (n)) - 1;
+}
+static inline uint64_t mask64lo(int n)
+{
+	return ((uint64_t)1 << (n)) - 1;
+}
+
+/* Get high bit set out of 32-bit argument, -1 if none set */
+static inline int highbit32(uint32_t v)
+{
+	return fls(v) - 1;
+}
+
+/* Get high bit set out of 64-bit argument, -1 if none set */
+static inline int highbit64(uint64_t v)
+{
+	return fls64(v) - 1;
+}
+
+/* Get low bit set out of 32-bit argument, -1 if none set */
+static inline int lowbit32(uint32_t v)
+{
+	return ffs(v) - 1;
+}
+
+/* Get low bit set out of 64-bit argument, -1 if none set */
+static inline int lowbit64(uint64_t v)
+{
+	uint32_t	w = (uint32_t)v;
+	int		n = 0;
+
+	if (w) {	/* lower bits */
+		n = ffs(w);
+	} else {	/* upper bits */
+		w = (uint32_t)(v >> 32);
+		if (w) {
+			n = ffs(w);
+			if (n)
+				n += 32;
+		}
+	}
+	return n - 1;
+}
+
 #endif


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 04/12] libfrog: move list_sort out of libxfs
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
                   ` (2 preceding siblings ...)
  2017-11-17 20:13 ` [PATCH 03/12] libfrog: add bit manipulation functions Darrick J. Wong
@ 2017-11-17 20:13 ` Darrick J. Wong
  2017-11-17 20:13 ` [PATCH 05/12] libfrog: promote avl64 code from xfs_repair Darrick J. Wong
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:13 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

List operations aren't really a part of libxfs, so move them to libfrog.
This is purely a directory tree restructuring; no functional changes,
though some indentation fixes are included.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 Makefile             |    8 
 copy/Makefile        |    4 
 db/Makefile          |    4 
 growfs/Makefile      |    4 
 io/Makefile          |    4 
 libfrog/Makefile     |    2 
 libfrog/list_sort.c  |  140 +++++++++
 libfrog/radix-tree.c |  810 ++++++++++++++++++++++++++++++++++++++++++++++++++
 libxfs/Makefile      |    2 
 libxfs/list_sort.c   |  141 ---------
 libxfs/radix-tree.c  |  808 --------------------------------------------------
 logprint/Makefile    |    4 
 mdrestore/Makefile   |    4 
 13 files changed, 969 insertions(+), 966 deletions(-)
 create mode 100644 libfrog/list_sort.c
 create mode 100644 libfrog/radix-tree.c
 delete mode 100644 libxfs/list_sort.c
 delete mode 100644 libxfs/radix-tree.c


diff --git a/Makefile b/Makefile
index 4146473..0dce80a 100644
--- a/Makefile
+++ b/Makefile
@@ -44,7 +44,8 @@ endif
 # header install rules to populate include/xfs correctly
 HDR_SUBDIRS = include libxfs
 
-DLIB_SUBDIRS = libfrog libxlog libxcmd libhandle
+LIBFROG_SUBDIR = libfrog
+DLIB_SUBDIRS = libxlog libxcmd libhandle
 LIB_SUBDIRS = libxfs $(DLIB_SUBDIRS)
 TOOL_SUBDIRS = copy db estimate fsck growfs io logprint mkfs quota \
 		mdrestore repair rtcp m4 man doc debian spaceman
@@ -66,7 +67,7 @@ LIBTOOLIZE_BIN=glibtoolize
 endif
 
 # include is listed last so it is processed last in clean rules.
-SUBDIRS = $(LIB_SUBDIRS) $(TOOL_SUBDIRS) include
+SUBDIRS = $(LIBFROG_SUBDIR) $(LIB_SUBDIRS) $(TOOL_SUBDIRS) include
 
 default: include/builddefs include/platform_defs.h
 ifeq ($(HAVE_BUILDDEFS), no)
@@ -78,7 +79,8 @@ endif
 
 # tool/lib dependencies
 # note: include/xfs is set up by libxfs, too, so everything is dependent on it.
-$(LIB_SUBDIRS) $(TOOL_SUBDIRS): include
+$(LIBFROG_SUBDIR): include
+$(LIB_SUBDIRS) $(TOOL_SUBDIRS): include libfrog
 $(DLIB_SUBDIRS) $(TOOL_SUBDIRS): libxfs
 db logprint: libxlog
 fsr: libhandle
diff --git a/copy/Makefile b/copy/Makefile
index e630b17..f86f2d7 100644
--- a/copy/Makefile
+++ b/copy/Makefile
@@ -9,8 +9,8 @@ LTCOMMAND = xfs_copy
 CFILES = xfs_copy.c
 HFILES = xfs_copy.h
 
-LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBUUID) $(LIBPTHREAD) $(LIBRT)
-LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG)
+LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBFROG) $(LIBUUID) $(LIBPTHREAD) $(LIBRT)
+LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG) $(LIBFROG)
 LLDFLAGS = -static-libtool-libs
 
 default: depend $(LTCOMMAND)
diff --git a/db/Makefile b/db/Makefile
index 8111bf1..6caa634 100644
--- a/db/Makefile
+++ b/db/Makefile
@@ -17,8 +17,8 @@ HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
 CFILES = $(HFILES:.h=.c) btdump.c
 LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
 
-LLDLIBS	= $(LIBXFS) $(LIBXLOG) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
-LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG)
+LLDLIBS	= $(LIBXFS) $(LIBXLOG) $(LIBFROG) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
+LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG) $(LIBFROG)
 LLDFLAGS += -static-libtool-libs
 
 ifeq ($(ENABLE_READLINE),yes)
diff --git a/growfs/Makefile b/growfs/Makefile
index 19616de..f0190e4 100644
--- a/growfs/Makefile
+++ b/growfs/Makefile
@@ -9,7 +9,7 @@ LTCOMMAND = xfs_growfs
 
 CFILES = xfs_growfs.c
 
-LLDLIBS = $(LIBXFS) $(LIBXCMD) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
+LLDLIBS = $(LIBXFS) $(LIBXCMD) $(LIBFROG) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
 ifeq ($(ENABLE_READLINE),yes)
 LLDLIBS += $(LIBREADLINE) $(LIBTERMCAP)
 endif
@@ -18,7 +18,7 @@ ifeq ($(ENABLE_EDITLINE),yes)
 LLDLIBS += $(LIBEDITLINE) $(LIBTERMCAP)
 endif
 
-LTDEPENDENCIES = $(LIBXFS) $(LIBXCMD)
+LTDEPENDENCIES = $(LIBXFS) $(LIBXCMD) $(LIBFROG)
 LLDFLAGS = -static-libtool-libs
 LSRCFILES = xfs_info.sh
 
diff --git a/io/Makefile b/io/Makefile
index b983bcc..2f11451 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -14,8 +14,8 @@ CFILES = init.c \
 	pwrite.c reflink.c scrub.c seek.c shutdown.c stat.c sync.c truncate.c \
 	utimes.c
 
-LLDLIBS = $(LIBXCMD) $(LIBHANDLE) $(LIBPTHREAD)
-LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE)
+LLDLIBS = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG) $(LIBPTHREAD)
+LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG)
 LLDFLAGS = -static-libtool-libs
 
 ifeq ($(HAVE_FADVISE),yes)
diff --git a/libfrog/Makefile b/libfrog/Makefile
index 6d9ed07..6034da5 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -11,6 +11,8 @@ LT_REVISION = 0
 LT_AGE = 0
 
 CFILES = \
+list_sort.c \
+radix-tree.c \
 util.c
 
 default: ltdepend $(LTLIBRARY)
diff --git a/libfrog/list_sort.c b/libfrog/list_sort.c
new file mode 100644
index 0000000..b77eece
--- /dev/null
+++ b/libfrog/list_sort.c
@@ -0,0 +1,140 @@
+/* List sorting code from Linux::lib/list_sort.c. */
+#include <stdlib.h>
+#include <string.h>
+#include "list.h"
+
+#define unlikely(x)	(x)
+#define MAX_LIST_LENGTH_BITS 20
+
+/*
+ * Returns a list organized in an intermediate format suited
+ * to chaining of merge() calls: null-terminated, no reserved or
+ * sentinel head node, "prev" links not maintained.
+ */
+static struct list_head *merge(void *priv,
+				int (*cmp)(void *priv, struct list_head *a,
+					struct list_head *b),
+				struct list_head *a, struct list_head *b)
+{
+	struct list_head head, *tail = &head;
+
+	while (a && b) {
+		/* if equal, take 'a' -- important for sort stability */
+		if ((*cmp)(priv, a, b) <= 0) {
+			tail->next = a;
+			a = a->next;
+		} else {
+			tail->next = b;
+			b = b->next;
+		}
+		tail = tail->next;
+	}
+	tail->next = a?:b;
+	return head.next;
+}
+
+/*
+ * Combine final list merge with restoration of standard doubly-linked
+ * list structure.  This approach duplicates code from merge(), but
+ * runs faster than the tidier alternatives of either a separate final
+ * prev-link restoration pass, or maintaining the prev links
+ * throughout.
+ */
+static void merge_and_restore_back_links(void *priv,
+				int (*cmp)(void *priv, struct list_head *a,
+					struct list_head *b),
+				struct list_head *head,
+				struct list_head *a, struct list_head *b)
+{
+	struct list_head *tail = head;
+	unsigned count = 0;
+
+	while (a && b) {
+		/* if equal, take 'a' -- important for sort stability */
+		if ((*cmp)(priv, a, b) <= 0) {
+			tail->next = a;
+			a->prev = tail;
+			a = a->next;
+		} else {
+			tail->next = b;
+			b->prev = tail;
+			b = b->next;
+		}
+		tail = tail->next;
+	}
+	tail->next = a ? : b;
+
+	do {
+		/*
+		 * In worst cases this loop may run many iterations.
+		 * Continue callbacks to the client even though no
+		 * element comparison is needed, so the client's cmp()
+		 * routine can invoke cond_resched() periodically.
+		 */
+		if (unlikely(!(++count)))
+			(*cmp)(priv, tail->next, tail->next);
+
+		tail->next->prev = tail;
+		tail = tail->next;
+	} while (tail->next);
+
+	tail->next = head;
+	head->prev = tail;
+}
+
+/**
+ * list_sort - sort a list
+ * @priv: private data, opaque to list_sort(), passed to @cmp
+ * @head: the list to sort
+ * @cmp: the elements comparison function
+ *
+ * This function implements "merge sort", which has O(nlog(n))
+ * complexity.
+ *
+ * The comparison function @cmp must return a negative value if @a
+ * should sort before @b, and a positive value if @a should sort after
+ * @b. If @a and @b are equivalent, and their original relative
+ * ordering is to be preserved, @cmp must return 0.
+ */
+void list_sort(void *priv, struct list_head *head,
+		int (*cmp)(void *priv, struct list_head *a,
+			struct list_head *b))
+{
+	struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
+						-- last slot is a sentinel */
+	int lev;  /* index into part[] */
+	int max_lev = 0;
+	struct list_head *list;
+
+	if (list_empty(head))
+		return;
+
+	memset(part, 0, sizeof(part));
+
+	head->prev->next = NULL;
+	list = head->next;
+
+	while (list) {
+		struct list_head *cur = list;
+		list = list->next;
+		cur->next = NULL;
+
+		for (lev = 0; part[lev]; lev++) {
+			cur = merge(priv, cmp, part[lev], cur);
+			part[lev] = NULL;
+		}
+		if (lev > max_lev) {
+			if (unlikely(lev >= ARRAY_SIZE(part)-1)) {
+				lev--;
+			}
+			max_lev = lev;
+		}
+		part[lev] = cur;
+	}
+
+	for (lev = 0; lev < max_lev; lev++)
+		if (part[lev])
+			list = merge(priv, cmp, part[lev], list);
+
+	merge_and_restore_back_links(priv, cmp, head, part[max_lev], list);
+}
diff --git a/libfrog/radix-tree.c b/libfrog/radix-tree.c
new file mode 100644
index 0000000..9fe5dd9
--- /dev/null
+++ b/libfrog/radix-tree.c
@@ -0,0 +1,810 @@
+/*
+ * Copyright (C) 2001 Momchil Velikov
+ * Portions Copyright (C) 2001 Christoph Hellwig
+ * Copyright (C) 2005 SGI, Christoph Lameter <clameter@sgi.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include "platform_defs.h"
+#include "radix-tree.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#define RADIX_TREE_MAP_SHIFT	6
+#define RADIX_TREE_MAP_SIZE	(1UL << RADIX_TREE_MAP_SHIFT)
+#define RADIX_TREE_MAP_MASK	(RADIX_TREE_MAP_SIZE-1)
+
+#ifdef RADIX_TREE_TAGS
+#define RADIX_TREE_TAG_LONGS	\
+	((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG)
+#endif
+
+struct radix_tree_node {
+	unsigned int	count;
+	void		*slots[RADIX_TREE_MAP_SIZE];
+#ifdef RADIX_TREE_TAGS
+	unsigned long	tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
+#endif
+};
+
+struct radix_tree_path {
+	struct radix_tree_node *node;
+	int offset;
+};
+
+#define RADIX_TREE_INDEX_BITS  (8 /* CHAR_BIT */ * sizeof(unsigned long))
+#define RADIX_TREE_MAX_PATH (RADIX_TREE_INDEX_BITS/RADIX_TREE_MAP_SHIFT + 2)
+
+static unsigned long height_to_maxindex[RADIX_TREE_MAX_PATH];
+
+/*
+ * Radix tree node cache.
+ */
+
+#define radix_tree_node_alloc(r)	((struct radix_tree_node *) \
+		calloc(1, sizeof(struct radix_tree_node)))
+#define radix_tree_node_free(n)		free(n)
+
+#ifdef RADIX_TREE_TAGS
+
+static inline void tag_set(struct radix_tree_node *node, unsigned int tag,
+		int offset)
+{
+	*((uint32_t *)node->tags[tag] + (offset >> 5)) |= (1 << (offset & 31));
+}
+
+static inline void tag_clear(struct radix_tree_node *node, unsigned int tag,
+		int offset)
+{
+	uint32_t	*p = (uint32_t*)node->tags[tag] + (offset >> 5);
+	uint32_t	m = 1 << (offset & 31);
+	*p &= ~m;
+}
+
+static inline int tag_get(struct radix_tree_node *node, unsigned int tag,
+		int offset)
+{
+	return 1 & (((const uint32_t *)node->tags[tag])[offset >> 5] >> (offset & 31));
+}
+
+/*
+ * Returns 1 if any slot in the node has this tag set.
+ * Otherwise returns 0.
+ */
+static inline int any_tag_set(struct radix_tree_node *node, unsigned int tag)
+{
+	int idx;
+	for (idx = 0; idx < RADIX_TREE_TAG_LONGS; idx++) {
+		if (node->tags[tag][idx])
+			return 1;
+	}
+	return 0;
+}
+
+#endif
+
+/*
+ *	Return the maximum key which can be store into a
+ *	radix tree with height HEIGHT.
+ */
+static inline unsigned long radix_tree_maxindex(unsigned int height)
+{
+	return height_to_maxindex[height];
+}
+
+/*
+ *	Extend a radix tree so it can store key @index.
+ */
+static int radix_tree_extend(struct radix_tree_root *root, unsigned long index)
+{
+	struct radix_tree_node *node;
+	unsigned int height;
+#ifdef RADIX_TREE_TAGS
+	char tags[RADIX_TREE_MAX_TAGS];
+	int tag;
+#endif
+
+	/* Figure out what the height should be.  */
+	height = root->height + 1;
+	while (index > radix_tree_maxindex(height))
+		height++;
+
+	if (root->rnode == NULL) {
+		root->height = height;
+		goto out;
+	}
+
+#ifdef RADIX_TREE_TAGS
+	/*
+	 * Prepare the tag status of the top-level node for propagation
+	 * into the newly-pushed top-level node(s)
+	 */
+	for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
+		tags[tag] = 0;
+		if (any_tag_set(root->rnode, tag))
+			tags[tag] = 1;
+	}
+#endif
+	do {
+		if (!(node = radix_tree_node_alloc(root)))
+			return -ENOMEM;
+
+		/* Increase the height.  */
+		node->slots[0] = root->rnode;
+
+#ifdef RADIX_TREE_TAGS
+		/* Propagate the aggregated tag info into the new root */
+		for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
+			if (tags[tag])
+				tag_set(node, tag, 0);
+		}
+#endif
+		node->count = 1;
+		root->rnode = node;
+		root->height++;
+	} while (height > root->height);
+out:
+	return 0;
+}
+
+/**
+ *	radix_tree_insert    -    insert into a radix tree
+ *	@root:		radix tree root
+ *	@index:		index key
+ *	@item:		item to insert
+ *
+ *	Insert an item into the radix tree at position @index.
+ */
+int radix_tree_insert(struct radix_tree_root *root,
+			unsigned long index, void *item)
+{
+	struct radix_tree_node *node = NULL, *slot;
+	unsigned int height, shift;
+	int offset;
+	int error;
+
+	/* Make sure the tree is high enough.  */
+	if ((!index && !root->rnode) ||
+			index > radix_tree_maxindex(root->height)) {
+		error = radix_tree_extend(root, index);
+		if (error)
+			return error;
+	}
+
+	slot = root->rnode;
+	height = root->height;
+	shift = (height-1) * RADIX_TREE_MAP_SHIFT;
+
+	offset = 0;			/* uninitialised var warning */
+	do {
+		if (slot == NULL) {
+			/* Have to add a child node.  */
+			if (!(slot = radix_tree_node_alloc(root)))
+				return -ENOMEM;
+			if (node) {
+				node->slots[offset] = slot;
+				node->count++;
+			} else
+				root->rnode = slot;
+		}
+
+		/* Go a level down */
+		offset = (index >> shift) & RADIX_TREE_MAP_MASK;
+		node = slot;
+		slot = node->slots[offset];
+		shift -= RADIX_TREE_MAP_SHIFT;
+		height--;
+	} while (height > 0);
+
+	if (slot != NULL)
+		return -EEXIST;
+
+	ASSERT(node);
+	node->count++;
+	node->slots[offset] = item;
+#ifdef RADIX_TREE_TAGS
+	ASSERT(!tag_get(node, 0, offset));
+	ASSERT(!tag_get(node, 1, offset));
+#endif
+	return 0;
+}
+
+static inline void **__lookup_slot(struct radix_tree_root *root,
+				   unsigned long index)
+{
+	unsigned int height, shift;
+	struct radix_tree_node **slot;
+
+	height = root->height;
+	if (index > radix_tree_maxindex(height))
+		return NULL;
+
+	shift = (height-1) * RADIX_TREE_MAP_SHIFT;
+	slot = &root->rnode;
+
+	while (height > 0) {
+		if (*slot == NULL)
+			return NULL;
+
+		slot = (struct radix_tree_node **)
+			((*slot)->slots +
+				((index >> shift) & RADIX_TREE_MAP_MASK));
+		shift -= RADIX_TREE_MAP_SHIFT;
+		height--;
+	}
+
+	return (void **)slot;
+}
+
+/**
+ *	radix_tree_lookup_slot    -    lookup a slot in a radix tree
+ *	@root:		radix tree root
+ *	@index:		index key
+ *
+ *	Lookup the slot corresponding to the position @index in the radix tree
+ *	@root. This is useful for update-if-exists operations.
+ */
+void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index)
+{
+	return __lookup_slot(root, index);
+}
+
+/**
+ *	radix_tree_lookup    -    perform lookup operation on a radix tree
+ *	@root:		radix tree root
+ *	@index:		index key
+ *
+ *	Lookup the item at the position @index in the radix tree @root.
+ */
+void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
+{
+	void **slot;
+
+	slot = __lookup_slot(root, index);
+	return slot != NULL ? *slot : NULL;
+}
+
+/**
+ *	raid_tree_first_key - find the first index key in the radix tree
+ *	@root:		radix tree root
+ *	@index:		where the first index will be placed
+ *
+ *	Returns the first entry and index key in the radix tree @root.
+ */
+void *radix_tree_lookup_first(struct radix_tree_root *root, unsigned long *index)
+{
+	unsigned int height, shift;
+	struct radix_tree_node *slot;
+	unsigned long i;
+
+	height = root->height;
+	*index = 0;
+	if (height == 0)
+		return NULL;
+
+	shift = (height-1) * RADIX_TREE_MAP_SHIFT;
+	slot = root->rnode;
+
+	for (; height > 1; height--) {
+		for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) {
+			if (slot->slots[i] != NULL)
+				break;
+		}
+		ASSERT(i < RADIX_TREE_MAP_SIZE);
+
+		*index |= (i << shift);
+		shift -= RADIX_TREE_MAP_SHIFT;
+		slot = slot->slots[i];
+	}
+	for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) {
+		if (slot->slots[i] != NULL) {
+			*index |= i;
+			return slot->slots[i];
+		}
+	}
+	return NULL;
+}
+
+#ifdef RADIX_TREE_TAGS
+
+/**
+ *	radix_tree_tag_set - set a tag on a radix tree node
+ *	@root:		radix tree root
+ *	@index:		index key
+ *	@tag:		tag index
+ *
+ *	Set the search tag (which must be < RADIX_TREE_MAX_TAGS)
+ *	corresponding to @index in the radix tree.  From
+ *	the root all the way down to the leaf node.
+ *
+ *	Returns the address of the tagged item.   Setting a tag on a not-present
+ *	item is a bug.
+ */
+void *radix_tree_tag_set(struct radix_tree_root *root,
+			unsigned long index, unsigned int tag)
+{
+	unsigned int height, shift;
+	struct radix_tree_node *slot;
+
+	height = root->height;
+	if (index > radix_tree_maxindex(height))
+		return NULL;
+
+	shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
+	slot = root->rnode;
+
+	while (height > 0) {
+		int offset;
+
+		offset = (index >> shift) & RADIX_TREE_MAP_MASK;
+		if (!tag_get(slot, tag, offset))
+			tag_set(slot, tag, offset);
+		slot = slot->slots[offset];
+		ASSERT(slot != NULL);
+		shift -= RADIX_TREE_MAP_SHIFT;
+		height--;
+	}
+
+	return slot;
+}
+
+/**
+ *	radix_tree_tag_clear - clear a tag on a radix tree node
+ *	@root:		radix tree root
+ *	@index:		index key
+ *	@tag:		tag index
+ *
+ *	Clear the search tag (which must be < RADIX_TREE_MAX_TAGS)
+ *	corresponding to @index in the radix tree.  If
+ *	this causes the leaf node to have no tags set then clear the tag in the
+ *	next-to-leaf node, etc.
+ *
+ *	Returns the address of the tagged item on success, else NULL.  ie:
+ *	has the same return value and semantics as radix_tree_lookup().
+ */
+void *radix_tree_tag_clear(struct radix_tree_root *root,
+			unsigned long index, unsigned int tag)
+{
+	struct radix_tree_path path[RADIX_TREE_MAX_PATH + 1], *pathp = path;
+	struct radix_tree_node *slot;
+	unsigned int height, shift;
+	void *ret = NULL;
+
+	height = root->height;
+	if (index > radix_tree_maxindex(height))
+		goto out;
+
+	shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
+	pathp->node = NULL;
+	slot = root->rnode;
+
+	while (height > 0) {
+		int offset;
+
+		if (slot == NULL)
+			goto out;
+
+		offset = (index >> shift) & RADIX_TREE_MAP_MASK;
+		pathp[1].offset = offset;
+		pathp[1].node = slot;
+		slot = slot->slots[offset];
+		pathp++;
+		shift -= RADIX_TREE_MAP_SHIFT;
+		height--;
+	}
+
+	ret = slot;
+	if (ret == NULL)
+		goto out;
+
+	do {
+		if (!tag_get(pathp->node, tag, pathp->offset))
+			goto out;
+		tag_clear(pathp->node, tag, pathp->offset);
+		if (any_tag_set(pathp->node, tag))
+			goto out;
+		pathp--;
+	} while (pathp->node);
+out:
+	return ret;
+}
+
+#endif
+
+static unsigned int
+__lookup(struct radix_tree_root *root, void **results, unsigned long index,
+	unsigned int max_items, unsigned long *next_index)
+{
+	unsigned int nr_found = 0;
+	unsigned int shift, height;
+	struct radix_tree_node *slot;
+	unsigned long i;
+
+	height = root->height;
+	if (height == 0)
+		goto out;
+
+	shift = (height-1) * RADIX_TREE_MAP_SHIFT;
+	slot = root->rnode;
+
+	for ( ; height > 1; height--) {
+
+		for (i = (index >> shift) & RADIX_TREE_MAP_MASK ;
+				i < RADIX_TREE_MAP_SIZE; i++) {
+			if (slot->slots[i] != NULL)
+				break;
+			index &= ~((1UL << shift) - 1);
+			index += 1UL << shift;
+			if (index == 0)
+				goto out;	/* 32-bit wraparound */
+		}
+		if (i == RADIX_TREE_MAP_SIZE)
+			goto out;
+
+		shift -= RADIX_TREE_MAP_SHIFT;
+		slot = slot->slots[i];
+	}
+
+	/* Bottom level: grab some items */
+	for (i = index & RADIX_TREE_MAP_MASK; i < RADIX_TREE_MAP_SIZE; i++) {
+		index++;
+		if (slot->slots[i]) {
+			results[nr_found++] = slot->slots[i];
+			if (nr_found == max_items)
+				goto out;
+		}
+	}
+out:
+	*next_index = index;
+	return nr_found;
+}
+
+/**
+ *	radix_tree_gang_lookup - perform multiple lookup on a radix tree
+ *	@root:		radix tree root
+ *	@results:	where the results of the lookup are placed
+ *	@first_index:	start the lookup from this key
+ *	@max_items:	place up to this many items at *results
+ *
+ *	Performs an index-ascending scan of the tree for present items.  Places
+ *	them at *@results and returns the number of items which were placed at
+ *	*@results.
+ *
+ *	The implementation is naive.
+ */
+unsigned int
+radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
+			unsigned long first_index, unsigned int max_items)
+{
+	const unsigned long max_index = radix_tree_maxindex(root->height);
+	unsigned long cur_index = first_index;
+	unsigned int ret = 0;
+
+	while (ret < max_items) {
+		unsigned int nr_found;
+		unsigned long next_index;	/* Index of next search */
+
+		if (cur_index > max_index)
+			break;
+		nr_found = __lookup(root, results + ret, cur_index,
+					max_items - ret, &next_index);
+		ret += nr_found;
+		if (next_index == 0)
+			break;
+		cur_index = next_index;
+	}
+	return ret;
+}
+
+/**
+ *	radix_tree_gang_lookup_ex - perform multiple lookup on a radix tree
+ *	@root:		radix tree root
+ *	@results:	where the results of the lookup are placed
+ *	@first_index:	start the lookup from this key
+ *	@last_index:	don't lookup past this key
+ *	@max_items:	place up to this many items at *results
+ *
+ *	Performs an index-ascending scan of the tree for present items starting
+ *	@first_index until @last_index up to as many as @max_items.  Places
+ *	them at *@results and returns the number of items which were placed
+ *	at *@results.
+ *
+ *	The implementation is naive.
+ */
+unsigned int
+radix_tree_gang_lookup_ex(struct radix_tree_root *root, void **results,
+			unsigned long first_index, unsigned long last_index,
+			unsigned int max_items)
+{
+	const unsigned long max_index = radix_tree_maxindex(root->height);
+	unsigned long cur_index = first_index;
+	unsigned int ret = 0;
+
+	while (ret < max_items && cur_index < last_index) {
+		unsigned int nr_found;
+		unsigned long next_index;	/* Index of next search */
+
+		if (cur_index > max_index)
+			break;
+		nr_found = __lookup(root, results + ret, cur_index,
+					max_items - ret, &next_index);
+		ret += nr_found;
+		if (next_index == 0)
+			break;
+		cur_index = next_index;
+	}
+	return ret;
+}
+
+#ifdef RADIX_TREE_TAGS
+
+static unsigned int
+__lookup_tag(struct radix_tree_root *root, void **results, unsigned long index,
+	unsigned int max_items, unsigned long *next_index, unsigned int tag)
+{
+	unsigned int nr_found = 0;
+	unsigned int shift;
+	unsigned int height = root->height;
+	struct radix_tree_node *slot;
+
+	shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
+	slot = root->rnode;
+
+	while (height > 0) {
+		unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK;
+
+		for ( ; i < RADIX_TREE_MAP_SIZE; i++) {
+			if (tag_get(slot, tag, i)) {
+				ASSERT(slot->slots[i] != NULL);
+				break;
+			}
+			index &= ~((1UL << shift) - 1);
+			index += 1UL << shift;
+			if (index == 0)
+				goto out;	/* 32-bit wraparound */
+		}
+		if (i == RADIX_TREE_MAP_SIZE)
+			goto out;
+		height--;
+		if (height == 0) {	/* Bottom level: grab some items */
+			unsigned long j = index & RADIX_TREE_MAP_MASK;
+
+			for ( ; j < RADIX_TREE_MAP_SIZE; j++) {
+				index++;
+				if (tag_get(slot, tag, j)) {
+					ASSERT(slot->slots[j] != NULL);
+					results[nr_found++] = slot->slots[j];
+					if (nr_found == max_items)
+						goto out;
+				}
+			}
+		}
+		shift -= RADIX_TREE_MAP_SHIFT;
+		slot = slot->slots[i];
+	}
+out:
+	*next_index = index;
+	return nr_found;
+}
+
+/**
+ *	radix_tree_gang_lookup_tag - perform multiple lookup on a radix tree
+ *	                             based on a tag
+ *	@root:		radix tree root
+ *	@results:	where the results of the lookup are placed
+ *	@first_index:	start the lookup from this key
+ *	@max_items:	place up to this many items at *results
+ *	@tag:		the tag index (< RADIX_TREE_MAX_TAGS)
+ *
+ *	Performs an index-ascending scan of the tree for present items which
+ *	have the tag indexed by @tag set.  Places the items at *@results and
+ *	returns the number of items which were placed at *@results.
+ */
+unsigned int
+radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results,
+		unsigned long first_index, unsigned int max_items,
+		unsigned int tag)
+{
+	const unsigned long max_index = radix_tree_maxindex(root->height);
+	unsigned long cur_index = first_index;
+	unsigned int ret = 0;
+
+	while (ret < max_items) {
+		unsigned int nr_found;
+		unsigned long next_index;	/* Index of next search */
+
+		if (cur_index > max_index)
+			break;
+		nr_found = __lookup_tag(root, results + ret, cur_index,
+					max_items - ret, &next_index, tag);
+		ret += nr_found;
+		if (next_index == 0)
+			break;
+		cur_index = next_index;
+	}
+	return ret;
+}
+
+#endif
+
+/**
+ *	radix_tree_shrink    -    shrink height of a radix tree to minimal
+ *	@root		radix tree root
+ */
+static inline void radix_tree_shrink(struct radix_tree_root *root)
+{
+	/* try to shrink tree height */
+	while (root->height > 1 &&
+			root->rnode->count == 1 &&
+			root->rnode->slots[0]) {
+		struct radix_tree_node *to_free = root->rnode;
+
+		root->rnode = to_free->slots[0];
+		root->height--;
+		/* must only free zeroed nodes into the slab */
+#ifdef RADIX_TREE_TAGS
+		tag_clear(to_free, 0, 0);
+		tag_clear(to_free, 1, 0);
+#endif
+		to_free->slots[0] = NULL;
+		to_free->count = 0;
+		radix_tree_node_free(to_free);
+	}
+}
+
+/**
+ *	radix_tree_delete    -    delete an item from a radix tree
+ *	@root:		radix tree root
+ *	@index:		index key
+ *
+ *	Remove the item at @index from the radix tree rooted at @root.
+ *
+ *	Returns the address of the deleted item, or NULL if it was not present.
+ */
+void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
+{
+	struct radix_tree_path path[RADIX_TREE_MAX_PATH + 1], *pathp = path;
+	struct radix_tree_path *orig_pathp;
+	struct radix_tree_node *slot;
+	unsigned int height, shift;
+	void *ret = NULL;
+#ifdef RADIX_TREE_TAGS
+	char tags[RADIX_TREE_MAX_TAGS];
+	int nr_cleared_tags;
+	int tag;
+#endif
+	int offset;
+
+	height = root->height;
+	if (index > radix_tree_maxindex(height))
+		goto out;
+
+	shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
+	pathp->node = NULL;
+	slot = root->rnode;
+
+	for ( ; height > 0; height--) {
+		if (slot == NULL)
+			goto out;
+
+		pathp++;
+		offset = (index >> shift) & RADIX_TREE_MAP_MASK;
+		pathp->offset = offset;
+		pathp->node = slot;
+		slot = slot->slots[offset];
+		shift -= RADIX_TREE_MAP_SHIFT;
+	}
+
+	ret = slot;
+	if (ret == NULL)
+		goto out;
+
+	orig_pathp = pathp;
+
+#ifdef RADIX_TREE_TAGS
+	/*
+	 * Clear all tags associated with the just-deleted item
+	 */
+	nr_cleared_tags = 0;
+	for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
+		tags[tag] = 1;
+		if (tag_get(pathp->node, tag, pathp->offset)) {
+			tag_clear(pathp->node, tag, pathp->offset);
+			if (!any_tag_set(pathp->node, tag)) {
+				tags[tag] = 0;
+				nr_cleared_tags++;
+			}
+		}
+	}
+
+	for (pathp--; nr_cleared_tags && pathp->node; pathp--) {
+		for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
+			if (tags[tag])
+				continue;
+
+			tag_clear(pathp->node, tag, pathp->offset);
+			if (any_tag_set(pathp->node, tag)) {
+				tags[tag] = 1;
+				nr_cleared_tags--;
+			}
+		}
+	}
+#endif
+	/* Now free the nodes we do not need anymore */
+	for (pathp = orig_pathp; pathp->node; pathp--) {
+		pathp->node->slots[pathp->offset] = NULL;
+		pathp->node->count--;
+
+		if (pathp->node->count) {
+			if (pathp->node == root->rnode)
+				radix_tree_shrink(root);
+			goto out;
+		}
+
+		/* Node with zero slots in use so free it */
+		radix_tree_node_free(pathp->node);
+	}
+	root->rnode = NULL;
+	root->height = 0;
+out:
+	return ret;
+}
+
+#ifdef RADIX_TREE_TAGS
+/**
+ *	radix_tree_tagged - test whether any items in the tree are tagged
+ *	@root:		radix tree root
+ *	@tag:		tag to test
+ */
+int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag)
+{
+	struct radix_tree_node *rnode;
+	rnode = root->rnode;
+	if (!rnode)
+		return 0;
+	return any_tag_set(rnode, tag);
+}
+#endif
+
+static unsigned long __maxindex(unsigned int height)
+{
+	unsigned int width = height * RADIX_TREE_MAP_SHIFT;
+	int shift = RADIX_TREE_INDEX_BITS - width;
+
+	if (shift < 0)
+		return ~0UL;
+	if (shift >= BITS_PER_LONG)
+		return 0UL;
+	return ~0UL >> shift;
+}
+
+static void radix_tree_init_maxindex(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(height_to_maxindex); i++)
+		height_to_maxindex[i] = __maxindex(i);
+}
+
+void radix_tree_init(void)
+{
+	radix_tree_init_maxindex();
+}
diff --git a/libxfs/Makefile b/libxfs/Makefile
index 994b1e5..0470f5f 100644
--- a/libxfs/Makefile
+++ b/libxfs/Makefile
@@ -58,9 +58,7 @@ CFILES = cache.c \
 	defer_item.c \
 	init.c \
 	kmem.c \
-	list_sort.c \
 	logitem.c \
-	radix-tree.c \
 	rdwr.c \
 	trans.c \
 	util.c \
diff --git a/libxfs/list_sort.c b/libxfs/list_sort.c
deleted file mode 100644
index 16258d9..0000000
--- a/libxfs/list_sort.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/* List sorting code from Linux::lib/list_sort.c. */
-
-#include "libxfs_priv.h"
-#include "libxfs_io.h"
-#include "init.h"
-#include "list.h"
-
-#define MAX_LIST_LENGTH_BITS 20
-
-/*
- * Returns a list organized in an intermediate format suited
- * to chaining of merge() calls: null-terminated, no reserved or
- * sentinel head node, "prev" links not maintained.
- */
-static struct list_head *merge(void *priv,
-				int (*cmp)(void *priv, struct list_head *a,
-					struct list_head *b),
-				struct list_head *a, struct list_head *b)
-{
-	struct list_head head, *tail = &head;
-
-	while (a && b) {
-		/* if equal, take 'a' -- important for sort stability */
-		if ((*cmp)(priv, a, b) <= 0) {
-			tail->next = a;
-			a = a->next;
-		} else {
-			tail->next = b;
-			b = b->next;
-		}
-		tail = tail->next;
-	}
-	tail->next = a?:b;
-	return head.next;
-}
-
-/*
- * Combine final list merge with restoration of standard doubly-linked
- * list structure.  This approach duplicates code from merge(), but
- * runs faster than the tidier alternatives of either a separate final
- * prev-link restoration pass, or maintaining the prev links
- * throughout.
- */
-static void merge_and_restore_back_links(void *priv,
-				int (*cmp)(void *priv, struct list_head *a,
-					struct list_head *b),
-				struct list_head *head,
-				struct list_head *a, struct list_head *b)
-{
-	struct list_head *tail = head;
-	unsigned count = 0;
-
-	while (a && b) {
-		/* if equal, take 'a' -- important for sort stability */
-		if ((*cmp)(priv, a, b) <= 0) {
-			tail->next = a;
-			a->prev = tail;
-			a = a->next;
-		} else {
-			tail->next = b;
-			b->prev = tail;
-			b = b->next;
-		}
-		tail = tail->next;
-	}
-	tail->next = a ? : b;
-
-	do {
-		/*
-		 * In worst cases this loop may run many iterations.
-		 * Continue callbacks to the client even though no
-		 * element comparison is needed, so the client's cmp()
-		 * routine can invoke cond_resched() periodically.
-		 */
-		if (unlikely(!(++count)))
-			(*cmp)(priv, tail->next, tail->next);
-
-		tail->next->prev = tail;
-		tail = tail->next;
-	} while (tail->next);
-
-	tail->next = head;
-	head->prev = tail;
-}
-
-/**
- * list_sort - sort a list
- * @priv: private data, opaque to list_sort(), passed to @cmp
- * @head: the list to sort
- * @cmp: the elements comparison function
- *
- * This function implements "merge sort", which has O(nlog(n))
- * complexity.
- *
- * The comparison function @cmp must return a negative value if @a
- * should sort before @b, and a positive value if @a should sort after
- * @b. If @a and @b are equivalent, and their original relative
- * ordering is to be preserved, @cmp must return 0.
- */
-void list_sort(void *priv, struct list_head *head,
-		int (*cmp)(void *priv, struct list_head *a,
-			struct list_head *b))
-{
-	struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
-						-- last slot is a sentinel */
-	int lev;  /* index into part[] */
-	int max_lev = 0;
-	struct list_head *list;
-
-	if (list_empty(head))
-		return;
-
-	memset(part, 0, sizeof(part));
-
-	head->prev->next = NULL;
-	list = head->next;
-
-	while (list) {
-		struct list_head *cur = list;
-		list = list->next;
-		cur->next = NULL;
-
-		for (lev = 0; part[lev]; lev++) {
-			cur = merge(priv, cmp, part[lev], cur);
-			part[lev] = NULL;
-		}
-		if (lev > max_lev) {
-			if (unlikely(lev >= ARRAY_SIZE(part)-1)) {
-				lev--;
-			}
-			max_lev = lev;
-		}
-		part[lev] = cur;
-	}
-
-	for (lev = 0; lev < max_lev; lev++)
-		if (part[lev])
-			list = merge(priv, cmp, part[lev], list);
-
-	merge_and_restore_back_links(priv, cmp, head, part[max_lev], list);
-}
diff --git a/libxfs/radix-tree.c b/libxfs/radix-tree.c
deleted file mode 100644
index 3f0257f..0000000
--- a/libxfs/radix-tree.c
+++ /dev/null
@@ -1,808 +0,0 @@
-/*
- * Copyright (C) 2001 Momchil Velikov
- * Portions Copyright (C) 2001 Christoph Hellwig
- * Copyright (C) 2005 SGI, Christoph Lameter <clameter@sgi.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include "platform_defs.h"
-#include "xfs.h"
-#include "radix-tree.h"
-
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-#endif
-
-#define RADIX_TREE_MAP_SHIFT	6
-#define RADIX_TREE_MAP_SIZE	(1UL << RADIX_TREE_MAP_SHIFT)
-#define RADIX_TREE_MAP_MASK	(RADIX_TREE_MAP_SIZE-1)
-
-#ifdef RADIX_TREE_TAGS
-#define RADIX_TREE_TAG_LONGS	\
-	((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG)
-#endif
-
-struct radix_tree_node {
-	unsigned int	count;
-	void		*slots[RADIX_TREE_MAP_SIZE];
-#ifdef RADIX_TREE_TAGS
-	unsigned long	tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
-#endif
-};
-
-struct radix_tree_path {
-	struct radix_tree_node *node;
-	int offset;
-};
-
-#define RADIX_TREE_INDEX_BITS  (8 /* CHAR_BIT */ * sizeof(unsigned long))
-#define RADIX_TREE_MAX_PATH (RADIX_TREE_INDEX_BITS/RADIX_TREE_MAP_SHIFT + 2)
-
-static unsigned long height_to_maxindex[RADIX_TREE_MAX_PATH];
-
-/*
- * Radix tree node cache.
- */
-
-#define radix_tree_node_alloc(r) 	((struct radix_tree_node *) \
-		calloc(1, sizeof(struct radix_tree_node)))
-#define radix_tree_node_free(n) 	free(n)
-
-#ifdef RADIX_TREE_TAGS
-
-static inline void tag_set(struct radix_tree_node *node, unsigned int tag,
-		int offset)
-{
-	*((uint32_t *)node->tags[tag] + (offset >> 5)) |= (1 << (offset & 31));
-}
-
-static inline void tag_clear(struct radix_tree_node *node, unsigned int tag,
-		int offset)
-{
-	uint32_t 	*p = (uint32_t*)node->tags[tag] + (offset >> 5);
-	uint32_t 	m = 1 << (offset & 31);
-	*p &= ~m;
-}
-
-static inline int tag_get(struct radix_tree_node *node, unsigned int tag,
-		int offset)
-{
-	return 1 & (((const uint32_t *)node->tags[tag])[offset >> 5] >> (offset & 31));
-}
-
-/*
- * Returns 1 if any slot in the node has this tag set.
- * Otherwise returns 0.
- */
-static inline int any_tag_set(struct radix_tree_node *node, unsigned int tag)
-{
-	int idx;
-	for (idx = 0; idx < RADIX_TREE_TAG_LONGS; idx++) {
-		if (node->tags[tag][idx])
-			return 1;
-	}
-	return 0;
-}
-
-#endif
-
-/*
- *	Return the maximum key which can be store into a
- *	radix tree with height HEIGHT.
- */
-static inline unsigned long radix_tree_maxindex(unsigned int height)
-{
-	return height_to_maxindex[height];
-}
-
-/*
- *	Extend a radix tree so it can store key @index.
- */
-static int radix_tree_extend(struct radix_tree_root *root, unsigned long index)
-{
-	struct radix_tree_node *node;
-	unsigned int height;
-#ifdef RADIX_TREE_TAGS
-	char tags[RADIX_TREE_MAX_TAGS];
-	int tag;
-#endif
-
-	/* Figure out what the height should be.  */
-	height = root->height + 1;
-	while (index > radix_tree_maxindex(height))
-		height++;
-
-	if (root->rnode == NULL) {
-		root->height = height;
-		goto out;
-	}
-
-#ifdef RADIX_TREE_TAGS
-	/*
-	 * Prepare the tag status of the top-level node for propagation
-	 * into the newly-pushed top-level node(s)
-	 */
-	for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
-		tags[tag] = 0;
-		if (any_tag_set(root->rnode, tag))
-			tags[tag] = 1;
-	}
-#endif
-	do {
-		if (!(node = radix_tree_node_alloc(root)))
-			return -ENOMEM;
-
-		/* Increase the height.  */
-		node->slots[0] = root->rnode;
-
-#ifdef RADIX_TREE_TAGS
-		/* Propagate the aggregated tag info into the new root */
-		for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
-			if (tags[tag])
-				tag_set(node, tag, 0);
-		}
-#endif
-		node->count = 1;
-		root->rnode = node;
-		root->height++;
-	} while (height > root->height);
-out:
-	return 0;
-}
-
-/**
- *	radix_tree_insert    -    insert into a radix tree
- *	@root:		radix tree root
- *	@index:		index key
- *	@item:		item to insert
- *
- *	Insert an item into the radix tree at position @index.
- */
-int radix_tree_insert(struct radix_tree_root *root,
-			unsigned long index, void *item)
-{
-	struct radix_tree_node *node = NULL, *slot;
-	unsigned int height, shift;
-	int offset;
-	int error;
-
-	/* Make sure the tree is high enough.  */
-	if ((!index && !root->rnode) ||
-			index > radix_tree_maxindex(root->height)) {
-		error = radix_tree_extend(root, index);
-		if (error)
-			return error;
-	}
-
-	slot = root->rnode;
-	height = root->height;
-	shift = (height-1) * RADIX_TREE_MAP_SHIFT;
-
-	offset = 0;			/* uninitialised var warning */
-	do {
-		if (slot == NULL) {
-			/* Have to add a child node.  */
-			if (!(slot = radix_tree_node_alloc(root)))
-				return -ENOMEM;
-			if (node) {
-				node->slots[offset] = slot;
-				node->count++;
-			} else
-				root->rnode = slot;
-		}
-
-		/* Go a level down */
-		offset = (index >> shift) & RADIX_TREE_MAP_MASK;
-		node = slot;
-		slot = node->slots[offset];
-		shift -= RADIX_TREE_MAP_SHIFT;
-		height--;
-	} while (height > 0);
-
-	if (slot != NULL)
-		return -EEXIST;
-
-	ASSERT(node);
-	node->count++;
-	node->slots[offset] = item;
-#ifdef RADIX_TREE_TAGS
-	ASSERT(!tag_get(node, 0, offset));
-	ASSERT(!tag_get(node, 1, offset));
-#endif
-	return 0;
-}
-
-static inline void **__lookup_slot(struct radix_tree_root *root,
-				   unsigned long index)
-{
-	unsigned int height, shift;
-	struct radix_tree_node **slot;
-
-	height = root->height;
-	if (index > radix_tree_maxindex(height))
-		return NULL;
-
-	shift = (height-1) * RADIX_TREE_MAP_SHIFT;
-	slot = &root->rnode;
-
-	while (height > 0) {
-		if (*slot == NULL)
-			return NULL;
-
-		slot = (struct radix_tree_node **)
-			((*slot)->slots +
-				((index >> shift) & RADIX_TREE_MAP_MASK));
-		shift -= RADIX_TREE_MAP_SHIFT;
-		height--;
-	}
-
-	return (void **)slot;
-}
-
-/**
- *	radix_tree_lookup_slot    -    lookup a slot in a radix tree
- *	@root:		radix tree root
- *	@index:		index key
- *
- *	Lookup the slot corresponding to the position @index in the radix tree
- *	@root. This is useful for update-if-exists operations.
- */
-void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index)
-{
-	return __lookup_slot(root, index);
-}
-
-/**
- *	radix_tree_lookup    -    perform lookup operation on a radix tree
- *	@root:		radix tree root
- *	@index:		index key
- *
- *	Lookup the item at the position @index in the radix tree @root.
- */
-void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
-{
-	void **slot;
-
-	slot = __lookup_slot(root, index);
-	return slot != NULL ? *slot : NULL;
-}
-
-/**
- *	raid_tree_first_key - find the first index key in the radix tree
- *	@root:		radix tree root
- *	@index:		where the first index will be placed
- *
- *	Returns the first entry and index key in the radix tree @root.
- */
-void *radix_tree_lookup_first(struct radix_tree_root *root, unsigned long *index)
-{
-	unsigned int height, shift;
-	struct radix_tree_node *slot;
-	unsigned long i;
-
-	height = root->height;
-	*index = 0;
-	if (height == 0)
-		return NULL;
-
-	shift = (height-1) * RADIX_TREE_MAP_SHIFT;
-	slot = root->rnode;
-
-	for (; height > 1; height--) {
-		for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) {
-			if (slot->slots[i] != NULL)
-				break;
-		}
-		ASSERT(i < RADIX_TREE_MAP_SIZE);
-
-		*index |= (i << shift);
-		shift -= RADIX_TREE_MAP_SHIFT;
-		slot = slot->slots[i];
-	}
-	for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) {
-		if (slot->slots[i] != NULL) {
-			*index |= i;
-			return slot->slots[i];
-		}
-	}
-	return NULL;
-}
-
-#ifdef RADIX_TREE_TAGS
-
-/**
- *	radix_tree_tag_set - set a tag on a radix tree node
- *	@root:		radix tree root
- *	@index:		index key
- *	@tag: 		tag index
- *
- *	Set the search tag (which must be < RADIX_TREE_MAX_TAGS)
- *	corresponding to @index in the radix tree.  From
- *	the root all the way down to the leaf node.
- *
- *	Returns the address of the tagged item.   Setting a tag on a not-present
- *	item is a bug.
- */
-void *radix_tree_tag_set(struct radix_tree_root *root,
-			unsigned long index, unsigned int tag)
-{
-	unsigned int height, shift;
-	struct radix_tree_node *slot;
-
-	height = root->height;
-	if (index > radix_tree_maxindex(height))
-		return NULL;
-
-	shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
-	slot = root->rnode;
-
-	while (height > 0) {
-		int offset;
-
-		offset = (index >> shift) & RADIX_TREE_MAP_MASK;
-		if (!tag_get(slot, tag, offset))
-			tag_set(slot, tag, offset);
-		slot = slot->slots[offset];
-		ASSERT(slot != NULL);
-		shift -= RADIX_TREE_MAP_SHIFT;
-		height--;
-	}
-
-	return slot;
-}
-
-/**
- *	radix_tree_tag_clear - clear a tag on a radix tree node
- *	@root:		radix tree root
- *	@index:		index key
- *	@tag: 		tag index
- *
- *	Clear the search tag (which must be < RADIX_TREE_MAX_TAGS)
- *	corresponding to @index in the radix tree.  If
- *	this causes the leaf node to have no tags set then clear the tag in the
- *	next-to-leaf node, etc.
- *
- *	Returns the address of the tagged item on success, else NULL.  ie:
- *	has the same return value and semantics as radix_tree_lookup().
- */
-void *radix_tree_tag_clear(struct radix_tree_root *root,
-			unsigned long index, unsigned int tag)
-{
-	struct radix_tree_path path[RADIX_TREE_MAX_PATH + 1], *pathp = path;
-	struct radix_tree_node *slot;
-	unsigned int height, shift;
-	void *ret = NULL;
-
-	height = root->height;
-	if (index > radix_tree_maxindex(height))
-		goto out;
-
-	shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
-	pathp->node = NULL;
-	slot = root->rnode;
-
-	while (height > 0) {
-		int offset;
-
-		if (slot == NULL)
-			goto out;
-
-		offset = (index >> shift) & RADIX_TREE_MAP_MASK;
-		pathp[1].offset = offset;
-		pathp[1].node = slot;
-		slot = slot->slots[offset];
-		pathp++;
-		shift -= RADIX_TREE_MAP_SHIFT;
-		height--;
-	}
-
-	ret = slot;
-	if (ret == NULL)
-		goto out;
-
-	do {
-		if (!tag_get(pathp->node, tag, pathp->offset))
-			goto out;
-		tag_clear(pathp->node, tag, pathp->offset);
-		if (any_tag_set(pathp->node, tag))
-			goto out;
-		pathp--;
-	} while (pathp->node);
-out:
-	return ret;
-}
-
-#endif
-
-static unsigned int
-__lookup(struct radix_tree_root *root, void **results, unsigned long index,
-	unsigned int max_items, unsigned long *next_index)
-{
-	unsigned int nr_found = 0;
-	unsigned int shift, height;
-	struct radix_tree_node *slot;
-	unsigned long i;
-
-	height = root->height;
-	if (height == 0)
-		goto out;
-
-	shift = (height-1) * RADIX_TREE_MAP_SHIFT;
-	slot = root->rnode;
-
-	for ( ; height > 1; height--) {
-
-		for (i = (index >> shift) & RADIX_TREE_MAP_MASK ;
-				i < RADIX_TREE_MAP_SIZE; i++) {
-			if (slot->slots[i] != NULL)
-				break;
-			index &= ~((1UL << shift) - 1);
-			index += 1UL << shift;
-			if (index == 0)
-				goto out;	/* 32-bit wraparound */
-		}
-		if (i == RADIX_TREE_MAP_SIZE)
-			goto out;
-
-		shift -= RADIX_TREE_MAP_SHIFT;
-		slot = slot->slots[i];
-	}
-
-	/* Bottom level: grab some items */
-	for (i = index & RADIX_TREE_MAP_MASK; i < RADIX_TREE_MAP_SIZE; i++) {
-		index++;
-		if (slot->slots[i]) {
-			results[nr_found++] = slot->slots[i];
-			if (nr_found == max_items)
-				goto out;
-		}
-	}
-out:
-	*next_index = index;
-	return nr_found;
-}
-
-/**
- *	radix_tree_gang_lookup - perform multiple lookup on a radix tree
- *	@root:		radix tree root
- *	@results:	where the results of the lookup are placed
- *	@first_index:	start the lookup from this key
- *	@max_items:	place up to this many items at *results
- *
- *	Performs an index-ascending scan of the tree for present items.  Places
- *	them at *@results and returns the number of items which were placed at
- *	*@results.
- *
- *	The implementation is naive.
- */
-unsigned int
-radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
-			unsigned long first_index, unsigned int max_items)
-{
-	const unsigned long max_index = radix_tree_maxindex(root->height);
-	unsigned long cur_index = first_index;
-	unsigned int ret = 0;
-
-	while (ret < max_items) {
-		unsigned int nr_found;
-		unsigned long next_index;	/* Index of next search */
-
-		if (cur_index > max_index)
-			break;
-		nr_found = __lookup(root, results + ret, cur_index,
-					max_items - ret, &next_index);
-		ret += nr_found;
-		if (next_index == 0)
-			break;
-		cur_index = next_index;
-	}
-	return ret;
-}
-
-/**
- *	radix_tree_gang_lookup_ex - perform multiple lookup on a radix tree
- *	@root:		radix tree root
- *	@results:	where the results of the lookup are placed
- *	@first_index:	start the lookup from this key
- *	@last_index:	don't lookup past this key
- *	@max_items:	place up to this many items at *results
- *
- *	Performs an index-ascending scan of the tree for present items starting
- *	@first_index until @last_index up to as many as @max_items.  Places
- *	them at *@results and returns the number of items which were placed
- *	at *@results.
- *
- *	The implementation is naive.
- */
-unsigned int
-radix_tree_gang_lookup_ex(struct radix_tree_root *root, void **results,
-			unsigned long first_index, unsigned long last_index,
-			unsigned int max_items)
-{
-	const unsigned long max_index = radix_tree_maxindex(root->height);
-	unsigned long cur_index = first_index;
-	unsigned int ret = 0;
-
-	while (ret < max_items && cur_index < last_index) {
-		unsigned int nr_found;
-		unsigned long next_index;	/* Index of next search */
-
-		if (cur_index > max_index)
-			break;
-		nr_found = __lookup(root, results + ret, cur_index,
-					max_items - ret, &next_index);
-		ret += nr_found;
-		if (next_index == 0)
-			break;
-		cur_index = next_index;
-	}
-	return ret;
-}
-
-#ifdef RADIX_TREE_TAGS
-
-static unsigned int
-__lookup_tag(struct radix_tree_root *root, void **results, unsigned long index,
-	unsigned int max_items, unsigned long *next_index, unsigned int tag)
-{
-	unsigned int nr_found = 0;
-	unsigned int shift;
-	unsigned int height = root->height;
-	struct radix_tree_node *slot;
-
-	shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
-	slot = root->rnode;
-
-	while (height > 0) {
-		unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK;
-
-		for ( ; i < RADIX_TREE_MAP_SIZE; i++) {
-			if (tag_get(slot, tag, i)) {
-				ASSERT(slot->slots[i] != NULL);
-				break;
-			}
-			index &= ~((1UL << shift) - 1);
-			index += 1UL << shift;
-			if (index == 0)
-				goto out;	/* 32-bit wraparound */
-		}
-		if (i == RADIX_TREE_MAP_SIZE)
-			goto out;
-		height--;
-		if (height == 0) {	/* Bottom level: grab some items */
-			unsigned long j = index & RADIX_TREE_MAP_MASK;
-
-			for ( ; j < RADIX_TREE_MAP_SIZE; j++) {
-				index++;
-				if (tag_get(slot, tag, j)) {
-					ASSERT(slot->slots[j] != NULL);
-					results[nr_found++] = slot->slots[j];
-					if (nr_found == max_items)
-						goto out;
-				}
-			}
-		}
-		shift -= RADIX_TREE_MAP_SHIFT;
-		slot = slot->slots[i];
-	}
-out:
-	*next_index = index;
-	return nr_found;
-}
-
-/**
- *	radix_tree_gang_lookup_tag - perform multiple lookup on a radix tree
- *	                             based on a tag
- *	@root:		radix tree root
- *	@results:	where the results of the lookup are placed
- *	@first_index:	start the lookup from this key
- *	@max_items:	place up to this many items at *results
- *	@tag:		the tag index (< RADIX_TREE_MAX_TAGS)
- *
- *	Performs an index-ascending scan of the tree for present items which
- *	have the tag indexed by @tag set.  Places the items at *@results and
- *	returns the number of items which were placed at *@results.
- */
-unsigned int
-radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results,
-		unsigned long first_index, unsigned int max_items,
-		unsigned int tag)
-{
-	const unsigned long max_index = radix_tree_maxindex(root->height);
-	unsigned long cur_index = first_index;
-	unsigned int ret = 0;
-
-	while (ret < max_items) {
-		unsigned int nr_found;
-		unsigned long next_index;	/* Index of next search */
-
-		if (cur_index > max_index)
-			break;
-		nr_found = __lookup_tag(root, results + ret, cur_index,
-					max_items - ret, &next_index, tag);
-		ret += nr_found;
-		if (next_index == 0)
-			break;
-		cur_index = next_index;
-	}
-	return ret;
-}
-
-#endif
-
-/**
- *	radix_tree_shrink    -    shrink height of a radix tree to minimal
- *	@root		radix tree root
- */
-static inline void radix_tree_shrink(struct radix_tree_root *root)
-{
-	/* try to shrink tree height */
-	while (root->height > 1 &&
-			root->rnode->count == 1 &&
-			root->rnode->slots[0]) {
-		struct radix_tree_node *to_free = root->rnode;
-
-		root->rnode = to_free->slots[0];
-		root->height--;
-		/* must only free zeroed nodes into the slab */
-#ifdef RADIX_TREE_TAGS
-		tag_clear(to_free, 0, 0);
-		tag_clear(to_free, 1, 0);
-#endif
-		to_free->slots[0] = NULL;
-		to_free->count = 0;
-		radix_tree_node_free(to_free);
-	}
-}
-
-/**
- *	radix_tree_delete    -    delete an item from a radix tree
- *	@root:		radix tree root
- *	@index:		index key
- *
- *	Remove the item at @index from the radix tree rooted at @root.
- *
- *	Returns the address of the deleted item, or NULL if it was not present.
- */
-void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
-{
-	struct radix_tree_path path[RADIX_TREE_MAX_PATH + 1], *pathp = path;
-	struct radix_tree_path *orig_pathp;
-	struct radix_tree_node *slot;
-	unsigned int height, shift;
-	void *ret = NULL;
-#ifdef RADIX_TREE_TAGS
-	char tags[RADIX_TREE_MAX_TAGS];
-	int nr_cleared_tags;
-	int tag;
-#endif
-	int offset;
-
-	height = root->height;
-	if (index > radix_tree_maxindex(height))
-		goto out;
-
-	shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
-	pathp->node = NULL;
-	slot = root->rnode;
-
-	for ( ; height > 0; height--) {
-		if (slot == NULL)
-			goto out;
-
-		pathp++;
-		offset = (index >> shift) & RADIX_TREE_MAP_MASK;
-		pathp->offset = offset;
-		pathp->node = slot;
-		slot = slot->slots[offset];
-		shift -= RADIX_TREE_MAP_SHIFT;
-	}
-
-	ret = slot;
-	if (ret == NULL)
-		goto out;
-
-	orig_pathp = pathp;
-
-#ifdef RADIX_TREE_TAGS
-	/*
-	 * Clear all tags associated with the just-deleted item
-	 */
-	nr_cleared_tags = 0;
-	for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
-		tags[tag] = 1;
-		if (tag_get(pathp->node, tag, pathp->offset)) {
-			tag_clear(pathp->node, tag, pathp->offset);
-			if (!any_tag_set(pathp->node, tag)) {
-				tags[tag] = 0;
-				nr_cleared_tags++;
-			}
-		}
-	}
-
-	for (pathp--; nr_cleared_tags && pathp->node; pathp--) {
-		for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
-			if (tags[tag])
-				continue;
-
-			tag_clear(pathp->node, tag, pathp->offset);
-			if (any_tag_set(pathp->node, tag)) {
-				tags[tag] = 1;
-				nr_cleared_tags--;
-			}
-		}
-	}
-#endif
-	/* Now free the nodes we do not need anymore */
-	for (pathp = orig_pathp; pathp->node; pathp--) {
-		pathp->node->slots[pathp->offset] = NULL;
-		pathp->node->count--;
-
-		if (pathp->node->count) {
-			if (pathp->node == root->rnode)
-				radix_tree_shrink(root);
-			goto out;
-		}
-
-		/* Node with zero slots in use so free it */
-		radix_tree_node_free(pathp->node);
-	}
-	root->rnode = NULL;
-	root->height = 0;
-out:
-	return ret;
-}
-
-#ifdef RADIX_TREE_TAGS
-/**
- *	radix_tree_tagged - test whether any items in the tree are tagged
- *	@root:		radix tree root
- *	@tag:		tag to test
- */
-int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag)
-{
-  	struct radix_tree_node *rnode;
-  	rnode = root->rnode;
-  	if (!rnode)
-  		return 0;
-	return any_tag_set(rnode, tag);
-}
-#endif
-
-static unsigned long __maxindex(unsigned int height)
-{
-	unsigned int width = height * RADIX_TREE_MAP_SHIFT;
-	int shift = RADIX_TREE_INDEX_BITS - width;
-
-	if (shift < 0)
-		return ~0UL;
-	if (shift >= BITS_PER_LONG)
-		return 0UL;
-	return ~0UL >> shift;
-}
-
-static void radix_tree_init_maxindex(void)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(height_to_maxindex); i++)
-		height_to_maxindex[i] = __maxindex(i);
-}
-
-void radix_tree_init(void)
-{
-	radix_tree_init_maxindex();
-}
diff --git a/logprint/Makefile b/logprint/Makefile
index 534bf5b..7fbbc98 100644
--- a/logprint/Makefile
+++ b/logprint/Makefile
@@ -12,8 +12,8 @@ CFILES = logprint.c \
 	 log_copy.c log_dump.c log_misc.c \
 	 log_print_all.c log_print_trans.c log_redo.c
 
-LLDLIBS	= $(LIBXFS) $(LIBXLOG) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
-LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG)
+LLDLIBS	= $(LIBXFS) $(LIBXLOG) $(LIBFROG) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
+LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG) $(LIBFROG)
 LLDFLAGS = -static-libtool-libs
 
 default: depend $(LTCOMMAND)
diff --git a/mdrestore/Makefile b/mdrestore/Makefile
index 5171306..136ae71 100644
--- a/mdrestore/Makefile
+++ b/mdrestore/Makefile
@@ -8,8 +8,8 @@ include $(TOPDIR)/include/builddefs
 LTCOMMAND = xfs_mdrestore
 CFILES = xfs_mdrestore.c
 
-LLDLIBS = $(LIBXFS) $(LIBRT) $(LIBPTHREAD) $(LIBUUID)
-LTDEPENDENCIES = $(LIBXFS)
+LLDLIBS = $(LIBXFS) $(LIBFROG) $(LIBRT) $(LIBPTHREAD) $(LIBUUID)
+LTDEPENDENCIES = $(LIBXFS) $(LIBFROG)
 LLDFLAGS = -static
 
 default: depend $(LTCOMMAND)


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 05/12] libfrog: promote avl64 code from xfs_repair
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
                   ` (3 preceding siblings ...)
  2017-11-17 20:13 ` [PATCH 04/12] libfrog: move list_sort out of libxfs Darrick J. Wong
@ 2017-11-17 20:13 ` Darrick J. Wong
  2017-11-17 20:14 ` [PATCH 06/12] libfrog: create a threaded workqueue Darrick J. Wong
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:13 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

xfs_scrub will make use of the avl64 code, so promote it out of repair
and into libfrog.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 include/avl64.h  |  133 +++++
 libfrog/Makefile |    1 
 libfrog/avl64.c  | 1418 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 repair/Makefile  |    4 
 repair/avl64.c   | 1418 ------------------------------------------------------
 repair/avl64.h   |  133 -----
 6 files changed, 1554 insertions(+), 1553 deletions(-)
 create mode 100644 include/avl64.h
 create mode 100644 libfrog/avl64.c
 delete mode 100644 repair/avl64.c
 delete mode 100644 repair/avl64.h


diff --git a/include/avl64.h b/include/avl64.h
new file mode 100644
index 0000000..cd079a0
--- /dev/null
+++ b/include/avl64.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#ifndef __XR_AVL64_H__
+#define __XR_AVL64_H__
+
+#include <sys/types.h>
+
+typedef struct	avl64node {
+	struct	avl64node	*avl_forw;	/* pointer to right child  (> parent) */
+	struct	avl64node *avl_back;	/* pointer to left child  (< parent) */
+	struct	avl64node *avl_parent;	/* parent pointer */
+	struct	avl64node *avl_nextino;	/* next in-order; NULL terminated list*/
+	char		 avl_balance;	/* tree balance */
+} avl64node_t;
+
+/*
+ * avl-tree operations
+ */
+typedef struct avl64ops {
+	uint64_t	(*avl_start)(avl64node_t *);
+	uint64_t	(*avl_end)(avl64node_t *);
+} avl64ops_t;
+
+/*
+ * avoid complaints about multiple def's since these are only used by
+ * the avl code internally
+ */
+#ifndef AVL_START
+#define	AVL_START(tree, n)	(*(tree)->avl_ops->avl_start)(n)
+#define	AVL_END(tree, n)	(*(tree)->avl_ops->avl_end)(n)
+#endif
+
+/*
+ * tree descriptor:
+ *	root points to the root of the tree.
+ *	firstino points to the first in the ordered list.
+ */
+typedef struct avl64tree_desc {
+	avl64node_t	*avl_root;
+	avl64node_t	*avl_firstino;
+	avl64ops_t	*avl_ops;
+} avl64tree_desc_t;
+
+/* possible values for avl_balance */
+
+#define AVL_BACK	1
+#define AVL_BALANCE	0
+#define AVL_FORW	2
+
+/*
+ * 'Exported' avl tree routines
+ */
+avl64node_t
+*avl64_insert(
+	avl64tree_desc_t *tree,
+	avl64node_t *newnode);
+
+void
+avl64_delete(
+	avl64tree_desc_t *tree,
+	avl64node_t *np);
+
+void
+avl64_insert_immediate(
+	avl64tree_desc_t *tree,
+	avl64node_t *afterp,
+	avl64node_t *newnode);
+
+void
+avl64_init_tree(
+	avl64tree_desc_t  *tree,
+	avl64ops_t *ops);
+
+avl64node_t *
+avl64_findrange(
+	avl64tree_desc_t *tree,
+	uint64_t value);
+
+avl64node_t *
+avl64_find(
+	avl64tree_desc_t *tree,
+	uint64_t value);
+
+avl64node_t *
+avl64_findanyrange(
+	avl64tree_desc_t *tree,
+	uint64_t	start,
+	uint64_t	end,
+	int     checklen);
+
+
+avl64node_t *
+avl64_findadjacent(
+	avl64tree_desc_t *tree,
+	uint64_t	value,
+	int		dir);
+
+void
+avl64_findranges(
+	avl64tree_desc_t *tree,
+	uint64_t	start,
+	uint64_t	end,
+	avl64node_t	        **startp,
+	avl64node_t		**endp);
+
+/*
+ * avoid complaints about multiple def's since these are only used by
+ * the avl code internally
+ */
+#ifndef AVL_PRECEED
+#define AVL_PRECEED	0x1
+#define AVL_SUCCEED	0x2
+
+#define AVL_INCLUDE_ZEROLEN	0x0000
+#define AVL_EXCLUDE_ZEROLEN	0x0001
+#endif
+
+#endif /* __XR_AVL64_H__ */
diff --git a/libfrog/Makefile b/libfrog/Makefile
index 6034da5..3fd42a4 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -11,6 +11,7 @@ LT_REVISION = 0
 LT_AGE = 0
 
 CFILES = \
+avl64.c \
 list_sort.c \
 radix-tree.c \
 util.c
diff --git a/libfrog/avl64.c b/libfrog/avl64.c
new file mode 100644
index 0000000..01cfee4
--- /dev/null
+++ b/libfrog/avl64.c
@@ -0,0 +1,1418 @@
+/*
+ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include <stdint.h>
+#include <stdio.h>
+#include "platform_defs.h"
+#include "avl64.h"
+
+#define CERT	ASSERT
+
+#ifdef AVL_DEBUG
+
+static void
+avl64_checknode(
+	avl64tree_desc_t *tree,
+	avl64node_t *np)
+{
+	avl64node_t *back = np->avl_back;
+	avl64node_t *forw = np->avl_forw;
+	avl64node_t *nextino = np->avl_nextino;
+	int bal = np->avl_balance;
+
+	ASSERT(bal != AVL_BALANCE || (!back && !forw) || (back && forw));
+	ASSERT(bal != AVL_FORW || forw);
+	ASSERT(bal != AVL_BACK || back);
+
+	if (forw) {
+		ASSERT(AVL_START(tree, np) < AVL_START(tree, forw));
+		ASSERT(np->avl_forw->avl_parent == np);
+		ASSERT(back || bal == AVL_FORW);
+	} else {
+		ASSERT(bal != AVL_FORW);
+		ASSERT(bal == AVL_BALANCE || back);
+		ASSERT(bal == AVL_BACK || !back);
+	}
+
+	if (back) {
+		ASSERT(AVL_START(tree, np) > AVL_START(tree, back));
+		ASSERT(np->avl_back->avl_parent == np);
+		ASSERT(forw || bal == AVL_BACK);
+	} else {
+		ASSERT(bal != AVL_BACK);
+		ASSERT(bal == AVL_BALANCE || forw);
+		ASSERT(bal == AVL_FORW || !forw);
+	}
+
+	if (nextino == NULL)
+		ASSERT(forw == NULL);
+	else
+		ASSERT(AVL_END(tree, np) <= AVL_START(tree, nextino));
+}
+
+static void
+avl64_checktree(
+	avl64tree_desc_t *tree,
+	avl64node_t *root)
+{
+	avl64node_t *nlast, *nnext, *np;
+	uint64_t offset = 0;
+	uint64_t end;
+
+	nlast = nnext = root;
+
+	ASSERT(!nnext || nnext->avl_parent == NULL);
+
+	while (nnext) {
+
+		avl64_checknode(tree, nnext);
+		end = AVL_END(tree, nnext);
+
+		if (end <= offset) {
+			if ((np = nnext->avl_forw) && np != nlast) {
+				nlast = nnext;
+				nnext = np;
+			} else {
+				nlast = nnext;
+				nnext = nnext->avl_parent;
+			}
+			continue;
+		}
+
+		nlast = nnext;
+		if (np = nnext->avl_back) {
+			if (AVL_END(tree, np) > offset) {
+				nnext = np;
+				continue;
+			}
+		}
+
+		np = nnext;
+		nnext = nnext->avl_forw;
+		if (!nnext)
+			nnext = np->avl_parent;
+
+		offset = end;
+	}
+}
+#else	/* ! AVL_DEBUG */
+#define avl64_checktree(t,x)
+#endif	/* AVL_DEBUG */
+
+
+/*
+ * Reset balance for np up through tree.
+ * ``direction'' is the way that np's balance
+ * is headed after the deletion of one of its children --
+ * e.g., deleting a avl_forw child sends avl_balance toward AVL_BACK.
+ * Called only when deleting a node from the tree.
+ */
+static void
+retreat(
+	avl64tree_desc_t *tree,
+	avl64node_t *np,
+	int direction)
+{
+	avl64node_t **rootp = &tree->avl_root;
+	avl64node_t *parent;
+	avl64node_t *child;
+	avl64node_t *tmp;
+	int	bal;
+
+	do {
+		ASSERT(direction == AVL_BACK || direction == AVL_FORW);
+
+		if (np->avl_balance == AVL_BALANCE) {
+			np->avl_balance = direction;
+			return;
+		}
+
+		parent = np->avl_parent;
+
+		/*
+		 * If balance is being restored, no local node
+		 * reorganization is necessary, but may be at
+		 * a higher node.  Reset direction and continue.
+		 */
+		if (direction != np->avl_balance) {
+			np->avl_balance = AVL_BALANCE;
+			if (parent) {
+				if (parent->avl_forw == np)
+					direction = AVL_BACK;
+				else
+					direction = AVL_FORW;
+
+				np = parent;
+				continue;
+			}
+			return;
+		}
+
+		/*
+		 * Imbalance.  If a avl_forw node was removed, direction
+		 * (and, by reduction, np->avl_balance) is/was AVL_BACK.
+		 */
+		if (np->avl_balance == AVL_BACK) {
+
+			ASSERT(direction == AVL_BACK);
+			child = np->avl_back;
+			bal = child->avl_balance;
+
+			if (bal != AVL_FORW) /* single LL */ {
+				/*
+				 * np gets pushed down to lesser child's
+				 * avl_forw branch.
+				 *
+				 *  np->    -D		    +B
+				 *	    / \		    / \
+				 * child-> B   deleted	   A  -D
+				 *	  / \		      /
+				 *	 A   C		     C
+				cmn_err(CE_CONT, "!LL delete b 0x%x c 0x%x\n",
+					np, child);
+				 */
+
+				np->avl_back = child->avl_forw;
+				if (child->avl_forw)
+					child->avl_forw->avl_parent = np;
+				child->avl_forw = np;
+
+				if (parent) {
+					if (parent->avl_forw == np) {
+						parent->avl_forw = child;
+						direction = AVL_BACK;
+					} else {
+						ASSERT(parent->avl_back == np);
+						parent->avl_back = child;
+						direction = AVL_FORW;
+					}
+				} else {
+					ASSERT(*rootp == np);
+					*rootp = child;
+				}
+				np->avl_parent = child;
+				child->avl_parent = parent;
+
+				if (bal == AVL_BALANCE) {
+					np->avl_balance = AVL_BACK;
+					child->avl_balance = AVL_FORW;
+					return;
+				} else {
+					np->avl_balance = AVL_BALANCE;
+					child->avl_balance = AVL_BALANCE;
+					np = parent;
+					avl64_checktree(tree, *rootp);
+					continue;
+				}
+			}
+
+			/* child->avl_balance == AVL_FORW  double LR rotation
+			 *
+			 * child's avl_forw node gets promoted up, along with
+			 * its avl_forw subtree
+			 *
+			 *  np->     -G			  C
+			 *	     / \		 / \
+			 * child-> +B   H	       -B   G
+			 *	   / \   \	       /   / \
+			 *	  A  +C   deleted     A   D   H
+			 *	       \
+			 *	        D
+			cmn_err(CE_CONT, "!LR delete b 0x%x c 0x%x t 0x%x\n",
+				np, child, child->avl_forw);
+			 */
+
+			tmp = child->avl_forw;
+			bal = tmp->avl_balance;
+
+			child->avl_forw = tmp->avl_back;
+			if (tmp->avl_back)
+				tmp->avl_back->avl_parent = child;
+
+			tmp->avl_back = child;
+			child->avl_parent = tmp;
+
+			np->avl_back = tmp->avl_forw;
+			if (tmp->avl_forw)
+				tmp->avl_forw->avl_parent = np;
+			tmp->avl_forw = np;
+
+			if (bal == AVL_FORW)
+				child->avl_balance = AVL_BACK;
+			else
+				child->avl_balance = AVL_BALANCE;
+
+			if (bal == AVL_BACK)
+				np->avl_balance = AVL_FORW;
+			else
+				np->avl_balance = AVL_BALANCE;
+
+			goto next;
+		}
+
+		ASSERT(np->avl_balance == AVL_FORW && direction == AVL_FORW);
+
+		child = np->avl_forw;
+		bal = child->avl_balance;
+
+		if (bal != AVL_BACK) /* single RR */ {
+			/*
+			 * np gets pushed down to greater child's
+			 * avl_back branch.
+			 *
+			 *  np->    +B		     -D
+			 *	    / \		     / \
+			 *   deleted   D <-child   +B   E
+			 *	      / \	     \
+			 *	     C   E	      C
+			cmn_err(CE_CONT, "!RR delete b 0x%x c 0x%x\n",
+				np, child);
+			 */
+
+			np->avl_forw = child->avl_back;
+			if (child->avl_back)
+				child->avl_back->avl_parent = np;
+			child->avl_back = np;
+
+			if (parent) {
+				if (parent->avl_forw == np) {
+					parent->avl_forw = child;
+					direction = AVL_BACK;
+				} else {
+					ASSERT(parent->avl_back == np);
+					parent->avl_back = child;
+					direction = AVL_FORW;
+				}
+			} else {
+				ASSERT(*rootp == np);
+				*rootp = child;
+			}
+			np->avl_parent = child;
+			child->avl_parent = parent;
+
+			if (bal == AVL_BALANCE) {
+				np->avl_balance = AVL_FORW;
+				child->avl_balance = AVL_BACK;
+				return;
+			} else {
+				np->avl_balance = AVL_BALANCE;
+				child->avl_balance = AVL_BALANCE;
+				np = parent;
+				avl64_checktree(tree, *rootp);
+				continue;
+			}
+		}
+
+		/* child->avl_balance == AVL_BACK  double RL rotation
+		cmn_err(CE_CONT, "!RL delete b 0x%x c 0x%x t 0x%x\n",
+			np, child, child->avl_back);
+		*/
+
+		tmp = child->avl_back;
+		bal = tmp->avl_balance;
+
+		child->avl_back = tmp->avl_forw;
+		if (tmp->avl_forw)
+			tmp->avl_forw->avl_parent = child;
+
+		tmp->avl_forw = child;
+		child->avl_parent = tmp;
+
+		np->avl_forw = tmp->avl_back;
+		if (tmp->avl_back)
+			tmp->avl_back->avl_parent = np;
+		tmp->avl_back = np;
+
+		if (bal == AVL_BACK)
+			child->avl_balance = AVL_FORW;
+		else
+			child->avl_balance = AVL_BALANCE;
+
+		if (bal == AVL_FORW)
+			np->avl_balance = AVL_BACK;
+		else
+			np->avl_balance = AVL_BALANCE;
+next:
+		np->avl_parent = tmp;
+		tmp->avl_balance = AVL_BALANCE;
+		tmp->avl_parent = parent;
+
+		if (parent) {
+			if (parent->avl_forw == np) {
+				parent->avl_forw = tmp;
+				direction = AVL_BACK;
+			} else {
+				ASSERT(parent->avl_back == np);
+				parent->avl_back = tmp;
+				direction = AVL_FORW;
+			}
+		} else {
+			ASSERT(*rootp == np);
+			*rootp = tmp;
+			return;
+		}
+
+		np = parent;
+		avl64_checktree(tree, *rootp);
+	} while (np);
+}
+
+/*
+ *	Remove node from tree.
+ *	avl_delete does the local tree manipulations,
+ *	calls retreat() to rebalance tree up to its root.
+ */
+void
+avl64_delete(
+	avl64tree_desc_t *tree,
+	avl64node_t *np)
+{
+	avl64node_t *forw = np->avl_forw;
+	avl64node_t *back = np->avl_back;
+	avl64node_t *parent = np->avl_parent;
+	avl64node_t *nnext;
+
+
+	if (np->avl_back) {
+		/*
+		 * a left child exits, then greatest left descendent's nextino
+		 * is pointing to np; make it point to np->nextino.
+		 */
+		nnext = np->avl_back;
+		while (nnext) {
+			if (!nnext->avl_forw)
+				break; /* can't find anything bigger */
+			nnext = nnext->avl_forw;
+		}
+	} else
+	if (np->avl_parent) {
+		/*
+		 * find nearest ancestor with lesser value. That ancestor's
+		 * nextino is pointing to np; make it point to np->nextino
+		 */
+		 nnext = np->avl_parent;
+		 while (nnext) {
+			if (AVL_END(tree, nnext) <= AVL_END(tree, np))
+				break;
+			nnext = nnext->avl_parent;
+		}
+	} else
+		nnext = NULL;
+
+	if (nnext) {
+		ASSERT(nnext->avl_nextino == np);
+		nnext->avl_nextino = np->avl_nextino;
+		/*
+		 *	Something preceeds np; np cannot be firstino.
+		 */
+		ASSERT(tree->avl_firstino != np);
+	}
+	else {
+		/*
+		 *	Nothing preceeding np; after deletion, np's nextino
+		 *	is firstino of tree.
+		 */
+		ASSERT(tree->avl_firstino == np);
+		tree->avl_firstino = np->avl_nextino;
+	}
+
+
+	/*
+	 * Degenerate cases...
+	 */
+	if (forw == NULL) {
+		forw = back;
+		goto attach;
+	}
+
+	if (back == NULL) {
+attach:
+		if (forw)
+			forw->avl_parent = parent;
+		if (parent) {
+			if (parent->avl_forw == np) {
+				parent->avl_forw = forw;
+				retreat(tree, parent, AVL_BACK);
+			} else {
+				ASSERT(parent->avl_back == np);
+				parent->avl_back = forw;
+				retreat(tree, parent, AVL_FORW);
+			}
+		} else {
+			ASSERT(tree->avl_root == np);
+			tree->avl_root = forw;
+		}
+		avl64_checktree(tree, tree->avl_root);
+		return;
+	}
+
+	/*
+	 * Harder case: children on both sides.
+	 * If back's avl_forw pointer is null, just have back
+	 * inherit np's avl_forw tree, remove np from the tree
+	 * and adjust balance counters starting at back.
+	 *
+	 * np->	    xI		    xH	(befor retreat())
+	 *	    / \		    / \
+	 * back->  H   J	   G   J
+	 *	  /   / \             / \
+	 *       G   ?   ?           ?   ?
+	 *      / \
+	 *     ?   ?
+	 */
+	if ((forw = back->avl_forw) == NULL) {
+		/*
+		 * AVL_FORW retreat below will set back's
+		 * balance to AVL_BACK.
+		 */
+		back->avl_balance = np->avl_balance;
+		back->avl_forw = forw = np->avl_forw;
+		forw->avl_parent = back;
+		back->avl_parent = parent;
+
+		if (parent) {
+			if (parent->avl_forw == np)
+				parent->avl_forw = back;
+			else {
+				ASSERT(parent->avl_back == np);
+				parent->avl_back = back;
+			}
+		} else {
+			ASSERT(tree->avl_root == np);
+			tree->avl_root = back;
+		}
+
+		/*
+		 * back is taking np's place in the tree, and
+		 * has therefore lost a avl_back node (itself).
+		 */
+		retreat(tree, back, AVL_FORW);
+		avl64_checktree(tree, tree->avl_root);
+		return;
+	}
+
+	/*
+	 * Hardest case: children on both sides, and back's
+	 * avl_forw pointer isn't null.  Find the immediately
+	 * inferior buffer by following back's avl_forw line
+	 * to the end, then have it inherit np's avl_forw tree.
+	 *
+	 * np->	    xI			      xH
+	 *	    / \			      / \
+	 *         G   J	     back->  G   J   (before retreat())
+	 *	  / \			    / \
+	 *       F   ?...		   F   ?1
+	 *      /     \
+	 *     ?       H  <-forw
+	 *	      /
+	 *	     ?1
+	 */
+	while ((back = forw->avl_forw))
+		forw = back;
+
+	/*
+	 * Will be adjusted by retreat() below.
+	 */
+	forw->avl_balance = np->avl_balance;
+
+	/*
+	 * forw inherits np's avl_forw...
+	 */
+	forw->avl_forw = np->avl_forw;
+	np->avl_forw->avl_parent = forw;
+
+	/*
+	 * ... forw's parent gets forw's avl_back...
+	 */
+	back = forw->avl_parent;
+	back->avl_forw = forw->avl_back;
+	if (forw->avl_back)
+		forw->avl_back->avl_parent = back;
+
+	/*
+	 * ... forw gets np's avl_back...
+	 */
+	forw->avl_back = np->avl_back;
+	np->avl_back->avl_parent = forw;
+
+	/*
+	 * ... and forw gets np's parent.
+	 */
+	forw->avl_parent = parent;
+
+	if (parent) {
+		if (parent->avl_forw == np)
+			parent->avl_forw = forw;
+		else
+			parent->avl_back = forw;
+	} else {
+		ASSERT(tree->avl_root == np);
+		tree->avl_root = forw;
+	}
+
+	/*
+	 * What used to be forw's parent is the starting
+	 * point for rebalancing.  It has lost a avl_forw node.
+	 */
+	retreat(tree, back, AVL_BACK);
+	avl64_checktree(tree, tree->avl_root);
+}
+
+
+/*
+ *	avl_findanyrange:
+ *
+ *	Given range r [start, end), find any range which is contained in r.
+ *	if checklen is non-zero, then only ranges of non-zero length are
+ *	considered in finding a match.
+ */
+avl64node_t *
+avl64_findanyrange(
+	avl64tree_desc_t *tree,
+	uint64_t start,
+	uint64_t end,
+	int	checklen)
+{
+	avl64node_t *np = tree->avl_root;
+
+	/* np = avl64_findadjacent(tree, start, AVL_SUCCEED); */
+	while (np) {
+		if (start < AVL_START(tree, np)) {
+			if (np->avl_back) {
+				np = np->avl_back;
+				continue;
+			}
+			/* if we were to add node with start, would
+			 * have a growth of AVL_BACK
+			 */
+			/* if succeeding node is needed, this is it.
+			 */
+			break;
+		}
+		if (start >= AVL_END(tree, np)) {
+			if (np->avl_forw) {
+				np = np->avl_forw;
+				continue;
+			}
+			/* if we were to add node with start, would
+			 * have a growth of AVL_FORW;
+			 */
+			/* we are looking for a succeeding node;
+			 * this is nextino.
+			 */
+			np = np->avl_nextino;
+			break;
+		}
+		/* AVL_START(tree, np) <= start < AVL_END(tree, np) */
+		break;
+	}
+	if (np) {
+		if (checklen == AVL_INCLUDE_ZEROLEN) {
+			if (end <= AVL_START(tree, np)) {
+				/* something follows start, but is
+				 * is entierly after the range (end)
+				 */
+				return(NULL);
+			}
+			/* np may stradle [start, end) */
+			return(np);
+		}
+		/*
+		 * find non-zero length region
+		 */
+		while (np && (AVL_END(tree, np) - AVL_START(tree, np) == 0)
+			&& (AVL_START(tree, np)  < end))
+				np = np->avl_nextino;
+
+		if ((np == NULL) || (AVL_START(tree, np) >= end))
+			return NULL;
+		return(np);
+	}
+	/*
+	 * nothing succeeds start, all existing ranges are before start.
+	 */
+	return NULL;
+}
+
+
+/*
+ * Returns a pointer to range which contains value.
+ */
+avl64node_t *
+avl64_findrange(
+	avl64tree_desc_t *tree,
+	uint64_t value)
+{
+	avl64node_t *np = tree->avl_root;
+
+	while (np) {
+		if (value < AVL_START(tree, np)) {
+			np = np->avl_back;
+			continue;
+		}
+		if (value >= AVL_END(tree, np)) {
+			np = np->avl_forw;
+			continue;
+		}
+		ASSERT(AVL_START(tree, np) <= value &&
+		       value < AVL_END(tree, np));
+		return np;
+	}
+	return NULL;
+}
+
+
+/*
+ * Returns a pointer to node which contains exact value.
+ */
+avl64node_t *
+avl64_find(
+	avl64tree_desc_t *tree,
+	uint64_t value)
+{
+	avl64node_t *np = tree->avl_root;
+	uint64_t nvalue;
+
+	while (np) {
+		nvalue = AVL_START(tree, np);
+		if (value < nvalue) {
+			np = np->avl_back;
+			continue;
+		}
+		if (value == nvalue) {
+			return np;
+		}
+		np = np->avl_forw;
+	}
+	return NULL;
+}
+
+
+/*
+ * Balance buffer AVL tree after attaching a new node to root.
+ * Called only by avl_insert.
+ */
+static void
+avl64_balance(
+	avl64node_t **rootp,
+	avl64node_t *np,
+	int growth)
+{
+	/*
+	 * At this point, np points to the node to which
+	 * a new node has been attached.  All that remains is to
+	 * propagate avl_balance up the tree.
+	 */
+	for ( ; ; ) {
+		avl64node_t *parent = np->avl_parent;
+		avl64node_t *child;
+
+		CERT(growth == AVL_BACK || growth == AVL_FORW);
+
+		/*
+		 * If the buffer was already balanced, set avl_balance
+		 * to the new direction.  Continue if there is a
+		 * parent after setting growth to reflect np's
+		 * relation to its parent.
+		 */
+		if (np->avl_balance == AVL_BALANCE) {
+			np->avl_balance = growth;
+			if (parent) {
+				if (parent->avl_forw == np)
+					growth = AVL_FORW;
+				else {
+					ASSERT(parent->avl_back == np);
+					growth = AVL_BACK;
+				}
+
+				np = parent;
+				continue;
+			}
+			break;
+		}
+
+		if (growth != np->avl_balance) {
+			/*
+			 * Subtree is now balanced -- no net effect
+			 * in the size of the subtree, so leave.
+			 */
+			np->avl_balance = AVL_BALANCE;
+			break;
+		}
+
+		if (growth == AVL_BACK) {
+
+			child = np->avl_back;
+			CERT(np->avl_balance == AVL_BACK && child);
+
+			if (child->avl_balance == AVL_BACK) { /* single LL */
+				/*
+				 * ``A'' just got inserted;
+				 * np points to ``E'', child to ``C'',
+				 * and it is already AVL_BACK --
+				 * child will get promoted to top of subtree.
+
+				np->	     -E			C
+					     / \	       / \
+				child->	   -C   F	     -B   E
+					   / \		     /   / \
+					 -B   D		    A   D   F
+					 /
+					A
+
+					Note that child->avl_parent and
+					avl_balance get set in common code.
+				 */
+				np->avl_parent = child;
+				np->avl_balance = AVL_BALANCE;
+				np->avl_back = child->avl_forw;
+				if (child->avl_forw)
+					child->avl_forw->avl_parent = np;
+				child->avl_forw = np;
+			} else {
+				/*
+				 * double LR
+				 *
+				 * child's avl_forw node gets promoted to
+				 * the top of the subtree.
+
+				np->	     -E		      C
+					     / \	     / \
+				child->	   +B   F	   -B   E
+					   / \		   /   / \
+					  A  +C		  A   D   F
+					       \
+						D
+
+				 */
+				avl64node_t *tmp = child->avl_forw;
+
+				CERT(child->avl_balance == AVL_FORW && tmp);
+
+				child->avl_forw = tmp->avl_back;
+				if (tmp->avl_back)
+					tmp->avl_back->avl_parent = child;
+
+				tmp->avl_back = child;
+				child->avl_parent = tmp;
+
+				np->avl_back = tmp->avl_forw;
+				if (tmp->avl_forw)
+					tmp->avl_forw->avl_parent = np;
+
+				tmp->avl_forw = np;
+				np->avl_parent = tmp;
+
+				if (tmp->avl_balance == AVL_BACK)
+					np->avl_balance = AVL_FORW;
+				else
+					np->avl_balance = AVL_BALANCE;
+
+				if (tmp->avl_balance == AVL_FORW)
+					child->avl_balance = AVL_BACK;
+				else
+					child->avl_balance = AVL_BALANCE;
+
+				/*
+				 * Set child to point to tmp since it is
+				 * now the top of the subtree, and will
+				 * get attached to the subtree parent in
+				 * the common code below.
+				 */
+				child = tmp;
+			}
+
+		} else /* growth == AVL_BACK */ {
+
+			/*
+			 * This code is the mirror image of AVL_FORW above.
+			 */
+
+			child = np->avl_forw;
+			CERT(np->avl_balance == AVL_FORW && child);
+
+			if (child->avl_balance == AVL_FORW) { /* single RR */
+				np->avl_parent = child;
+				np->avl_balance = AVL_BALANCE;
+				np->avl_forw = child->avl_back;
+				if (child->avl_back)
+					child->avl_back->avl_parent = np;
+				child->avl_back = np;
+			} else {
+				/*
+				 * double RL
+				 */
+				avl64node_t *tmp = child->avl_back;
+
+				ASSERT(child->avl_balance == AVL_BACK && tmp);
+
+				child->avl_back = tmp->avl_forw;
+				if (tmp->avl_forw)
+					tmp->avl_forw->avl_parent = child;
+
+				tmp->avl_forw = child;
+				child->avl_parent = tmp;
+
+				np->avl_forw = tmp->avl_back;
+				if (tmp->avl_back)
+					tmp->avl_back->avl_parent = np;
+
+				tmp->avl_back = np;
+				np->avl_parent = tmp;
+
+				if (tmp->avl_balance == AVL_FORW)
+					np->avl_balance = AVL_BACK;
+				else
+					np->avl_balance = AVL_BALANCE;
+
+				if (tmp->avl_balance == AVL_BACK)
+					child->avl_balance = AVL_FORW;
+				else
+					child->avl_balance = AVL_BALANCE;
+
+				child = tmp;
+			}
+		}
+
+		child->avl_parent = parent;
+		child->avl_balance = AVL_BALANCE;
+
+		if (parent) {
+			if (parent->avl_back == np)
+				parent->avl_back = child;
+			else
+				parent->avl_forw = child;
+		} else {
+			ASSERT(*rootp == np);
+			*rootp = child;
+		}
+
+		break;
+	}
+}
+
+static
+avl64node_t *
+avl64_insert_find_growth(
+		avl64tree_desc_t *tree,
+		uint64_t start,	/* range start at start, */
+		uint64_t end,	/* exclusive */
+		int   *growthp)	/* OUT */
+{
+	avl64node_t *root = tree->avl_root;
+	avl64node_t *np;
+
+	np = root;
+	ASSERT(np); /* caller ensures that there is atleast one node in tree */
+
+	for ( ; ; ) {
+		CERT(np->avl_parent || root == np);
+		CERT(!np->avl_parent || root != np);
+		CERT(!(np->avl_back) || np->avl_back->avl_parent == np);
+		CERT(!(np->avl_forw) || np->avl_forw->avl_parent == np);
+		CERT(np->avl_balance != AVL_FORW || np->avl_forw);
+		CERT(np->avl_balance != AVL_BACK || np->avl_back);
+		CERT(np->avl_balance != AVL_BALANCE ||
+		     np->avl_back == NULL || np->avl_forw);
+		CERT(np->avl_balance != AVL_BALANCE ||
+		     np->avl_forw == NULL || np->avl_back);
+
+		if (AVL_START(tree, np) >= end) {
+			if (np->avl_back) {
+				np = np->avl_back;
+				continue;
+			}
+			*growthp = AVL_BACK;
+			break;
+		}
+
+		if (AVL_END(tree, np) <= start) {
+			if (np->avl_forw) {
+				np = np->avl_forw;
+				continue;
+			}
+			*growthp = AVL_FORW;
+			break;
+		}
+		/* found exact match -- let caller decide if it is an error */
+		return(NULL);
+	}
+	return(np);
+}
+
+
+static void
+avl64_insert_grow(
+	avl64tree_desc_t *tree,
+	avl64node_t *parent,
+	avl64node_t *newnode,
+	int growth)
+{
+	avl64node_t *nnext;
+	uint64_t start = AVL_START(tree, newnode);
+
+	if (growth == AVL_BACK) {
+
+		parent->avl_back = newnode;
+		/*
+		 * we are growing to the left; previous in-order to newnode is
+		 * closest ancestor with lesser value. Before this
+		 * insertion, this ancestor will be pointing to
+		 * newnode's parent. After insertion, next in-order to newnode
+		 * is the parent.
+		 */
+		newnode->avl_nextino = parent;
+		nnext = parent;
+		while (nnext) {
+			if (AVL_END(tree, nnext) <= start)
+				break;
+			nnext = nnext->avl_parent;
+		}
+		if (nnext)  {
+			/*
+			 * nnext will be null if newnode is
+			 * the least element, and hence very first in the list.
+			 */
+			ASSERT(nnext->avl_nextino == parent);
+			nnext->avl_nextino = newnode;
+		}
+	}
+	else {
+		parent->avl_forw = newnode;
+		newnode->avl_nextino = parent->avl_nextino;
+		parent->avl_nextino = newnode;
+	}
+}
+
+
+avl64node_t *
+avl64_insert(
+	avl64tree_desc_t *tree,
+	avl64node_t *newnode)
+{
+	avl64node_t *np;
+	uint64_t start = AVL_START(tree, newnode);
+	uint64_t end = AVL_END(tree, newnode);
+	int growth;
+
+	ASSERT(newnode);
+	/*
+	 * Clean all pointers for sanity; some will be reset as necessary.
+	 */
+	newnode->avl_nextino = NULL;
+	newnode->avl_parent = NULL;
+	newnode->avl_forw = NULL;
+	newnode->avl_back = NULL;
+	newnode->avl_balance = AVL_BALANCE;
+
+	if ((np = tree->avl_root) == NULL) { /* degenerate case... */
+		tree->avl_root = newnode;
+		tree->avl_firstino = newnode;
+		return newnode;
+	}
+
+	if ((np = avl64_insert_find_growth(tree, start, end, &growth))
+			== NULL) {
+		if (start != end)  { /* non-zero length range */
+			fprintf(stderr,
+		_("avl_insert: Warning! duplicate range [%llu,%llu]\n"),
+				(unsigned long long)start,
+				(unsigned long long)end);
+		}
+		return(NULL);
+	}
+
+	avl64_insert_grow(tree, np, newnode, growth);
+	if (growth == AVL_BACK) {
+		/*
+		 * Growing to left. if np was firstino, newnode will be firstino
+		 */
+		 if (tree->avl_firstino == np)
+			tree->avl_firstino = newnode;
+	}
+#ifdef notneeded
+	else
+	if (growth == AVL_FORW)
+		/*
+		 * Cannot possibly be firstino; there is somebody to our left.
+		 */
+		 ;
+#endif
+
+	newnode->avl_parent = np;
+	CERT(np->avl_forw == newnode || np->avl_back == newnode);
+
+	avl64_balance(&tree->avl_root, np, growth);
+
+	avl64_checktree(tree, tree->avl_root);
+
+	return newnode;
+}
+
+/*
+ *
+ * avl64_insert_immediate(tree, afterp, newnode):
+ *	insert newnode immediately into tree immediately after afterp.
+ *	after insertion, newnode is right child of afterp.
+ */
+void
+avl64_insert_immediate(
+		avl64tree_desc_t *tree,
+		avl64node_t *afterp,
+		avl64node_t *newnode)
+{
+	/*
+	 * Clean all pointers for sanity; some will be reset as necessary.
+	 */
+	newnode->avl_nextino = NULL;
+	newnode->avl_parent = NULL;
+	newnode->avl_forw = NULL;
+	newnode->avl_back = NULL;
+	newnode->avl_balance = AVL_BALANCE;
+
+	if (afterp == NULL) {
+		tree->avl_root = newnode;
+		tree->avl_firstino = newnode;
+		return;
+	}
+
+	ASSERT(afterp->avl_forw == NULL);
+	avl64_insert_grow(tree, afterp, newnode, AVL_FORW); /* grow to right */
+	CERT(afterp->avl_forw == newnode);
+	avl64_balance(&tree->avl_root, afterp, AVL_FORW);
+	avl64_checktree(tree, tree->avl_root);
+}
+
+
+/*
+ *	Returns first in order node
+ */
+avl64node_t *
+avl64_firstino(avl64node_t *root)
+{
+	avl64node_t *np;
+
+	if ((np = root) == NULL)
+		return NULL;
+
+	while (np->avl_back)
+		np = np->avl_back;
+	return np;
+}
+
+/*
+ *	Returns last in order node
+ */
+avl64node_t *
+avl64_lastino(avl64node_t *root)
+{
+	avl64node_t *np;
+
+	if ((np = root) == NULL)
+		return NULL;
+
+	while (np->avl_forw)
+		np = np->avl_forw;
+	return np;
+}
+
+void
+avl64_init_tree(avl64tree_desc_t *tree, avl64ops_t *ops)
+{
+	tree->avl_root = NULL;
+	tree->avl_firstino = NULL;
+	tree->avl_ops = ops;
+}
+
+#ifdef AVL_DEBUG
+static void
+avl64_printnode(avl64tree_desc_t *tree, avl64node_t *np, int nl)
+{
+	printf("[%d-%d]%c", AVL_START(tree, np),
+		(AVL_END(tree, np) - 1), nl ? '\n' : ' ');
+}
+#endif
+#ifdef STAND_ALONE_DEBUG
+
+struct avl_debug_node {
+	avl64node_t	avl_node;
+	xfs_off_t		avl_start;
+	unsigned int	avl_size;
+}
+
+avl64ops_t avl_debug_ops = {
+	avl_debug_start,
+	avl_debug_end,
+}
+
+static uint64_t
+avl64_debug_start(avl64node_t *node)
+{
+	return (uint64_t)(struct avl_debug_node *)node->avl_start;
+}
+
+static uint64_t
+avl64_debug_end(avl64node_t *node)
+{
+	return (uint64_t)
+		((struct avl_debug_node *)node->avl_start +
+		 (struct avl_debug_node *)node->avl_size);
+}
+
+avl_debug_node	freenodes[100];
+avl_debug_node	*freehead = &freenodes[0];
+
+static avl64node_t *
+alloc_avl64_debug_node()
+{
+	freehead->avl_balance = AVL_BALANCE;
+	freehead->avl_parent = freehead->avl_forw = freehead->avl_back = NULL;
+	return(freehead++);
+}
+
+static void
+avl64_print(avl64tree_desc_t *tree, avl64node_t *root, int depth)
+{
+	int i;
+
+	if (!root)
+		return;
+	if (root->avl_forw)
+		avl64_print(tree, root->avl_forw, depth+5);
+	for (i = 0; i < depth; i++)
+		putchar((int) ' ');
+	avl64_printnode(tree, root,1);
+	if (root->avl_back)
+		avl64_print(tree, root->avl_back, depth+5);
+}
+
+main()
+{
+	int		i, j;
+	avl64node_t	*np;
+	avl64tree_desc_t	tree;
+	char		linebuf[256], cmd[256];
+
+	avl64_init_tree(&tree, &avl_debug_ops);
+
+	for (i = 100; i > 0; i = i - 10)
+	{
+		np = alloc__debug_avlnode();
+		ASSERT(np);
+		np->avl_start = i;
+		np->avl_size = 10;
+		avl64_insert(&tree, np);
+	}
+	avl64_print(&tree, tree.avl_root, 0);
+
+	for (np = tree.avl_firstino; np != NULL; np = np->avl_nextino)
+		avl64_printnode(&tree, np, 0);
+	printf("\n");
+
+	while (1) {
+		printf(_("Command [fpdir] : "));
+		fgets(linebuf, 256, stdin);
+		if (feof(stdin)) break;
+		cmd[0] = NULL;
+		if (sscanf(linebuf, "%[fpdir]%d", cmd, &i) != 2)
+			continue;
+		switch (cmd[0]) {
+		case 'd':
+		case 'f':
+			printf(_("end of range ? "));
+			fgets(linebuf, 256, stdin);
+			j = atoi(linebuf);
+
+			if (i == j) j = i+1;
+			np = avl64_findinrange(&tree,i,j);
+			if (np) {
+				avl64_printnode(&tree, np, 1);
+				if (cmd[0] == 'd')
+					avl64_delete(&tree, np);
+			} else
+				printf(_("Cannot find %d\n"), i);
+			break;
+		case 'p':
+			avl64_print(&tree, tree.avl_root, 0);
+			for (np = tree.avl_firstino;
+				np != NULL; np = np->avl_nextino)
+					avl64_printnode(&tree, np, 0);
+			printf("\n");
+			break;
+		case 'i':
+			np = alloc_avlnode();
+			ASSERT(np);
+			np->avl_start = i;
+			printf(_("size of range ? "));
+			fgets(linebuf, 256, stdin);
+			j = atoi(linebuf);
+
+			np->avl_size = j;
+			avl64_insert(&tree, np);
+			break;
+		case 'r': {
+			avl64node_t	*b, *e, *t;
+			int		checklen;
+
+			printf(_("End of range ? "));
+			fgets(linebuf, 256, stdin);
+			j = atoi(linebuf);
+
+			printf(_("checklen 0/1 ? "));
+			fgets(linebuf, 256, stdin);
+			checklen = atoi(linebuf);
+
+
+			b = avl64_findanyrange(&tree, i, j, checklen);
+			if (b) {
+				printf(_("Found something\n"));
+				t = b;
+				while (t)  {
+					if (t != b &&
+					    AVL_START(&tree, t) >= j)
+						break;
+					avl64_printnode(&tree, t, 0);
+					t = t->avl_nextino;
+				}
+				printf("\n");
+			}
+		     }
+		}
+	}
+}
+#endif
+
+/*
+ *	Given a tree, find value; will find return range enclosing value,
+ *	or range immediately succeeding value,
+ *	or range immediately preceeding value.
+ */
+avl64node_t *
+avl64_findadjacent(
+	avl64tree_desc_t *tree,
+	uint64_t value,
+	int		dir)
+{
+	avl64node_t *np = tree->avl_root;
+
+	while (np) {
+		if (value < AVL_START(tree, np)) {
+			if (np->avl_back) {
+				np = np->avl_back;
+				continue;
+			}
+			/* if we were to add node with value, would
+			 * have a growth of AVL_BACK
+			 */
+			if (dir == AVL_SUCCEED) {
+				/* if succeeding node is needed, this is it.
+				 */
+				return(np);
+			}
+			if (dir == AVL_PRECEED) {
+				/*
+				 * find nearest ancestor with lesser value.
+				 */
+				 np = np->avl_parent;
+				 while (np) {
+					if (AVL_END(tree, np) <= value)
+						break;
+					np = np->avl_parent;
+				}
+				return(np);
+			}
+			ASSERT(dir == AVL_SUCCEED || dir == AVL_PRECEED);
+			break;
+		}
+		if (value >= AVL_END(tree, np)) {
+			if (np->avl_forw) {
+				np = np->avl_forw;
+				continue;
+			}
+			/* if we were to add node with value, would
+			 * have a growth of AVL_FORW;
+			 */
+			if (dir == AVL_SUCCEED) {
+				/* we are looking for a succeeding node;
+				 * this is nextino.
+				 */
+				return(np->avl_nextino);
+			}
+			if (dir == AVL_PRECEED) {
+				/* looking for a preceeding node; this is it. */
+				return(np);
+			}
+			ASSERT(dir == AVL_SUCCEED || dir == AVL_PRECEED);
+		}
+		/* AVL_START(tree, np) <= value < AVL_END(tree, np) */
+		return(np);
+	}
+	return NULL;
+}
+
+
+/*
+ *	avl_findranges:
+ *
+ *	Given range r [start, end), find all ranges in tree which are contained
+ *	in r. At return, startp and endp point to first and last of
+ *	a chain of elements which describe the contained ranges. Elements
+ *	in startp ... endp are in sort order, and can be accessed by
+ *	using avl_nextino.
+ */
+
+void
+avl64_findranges(
+	avl64tree_desc_t *tree,
+	uint64_t start,
+	uint64_t end,
+	avl64node_t	        **startp,
+	avl64node_t		**endp)
+{
+	avl64node_t *np;
+
+	np = avl64_findadjacent(tree, start, AVL_SUCCEED);
+	if (np == NULL				/* nothing succeding start */
+		|| (np && (end <= AVL_START(tree, np))))
+						/* something follows start,
+						but... is entirely after end */
+	{
+		*startp = NULL;
+		*endp = NULL;
+		return;
+	}
+
+	*startp = np;
+
+	/* see if end is in this region itself */
+	if (end <= AVL_END(tree, np) ||
+	    np->avl_nextino == NULL ||
+	    (np->avl_nextino &&
+	    (end <= AVL_START(tree, np->avl_nextino)))) {
+		*endp = np;
+		return;
+	}
+	/* have to munge for end */
+	/*
+	 * note: have to look for (end - 1), since
+	 * findadjacent will look for exact value, and does not
+	 * care about the fact that end is actually one more
+	 * than the value actually being looked for; thus feed it one less.
+	 */
+	*endp = avl64_findadjacent(tree, (end-1), AVL_PRECEED);
+	ASSERT(*endp);
+}
diff --git a/repair/Makefile b/repair/Makefile
index 4184a70..c348cec 100644
--- a/repair/Makefile
+++ b/repair/Makefile
@@ -9,11 +9,11 @@ LSRCFILES = README
 
 LTCOMMAND = xfs_repair
 
-HFILES = agheader.h attr_repair.h avl.h avl64.h bmap.h btree.h \
+HFILES = agheader.h attr_repair.h avl.h bmap.h btree.h \
 	da_util.h dinode.h dir2.h err_protos.h globals.h incore.h protos.h \
 	rt.h progress.h scan.h versions.h prefetch.h rmap.h slab.h threads.h
 
-CFILES = agheader.c attr_repair.c avl.c avl64.c bmap.c btree.c \
+CFILES = agheader.c attr_repair.c avl.c bmap.c btree.c \
 	da_util.c dino_chunks.c dinode.c dir2.c globals.c incore.c \
 	incore_bmc.c init.c incore_ext.c incore_ino.c phase1.c \
 	phase2.c phase3.c phase4.c phase5.c phase6.c phase7.c \
diff --git a/repair/avl64.c b/repair/avl64.c
deleted file mode 100644
index 8f4a121..0000000
--- a/repair/avl64.c
+++ /dev/null
@@ -1,1418 +0,0 @@
-/*
- * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms 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.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include <stdio.h>
-#include "libxfs.h"
-#include "avl64.h"
-
-#define CERT	ASSERT
-
-#ifdef AVL_DEBUG
-
-static void
-avl64_checknode(
-	avl64tree_desc_t *tree,
-	avl64node_t *np)
-{
-	avl64node_t *back = np->avl_back;
-	avl64node_t *forw = np->avl_forw;
-	avl64node_t *nextino = np->avl_nextino;
-	int bal = np->avl_balance;
-
-	ASSERT(bal != AVL_BALANCE || (!back && !forw) || (back && forw));
-	ASSERT(bal != AVL_FORW || forw);
-	ASSERT(bal != AVL_BACK || back);
-
-	if (forw) {
-		ASSERT(AVL_START(tree, np) < AVL_START(tree, forw));
-		ASSERT(np->avl_forw->avl_parent == np);
-		ASSERT(back || bal == AVL_FORW);
-	} else {
-		ASSERT(bal != AVL_FORW);
-		ASSERT(bal == AVL_BALANCE || back);
-		ASSERT(bal == AVL_BACK || !back);
-	}
-
-	if (back) {
-		ASSERT(AVL_START(tree, np) > AVL_START(tree, back));
-		ASSERT(np->avl_back->avl_parent == np);
-		ASSERT(forw || bal == AVL_BACK);
-	} else {
-		ASSERT(bal != AVL_BACK);
-		ASSERT(bal == AVL_BALANCE || forw);
-		ASSERT(bal == AVL_FORW || !forw);
-	}
-
-	if (nextino == NULL)
-		ASSERT(forw == NULL);
-	else
-		ASSERT(AVL_END(tree, np) <= AVL_START(tree, nextino));
-}
-
-static void
-avl64_checktree(
-	avl64tree_desc_t *tree,
-	avl64node_t *root)
-{
-	avl64node_t *nlast, *nnext, *np;
-	uint64_t offset = 0;
-	uint64_t end;
-
-	nlast = nnext = root;
-
-	ASSERT(!nnext || nnext->avl_parent == NULL);
-
-	while (nnext) {
-
-		avl64_checknode(tree, nnext);
-		end = AVL_END(tree, nnext);
-
-		if (end <= offset) {
-			if ((np = nnext->avl_forw) && np != nlast) {
-				nlast = nnext;
-				nnext = np;
-			} else {
-				nlast = nnext;
-				nnext = nnext->avl_parent;
-			}
-			continue;
-		}
-
-		nlast = nnext;
-		if (np = nnext->avl_back) {
-			if (AVL_END(tree, np) > offset) {
-				nnext = np;
-				continue;
-			}
-		}
-
-		np = nnext;
-		nnext = nnext->avl_forw;
-		if (!nnext)
-			nnext = np->avl_parent;
-
-		offset = end;
-	}
-}
-#else	/* ! AVL_DEBUG */
-#define avl64_checktree(t,x)
-#endif	/* AVL_DEBUG */
-
-
-/*
- * Reset balance for np up through tree.
- * ``direction'' is the way that np's balance
- * is headed after the deletion of one of its children --
- * e.g., deleting a avl_forw child sends avl_balance toward AVL_BACK.
- * Called only when deleting a node from the tree.
- */
-static void
-retreat(
-	avl64tree_desc_t *tree,
-	avl64node_t *np,
-	int direction)
-{
-	avl64node_t **rootp = &tree->avl_root;
-	avl64node_t *parent;
-	avl64node_t *child;
-	avl64node_t *tmp;
-	int	bal;
-
-	do {
-		ASSERT(direction == AVL_BACK || direction == AVL_FORW);
-
-		if (np->avl_balance == AVL_BALANCE) {
-			np->avl_balance = direction;
-			return;
-		}
-
-		parent = np->avl_parent;
-
-		/*
-		 * If balance is being restored, no local node
-		 * reorganization is necessary, but may be at
-		 * a higher node.  Reset direction and continue.
-		 */
-		if (direction != np->avl_balance) {
-			np->avl_balance = AVL_BALANCE;
-			if (parent) {
-				if (parent->avl_forw == np)
-					direction = AVL_BACK;
-				else
-					direction = AVL_FORW;
-
-				np = parent;
-				continue;
-			}
-			return;
-		}
-
-		/*
-		 * Imbalance.  If a avl_forw node was removed, direction
-		 * (and, by reduction, np->avl_balance) is/was AVL_BACK.
-		 */
-		if (np->avl_balance == AVL_BACK) {
-
-			ASSERT(direction == AVL_BACK);
-			child = np->avl_back;
-			bal = child->avl_balance;
-
-			if (bal != AVL_FORW) /* single LL */ {
-				/*
-				 * np gets pushed down to lesser child's
-				 * avl_forw branch.
-				 *
-				 *  np->    -D		    +B
-				 *	    / \		    / \
-				 * child-> B   deleted	   A  -D
-				 *	  / \		      /
-				 *	 A   C		     C
-				cmn_err(CE_CONT, "!LL delete b 0x%x c 0x%x\n",
-					np, child);
-				 */
-
-				np->avl_back = child->avl_forw;
-				if (child->avl_forw)
-					child->avl_forw->avl_parent = np;
-				child->avl_forw = np;
-
-				if (parent) {
-					if (parent->avl_forw == np) {
-						parent->avl_forw = child;
-						direction = AVL_BACK;
-					} else {
-						ASSERT(parent->avl_back == np);
-						parent->avl_back = child;
-						direction = AVL_FORW;
-					}
-				} else {
-					ASSERT(*rootp == np);
-					*rootp = child;
-				}
-				np->avl_parent = child;
-				child->avl_parent = parent;
-
-				if (bal == AVL_BALANCE) {
-					np->avl_balance = AVL_BACK;
-					child->avl_balance = AVL_FORW;
-					return;
-				} else {
-					np->avl_balance = AVL_BALANCE;
-					child->avl_balance = AVL_BALANCE;
-					np = parent;
-					avl64_checktree(tree, *rootp);
-					continue;
-				}
-			}
-
-			/* child->avl_balance == AVL_FORW  double LR rotation
-			 *
-			 * child's avl_forw node gets promoted up, along with
-			 * its avl_forw subtree
-			 *
-			 *  np->     -G			  C
-			 *	     / \		 / \
-			 * child-> +B   H	       -B   G
-			 *	   / \   \	       /   / \
-			 *	  A  +C   deleted     A   D   H
-			 *	       \
-			 *	        D
-			cmn_err(CE_CONT, "!LR delete b 0x%x c 0x%x t 0x%x\n",
-				np, child, child->avl_forw);
-			 */
-
-			tmp = child->avl_forw;
-			bal = tmp->avl_balance;
-
-			child->avl_forw = tmp->avl_back;
-			if (tmp->avl_back)
-				tmp->avl_back->avl_parent = child;
-
-			tmp->avl_back = child;
-			child->avl_parent = tmp;
-
-			np->avl_back = tmp->avl_forw;
-			if (tmp->avl_forw)
-				tmp->avl_forw->avl_parent = np;
-			tmp->avl_forw = np;
-
-			if (bal == AVL_FORW)
-				child->avl_balance = AVL_BACK;
-			else
-				child->avl_balance = AVL_BALANCE;
-
-			if (bal == AVL_BACK)
-				np->avl_balance = AVL_FORW;
-			else
-				np->avl_balance = AVL_BALANCE;
-
-			goto next;
-		}
-
-		ASSERT(np->avl_balance == AVL_FORW && direction == AVL_FORW);
-
-		child = np->avl_forw;
-		bal = child->avl_balance;
-
-		if (bal != AVL_BACK) /* single RR */ {
-			/*
-			 * np gets pushed down to greater child's
-			 * avl_back branch.
-			 *
-			 *  np->    +B		     -D
-			 *	    / \		     / \
-			 *   deleted   D <-child   +B   E
-			 *	      / \	     \
-			 *	     C   E	      C
-			cmn_err(CE_CONT, "!RR delete b 0x%x c 0x%x\n",
-				np, child);
-			 */
-
-			np->avl_forw = child->avl_back;
-			if (child->avl_back)
-				child->avl_back->avl_parent = np;
-			child->avl_back = np;
-
-			if (parent) {
-				if (parent->avl_forw == np) {
-					parent->avl_forw = child;
-					direction = AVL_BACK;
-				} else {
-					ASSERT(parent->avl_back == np);
-					parent->avl_back = child;
-					direction = AVL_FORW;
-				}
-			} else {
-				ASSERT(*rootp == np);
-				*rootp = child;
-			}
-			np->avl_parent = child;
-			child->avl_parent = parent;
-
-			if (bal == AVL_BALANCE) {
-				np->avl_balance = AVL_FORW;
-				child->avl_balance = AVL_BACK;
-				return;
-			} else {
-				np->avl_balance = AVL_BALANCE;
-				child->avl_balance = AVL_BALANCE;
-				np = parent;
-				avl64_checktree(tree, *rootp);
-				continue;
-			}
-		}
-
-		/* child->avl_balance == AVL_BACK  double RL rotation
-		cmn_err(CE_CONT, "!RL delete b 0x%x c 0x%x t 0x%x\n",
-			np, child, child->avl_back);
-		*/
-
-		tmp = child->avl_back;
-		bal = tmp->avl_balance;
-
-		child->avl_back = tmp->avl_forw;
-		if (tmp->avl_forw)
-			tmp->avl_forw->avl_parent = child;
-
-		tmp->avl_forw = child;
-		child->avl_parent = tmp;
-
-		np->avl_forw = tmp->avl_back;
-		if (tmp->avl_back)
-			tmp->avl_back->avl_parent = np;
-		tmp->avl_back = np;
-
-		if (bal == AVL_BACK)
-			child->avl_balance = AVL_FORW;
-		else
-			child->avl_balance = AVL_BALANCE;
-
-		if (bal == AVL_FORW)
-			np->avl_balance = AVL_BACK;
-		else
-			np->avl_balance = AVL_BALANCE;
-next:
-		np->avl_parent = tmp;
-		tmp->avl_balance = AVL_BALANCE;
-		tmp->avl_parent = parent;
-
-		if (parent) {
-			if (parent->avl_forw == np) {
-				parent->avl_forw = tmp;
-				direction = AVL_BACK;
-			} else {
-				ASSERT(parent->avl_back == np);
-				parent->avl_back = tmp;
-				direction = AVL_FORW;
-			}
-		} else {
-			ASSERT(*rootp == np);
-			*rootp = tmp;
-			return;
-		}
-
-		np = parent;
-		avl64_checktree(tree, *rootp);
-	} while (np);
-}
-
-/*
- *	Remove node from tree.
- *	avl_delete does the local tree manipulations,
- *	calls retreat() to rebalance tree up to its root.
- */
-void
-avl64_delete(
-	avl64tree_desc_t *tree,
-	avl64node_t *np)
-{
-	avl64node_t *forw = np->avl_forw;
-	avl64node_t *back = np->avl_back;
-	avl64node_t *parent = np->avl_parent;
-	avl64node_t *nnext;
-
-
-	if (np->avl_back) {
-		/*
-		 * a left child exits, then greatest left descendent's nextino
-		 * is pointing to np; make it point to np->nextino.
-		 */
-		nnext = np->avl_back;
-		while (nnext) {
-			if (!nnext->avl_forw)
-				break; /* can't find anything bigger */
-			nnext = nnext->avl_forw;
-		}
-	} else
-	if (np->avl_parent) {
-		/*
-		 * find nearest ancestor with lesser value. That ancestor's
-		 * nextino is pointing to np; make it point to np->nextino
-		 */
-		 nnext = np->avl_parent;
-		 while (nnext) {
-			if (AVL_END(tree, nnext) <= AVL_END(tree, np))
-				break;
-			nnext = nnext->avl_parent;
-		}
-	} else
-		nnext = NULL;
-
-	if (nnext) {
-		ASSERT(nnext->avl_nextino == np);
-		nnext->avl_nextino = np->avl_nextino;
-		/*
-		 *	Something preceeds np; np cannot be firstino.
-		 */
-		ASSERT(tree->avl_firstino != np);
-	}
-	else {
-		/*
-		 *	Nothing preceeding np; after deletion, np's nextino
-		 *	is firstino of tree.
-		 */
-		ASSERT(tree->avl_firstino == np);
-		tree->avl_firstino = np->avl_nextino;
-	}
-
-
-	/*
-	 * Degenerate cases...
-	 */
-	if (forw == NULL) {
-		forw = back;
-		goto attach;
-	}
-
-	if (back == NULL) {
-attach:
-		if (forw)
-			forw->avl_parent = parent;
-		if (parent) {
-			if (parent->avl_forw == np) {
-				parent->avl_forw = forw;
-				retreat(tree, parent, AVL_BACK);
-			} else {
-				ASSERT(parent->avl_back == np);
-				parent->avl_back = forw;
-				retreat(tree, parent, AVL_FORW);
-			}
-		} else {
-			ASSERT(tree->avl_root == np);
-			tree->avl_root = forw;
-		}
-		avl64_checktree(tree, tree->avl_root);
-		return;
-	}
-
-	/*
-	 * Harder case: children on both sides.
-	 * If back's avl_forw pointer is null, just have back
-	 * inherit np's avl_forw tree, remove np from the tree
-	 * and adjust balance counters starting at back.
-	 *
-	 * np->	    xI		    xH	(befor retreat())
-	 *	    / \		    / \
-	 * back->  H   J	   G   J
-	 *	  /   / \             / \
-	 *       G   ?   ?           ?   ?
-	 *      / \
-	 *     ?   ?
-	 */
-	if ((forw = back->avl_forw) == NULL) {
-		/*
-		 * AVL_FORW retreat below will set back's
-		 * balance to AVL_BACK.
-		 */
-		back->avl_balance = np->avl_balance;
-		back->avl_forw = forw = np->avl_forw;
-		forw->avl_parent = back;
-		back->avl_parent = parent;
-
-		if (parent) {
-			if (parent->avl_forw == np)
-				parent->avl_forw = back;
-			else {
-				ASSERT(parent->avl_back == np);
-				parent->avl_back = back;
-			}
-		} else {
-			ASSERT(tree->avl_root == np);
-			tree->avl_root = back;
-		}
-
-		/*
-		 * back is taking np's place in the tree, and
-		 * has therefore lost a avl_back node (itself).
-		 */
-		retreat(tree, back, AVL_FORW);
-		avl64_checktree(tree, tree->avl_root);
-		return;
-	}
-
-	/*
-	 * Hardest case: children on both sides, and back's
-	 * avl_forw pointer isn't null.  Find the immediately
-	 * inferior buffer by following back's avl_forw line
-	 * to the end, then have it inherit np's avl_forw tree.
-	 *
-	 * np->	    xI			      xH
-	 *	    / \			      / \
-	 *         G   J	     back->  G   J   (before retreat())
-	 *	  / \			    / \
-	 *       F   ?...		   F   ?1
-	 *      /     \
-	 *     ?       H  <-forw
-	 *	      /
-	 *	     ?1
-	 */
-	while ((back = forw->avl_forw))
-		forw = back;
-
-	/*
-	 * Will be adjusted by retreat() below.
-	 */
-	forw->avl_balance = np->avl_balance;
-
-	/*
-	 * forw inherits np's avl_forw...
-	 */
-	forw->avl_forw = np->avl_forw;
-	np->avl_forw->avl_parent = forw;
-
-	/*
-	 * ... forw's parent gets forw's avl_back...
-	 */
-	back = forw->avl_parent;
-	back->avl_forw = forw->avl_back;
-	if (forw->avl_back)
-		forw->avl_back->avl_parent = back;
-
-	/*
-	 * ... forw gets np's avl_back...
-	 */
-	forw->avl_back = np->avl_back;
-	np->avl_back->avl_parent = forw;
-
-	/*
-	 * ... and forw gets np's parent.
-	 */
-	forw->avl_parent = parent;
-
-	if (parent) {
-		if (parent->avl_forw == np)
-			parent->avl_forw = forw;
-		else
-			parent->avl_back = forw;
-	} else {
-		ASSERT(tree->avl_root == np);
-		tree->avl_root = forw;
-	}
-
-	/*
-	 * What used to be forw's parent is the starting
-	 * point for rebalancing.  It has lost a avl_forw node.
-	 */
-	retreat(tree, back, AVL_BACK);
-	avl64_checktree(tree, tree->avl_root);
-}
-
-
-/*
- *	avl_findanyrange:
- *
- *	Given range r [start, end), find any range which is contained in r.
- *	if checklen is non-zero, then only ranges of non-zero length are
- *	considered in finding a match.
- */
-avl64node_t *
-avl64_findanyrange(
-	avl64tree_desc_t *tree,
-	uint64_t start,
-	uint64_t end,
-	int	checklen)
-{
-	avl64node_t *np = tree->avl_root;
-
-	/* np = avl64_findadjacent(tree, start, AVL_SUCCEED); */
-	while (np) {
-		if (start < AVL_START(tree, np)) {
-			if (np->avl_back) {
-				np = np->avl_back;
-				continue;
-			}
-			/* if we were to add node with start, would
-			 * have a growth of AVL_BACK
-			 */
-			/* if succeeding node is needed, this is it.
-			 */
-			break;
-		}
-		if (start >= AVL_END(tree, np)) {
-			if (np->avl_forw) {
-				np = np->avl_forw;
-				continue;
-			}
-			/* if we were to add node with start, would
-			 * have a growth of AVL_FORW;
-			 */
-			/* we are looking for a succeeding node;
-			 * this is nextino.
-			 */
-			np = np->avl_nextino;
-			break;
-		}
-		/* AVL_START(tree, np) <= start < AVL_END(tree, np) */
-		break;
-	}
-	if (np) {
-		if (checklen == AVL_INCLUDE_ZEROLEN) {
-			if (end <= AVL_START(tree, np)) {
-				/* something follows start, but is
-				 * is entierly after the range (end)
-				 */
-				return(NULL);
-			}
-			/* np may stradle [start, end) */
-			return(np);
-		}
-		/*
-		 * find non-zero length region
-		 */
-		while (np && (AVL_END(tree, np) - AVL_START(tree, np) == 0)
-			&& (AVL_START(tree, np)  < end))
-				np = np->avl_nextino;
-
-		if ((np == NULL) || (AVL_START(tree, np) >= end))
-			return NULL;
-		return(np);
-	}
-	/*
-	 * nothing succeeds start, all existing ranges are before start.
-	 */
-	return NULL;
-}
-
-
-/*
- * Returns a pointer to range which contains value.
- */
-avl64node_t *
-avl64_findrange(
-	avl64tree_desc_t *tree,
-	uint64_t value)
-{
-	avl64node_t *np = tree->avl_root;
-
-	while (np) {
-		if (value < AVL_START(tree, np)) {
-			np = np->avl_back;
-			continue;
-		}
-		if (value >= AVL_END(tree, np)) {
-			np = np->avl_forw;
-			continue;
-		}
-		ASSERT(AVL_START(tree, np) <= value &&
-		       value < AVL_END(tree, np));
-		return np;
-	}
-	return NULL;
-}
-
-
-/*
- * Returns a pointer to node which contains exact value.
- */
-avl64node_t *
-avl64_find(
-	avl64tree_desc_t *tree,
-	uint64_t value)
-{
-	avl64node_t *np = tree->avl_root;
-	uint64_t nvalue;
-
-	while (np) {
-		nvalue = AVL_START(tree, np);
-		if (value < nvalue) {
-			np = np->avl_back;
-			continue;
-		}
-		if (value == nvalue) {
-			return np;
-		}
-		np = np->avl_forw;
-	}
-	return NULL;
-}
-
-
-/*
- * Balance buffer AVL tree after attaching a new node to root.
- * Called only by avl_insert.
- */
-static void
-avl64_balance(
-	avl64node_t **rootp,
-	avl64node_t *np,
-	int growth)
-{
-	/*
-	 * At this point, np points to the node to which
-	 * a new node has been attached.  All that remains is to
-	 * propagate avl_balance up the tree.
-	 */
-	for ( ; ; ) {
-		avl64node_t *parent = np->avl_parent;
-		avl64node_t *child;
-
-		CERT(growth == AVL_BACK || growth == AVL_FORW);
-
-		/*
-		 * If the buffer was already balanced, set avl_balance
-		 * to the new direction.  Continue if there is a
-		 * parent after setting growth to reflect np's
-		 * relation to its parent.
-		 */
-		if (np->avl_balance == AVL_BALANCE) {
-			np->avl_balance = growth;
-			if (parent) {
-				if (parent->avl_forw == np)
-					growth = AVL_FORW;
-				else {
-					ASSERT(parent->avl_back == np);
-					growth = AVL_BACK;
-				}
-
-				np = parent;
-				continue;
-			}
-			break;
-		}
-
-		if (growth != np->avl_balance) {
-			/*
-			 * Subtree is now balanced -- no net effect
-			 * in the size of the subtree, so leave.
-			 */
-			np->avl_balance = AVL_BALANCE;
-			break;
-		}
-
-		if (growth == AVL_BACK) {
-
-			child = np->avl_back;
-			CERT(np->avl_balance == AVL_BACK && child);
-
-			if (child->avl_balance == AVL_BACK) { /* single LL */
-				/*
-				 * ``A'' just got inserted;
-				 * np points to ``E'', child to ``C'',
-				 * and it is already AVL_BACK --
-				 * child will get promoted to top of subtree.
-
-				np->	     -E			C
-					     / \	       / \
-				child->	   -C   F	     -B   E
-					   / \		     /   / \
-					 -B   D		    A   D   F
-					 /
-					A
-
-					Note that child->avl_parent and
-					avl_balance get set in common code.
-				 */
-				np->avl_parent = child;
-				np->avl_balance = AVL_BALANCE;
-				np->avl_back = child->avl_forw;
-				if (child->avl_forw)
-					child->avl_forw->avl_parent = np;
-				child->avl_forw = np;
-			} else {
-				/*
-				 * double LR
-				 *
-				 * child's avl_forw node gets promoted to
-				 * the top of the subtree.
-
-				np->	     -E		      C
-					     / \	     / \
-				child->	   +B   F	   -B   E
-					   / \		   /   / \
-					  A  +C		  A   D   F
-					       \
-						D
-
-				 */
-				avl64node_t *tmp = child->avl_forw;
-
-				CERT(child->avl_balance == AVL_FORW && tmp);
-
-				child->avl_forw = tmp->avl_back;
-				if (tmp->avl_back)
-					tmp->avl_back->avl_parent = child;
-
-				tmp->avl_back = child;
-				child->avl_parent = tmp;
-
-				np->avl_back = tmp->avl_forw;
-				if (tmp->avl_forw)
-					tmp->avl_forw->avl_parent = np;
-
-				tmp->avl_forw = np;
-				np->avl_parent = tmp;
-
-				if (tmp->avl_balance == AVL_BACK)
-					np->avl_balance = AVL_FORW;
-				else
-					np->avl_balance = AVL_BALANCE;
-
-				if (tmp->avl_balance == AVL_FORW)
-					child->avl_balance = AVL_BACK;
-				else
-					child->avl_balance = AVL_BALANCE;
-
-				/*
-				 * Set child to point to tmp since it is
-				 * now the top of the subtree, and will
-				 * get attached to the subtree parent in
-				 * the common code below.
-				 */
-				child = tmp;
-			}
-
-		} else /* growth == AVL_BACK */ {
-
-			/*
-			 * This code is the mirror image of AVL_FORW above.
-			 */
-
-			child = np->avl_forw;
-			CERT(np->avl_balance == AVL_FORW && child);
-
-			if (child->avl_balance == AVL_FORW) { /* single RR */
-				np->avl_parent = child;
-				np->avl_balance = AVL_BALANCE;
-				np->avl_forw = child->avl_back;
-				if (child->avl_back)
-					child->avl_back->avl_parent = np;
-				child->avl_back = np;
-			} else {
-				/*
-				 * double RL
-				 */
-				avl64node_t *tmp = child->avl_back;
-
-				ASSERT(child->avl_balance == AVL_BACK && tmp);
-
-				child->avl_back = tmp->avl_forw;
-				if (tmp->avl_forw)
-					tmp->avl_forw->avl_parent = child;
-
-				tmp->avl_forw = child;
-				child->avl_parent = tmp;
-
-				np->avl_forw = tmp->avl_back;
-				if (tmp->avl_back)
-					tmp->avl_back->avl_parent = np;
-
-				tmp->avl_back = np;
-				np->avl_parent = tmp;
-
-				if (tmp->avl_balance == AVL_FORW)
-					np->avl_balance = AVL_BACK;
-				else
-					np->avl_balance = AVL_BALANCE;
-
-				if (tmp->avl_balance == AVL_BACK)
-					child->avl_balance = AVL_FORW;
-				else
-					child->avl_balance = AVL_BALANCE;
-
-				child = tmp;
-			}
-		}
-
-		child->avl_parent = parent;
-		child->avl_balance = AVL_BALANCE;
-
-		if (parent) {
-			if (parent->avl_back == np)
-				parent->avl_back = child;
-			else
-				parent->avl_forw = child;
-		} else {
-			ASSERT(*rootp == np);
-			*rootp = child;
-		}
-
-		break;
-	}
-}
-
-static
-avl64node_t *
-avl64_insert_find_growth(
-		avl64tree_desc_t *tree,
-		uint64_t start,	/* range start at start, */
-		uint64_t end,	/* exclusive */
-		int   *growthp)	/* OUT */
-{
-	avl64node_t *root = tree->avl_root;
-	avl64node_t *np;
-
-	np = root;
-	ASSERT(np); /* caller ensures that there is atleast one node in tree */
-
-	for ( ; ; ) {
-		CERT(np->avl_parent || root == np);
-		CERT(!np->avl_parent || root != np);
-		CERT(!(np->avl_back) || np->avl_back->avl_parent == np);
-		CERT(!(np->avl_forw) || np->avl_forw->avl_parent == np);
-		CERT(np->avl_balance != AVL_FORW || np->avl_forw);
-		CERT(np->avl_balance != AVL_BACK || np->avl_back);
-		CERT(np->avl_balance != AVL_BALANCE ||
-		     np->avl_back == NULL || np->avl_forw);
-		CERT(np->avl_balance != AVL_BALANCE ||
-		     np->avl_forw == NULL || np->avl_back);
-
-		if (AVL_START(tree, np) >= end) {
-			if (np->avl_back) {
-				np = np->avl_back;
-				continue;
-			}
-			*growthp = AVL_BACK;
-			break;
-		}
-
-		if (AVL_END(tree, np) <= start) {
-			if (np->avl_forw) {
-				np = np->avl_forw;
-				continue;
-			}
-			*growthp = AVL_FORW;
-			break;
-		}
-		/* found exact match -- let caller decide if it is an error */
-		return(NULL);
-	}
-	return(np);
-}
-
-
-static void
-avl64_insert_grow(
-	avl64tree_desc_t *tree,
-	avl64node_t *parent,
-	avl64node_t *newnode,
-	int growth)
-{
-	avl64node_t *nnext;
-	uint64_t start = AVL_START(tree, newnode);
-
-	if (growth == AVL_BACK) {
-
-		parent->avl_back = newnode;
-		/*
-		 * we are growing to the left; previous in-order to newnode is
-		 * closest ancestor with lesser value. Before this
-		 * insertion, this ancestor will be pointing to
-		 * newnode's parent. After insertion, next in-order to newnode
-		 * is the parent.
-		 */
-		newnode->avl_nextino = parent;
-		nnext = parent;
-		while (nnext) {
-			if (AVL_END(tree, nnext) <= start)
-				break;
-			nnext = nnext->avl_parent;
-		}
-		if (nnext)  {
-			/*
-			 * nnext will be null if newnode is
-			 * the least element, and hence very first in the list.
-			 */
-			ASSERT(nnext->avl_nextino == parent);
-			nnext->avl_nextino = newnode;
-		}
-	}
-	else {
-		parent->avl_forw = newnode;
-		newnode->avl_nextino = parent->avl_nextino;
-		parent->avl_nextino = newnode;
-	}
-}
-
-
-avl64node_t *
-avl64_insert(
-	avl64tree_desc_t *tree,
-	avl64node_t *newnode)
-{
-	avl64node_t *np;
-	uint64_t start = AVL_START(tree, newnode);
-	uint64_t end = AVL_END(tree, newnode);
-	int growth;
-
-	ASSERT(newnode);
-	/*
-	 * Clean all pointers for sanity; some will be reset as necessary.
-	 */
-	newnode->avl_nextino = NULL;
-	newnode->avl_parent = NULL;
-	newnode->avl_forw = NULL;
-	newnode->avl_back = NULL;
-	newnode->avl_balance = AVL_BALANCE;
-
-	if ((np = tree->avl_root) == NULL) { /* degenerate case... */
-		tree->avl_root = newnode;
-		tree->avl_firstino = newnode;
-		return newnode;
-	}
-
-	if ((np = avl64_insert_find_growth(tree, start, end, &growth))
-			== NULL) {
-		if (start != end)  { /* non-zero length range */
-			fprintf(stderr,
-		_("avl_insert: Warning! duplicate range [%llu,%llu]\n"),
-				(unsigned long long)start,
-				(unsigned long long)end);
-		}
-		return(NULL);
-	}
-
-	avl64_insert_grow(tree, np, newnode, growth);
-	if (growth == AVL_BACK) {
-		/*
-		 * Growing to left. if np was firstino, newnode will be firstino
-		 */
-		 if (tree->avl_firstino == np)
-			tree->avl_firstino = newnode;
-	}
-#ifdef notneeded
-	else
-	if (growth == AVL_FORW)
-		/*
-		 * Cannot possibly be firstino; there is somebody to our left.
-		 */
-		 ;
-#endif
-
-	newnode->avl_parent = np;
-	CERT(np->avl_forw == newnode || np->avl_back == newnode);
-
-	avl64_balance(&tree->avl_root, np, growth);
-
-	avl64_checktree(tree, tree->avl_root);
-
-	return newnode;
-}
-
-/*
- *
- * avl64_insert_immediate(tree, afterp, newnode):
- *	insert newnode immediately into tree immediately after afterp.
- *	after insertion, newnode is right child of afterp.
- */
-void
-avl64_insert_immediate(
-		avl64tree_desc_t *tree,
-		avl64node_t *afterp,
-		avl64node_t *newnode)
-{
-	/*
-	 * Clean all pointers for sanity; some will be reset as necessary.
-	 */
-	newnode->avl_nextino = NULL;
-	newnode->avl_parent = NULL;
-	newnode->avl_forw = NULL;
-	newnode->avl_back = NULL;
-	newnode->avl_balance = AVL_BALANCE;
-
-	if (afterp == NULL) {
-		tree->avl_root = newnode;
-		tree->avl_firstino = newnode;
-		return;
-	}
-
-	ASSERT(afterp->avl_forw == NULL);
-	avl64_insert_grow(tree, afterp, newnode, AVL_FORW); /* grow to right */
-	CERT(afterp->avl_forw == newnode);
-	avl64_balance(&tree->avl_root, afterp, AVL_FORW);
-	avl64_checktree(tree, tree->avl_root);
-}
-
-
-/*
- *	Returns first in order node
- */
-avl64node_t *
-avl64_firstino(avl64node_t *root)
-{
-	avl64node_t *np;
-
-	if ((np = root) == NULL)
-		return NULL;
-
-	while (np->avl_back)
-		np = np->avl_back;
-	return np;
-}
-
-/*
- *	Returns last in order node
- */
-avl64node_t *
-avl64_lastino(avl64node_t *root)
-{
-	avl64node_t *np;
-
-	if ((np = root) == NULL)
-		return NULL;
-
-	while (np->avl_forw)
-		np = np->avl_forw;
-	return np;
-}
-
-void
-avl64_init_tree(avl64tree_desc_t *tree, avl64ops_t *ops)
-{
-	tree->avl_root = NULL;
-	tree->avl_firstino = NULL;
-	tree->avl_ops = ops;
-}
-
-#ifdef AVL_DEBUG
-static void
-avl64_printnode(avl64tree_desc_t *tree, avl64node_t *np, int nl)
-{
-	printf("[%d-%d]%c", AVL_START(tree, np),
-		(AVL_END(tree, np) - 1), nl ? '\n' : ' ');
-}
-#endif
-#ifdef STAND_ALONE_DEBUG
-
-struct avl_debug_node {
-	avl64node_t	avl_node;
-	xfs_off_t		avl_start;
-	unsigned int	avl_size;
-}
-
-avl64ops_t avl_debug_ops = {
-	avl_debug_start,
-	avl_debug_end,
-}
-
-static uint64_t
-avl64_debug_start(avl64node_t *node)
-{
-	return (uint64_t)(struct avl_debug_node *)node->avl_start;
-}
-
-static uint64_t
-avl64_debug_end(avl64node_t *node)
-{
-	return (uint64_t)
-		((struct avl_debug_node *)node->avl_start +
-		 (struct avl_debug_node *)node->avl_size);
-}
-
-avl_debug_node	freenodes[100];
-avl_debug_node	*freehead = &freenodes[0];
-
-static avl64node_t *
-alloc_avl64_debug_node()
-{
-	freehead->avl_balance = AVL_BALANCE;
-	freehead->avl_parent = freehead->avl_forw = freehead->avl_back = NULL;
-	return(freehead++);
-}
-
-static void
-avl64_print(avl64tree_desc_t *tree, avl64node_t *root, int depth)
-{
-	int i;
-
-	if (!root)
-		return;
-	if (root->avl_forw)
-		avl64_print(tree, root->avl_forw, depth+5);
-	for (i = 0; i < depth; i++)
-		putchar((int) ' ');
-	avl64_printnode(tree, root,1);
-	if (root->avl_back)
-		avl64_print(tree, root->avl_back, depth+5);
-}
-
-main()
-{
-	int		i, j;
-	avl64node_t	*np;
-	avl64tree_desc_t	tree;
-	char		linebuf[256], cmd[256];
-
-	avl64_init_tree(&tree, &avl_debug_ops);
-
-	for (i = 100; i > 0; i = i - 10)
-	{
-		np = alloc__debug_avlnode();
-		ASSERT(np);
-		np->avl_start = i;
-		np->avl_size = 10;
-		avl64_insert(&tree, np);
-	}
-	avl64_print(&tree, tree.avl_root, 0);
-
-	for (np = tree.avl_firstino; np != NULL; np = np->avl_nextino)
-		avl64_printnode(&tree, np, 0);
-	printf("\n");
-
-	while (1) {
-		printf(_("Command [fpdir] : "));
-		fgets(linebuf, 256, stdin);
-		if (feof(stdin)) break;
-		cmd[0] = NULL;
-		if (sscanf(linebuf, "%[fpdir]%d", cmd, &i) != 2)
-			continue;
-		switch (cmd[0]) {
-		case 'd':
-		case 'f':
-			printf(_("end of range ? "));
-			fgets(linebuf, 256, stdin);
-			j = atoi(linebuf);
-
-			if (i == j) j = i+1;
-			np = avl64_findinrange(&tree,i,j);
-			if (np) {
-				avl64_printnode(&tree, np, 1);
-				if (cmd[0] == 'd')
-					avl64_delete(&tree, np);
-			} else
-				printf(_("Cannot find %d\n"), i);
-			break;
-		case 'p':
-			avl64_print(&tree, tree.avl_root, 0);
-			for (np = tree.avl_firstino;
-				np != NULL; np = np->avl_nextino)
-					avl64_printnode(&tree, np, 0);
-			printf("\n");
-			break;
-		case 'i':
-			np = alloc_avlnode();
-			ASSERT(np);
-			np->avl_start = i;
-			printf(_("size of range ? "));
-			fgets(linebuf, 256, stdin);
-			j = atoi(linebuf);
-
-			np->avl_size = j;
-			avl64_insert(&tree, np);
-			break;
-		case 'r': {
-			avl64node_t	*b, *e, *t;
-			int		checklen;
-
-			printf(_("End of range ? "));
-			fgets(linebuf, 256, stdin);
-			j = atoi(linebuf);
-
-			printf(_("checklen 0/1 ? "));
-			fgets(linebuf, 256, stdin);
-			checklen = atoi(linebuf);
-
-
-			b = avl64_findanyrange(&tree, i, j, checklen);
-			if (b) {
-				printf(_("Found something\n"));
-				t = b;
-				while (t)  {
-					if (t != b &&
-					    AVL_START(&tree, t) >= j)
-						break;
-					avl64_printnode(&tree, t, 0);
-					t = t->avl_nextino;
-				}
-				printf("\n");
-			}
-		     }
-		}
-	}
-}
-#endif
-
-/*
- *	Given a tree, find value; will find return range enclosing value,
- *	or range immediately succeeding value,
- *	or range immediately preceeding value.
- */
-avl64node_t *
-avl64_findadjacent(
-	avl64tree_desc_t *tree,
-	uint64_t value,
-	int		dir)
-{
-	avl64node_t *np = tree->avl_root;
-
-	while (np) {
-		if (value < AVL_START(tree, np)) {
-			if (np->avl_back) {
-				np = np->avl_back;
-				continue;
-			}
-			/* if we were to add node with value, would
-			 * have a growth of AVL_BACK
-			 */
-			if (dir == AVL_SUCCEED) {
-				/* if succeeding node is needed, this is it.
-				 */
-				return(np);
-			}
-			if (dir == AVL_PRECEED) {
-				/*
-				 * find nearest ancestor with lesser value.
-				 */
-				 np = np->avl_parent;
-				 while (np) {
-					if (AVL_END(tree, np) <= value)
-						break;
-					np = np->avl_parent;
-				}
-				return(np);
-			}
-			ASSERT(dir == AVL_SUCCEED || dir == AVL_PRECEED);
-			break;
-		}
-		if (value >= AVL_END(tree, np)) {
-			if (np->avl_forw) {
-				np = np->avl_forw;
-				continue;
-			}
-			/* if we were to add node with value, would
-			 * have a growth of AVL_FORW;
-			 */
-			if (dir == AVL_SUCCEED) {
-				/* we are looking for a succeeding node;
-				 * this is nextino.
-				 */
-				return(np->avl_nextino);
-			}
-			if (dir == AVL_PRECEED) {
-				/* looking for a preceeding node; this is it. */
-				return(np);
-			}
-			ASSERT(dir == AVL_SUCCEED || dir == AVL_PRECEED);
-		}
-		/* AVL_START(tree, np) <= value < AVL_END(tree, np) */
-		return(np);
-	}
-	return NULL;
-}
-
-
-/*
- *	avl_findranges:
- *
- *	Given range r [start, end), find all ranges in tree which are contained
- *	in r. At return, startp and endp point to first and last of
- *	a chain of elements which describe the contained ranges. Elements
- *	in startp ... endp are in sort order, and can be accessed by
- *	using avl_nextino.
- */
-
-void
-avl64_findranges(
-	avl64tree_desc_t *tree,
-	uint64_t start,
-	uint64_t end,
-	avl64node_t	        **startp,
-	avl64node_t		**endp)
-{
-	avl64node_t *np;
-
-	np = avl64_findadjacent(tree, start, AVL_SUCCEED);
-	if (np == NULL				/* nothing succeding start */
-		|| (np && (end <= AVL_START(tree, np))))
-						/* something follows start,
-						but... is entirely after end */
-	{
-		*startp = NULL;
-		*endp = NULL;
-		return;
-	}
-
-	*startp = np;
-
-	/* see if end is in this region itself */
-	if (end <= AVL_END(tree, np) ||
-	    np->avl_nextino == NULL ||
-	    (np->avl_nextino &&
-	    (end <= AVL_START(tree, np->avl_nextino)))) {
-		*endp = np;
-		return;
-	}
-	/* have to munge for end */
-	/*
-	 * note: have to look for (end - 1), since
-	 * findadjacent will look for exact value, and does not
-	 * care about the fact that end is actually one more
-	 * than the value actually being looked for; thus feed it one less.
-	 */
-	*endp = avl64_findadjacent(tree, (end-1), AVL_PRECEED);
-	ASSERT(*endp);
-}
diff --git a/repair/avl64.h b/repair/avl64.h
deleted file mode 100644
index cd079a0..0000000
--- a/repair/avl64.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms 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.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-#ifndef __XR_AVL64_H__
-#define __XR_AVL64_H__
-
-#include <sys/types.h>
-
-typedef struct	avl64node {
-	struct	avl64node	*avl_forw;	/* pointer to right child  (> parent) */
-	struct	avl64node *avl_back;	/* pointer to left child  (< parent) */
-	struct	avl64node *avl_parent;	/* parent pointer */
-	struct	avl64node *avl_nextino;	/* next in-order; NULL terminated list*/
-	char		 avl_balance;	/* tree balance */
-} avl64node_t;
-
-/*
- * avl-tree operations
- */
-typedef struct avl64ops {
-	uint64_t	(*avl_start)(avl64node_t *);
-	uint64_t	(*avl_end)(avl64node_t *);
-} avl64ops_t;
-
-/*
- * avoid complaints about multiple def's since these are only used by
- * the avl code internally
- */
-#ifndef AVL_START
-#define	AVL_START(tree, n)	(*(tree)->avl_ops->avl_start)(n)
-#define	AVL_END(tree, n)	(*(tree)->avl_ops->avl_end)(n)
-#endif
-
-/*
- * tree descriptor:
- *	root points to the root of the tree.
- *	firstino points to the first in the ordered list.
- */
-typedef struct avl64tree_desc {
-	avl64node_t	*avl_root;
-	avl64node_t	*avl_firstino;
-	avl64ops_t	*avl_ops;
-} avl64tree_desc_t;
-
-/* possible values for avl_balance */
-
-#define AVL_BACK	1
-#define AVL_BALANCE	0
-#define AVL_FORW	2
-
-/*
- * 'Exported' avl tree routines
- */
-avl64node_t
-*avl64_insert(
-	avl64tree_desc_t *tree,
-	avl64node_t *newnode);
-
-void
-avl64_delete(
-	avl64tree_desc_t *tree,
-	avl64node_t *np);
-
-void
-avl64_insert_immediate(
-	avl64tree_desc_t *tree,
-	avl64node_t *afterp,
-	avl64node_t *newnode);
-
-void
-avl64_init_tree(
-	avl64tree_desc_t  *tree,
-	avl64ops_t *ops);
-
-avl64node_t *
-avl64_findrange(
-	avl64tree_desc_t *tree,
-	uint64_t value);
-
-avl64node_t *
-avl64_find(
-	avl64tree_desc_t *tree,
-	uint64_t value);
-
-avl64node_t *
-avl64_findanyrange(
-	avl64tree_desc_t *tree,
-	uint64_t	start,
-	uint64_t	end,
-	int     checklen);
-
-
-avl64node_t *
-avl64_findadjacent(
-	avl64tree_desc_t *tree,
-	uint64_t	value,
-	int		dir);
-
-void
-avl64_findranges(
-	avl64tree_desc_t *tree,
-	uint64_t	start,
-	uint64_t	end,
-	avl64node_t	        **startp,
-	avl64node_t		**endp);
-
-/*
- * avoid complaints about multiple def's since these are only used by
- * the avl code internally
- */
-#ifndef AVL_PRECEED
-#define AVL_PRECEED	0x1
-#define AVL_SUCCEED	0x2
-
-#define AVL_INCLUDE_ZEROLEN	0x0000
-#define AVL_EXCLUDE_ZEROLEN	0x0001
-#endif
-
-#endif /* __XR_AVL64_H__ */


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 06/12] libfrog: create a threaded workqueue
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
                   ` (4 preceding siblings ...)
  2017-11-17 20:13 ` [PATCH 05/12] libfrog: promote avl64 code from xfs_repair Darrick J. Wong
@ 2017-11-17 20:14 ` Darrick J. Wong
  2017-11-28  6:00   ` [PATCH v2 " Darrick J. Wong
  2017-11-17 20:14 ` [PATCH 07/12] libfrog: move topology code out of libxcmd Darrick J. Wong
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:14 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Create a thread pool that queues and runs discrete work items.  This
will be a namespaced version of the pool in repair/threads.c; a
subsequent patch will switch repair over.  xfs_scrub will use the
generic thread pool.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 include/workqueue.h |   55 ++++++++++++++++
 libfrog/Makefile    |    3 +
 libfrog/workqueue.c |  177 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 234 insertions(+), 1 deletion(-)
 create mode 100644 include/workqueue.h
 create mode 100644 libfrog/workqueue.c


diff --git a/include/workqueue.h b/include/workqueue.h
new file mode 100644
index 0000000..b4b3541
--- /dev/null
+++ b/include/workqueue.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * This code was adapted from repair/threads.h.
+ */
+#ifndef	_WORKQUEUE_H_
+#define	_WORKQUEUE_H_
+
+struct workqueue;
+
+typedef void workqueue_func_t(struct workqueue *wq, uint32_t index, void *arg);
+
+struct workqueue_item {
+	struct workqueue	*queue;
+	struct workqueue_item	*next;
+	workqueue_func_t	*function;
+	void			*arg;
+	uint32_t		index;
+};
+
+struct workqueue {
+	void			*wq_ctx;
+	pthread_t		*threads;
+	struct workqueue_item	*next_item;
+	struct workqueue_item	*last_item;
+	pthread_mutex_t		lock;
+	pthread_cond_t		wakeup;
+	unsigned int		item_count;
+	unsigned int		thread_count;
+	bool			terminate;
+};
+
+int workqueue_create(struct workqueue *wq, void *wq_ctx,
+		unsigned int nr_workers);
+int workqueue_add(struct workqueue *wq, workqueue_func_t fn,
+		uint32_t index, void *arg);
+void workqueue_destroy(struct workqueue *wq);
+
+#endif	/* _WORKQUEUE_H_ */
diff --git a/libfrog/Makefile b/libfrog/Makefile
index 3fd42a4..9a43621 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -14,7 +14,8 @@ CFILES = \
 avl64.c \
 list_sort.c \
 radix-tree.c \
-util.c
+util.c \
+workqueue.c
 
 default: ltdepend $(LTLIBRARY)
 
diff --git a/libfrog/workqueue.c b/libfrog/workqueue.c
new file mode 100644
index 0000000..d9217de
--- /dev/null
+++ b/libfrog/workqueue.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * This code was adapted from repair/threads.c.
+ */
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+#include "workqueue.h"
+
+/* Main processing thread */
+static void *
+workqueue_thread(void *arg)
+{
+	struct workqueue	*wq;
+	struct workqueue_item	*wi;
+
+	wq = (struct workqueue*)arg;
+
+	/*
+	 * Loop pulling work from the passed in work queue.
+	 * Check for notification to exit after every chunk of work.
+	 */
+	while (1) {
+		pthread_mutex_lock(&wq->lock);
+
+		/*
+		 * Wait for work.
+		 */
+		while (wq->next_item == NULL && !wq->terminate) {
+			assert(wq->item_count == 0);
+			pthread_cond_wait(&wq->wakeup, &wq->lock);
+		}
+		if (wq->next_item == NULL && wq->terminate) {
+			pthread_mutex_unlock(&wq->lock);
+			break;
+		}
+
+		/*
+		 *  Dequeue work from the head of the list.
+		 */
+		assert(wq->item_count > 0);
+		wi = wq->next_item;
+		wq->next_item = wi->next;
+		wq->item_count--;
+
+		pthread_mutex_unlock(&wq->lock);
+
+		(wi->function)(wi->queue, wi->index, wi->arg);
+		free(wi);
+	}
+
+	return NULL;
+}
+
+/* Allocate a work queue and threads. */
+int
+workqueue_create(
+	struct workqueue	*wq,
+	void			*wq_ctx,
+	unsigned int		nr_workers)
+{
+	unsigned int		i;
+	int			err = 0;
+
+	memset(wq, 0, sizeof(*wq));
+	pthread_cond_init(&wq->wakeup, NULL);
+	pthread_mutex_init(&wq->lock, NULL);
+
+	wq->wq_ctx = wq_ctx;
+	wq->thread_count = nr_workers;
+	wq->threads = malloc(nr_workers * sizeof(pthread_t));
+	wq->terminate = false;
+
+	for (i = 0; i < nr_workers; i++) {
+		err = pthread_create(&wq->threads[i], NULL, workqueue_thread,
+				wq);
+		if (err)
+			break;
+	}
+
+	if (err)
+		workqueue_destroy(wq);
+	return err;
+}
+
+/*
+ * Create a work item consisting of a function and some arguments and
+ * schedule the work item to be run via the thread pool.
+ */
+int
+workqueue_add(
+	struct workqueue	*wq,
+	workqueue_func_t	func,
+	uint32_t		index,
+	void			*arg)
+{
+	struct workqueue_item	*wi;
+
+	if (wq->thread_count == 0) {
+		func(wq, index, arg);
+		return 0;
+	}
+
+	wi = malloc(sizeof(struct workqueue_item));
+	if (wi == NULL)
+		return ENOMEM;
+
+	wi->function = func;
+	wi->index = index;
+	wi->arg = arg;
+	wi->queue = wq;
+	wi->next = NULL;
+
+	/*
+	 *  Now queue the new work structure to the work queue.
+	 */
+	pthread_mutex_lock(&wq->lock);
+	if (wq->next_item == NULL) {
+		wq->next_item = wi;
+		assert(wq->item_count == 0);
+		pthread_cond_signal(&wq->wakeup);
+	} else {
+		wq->last_item->next = wi;
+	}
+	wq->last_item = wi;
+	wq->item_count++;
+	pthread_mutex_unlock(&wq->lock);
+
+	return 0;
+}
+
+/*
+ * Wait for all pending work items to be processed and tear down the
+ * workqueue.
+ */
+void
+workqueue_destroy(
+	struct workqueue	*wq)
+{
+	unsigned int		i;
+
+	pthread_mutex_lock(&wq->lock);
+	wq->terminate = 1;
+	pthread_mutex_unlock(&wq->lock);
+
+	pthread_cond_broadcast(&wq->wakeup);
+
+	for (i = 0; i < wq->thread_count; i++)
+		pthread_join(wq->threads[i], NULL);
+
+	free(wq->threads);
+	pthread_mutex_destroy(&wq->lock);
+	pthread_cond_destroy(&wq->wakeup);
+	memset(wq, 0, sizeof(*wq));
+}


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 07/12] libfrog: move topology code out of libxcmd
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
                   ` (5 preceding siblings ...)
  2017-11-17 20:14 ` [PATCH 06/12] libfrog: create a threaded workqueue Darrick J. Wong
@ 2017-11-17 20:14 ` Darrick J. Wong
  2017-11-17 20:14 ` [PATCH 08/12] libfrog: move conversion factors " Darrick J. Wong
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:14 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Move the filesystem topology code out of libxcmd.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 libfrog/Makefile   |    1 
 libfrog/topology.c |  342 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 libxcmd/Makefile   |    2 
 libxcmd/topology.c |  342 ----------------------------------------------------
 4 files changed, 344 insertions(+), 343 deletions(-)
 create mode 100644 libfrog/topology.c
 delete mode 100644 libxcmd/topology.c


diff --git a/libfrog/Makefile b/libfrog/Makefile
index 9a43621..bffc346 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -14,6 +14,7 @@ CFILES = \
 avl64.c \
 list_sort.c \
 radix-tree.c \
+topology.c \
 util.c \
 workqueue.c
 
diff --git a/libfrog/topology.c b/libfrog/topology.c
new file mode 100644
index 0000000..f66dd1b
--- /dev/null
+++ b/libfrog/topology.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "libxfs.h"
+#include "libxcmd.h"
+#ifdef ENABLE_BLKID
+#  include <blkid/blkid.h>
+#endif /* ENABLE_BLKID */
+#include "xfs_multidisk.h"
+
+#define TERABYTES(count, blog)	((uint64_t)(count) << (40 - (blog)))
+#define GIGABYTES(count, blog)	((uint64_t)(count) << (30 - (blog)))
+#define MEGABYTES(count, blog)	((uint64_t)(count) << (20 - (blog)))
+
+void
+calc_default_ag_geometry(
+	int		blocklog,
+	uint64_t	dblocks,
+	int		multidisk,
+	uint64_t	*agsize,
+	uint64_t	*agcount)
+{
+	uint64_t	blocks = 0;
+	int		shift = 0;
+
+	/*
+	 * First handle the high extreme - the point at which we will
+	 * always use the maximum AG size.
+	 *
+	 * This applies regardless of storage configuration.
+	 */
+	if (dblocks >= TERABYTES(32, blocklog)) {
+		blocks = XFS_AG_MAX_BLOCKS(blocklog);
+		goto done;
+	}
+
+	/*
+	 * For a single underlying storage device over 4TB in size
+	 * use the maximum AG size.  Between 128MB and 4TB, just use
+	 * 4 AGs and scale up smoothly between min/max AG sizes.
+	 */
+	if (!multidisk) {
+		if (dblocks >= TERABYTES(4, blocklog)) {
+			blocks = XFS_AG_MAX_BLOCKS(blocklog);
+			goto done;
+		} else if (dblocks >= MEGABYTES(128, blocklog)) {
+			shift = XFS_NOMULTIDISK_AGLOG;
+			goto calc_blocks;
+		}
+	}
+
+	/*
+	 * For the multidisk configs we choose an AG count based on the number
+	 * of data blocks available, trying to keep the number of AGs higher
+	 * than the single disk configurations. This makes the assumption that
+	 * larger filesystems have more parallelism available to them.
+	 */
+	shift = XFS_MULTIDISK_AGLOG;
+	if (dblocks <= GIGABYTES(512, blocklog))
+		shift--;
+	if (dblocks <= GIGABYTES(8, blocklog))
+		shift--;
+	if (dblocks < MEGABYTES(128, blocklog))
+		shift--;
+	if (dblocks < MEGABYTES(64, blocklog))
+		shift--;
+	if (dblocks < MEGABYTES(32, blocklog))
+		shift--;
+
+	/*
+	 * If dblocks is not evenly divisible by the number of
+	 * desired AGs, round "blocks" up so we don't lose the
+	 * last bit of the filesystem. The same principle applies
+	 * to the AG count, so we don't lose the last AG!
+	 */
+calc_blocks:
+	ASSERT(shift >= 0 && shift <= XFS_MULTIDISK_AGLOG);
+	blocks = dblocks >> shift;
+	if (dblocks & xfs_mask32lo(shift)) {
+		if (blocks < XFS_AG_MAX_BLOCKS(blocklog))
+		    blocks++;
+	}
+done:
+	*agsize = blocks;
+	*agcount = dblocks / blocks + (dblocks % blocks != 0);
+}
+
+/*
+ * Check for existing filesystem or partition table on device.
+ * Returns:
+ *	 1 for existing fs or partition
+ *	 0 for nothing found
+ *	-1 for internal error
+ */
+#ifdef ENABLE_BLKID
+int
+check_overwrite(
+	const char	*device)
+{
+	const char	*type;
+	blkid_probe	pr = NULL;
+	int		ret;
+	int		fd;
+	long long	size;
+	int		bsz;
+
+	if (!device || !*device)
+		return 0;
+
+	ret = -1; /* will reset on success of all setup calls */
+
+	fd = open(device, O_RDONLY);
+	if (fd < 0)
+		goto out;
+	platform_findsizes((char *)device, fd, &size, &bsz);
+	close(fd);
+
+	/* nothing to overwrite on a 0-length device */
+	if (size == 0) {
+		ret = 0;
+		goto out;
+	}
+
+	pr = blkid_new_probe_from_filename(device);
+	if (!pr)
+		goto out;
+
+	ret = blkid_probe_enable_partitions(pr, 1);
+	if (ret < 0)
+		goto out;
+
+	ret = blkid_do_fullprobe(pr);
+	if (ret < 0)
+		goto out;
+
+	/*
+	 * Blkid returns 1 for nothing found and 0 when it finds a signature,
+	 * but we want the exact opposite, so reverse the return value here.
+	 *
+	 * In addition print some useful diagnostics about what actually is
+	 * on the device.
+	 */
+	if (ret) {
+		ret = 0;
+		goto out;
+	}
+
+	if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) {
+		fprintf(stderr,
+			_("%s: %s appears to contain an existing "
+			"filesystem (%s).\n"), progname, device, type);
+	} else if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) {
+		fprintf(stderr,
+			_("%s: %s appears to contain a partition "
+			"table (%s).\n"), progname, device, type);
+	} else {
+		fprintf(stderr,
+			_("%s: %s appears to contain something weird "
+			"according to blkid\n"), progname, device);
+	}
+	ret = 1;
+out:
+	if (pr)
+		blkid_free_probe(pr);
+	if (ret == -1)
+		fprintf(stderr,
+			_("%s: probe of %s failed, cannot detect "
+			  "existing filesystem.\n"), progname, device);
+	return ret;
+}
+
+static void blkid_get_topology(
+	const char	*device,
+	int		*sunit,
+	int		*swidth,
+	int		*lsectorsize,
+	int		*psectorsize,
+	int		force_overwrite)
+{
+
+	blkid_topology tp;
+	blkid_probe pr;
+	unsigned long val;
+	struct stat statbuf;
+
+	/* can't get topology info from a file */
+	if (!stat(device, &statbuf) && S_ISREG(statbuf.st_mode)) {
+		fprintf(stderr,
+	_("%s: Warning: trying to probe topology of a file %s!\n"),
+			progname, device);
+		return;
+	}
+
+	pr = blkid_new_probe_from_filename(device);
+	if (!pr)
+		return;
+
+	tp = blkid_probe_get_topology(pr);
+	if (!tp)
+		goto out_free_probe;
+
+	val = blkid_topology_get_logical_sector_size(tp);
+	*lsectorsize = val;
+	val = blkid_topology_get_physical_sector_size(tp);
+	*psectorsize = val;
+	val = blkid_topology_get_minimum_io_size(tp);
+	*sunit = val;
+	val = blkid_topology_get_optimal_io_size(tp);
+	*swidth = val;
+
+	/*
+	 * If the reported values are the same as the physical sector size
+	 * do not bother to report anything.  It will only cause warnings
+	 * if people specify larger stripe units or widths manually.
+	 */
+	if (*sunit == *psectorsize || *swidth == *psectorsize) {
+		*sunit = 0;
+		*swidth = 0;
+	}
+
+	/*
+	 * Blkid reports the information in terms of bytes, but we want it in
+	 * terms of 512 bytes blocks (only to convert it to bytes later..)
+	 */
+	*sunit = *sunit >> 9;
+	*swidth = *swidth >> 9;
+
+	if (blkid_topology_get_alignment_offset(tp) != 0) {
+		fprintf(stderr,
+			_("warning: device is not properly aligned %s\n"),
+			device);
+
+		if (!force_overwrite) {
+			fprintf(stderr,
+				_("Use -f to force usage of a misaligned device\n"));
+
+			exit(EXIT_FAILURE);
+		}
+		/* Do not use physical sector size if the device is misaligned */
+		*psectorsize = *lsectorsize;
+	}
+
+	blkid_free_probe(pr);
+	return;
+
+out_free_probe:
+	blkid_free_probe(pr);
+	fprintf(stderr,
+		_("warning: unable to probe device topology for device %s\n"),
+		device);
+}
+#else /* ifdef ENABLE_BLKID */
+/*
+ * Without blkid, we can't do a good check for signatures.
+ * So instead of some messy attempts, just disable any checks
+ * and always return 'nothing found'.
+ */
+#  warning BLKID is disabled, so signature detection and block device\
+ access are not working!
+int
+check_overwrite(
+	const char	*device)
+{
+	return 1;
+}
+
+static void blkid_get_topology(
+	const char	*device,
+	int		*sunit,
+	int		*swidth,
+	int		*lsectorsize,
+	int		*psectorsize,
+	int		force_overwrite)
+{
+	/*
+	 * Shouldn't make any difference (no blkid = no block device access),
+	 * but make sure this dummy replacement returns with at least some
+	 * sanity.
+	 */
+	*lsectorsize = *psectorsize = 512;
+}
+
+#endif /* ENABLE_BLKID */
+
+void get_topology(
+	libxfs_init_t		*xi,
+	struct fs_topology	*ft,
+	int			force_overwrite)
+{
+	struct stat statbuf;
+	char *dfile = xi->volname ? xi->volname : xi->dname;
+
+	/*
+	 * If our target is a regular file, use platform_findsizes
+	 * to try to obtain the underlying filesystem's requirements
+	 * for direct IO; we'll set our sector size to that if possible.
+	 */
+	if (xi->disfile ||
+	    (!stat(dfile, &statbuf) && S_ISREG(statbuf.st_mode))) {
+		int fd;
+		int flags = O_RDONLY;
+		long long dummy;
+
+		/* with xi->disfile we may not have the file yet! */
+		if (xi->disfile)
+			flags |= O_CREAT;
+
+		fd = open(dfile, flags, 0666);
+		if (fd >= 0) {
+			platform_findsizes(dfile, fd, &dummy, &ft->lsectorsize);
+			close(fd);
+			ft->psectorsize = ft->lsectorsize;
+		} else
+			ft->psectorsize = ft->lsectorsize = BBSIZE;
+	} else {
+		blkid_get_topology(dfile, &ft->dsunit, &ft->dswidth,
+				   &ft->lsectorsize, &ft->psectorsize,
+				   force_overwrite);
+	}
+
+	if (xi->rtname && !xi->risfile) {
+		int sunit, lsectorsize, psectorsize;
+
+		blkid_get_topology(xi->rtname, &sunit, &ft->rtswidth,
+				   &lsectorsize, &psectorsize, force_overwrite);
+	}
+}
diff --git a/libxcmd/Makefile b/libxcmd/Makefile
index aab8d6d..7701ed9 100644
--- a/libxcmd/Makefile
+++ b/libxcmd/Makefile
@@ -10,7 +10,7 @@ LT_CURRENT = 0
 LT_REVISION = 0
 LT_AGE = 0
 
-CFILES = command.c input.c paths.c projects.c help.c quit.c topology.c
+CFILES = command.c input.c paths.c projects.c help.c quit.c
 
 ifeq ($(HAVE_GETMNTENT),yes)
 LCFLAGS += -DHAVE_GETMNTENT
diff --git a/libxcmd/topology.c b/libxcmd/topology.c
deleted file mode 100644
index f66dd1b..0000000
--- a/libxcmd/topology.c
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of 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.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include "libxfs.h"
-#include "libxcmd.h"
-#ifdef ENABLE_BLKID
-#  include <blkid/blkid.h>
-#endif /* ENABLE_BLKID */
-#include "xfs_multidisk.h"
-
-#define TERABYTES(count, blog)	((uint64_t)(count) << (40 - (blog)))
-#define GIGABYTES(count, blog)	((uint64_t)(count) << (30 - (blog)))
-#define MEGABYTES(count, blog)	((uint64_t)(count) << (20 - (blog)))
-
-void
-calc_default_ag_geometry(
-	int		blocklog,
-	uint64_t	dblocks,
-	int		multidisk,
-	uint64_t	*agsize,
-	uint64_t	*agcount)
-{
-	uint64_t	blocks = 0;
-	int		shift = 0;
-
-	/*
-	 * First handle the high extreme - the point at which we will
-	 * always use the maximum AG size.
-	 *
-	 * This applies regardless of storage configuration.
-	 */
-	if (dblocks >= TERABYTES(32, blocklog)) {
-		blocks = XFS_AG_MAX_BLOCKS(blocklog);
-		goto done;
-	}
-
-	/*
-	 * For a single underlying storage device over 4TB in size
-	 * use the maximum AG size.  Between 128MB and 4TB, just use
-	 * 4 AGs and scale up smoothly between min/max AG sizes.
-	 */
-	if (!multidisk) {
-		if (dblocks >= TERABYTES(4, blocklog)) {
-			blocks = XFS_AG_MAX_BLOCKS(blocklog);
-			goto done;
-		} else if (dblocks >= MEGABYTES(128, blocklog)) {
-			shift = XFS_NOMULTIDISK_AGLOG;
-			goto calc_blocks;
-		}
-	}
-
-	/*
-	 * For the multidisk configs we choose an AG count based on the number
-	 * of data blocks available, trying to keep the number of AGs higher
-	 * than the single disk configurations. This makes the assumption that
-	 * larger filesystems have more parallelism available to them.
-	 */
-	shift = XFS_MULTIDISK_AGLOG;
-	if (dblocks <= GIGABYTES(512, blocklog))
-		shift--;
-	if (dblocks <= GIGABYTES(8, blocklog))
-		shift--;
-	if (dblocks < MEGABYTES(128, blocklog))
-		shift--;
-	if (dblocks < MEGABYTES(64, blocklog))
-		shift--;
-	if (dblocks < MEGABYTES(32, blocklog))
-		shift--;
-
-	/*
-	 * If dblocks is not evenly divisible by the number of
-	 * desired AGs, round "blocks" up so we don't lose the
-	 * last bit of the filesystem. The same principle applies
-	 * to the AG count, so we don't lose the last AG!
-	 */
-calc_blocks:
-	ASSERT(shift >= 0 && shift <= XFS_MULTIDISK_AGLOG);
-	blocks = dblocks >> shift;
-	if (dblocks & xfs_mask32lo(shift)) {
-		if (blocks < XFS_AG_MAX_BLOCKS(blocklog))
-		    blocks++;
-	}
-done:
-	*agsize = blocks;
-	*agcount = dblocks / blocks + (dblocks % blocks != 0);
-}
-
-/*
- * Check for existing filesystem or partition table on device.
- * Returns:
- *	 1 for existing fs or partition
- *	 0 for nothing found
- *	-1 for internal error
- */
-#ifdef ENABLE_BLKID
-int
-check_overwrite(
-	const char	*device)
-{
-	const char	*type;
-	blkid_probe	pr = NULL;
-	int		ret;
-	int		fd;
-	long long	size;
-	int		bsz;
-
-	if (!device || !*device)
-		return 0;
-
-	ret = -1; /* will reset on success of all setup calls */
-
-	fd = open(device, O_RDONLY);
-	if (fd < 0)
-		goto out;
-	platform_findsizes((char *)device, fd, &size, &bsz);
-	close(fd);
-
-	/* nothing to overwrite on a 0-length device */
-	if (size == 0) {
-		ret = 0;
-		goto out;
-	}
-
-	pr = blkid_new_probe_from_filename(device);
-	if (!pr)
-		goto out;
-
-	ret = blkid_probe_enable_partitions(pr, 1);
-	if (ret < 0)
-		goto out;
-
-	ret = blkid_do_fullprobe(pr);
-	if (ret < 0)
-		goto out;
-
-	/*
-	 * Blkid returns 1 for nothing found and 0 when it finds a signature,
-	 * but we want the exact opposite, so reverse the return value here.
-	 *
-	 * In addition print some useful diagnostics about what actually is
-	 * on the device.
-	 */
-	if (ret) {
-		ret = 0;
-		goto out;
-	}
-
-	if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) {
-		fprintf(stderr,
-			_("%s: %s appears to contain an existing "
-			"filesystem (%s).\n"), progname, device, type);
-	} else if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) {
-		fprintf(stderr,
-			_("%s: %s appears to contain a partition "
-			"table (%s).\n"), progname, device, type);
-	} else {
-		fprintf(stderr,
-			_("%s: %s appears to contain something weird "
-			"according to blkid\n"), progname, device);
-	}
-	ret = 1;
-out:
-	if (pr)
-		blkid_free_probe(pr);
-	if (ret == -1)
-		fprintf(stderr,
-			_("%s: probe of %s failed, cannot detect "
-			  "existing filesystem.\n"), progname, device);
-	return ret;
-}
-
-static void blkid_get_topology(
-	const char	*device,
-	int		*sunit,
-	int		*swidth,
-	int		*lsectorsize,
-	int		*psectorsize,
-	int		force_overwrite)
-{
-
-	blkid_topology tp;
-	blkid_probe pr;
-	unsigned long val;
-	struct stat statbuf;
-
-	/* can't get topology info from a file */
-	if (!stat(device, &statbuf) && S_ISREG(statbuf.st_mode)) {
-		fprintf(stderr,
-	_("%s: Warning: trying to probe topology of a file %s!\n"),
-			progname, device);
-		return;
-	}
-
-	pr = blkid_new_probe_from_filename(device);
-	if (!pr)
-		return;
-
-	tp = blkid_probe_get_topology(pr);
-	if (!tp)
-		goto out_free_probe;
-
-	val = blkid_topology_get_logical_sector_size(tp);
-	*lsectorsize = val;
-	val = blkid_topology_get_physical_sector_size(tp);
-	*psectorsize = val;
-	val = blkid_topology_get_minimum_io_size(tp);
-	*sunit = val;
-	val = blkid_topology_get_optimal_io_size(tp);
-	*swidth = val;
-
-	/*
-	 * If the reported values are the same as the physical sector size
-	 * do not bother to report anything.  It will only cause warnings
-	 * if people specify larger stripe units or widths manually.
-	 */
-	if (*sunit == *psectorsize || *swidth == *psectorsize) {
-		*sunit = 0;
-		*swidth = 0;
-	}
-
-	/*
-	 * Blkid reports the information in terms of bytes, but we want it in
-	 * terms of 512 bytes blocks (only to convert it to bytes later..)
-	 */
-	*sunit = *sunit >> 9;
-	*swidth = *swidth >> 9;
-
-	if (blkid_topology_get_alignment_offset(tp) != 0) {
-		fprintf(stderr,
-			_("warning: device is not properly aligned %s\n"),
-			device);
-
-		if (!force_overwrite) {
-			fprintf(stderr,
-				_("Use -f to force usage of a misaligned device\n"));
-
-			exit(EXIT_FAILURE);
-		}
-		/* Do not use physical sector size if the device is misaligned */
-		*psectorsize = *lsectorsize;
-	}
-
-	blkid_free_probe(pr);
-	return;
-
-out_free_probe:
-	blkid_free_probe(pr);
-	fprintf(stderr,
-		_("warning: unable to probe device topology for device %s\n"),
-		device);
-}
-#else /* ifdef ENABLE_BLKID */
-/*
- * Without blkid, we can't do a good check for signatures.
- * So instead of some messy attempts, just disable any checks
- * and always return 'nothing found'.
- */
-#  warning BLKID is disabled, so signature detection and block device\
- access are not working!
-int
-check_overwrite(
-	const char	*device)
-{
-	return 1;
-}
-
-static void blkid_get_topology(
-	const char	*device,
-	int		*sunit,
-	int		*swidth,
-	int		*lsectorsize,
-	int		*psectorsize,
-	int		force_overwrite)
-{
-	/*
-	 * Shouldn't make any difference (no blkid = no block device access),
-	 * but make sure this dummy replacement returns with at least some
-	 * sanity.
-	 */
-	*lsectorsize = *psectorsize = 512;
-}
-
-#endif /* ENABLE_BLKID */
-
-void get_topology(
-	libxfs_init_t		*xi,
-	struct fs_topology	*ft,
-	int			force_overwrite)
-{
-	struct stat statbuf;
-	char *dfile = xi->volname ? xi->volname : xi->dname;
-
-	/*
-	 * If our target is a regular file, use platform_findsizes
-	 * to try to obtain the underlying filesystem's requirements
-	 * for direct IO; we'll set our sector size to that if possible.
-	 */
-	if (xi->disfile ||
-	    (!stat(dfile, &statbuf) && S_ISREG(statbuf.st_mode))) {
-		int fd;
-		int flags = O_RDONLY;
-		long long dummy;
-
-		/* with xi->disfile we may not have the file yet! */
-		if (xi->disfile)
-			flags |= O_CREAT;
-
-		fd = open(dfile, flags, 0666);
-		if (fd >= 0) {
-			platform_findsizes(dfile, fd, &dummy, &ft->lsectorsize);
-			close(fd);
-			ft->psectorsize = ft->lsectorsize;
-		} else
-			ft->psectorsize = ft->lsectorsize = BBSIZE;
-	} else {
-		blkid_get_topology(dfile, &ft->dsunit, &ft->dswidth,
-				   &ft->lsectorsize, &ft->psectorsize,
-				   force_overwrite);
-	}
-
-	if (xi->rtname && !xi->risfile) {
-		int sunit, lsectorsize, psectorsize;
-
-		blkid_get_topology(xi->rtname, &sunit, &ft->rtswidth,
-				   &lsectorsize, &psectorsize, force_overwrite);
-	}
-}


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 08/12] libfrog: move conversion factors out of libxcmd
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
                   ` (6 preceding siblings ...)
  2017-11-17 20:14 ` [PATCH 07/12] libfrog: move topology code out of libxcmd Darrick J. Wong
@ 2017-11-17 20:14 ` Darrick J. Wong
  2017-11-27 21:47   ` Eric Sandeen
                     ` (2 more replies)
  2017-11-17 20:14 ` [PATCH 09/12] libfrog: move paths.c " Darrick J. Wong
                   ` (4 subsequent siblings)
  12 siblings, 3 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:14 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Move all the conversion functions out of libxcmd since they'll be used
by scrub, which doesn't have a commandline.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 include/convert.h |   39 ++++++
 include/input.h   |   15 --
 libfrog/Makefile  |    1 
 libfrog/convert.c |  373 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 414 insertions(+), 14 deletions(-)
 create mode 100644 include/convert.h
 create mode 100644 libfrog/convert.c


diff --git a/include/convert.h b/include/convert.h
new file mode 100644
index 0000000..cff9778
--- /dev/null
+++ b/include/convert.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __CONVERT_H__
+#define __CONVERT_H__
+
+extern int64_t	cvt_s64(char *s, int base);
+extern int32_t	cvt_s32(char *s, int base);
+extern int16_t	cvt_s16(char *s, int base);
+
+extern uint64_t	cvt_u64(char *s, int base);
+extern uint32_t	cvt_u32(char *s, int base);
+extern uint16_t	cvt_u16(char *s, int base);
+
+extern long long cvtnum(size_t blocksize, size_t sectorsize, char *s);
+extern void cvtstr(double value, char *str, size_t sz);
+extern unsigned long cvttime(char *s);
+
+extern uid_t	uid_from_string(char *user);
+extern gid_t	gid_from_string(char *group);
+extern prid_t	prid_from_string(char *project);
+
+#endif	/* __CONVERT_H__ */
diff --git a/include/input.h b/include/input.h
index 145114b..fe107b9 100644
--- a/include/input.h
+++ b/include/input.h
@@ -22,24 +22,14 @@
 #include <grp.h>
 #include <sys/types.h>
 #include "project.h"
+#include "convert.h"
 #include <stdbool.h>
 
 extern char	**breakline(char *input, int *count);
 extern void	doneline(char *input, char **vec);
 extern char	*fetchline(void);
 
-extern int64_t	cvt_s64(char *s, int base);
-extern int32_t	cvt_s32(char *s, int base);
-extern int16_t	cvt_s16(char *s, int base);
-
-extern uint64_t	cvt_u64(char *s, int base);
-extern uint32_t	cvt_u32(char *s, int base);
-extern uint16_t	cvt_u16(char *s, int base);
-
 extern size_t numlen(uint64_t val, size_t base);
-extern long long cvtnum(size_t blocksize, size_t sectorsize, char *s);
-extern void	cvtstr(double value, char *str, size_t sz);
-extern unsigned long cvttime(char *s);
 
 extern struct timeval tadd(struct timeval t1, struct timeval t2);
 extern struct timeval tsub(struct timeval t1, struct timeval t2);
@@ -53,9 +43,6 @@ enum {
 
 extern void	timestr(struct timeval *tv, char *str, size_t sz, int flags);
 
-extern uid_t	uid_from_string(char *user);
-extern gid_t	gid_from_string(char *group);
-extern prid_t	prid_from_string(char *project);
 extern bool	isdigits_only(const char *str);
 extern int	timespec_from_string(const char *sec, const char *nsec, struct timespec *ts);
 
diff --git a/libfrog/Makefile b/libfrog/Makefile
index bffc346..467362c 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -12,6 +12,7 @@ LT_AGE = 0
 
 CFILES = \
 avl64.c \
+convert.c \
 list_sort.c \
 radix-tree.c \
 topology.c \
diff --git a/libfrog/convert.c b/libfrog/convert.c
new file mode 100644
index 0000000..7009a05
--- /dev/null
+++ b/libfrog/convert.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "platform_defs.h"
+#include "input.h"
+#include <ctype.h>
+#include <stdbool.h>
+
+/*
+ * Convert string to int64_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+int64_t
+cvt_s64(
+	char		*s,
+	int		base)
+{
+	long long	i;
+	char		*sp;
+
+	errno = 0;
+	i = strtoll(s, &sp, base);
+	/*
+	 * If the input would over or underflow, return the clamped
+	 * value and let the user check errno.  If we went all the
+	 * way to the end of the input, return the converted value;
+	 * errno will be zero.
+	 */
+	if (errno || (*sp == '\0' && sp != s))
+		return i;
+
+	/* Not all the input was consumed, return error. */
+	errno = -ERANGE;
+	return INT64_MIN;
+}
+
+/*
+ * Convert string to int32_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+int32_t
+cvt_s32(
+	char		*s,
+	int		base)
+{
+	int64_t		i;
+
+	i = cvt_s64(s, base);
+	if (errno)
+		return i;
+	if (i > INT32_MAX || i < INT32_MIN) {
+		errno = -ERANGE;
+		return INT32_MIN;
+	}
+	return i;
+}
+
+/*
+ * Convert string to int16_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+int16_t
+cvt_s16(
+	char		*s,
+	int		base)
+{
+	int64_t		i;
+
+	i = cvt_s64(s, base);
+	if (errno)
+		return i;
+	if (i > INT16_MAX || i < INT16_MIN) {
+		errno = -ERANGE;
+		return INT16_MIN;
+	}
+	return i;
+}
+
+/*
+ * Convert string to uint64_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+uint64_t
+cvt_u64(
+	char		*s,
+	int		base)
+{
+	long long	i;
+	char		*sp;
+
+	errno = 0;
+	i = strtoll(s, &sp, base);
+	/*
+	 * If the input would over or underflow, return the clamped
+	 * value and let the user check errno.  If we went all the
+	 * way to the end of the input, return the converted value;
+	 * errno will be zero.
+	 */
+	if (errno || (*sp == '\0' && sp != s))
+		return i;
+
+	/* Not all the input was consumed, return error. */
+	errno = -ERANGE;
+	return UINT64_MAX;
+}
+
+/*
+ * Convert string to uint32_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+uint32_t
+cvt_u32(
+	char		*s,
+	int		base)
+{
+	uint64_t	i;
+
+	i = cvt_u64(s, base);
+	if (errno)
+		return i;
+	if (i > UINT32_MAX) {
+		errno = -ERANGE;
+		return UINT32_MAX;
+	}
+	return i;
+}
+
+/*
+ * Convert string to uint16_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+uint16_t
+cvt_u16(
+	char		*s,
+	int		base)
+{
+	uint64_t	i;
+
+	i = cvt_u64(s, base);
+	if (errno)
+		return i;
+	if (i > UINT16_MAX) {
+		errno = -ERANGE;
+		return UINT16_MAX;
+	}
+	return i;
+}
+
+#define EXABYTES(x)	((long long)(x) << 60)
+#define PETABYTES(x)	((long long)(x) << 50)
+#define TERABYTES(x)	((long long)(x) << 40)
+#define GIGABYTES(x)	((long long)(x) << 30)
+#define MEGABYTES(x)	((long long)(x) << 20)
+#define KILOBYTES(x)	((long long)(x) << 10)
+
+long long
+cvtnum(
+	size_t		blocksize,
+	size_t		sectorsize,
+	char		*s)
+{
+	long long	i;
+	char		*sp;
+	int		c;
+
+	i = strtoll(s, &sp, 0);
+	if (i == 0 && sp == s)
+		return -1LL;
+	if (*sp == '\0')
+		return i;
+
+	if (sp[1] != '\0')
+		return -1LL;
+
+	c = tolower(*sp);
+	switch (c) {
+	case 'b':
+		return i * blocksize;
+	case 's':
+		return i * sectorsize;
+	case 'k':
+		return KILOBYTES(i);
+	case 'm':
+		return MEGABYTES(i);
+	case 'g':
+		return GIGABYTES(i);
+	case 't':
+		return TERABYTES(i);
+	case 'p':
+		return PETABYTES(i);
+	case 'e':
+		return  EXABYTES(i);
+	}
+	return -1LL;
+}
+
+#define TO_EXABYTES(x)	((x) / EXABYTES(1))
+#define TO_PETABYTES(x)	((x) / PETABYTES(1))
+#define TO_TERABYTES(x)	((x) / TERABYTES(1))
+#define TO_GIGABYTES(x)	((x) / GIGABYTES(1))
+#define TO_MEGABYTES(x)	((x) / MEGABYTES(1))
+#define TO_KILOBYTES(x)	((x) / KILOBYTES(1))
+
+void
+cvtstr(
+	double		value,
+	char		*str,
+	size_t		size)
+{
+	char		*fmt;
+	int		precise;
+
+	precise = ((double)value * 1000 == (double)(int)value * 1000);
+
+	if (value >= EXABYTES(1)) {
+		fmt = precise ? "%.f EiB" : "%.3f EiB";
+		snprintf(str, size, fmt, TO_EXABYTES(value));
+	} else if (value >= PETABYTES(1)) {
+		fmt = precise ? "%.f PiB" : "%.3f PiB";
+		snprintf(str, size, fmt, TO_PETABYTES(value));
+	} else if (value >= TERABYTES(1)) {
+		fmt = precise ? "%.f TiB" : "%.3f TiB";
+		snprintf(str, size, fmt, TO_TERABYTES(value));
+	} else if (value >= GIGABYTES(1)) {
+		fmt = precise ? "%.f GiB" : "%.3f GiB";
+		snprintf(str, size, fmt, TO_GIGABYTES(value));
+	} else if (value >= MEGABYTES(1)) {
+		fmt = precise ? "%.f MiB" : "%.3f MiB";
+		snprintf(str, size, fmt, TO_MEGABYTES(value));
+	} else if (value >= KILOBYTES(1)) {
+		fmt = precise ? "%.f KiB" : "%.3f KiB";
+		snprintf(str, size, fmt, TO_KILOBYTES(value));
+	} else {
+		snprintf(str, size, "%f bytes", value);
+	}
+}
+
+#define MINUTES_TO_SECONDS(m)	((m) * 60)
+#define HOURS_TO_SECONDS(h)	((h) * MINUTES_TO_SECONDS(60))
+#define DAYS_TO_SECONDS(d)	((d) * HOURS_TO_SECONDS(24))
+#define WEEKS_TO_SECONDS(w)	((w) * DAYS_TO_SECONDS(7))
+
+unsigned long
+cvttime(
+	char		*s)
+{
+	unsigned long	i;
+	char		*sp;
+
+	i = strtoul(s, &sp, 0);
+	if (i == 0 && sp == s)
+		return 0;
+	if (*sp == '\0')
+		return i;
+	if ((*sp == 'm' && sp[1] == '\0') ||
+	    (strcmp(sp, "minutes") == 0) ||
+	    (strcmp(sp, "minute") == 0))
+		return MINUTES_TO_SECONDS(i);
+	if ((*sp == 'h' && sp[1] == '\0') ||
+	    (strcmp(sp, "hours") == 0) ||
+	    (strcmp(sp, "hour") == 0))
+		return HOURS_TO_SECONDS(i);
+	if ((*sp == 'd' && sp[1] == '\0') ||
+	    (strcmp(sp, "days") == 0) ||
+	    (strcmp(sp, "day") == 0))
+		return DAYS_TO_SECONDS(i);
+	if ((*sp == 'w' && sp[1] == '\0') ||
+	    (strcmp(sp, "weeks") == 0) ||
+	    (strcmp(sp, "week") == 0))
+		return WEEKS_TO_SECONDS(i);
+	return 0;
+}
+
+/*
+ * Convert from arbitrary user strings into a numeric ID.
+ * If it's all numeric, we convert that inplace, else we do
+ * the name lookup, and return the found identifier.
+ */
+
+prid_t
+prid_from_string(
+	char		*project)
+{
+	fs_project_t	*prj;
+	unsigned long	prid_long;
+	char		*sp;
+
+	/*
+	 * Allow either a full numeric or a valid projectname, even
+	 * if it starts with a digit.
+	 */
+	prid_long = strtoul(project, &sp, 10);
+	if (*project != '\0' && *sp == '\0') {
+		if ((prid_long == ULONG_MAX && errno == ERANGE)
+				|| (prid_long > (prid_t)-1))
+			return -1;
+		return (prid_t)prid_long;
+	}
+	prj = getprnam(project);
+	if (prj)
+		return prj->pr_prid;
+	return -1;
+}
+
+uid_t
+uid_from_string(
+	char		*user)
+{
+	struct passwd	*pwd;
+	unsigned long	uid_long;
+	char		*sp;
+
+	uid_long = strtoul(user, &sp, 10);
+	if (sp != user && *sp == '\0') {
+		if ((uid_long == ULONG_MAX && errno == ERANGE)
+				|| (uid_long > (uid_t)-1))
+			return -1;
+		return (uid_t)uid_long;
+	}
+	pwd = getpwnam(user);
+	if (pwd)
+		return pwd->pw_uid;
+	return -1;
+}
+
+gid_t
+gid_from_string(
+	char		*group)
+{
+	struct group	*grp;
+	unsigned long	gid_long;
+	char		*sp;
+
+	gid_long = strtoul(group, &sp, 10);
+	if (sp != group && *sp == '\0') {
+		if ((gid_long == ULONG_MAX && errno == ERANGE)
+				|| (gid_long > (gid_t)-1))
+			return -1;
+		return (gid_t)gid_long;
+	}
+	grp = getgrnam(group);
+	if (grp)
+		return grp->gr_gid;
+	return -1;
+}


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 09/12] libfrog: move paths.c out of libxcmd
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
                   ` (7 preceding siblings ...)
  2017-11-17 20:14 ` [PATCH 08/12] libfrog: move conversion factors " Darrick J. Wong
@ 2017-11-17 20:14 ` Darrick J. Wong
  2017-11-17 20:14 ` [PATCH 10/12] libfrog: add missing function fs_table_destroy Darrick J. Wong
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:14 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Move the fs_table code into libfrog since it's not really a command.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 libfrog/Makefile   |   10 +
 libfrog/paths.c    |  586 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 libfrog/projects.c |  201 ++++++++++++++++++
 libxcmd/Makefile   |   10 -
 libxcmd/paths.c    |  586 ----------------------------------------------------
 libxcmd/projects.c |  201 ------------------
 quota/Makefile     |    4 
 spaceman/Makefile  |    4 
 8 files changed, 802 insertions(+), 800 deletions(-)
 create mode 100644 libfrog/paths.c
 create mode 100644 libfrog/projects.c
 delete mode 100644 libxcmd/paths.c
 delete mode 100644 libxcmd/projects.c


diff --git a/libfrog/Makefile b/libfrog/Makefile
index 467362c..4c15605 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -14,11 +14,21 @@ CFILES = \
 avl64.c \
 convert.c \
 list_sort.c \
+paths.c \
+projects.c \
 radix-tree.c \
 topology.c \
 util.c \
 workqueue.c
 
+ifeq ($(HAVE_GETMNTENT),yes)
+LCFLAGS += -DHAVE_GETMNTENT
+endif
+
+ifeq ($(HAVE_GETMNTINFO),yes)
+LCFLAGS += -DHAVE_GETMNTINFO
+endif
+
 default: ltdepend $(LTLIBRARY)
 
 include $(BUILDRULES)
diff --git a/libfrog/paths.c b/libfrog/paths.c
new file mode 100644
index 0000000..b767e9d
--- /dev/null
+++ b/libfrog/paths.c
@@ -0,0 +1,586 @@
+/*
+ * Copyright (c) 2005-2006 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <paths.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "path.h"
+#include "input.h"
+#include "project.h"
+#include <limits.h>
+
+extern char *progname;
+
+int fs_count;
+int xfs_fs_count;
+struct fs_path *fs_table;
+struct fs_path *fs_path;
+
+char *mtab_file;
+#define PROC_MOUNTS	"/proc/self/mounts"
+
+static int
+fs_device_number(
+	const char	*name,
+	dev_t		*devnum)
+{
+	struct stat	sbuf;
+
+	if (stat(name, &sbuf) < 0)
+		return errno;
+	/*
+	 * We want to match st_rdev if the path provided is a device
+	 * special file.  Otherwise we are looking for the the
+	 * device id for the containing filesystem, in st_dev.
+	 */
+	if (S_ISBLK(sbuf.st_mode) || S_ISCHR(sbuf.st_mode))
+		*devnum = sbuf.st_rdev;
+	else
+		*devnum = sbuf.st_dev;
+
+	return 0;
+}
+
+/*
+ * Find the FS table entry for the given path.  The "flags" argument
+ * is a mask containing FS_MOUNT_POINT or FS_PROJECT_PATH (or both)
+ * to indicate the type of table entry sought.
+ * fs_table_lookup() finds the fs table entry for the filesystem hosting
+ * the file represented in the "dir" argument. To compare against actual
+ * mount point entries, use fs_table_lookup_mount() instead.
+ */
+struct fs_path *
+fs_table_lookup(
+	const char	*dir,
+	uint		flags)
+{
+	uint		i;
+	dev_t		dev = 0;
+
+	if (fs_device_number(dir, &dev))
+		return NULL;
+
+	for (i = 0; i < fs_count; i++) {
+		if (flags && !(flags & fs_table[i].fs_flags))
+			continue;
+		if (fs_table[i].fs_datadev == dev)
+			return &fs_table[i];
+	}
+	return NULL;
+}
+
+/*
+ * Find the FS table entry describing an actual mount for the given path.
+ * Unlike fs_table_lookup(), fs_table_lookup_mount() compares the "dir"
+ * argument to actual mount point entries in the table. Accordingly, it
+ * will find matches only if the "dir" argument is indeed mounted.
+ */
+struct fs_path *
+fs_table_lookup_mount(
+	const char	*dir)
+{
+	uint		i;
+	dev_t		dev = 0;
+	char		rpath[PATH_MAX];
+
+	if (fs_device_number(dir, &dev))
+		return NULL;
+
+	for (i = 0; i < fs_count; i++) {
+		if (fs_table[i].fs_flags != FS_MOUNT_POINT)
+			continue;
+		if (!realpath(fs_table[i].fs_dir, rpath))
+			continue;
+		if (strcmp(rpath, dir) == 0)
+			return &fs_table[i];
+	}
+	return NULL;
+}
+
+static int
+fs_table_insert(
+	char		*dir,
+	uint		prid,
+	uint		flags,
+	char		*fsname,
+	char		*fslog,
+	char		*fsrt)
+{
+	dev_t		datadev, logdev, rtdev;
+	struct fs_path	*tmp_fs_table;
+	int		error;
+
+	datadev = logdev = rtdev = 0;
+	error = fs_device_number(dir, &datadev);
+	if (error)
+		goto out_nodev;
+	if (fslog) {
+		error = fs_device_number(fslog, &logdev);
+		if (error)
+			goto out_nodev;
+	}
+	if (fsrt) {
+		error = fs_device_number(fsrt, &rtdev);
+		if (error)
+			goto out_nodev;
+	}
+
+	if (!platform_test_xfs_path(dir))
+		flags |= FS_FOREIGN;
+
+	/*
+	 * Make copies of the directory and data device path.
+	 * The log device and real-time device, if non-null,
+	 * are already the result of strdup() calls so we
+	 * don't need to duplicate those.  In fact, this
+	 * function is assumed to "consume" both of those
+	 * pointers, meaning if an error occurs they will
+	 * both get freed.
+	 */
+	error = ENOMEM;
+	dir = strdup(dir);
+	if (!dir)
+		goto out_nodev;
+	fsname = strdup(fsname);
+	if (!fsname)
+		goto out_noname;
+
+	tmp_fs_table = realloc(fs_table, sizeof(fs_path_t) * (fs_count + 1));
+	if (!tmp_fs_table)
+		goto out_norealloc;
+	fs_table = tmp_fs_table;
+
+	/* Put foreign filesystems at the end, xfs filesystems at the front */
+	if (flags & FS_FOREIGN || fs_count == 0) {
+		fs_path = &fs_table[fs_count];
+	} else {
+		/* move foreign fs entries down, insert new one just before */
+		memmove(&fs_table[xfs_fs_count + 1], &fs_table[xfs_fs_count],
+			sizeof(fs_path_t)*(fs_count - xfs_fs_count));
+		fs_path = &fs_table[xfs_fs_count];
+	}
+	fs_path->fs_dir = dir;
+	fs_path->fs_prid = prid;
+	fs_path->fs_flags = flags;
+	fs_path->fs_name = fsname;
+	fs_path->fs_log = fslog;
+	fs_path->fs_rt = fsrt;
+	fs_path->fs_datadev = datadev;
+	fs_path->fs_logdev = logdev;
+	fs_path->fs_rtdev = rtdev;
+	fs_count++;
+	if (!(flags & FS_FOREIGN))
+		xfs_fs_count++;
+
+	return 0;
+
+out_norealloc:
+	free(fsname);
+out_noname:
+	free(dir);
+out_nodev:
+	/* "Consume" fslog and fsrt even if there's an error */
+	free(fslog);
+	free(fsrt);
+
+	return error;
+}
+
+/*
+ * Table iteration (cursor-based) interfaces
+ */
+
+/*
+ * Initialize an fs_table cursor.  If a directory path is supplied,
+ * the cursor is set up to appear as though the table contains only
+ * a single entry which represents the directory specified.
+ * Otherwise it is set up to prepare for visiting all entries in the
+ * global table, starting with the first.  "flags" can be either
+ * FS_MOUNT_POINT or FS_PROJECT_PATH to limit what type of entries
+ * will be selected by fs_cursor_next_entry().  0 can be used as a
+ * wild card (selecting either type).
+ */
+void
+fs_cursor_initialise(
+	char		*dir,
+	uint		flags,
+	fs_cursor_t	*cur)
+{
+	fs_path_t	*path;
+
+	memset(cur, 0, sizeof(*cur));
+	if (dir) {
+		if ((path = fs_table_lookup(dir, flags)) == NULL)
+			return;
+		cur->local = *path;
+		cur->count = 1;
+		cur->table = &cur->local;
+	} else {
+		cur->count = fs_count;
+		cur->table = fs_table;
+	}
+	cur->flags = flags;
+}
+
+/*
+ * Use the cursor to find the next entry in the table having the
+ * type specified by the cursor's "flags" field.
+ */
+struct fs_path *
+fs_cursor_next_entry(
+	fs_cursor_t	*cur)
+{
+	while (cur->index < cur->count) {
+		fs_path_t	*next = &cur->table[cur->index++];
+
+		if (!cur->flags || (cur->flags & next->fs_flags))
+			return next;
+	}
+	return NULL;
+}
+
+
+#if defined(HAVE_GETMNTENT)
+#include <mntent.h>
+
+/*
+ * Determines whether the "logdev" or "rtdev" mount options are
+ * present for the given mount point.  If so, the value for each (a
+ * device path) is returned in the pointers whose addresses are
+ * provided.  The pointers are assigned NULL for an option not
+ * present.  Note that the path buffers returned are allocated
+ * dynamically and it is the caller's responsibility to free them.
+ */
+static int
+fs_extract_mount_options(
+	struct mntent	*mnt,
+	char		**logp,
+	char		**rtp)
+{
+	char		*fslog, *fsrt;
+
+	/*
+	 * Extract log device and realtime device from mount options.
+	 *
+	 * Note: the glibc hasmntopt implementation requires that the
+	 * character in mnt_opts immediately after the search string
+	 * must be a NULL ('\0'), a comma (','), or an equals ('=').
+	 * Therefore we cannot search for 'logdev=' directly.
+	 */
+	if ((fslog = hasmntopt(mnt, "logdev")) && fslog[6] == '=')
+		fslog += 7;
+	if ((fsrt = hasmntopt(mnt, "rtdev")) && fsrt[5] == '=')
+		fsrt += 6;
+
+	/* Do this only after we've finished processing mount options */
+	if (fslog) {
+		fslog = strndup(fslog, strcspn(fslog, " ,"));
+		if (!fslog)
+			goto out_nomem;
+	}
+	if (fsrt) {
+		fsrt = strndup(fsrt, strcspn(fsrt, " ,"));
+		if (!fsrt) {
+			free(fslog);
+			goto out_nomem;
+		}
+	}
+	*logp = fslog;
+	*rtp = fsrt;
+
+	return 0;
+
+out_nomem:
+	*logp = NULL;
+	*rtp = NULL;
+	fprintf(stderr, _("%s: unable to extract mount options for \"%s\"\n"),
+		progname, mnt->mnt_dir);
+	return ENOMEM;
+}
+
+/*
+ * If *path is NULL, initialize the fs table with all xfs mount points in mtab
+ * If *path is specified, search for that path in mtab
+ *
+ * Everything - path, devices, and mountpoints - are boiled down to realpath()
+ * for comparison, but fs_table is populated with what comes from getmntent.
+ */
+static int
+fs_table_initialise_mounts(
+	char		*path)
+{
+	struct mntent	*mnt;
+	FILE		*mtp;
+	char		*fslog, *fsrt;
+	int		error, found;
+	char		rpath[PATH_MAX], rmnt_fsname[PATH_MAX], rmnt_dir[PATH_MAX];
+
+	error = found = 0;
+	fslog = fsrt = NULL;
+
+	if (!mtab_file) {
+		mtab_file = PROC_MOUNTS;
+		if (access(mtab_file, R_OK) != 0)
+			mtab_file = MOUNTED;
+	}
+
+	if ((mtp = setmntent(mtab_file, "r")) == NULL)
+		return ENOENT;
+
+	/* Use realpath to resolve symlinks, relative paths, etc */
+	if (path)
+		if (!realpath(path, rpath))
+			return errno;
+
+	while ((mnt = getmntent(mtp)) != NULL) {
+		if (!realpath(mnt->mnt_dir, rmnt_dir))
+			continue;
+		if (!realpath(mnt->mnt_fsname, rmnt_fsname))
+			continue;
+
+		if (path &&
+		    ((strcmp(rpath, rmnt_dir) != 0) &&
+		     (strcmp(rpath, rmnt_fsname) != 0)))
+			continue;
+		if (fs_extract_mount_options(mnt, &fslog, &fsrt))
+			continue;
+		(void) fs_table_insert(mnt->mnt_dir, 0, FS_MOUNT_POINT,
+					mnt->mnt_fsname, fslog, fsrt);
+		if (path) {
+			found = 1;
+			break;
+		}
+	}
+	endmntent(mtp);
+
+	if (path && !found)
+		error = ENXIO;
+
+	return error;
+}
+
+#elif defined(HAVE_GETMNTINFO)
+#include <sys/mount.h>
+
+/*
+ * If *path is NULL, initialize the fs table with all xfs mount points in mtab
+ * If *path is specified, search for that path in mtab
+ *
+ * Everything - path, devices, and mountpoints - are boiled down to realpath()
+ * for comparison, but fs_table is populated with what comes from getmntinfo.
+ */
+static int
+fs_table_initialise_mounts(
+	char		*path)
+{
+	struct statfs	*stats;
+	int		i, count, error, found;
+	char		rpath[PATH_MAX], rmntfromname[PATH_MAX], rmntonname[PATH_MAX];
+
+	error = found = 0;
+	if ((count = getmntinfo(&stats, 0)) < 0) {
+		fprintf(stderr, _("%s: getmntinfo() failed: %s\n"),
+				progname, strerror(errno));
+		return 0;
+	}
+
+	/* Use realpath to resolve symlinks, relative paths, etc */
+	if (path)
+		if (!realpath(path, rpath))
+			return errno;
+
+	for (i = 0; i < count; i++) {
+		if (!realpath(stats[i].f_mntfromname, rmntfromname))
+			continue;
+		if (!realpath(stats[i].f_mntonname, rmntonname))
+			continue;
+
+		if (path &&
+		    ((strcmp(rpath, rmntonname) != 0) &&
+		     (strcmp(rpath, rmntfromname) != 0)))
+			continue;
+		/* TODO: external log and realtime device? */
+		(void) fs_table_insert(stats[i].f_mntonname, 0,
+					FS_MOUNT_POINT, stats[i].f_mntfromname,
+					NULL, NULL);
+		if (path) {
+			found = 1;
+			break;
+		}
+	}
+	if (path && !found)
+		error = ENXIO;
+
+	return error;
+}
+
+#else
+# error "How do I extract info about mounted filesystems on this platform?"
+#endif
+
+/*
+ * Given a directory, match it up to a filesystem mount point.
+ */
+static struct fs_path *
+fs_mount_point_from_path(
+	const char	*dir)
+{
+	fs_cursor_t	cursor;
+	fs_path_t	*fs;
+	dev_t		dev = 0;
+
+	if (fs_device_number(dir, &dev))
+		return NULL;
+
+	fs_cursor_initialise(NULL, FS_MOUNT_POINT, &cursor);
+	while ((fs = fs_cursor_next_entry(&cursor))) {
+		if (fs->fs_datadev == dev)
+			break;
+	}
+	return fs;
+}
+
+static void
+fs_table_insert_mount(
+	char		*mount)
+{
+	int		error;
+
+	error = fs_table_initialise_mounts(mount);
+	if (error)
+		fprintf(stderr, _("%s: cannot setup path for mount %s: %s\n"),
+			progname, mount, strerror(error));
+}
+
+static int
+fs_table_initialise_projects(
+	char		*project)
+{
+	fs_project_path_t *path;
+	fs_path_t	*fs;
+	prid_t		prid = 0;
+	int		error = 0, found = 0;
+
+	if (project)
+		prid = prid_from_string(project);
+
+	setprpathent();
+	while ((path = getprpathent()) != NULL) {
+		if (project && prid != path->pp_prid)
+			continue;
+		fs = fs_mount_point_from_path(path->pp_pathname);
+		if (!fs) {
+			fprintf(stderr, _("%s: cannot find mount point for path `%s': %s\n"),
+					progname, path->pp_pathname, strerror(errno));
+			continue;
+		}
+		(void) fs_table_insert(path->pp_pathname, path->pp_prid,
+					FS_PROJECT_PATH, fs->fs_name,
+					NULL, NULL);
+		if (project) {
+			found = 1;
+			break;
+		}
+	}
+	endprpathent();
+
+	if (project && !found)
+		error = ENOENT;
+
+	return error;
+}
+
+static void
+fs_table_insert_project(
+	char		*project)
+{
+	int		error;
+
+	error = fs_table_initialise_projects(project);
+	if (error)
+		fprintf(stderr, _("%s: cannot setup path for project %s: %s\n"),
+			progname, project, strerror(error));
+}
+
+/*
+ * Initialize fs_table to contain the given set of mount points and
+ * projects.  If mount_count is zero, mounts is ignored and the
+ * table is populated with mounted filesystems.  If project_count is
+ * zero, projects is ignored and the table is populated with all
+ * projects defined in the projects file.
+ */
+void
+fs_table_initialise(
+	int	mount_count,
+	char	*mounts[],
+	int	project_count,
+	char	*projects[])
+{
+	int	error;
+	int	i;
+
+	if (mount_count) {
+		for (i = 0; i < mount_count; i++)
+			fs_table_insert_mount(mounts[i]);
+	} else {
+		error = fs_table_initialise_mounts(NULL);
+		if (error)
+			goto out_error;
+	}
+	if (project_count) {
+		for (i = 0; i < project_count; i++)
+			fs_table_insert_project(projects[i]);
+	} else {
+		error = fs_table_initialise_projects(NULL);
+		if (error)
+			goto out_error;
+	}
+
+	return;
+
+out_error:
+	fprintf(stderr, _("%s: cannot initialise path table: %s\n"),
+		progname, strerror(error));
+}
+
+void
+fs_table_insert_project_path(
+	char		*dir,
+	prid_t		prid)
+{
+	fs_path_t	*fs;
+	int		error = 0;
+
+	fs = fs_mount_point_from_path(dir);
+	if (fs)
+		error = fs_table_insert(dir, prid, FS_PROJECT_PATH,
+					fs->fs_name, NULL, NULL);
+	else
+		error = ENOENT;
+
+	if (error) {
+		fprintf(stderr, _("%s: cannot setup path for project dir %s: %s\n"),
+				progname, dir, strerror(error));
+		exit(1);
+	}
+}
diff --git a/libfrog/projects.c b/libfrog/projects.c
new file mode 100644
index 0000000..c9e863d
--- /dev/null
+++ b/libfrog/projects.c
@@ -0,0 +1,201 @@
+/*
+ * 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 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "project.h"
+
+#define PROJID		"/etc/projid"
+#define PROJECT_PATHS	"/etc/projects"
+char *projid_file;
+char *projects_file;
+
+static FILE *projects;
+static fs_project_t p;
+static char projects_buffer[512];
+
+static FILE *project_paths;
+static fs_project_path_t pp;
+static char project_paths_buffer[1024];
+
+void
+setprfiles(void)
+{
+	if (!projid_file)
+		projid_file = PROJID;
+	if (!projects_file)
+		projects_file = PROJECT_PATHS;
+}
+
+void
+setprent(void)
+{
+	setprfiles();
+	projects = fopen(projid_file, "r");
+}
+
+void
+setprpathent(void)
+{
+	setprfiles();
+	project_paths = fopen(projects_file, "r");
+}
+
+void
+endprent(void)
+{
+	if (projects)
+		fclose(projects);
+	projects = NULL;
+}
+
+void
+endprpathent(void)
+{
+	if (project_paths)
+		fclose(project_paths);
+	project_paths = NULL;
+}
+
+fs_project_t *
+getprent(void)
+{
+	char	*idstart, *idend;
+	size_t	size = sizeof(projects_buffer) - 1;
+
+	if (!projects)
+		return NULL;
+	for (;;) {
+		if (!fgets(projects_buffer, size, projects))
+			break;
+		/*
+		 * /etc/projid file format -- "name:id\n", ignore "^#..."
+		 */
+		if (projects_buffer[0] == '#')
+			continue;
+		idstart = strchr(projects_buffer, ':');
+		if (!idstart)
+			continue;
+		if ((idstart + 1) - projects_buffer >= size)
+			continue;
+		idend = strchr(idstart+1, ':');
+		if (idend)
+			*idend = '\0';
+		*idstart = '\0';
+		p.pr_prid = atoi(idstart+1);
+		p.pr_name = &projects_buffer[0];
+		return &p;
+	}
+
+	return NULL;
+}
+
+fs_project_t *
+getprnam(
+	char		*name)
+{
+	fs_project_t	*p = NULL;
+
+	setprent();
+	while ((p = getprent()) != NULL)
+		if (strcmp(p->pr_name, name) == 0)
+			break;
+	endprent();
+	return p;
+}
+
+fs_project_t *
+getprprid(
+	prid_t		prid)
+{
+	fs_project_t	*p = NULL;
+
+	setprent();
+	while ((p = getprent()) != NULL)
+		if (p->pr_prid == prid)
+			break;
+	endprent();
+	return p;
+}
+
+fs_project_path_t *
+getprpathent(void)
+{
+	char		*nmstart, *nmend;
+	size_t		size = sizeof(project_paths_buffer) - 1;
+
+	if (!project_paths)
+		return NULL;
+	for (;;) {
+		if (!fgets(project_paths_buffer, size, project_paths))
+			break;
+		/*
+		 * /etc/projects format -- "id:pathname\n", ignore "^#..."
+		 */
+		if (project_paths_buffer[0] == '#')
+			continue;
+		nmstart = strchr(project_paths_buffer, ':');
+		if (!nmstart)
+			continue;
+		if ((nmstart + 1) - project_paths_buffer >= size)
+			continue;
+		nmend = strchr(nmstart + 1, '\n');
+		if (nmend)
+			*nmend = '\0';
+		*nmstart = '\0';
+		pp.pp_pathname = nmstart + 1;
+		pp.pp_prid = atoi(&project_paths_buffer[0]);
+		return &pp;
+	}
+
+	return NULL;
+}
+
+
+int
+getprojid(
+	const char	*name,
+	int		fd,
+	prid_t		*projid)
+{
+	struct fsxattr	fsx;
+
+	if (xfsctl(name, fd, FS_IOC_FSGETXATTR, &fsx)) {
+		perror("FS_IOC_FSGETXATTR");
+		return -1;
+	}
+	*projid = fsx.fsx_projid;
+	return 0;
+}
+
+int
+setprojid(
+	const char	*name,
+	int		fd,
+	prid_t		projid)
+{
+	struct fsxattr	fsx;
+	int		error;
+
+	if ((error = xfsctl(name, fd, FS_IOC_FSGETXATTR, &fsx)) == 0) {
+		fsx.fsx_projid = projid;
+		error = xfsctl(name, fd, FS_IOC_FSSETXATTR, &fsx);
+	}
+	return error;
+}
diff --git a/libxcmd/Makefile b/libxcmd/Makefile
index 7701ed9..de0e49c 100644
--- a/libxcmd/Makefile
+++ b/libxcmd/Makefile
@@ -10,15 +10,7 @@ LT_CURRENT = 0
 LT_REVISION = 0
 LT_AGE = 0
 
-CFILES = command.c input.c paths.c projects.c help.c quit.c
-
-ifeq ($(HAVE_GETMNTENT),yes)
-LCFLAGS += -DHAVE_GETMNTENT
-endif
-
-ifeq ($(HAVE_GETMNTINFO),yes)
-LCFLAGS += -DHAVE_GETMNTINFO
-endif
+CFILES = command.c input.c help.c quit.c
 
 ifeq ($(ENABLE_READLINE),yes)
 LCFLAGS += -DENABLE_READLINE
diff --git a/libxcmd/paths.c b/libxcmd/paths.c
deleted file mode 100644
index b767e9d..0000000
--- a/libxcmd/paths.c
+++ /dev/null
@@ -1,586 +0,0 @@
-/*
- * Copyright (c) 2005-2006 Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms 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.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include <paths.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "path.h"
-#include "input.h"
-#include "project.h"
-#include <limits.h>
-
-extern char *progname;
-
-int fs_count;
-int xfs_fs_count;
-struct fs_path *fs_table;
-struct fs_path *fs_path;
-
-char *mtab_file;
-#define PROC_MOUNTS	"/proc/self/mounts"
-
-static int
-fs_device_number(
-	const char	*name,
-	dev_t		*devnum)
-{
-	struct stat	sbuf;
-
-	if (stat(name, &sbuf) < 0)
-		return errno;
-	/*
-	 * We want to match st_rdev if the path provided is a device
-	 * special file.  Otherwise we are looking for the the
-	 * device id for the containing filesystem, in st_dev.
-	 */
-	if (S_ISBLK(sbuf.st_mode) || S_ISCHR(sbuf.st_mode))
-		*devnum = sbuf.st_rdev;
-	else
-		*devnum = sbuf.st_dev;
-
-	return 0;
-}
-
-/*
- * Find the FS table entry for the given path.  The "flags" argument
- * is a mask containing FS_MOUNT_POINT or FS_PROJECT_PATH (or both)
- * to indicate the type of table entry sought.
- * fs_table_lookup() finds the fs table entry for the filesystem hosting
- * the file represented in the "dir" argument. To compare against actual
- * mount point entries, use fs_table_lookup_mount() instead.
- */
-struct fs_path *
-fs_table_lookup(
-	const char	*dir,
-	uint		flags)
-{
-	uint		i;
-	dev_t		dev = 0;
-
-	if (fs_device_number(dir, &dev))
-		return NULL;
-
-	for (i = 0; i < fs_count; i++) {
-		if (flags && !(flags & fs_table[i].fs_flags))
-			continue;
-		if (fs_table[i].fs_datadev == dev)
-			return &fs_table[i];
-	}
-	return NULL;
-}
-
-/*
- * Find the FS table entry describing an actual mount for the given path.
- * Unlike fs_table_lookup(), fs_table_lookup_mount() compares the "dir"
- * argument to actual mount point entries in the table. Accordingly, it
- * will find matches only if the "dir" argument is indeed mounted.
- */
-struct fs_path *
-fs_table_lookup_mount(
-	const char	*dir)
-{
-	uint		i;
-	dev_t		dev = 0;
-	char		rpath[PATH_MAX];
-
-	if (fs_device_number(dir, &dev))
-		return NULL;
-
-	for (i = 0; i < fs_count; i++) {
-		if (fs_table[i].fs_flags != FS_MOUNT_POINT)
-			continue;
-		if (!realpath(fs_table[i].fs_dir, rpath))
-			continue;
-		if (strcmp(rpath, dir) == 0)
-			return &fs_table[i];
-	}
-	return NULL;
-}
-
-static int
-fs_table_insert(
-	char		*dir,
-	uint		prid,
-	uint		flags,
-	char		*fsname,
-	char		*fslog,
-	char		*fsrt)
-{
-	dev_t		datadev, logdev, rtdev;
-	struct fs_path	*tmp_fs_table;
-	int		error;
-
-	datadev = logdev = rtdev = 0;
-	error = fs_device_number(dir, &datadev);
-	if (error)
-		goto out_nodev;
-	if (fslog) {
-		error = fs_device_number(fslog, &logdev);
-		if (error)
-			goto out_nodev;
-	}
-	if (fsrt) {
-		error = fs_device_number(fsrt, &rtdev);
-		if (error)
-			goto out_nodev;
-	}
-
-	if (!platform_test_xfs_path(dir))
-		flags |= FS_FOREIGN;
-
-	/*
-	 * Make copies of the directory and data device path.
-	 * The log device and real-time device, if non-null,
-	 * are already the result of strdup() calls so we
-	 * don't need to duplicate those.  In fact, this
-	 * function is assumed to "consume" both of those
-	 * pointers, meaning if an error occurs they will
-	 * both get freed.
-	 */
-	error = ENOMEM;
-	dir = strdup(dir);
-	if (!dir)
-		goto out_nodev;
-	fsname = strdup(fsname);
-	if (!fsname)
-		goto out_noname;
-
-	tmp_fs_table = realloc(fs_table, sizeof(fs_path_t) * (fs_count + 1));
-	if (!tmp_fs_table)
-		goto out_norealloc;
-	fs_table = tmp_fs_table;
-
-	/* Put foreign filesystems at the end, xfs filesystems at the front */
-	if (flags & FS_FOREIGN || fs_count == 0) {
-		fs_path = &fs_table[fs_count];
-	} else {
-		/* move foreign fs entries down, insert new one just before */
-		memmove(&fs_table[xfs_fs_count + 1], &fs_table[xfs_fs_count],
-			sizeof(fs_path_t)*(fs_count - xfs_fs_count));
-		fs_path = &fs_table[xfs_fs_count];
-	}
-	fs_path->fs_dir = dir;
-	fs_path->fs_prid = prid;
-	fs_path->fs_flags = flags;
-	fs_path->fs_name = fsname;
-	fs_path->fs_log = fslog;
-	fs_path->fs_rt = fsrt;
-	fs_path->fs_datadev = datadev;
-	fs_path->fs_logdev = logdev;
-	fs_path->fs_rtdev = rtdev;
-	fs_count++;
-	if (!(flags & FS_FOREIGN))
-		xfs_fs_count++;
-
-	return 0;
-
-out_norealloc:
-	free(fsname);
-out_noname:
-	free(dir);
-out_nodev:
-	/* "Consume" fslog and fsrt even if there's an error */
-	free(fslog);
-	free(fsrt);
-
-	return error;
-}
-
-/*
- * Table iteration (cursor-based) interfaces
- */
-
-/*
- * Initialize an fs_table cursor.  If a directory path is supplied,
- * the cursor is set up to appear as though the table contains only
- * a single entry which represents the directory specified.
- * Otherwise it is set up to prepare for visiting all entries in the
- * global table, starting with the first.  "flags" can be either
- * FS_MOUNT_POINT or FS_PROJECT_PATH to limit what type of entries
- * will be selected by fs_cursor_next_entry().  0 can be used as a
- * wild card (selecting either type).
- */
-void
-fs_cursor_initialise(
-	char		*dir,
-	uint		flags,
-	fs_cursor_t	*cur)
-{
-	fs_path_t	*path;
-
-	memset(cur, 0, sizeof(*cur));
-	if (dir) {
-		if ((path = fs_table_lookup(dir, flags)) == NULL)
-			return;
-		cur->local = *path;
-		cur->count = 1;
-		cur->table = &cur->local;
-	} else {
-		cur->count = fs_count;
-		cur->table = fs_table;
-	}
-	cur->flags = flags;
-}
-
-/*
- * Use the cursor to find the next entry in the table having the
- * type specified by the cursor's "flags" field.
- */
-struct fs_path *
-fs_cursor_next_entry(
-	fs_cursor_t	*cur)
-{
-	while (cur->index < cur->count) {
-		fs_path_t	*next = &cur->table[cur->index++];
-
-		if (!cur->flags || (cur->flags & next->fs_flags))
-			return next;
-	}
-	return NULL;
-}
-
-
-#if defined(HAVE_GETMNTENT)
-#include <mntent.h>
-
-/*
- * Determines whether the "logdev" or "rtdev" mount options are
- * present for the given mount point.  If so, the value for each (a
- * device path) is returned in the pointers whose addresses are
- * provided.  The pointers are assigned NULL for an option not
- * present.  Note that the path buffers returned are allocated
- * dynamically and it is the caller's responsibility to free them.
- */
-static int
-fs_extract_mount_options(
-	struct mntent	*mnt,
-	char		**logp,
-	char		**rtp)
-{
-	char		*fslog, *fsrt;
-
-	/*
-	 * Extract log device and realtime device from mount options.
-	 *
-	 * Note: the glibc hasmntopt implementation requires that the
-	 * character in mnt_opts immediately after the search string
-	 * must be a NULL ('\0'), a comma (','), or an equals ('=').
-	 * Therefore we cannot search for 'logdev=' directly.
-	 */
-	if ((fslog = hasmntopt(mnt, "logdev")) && fslog[6] == '=')
-		fslog += 7;
-	if ((fsrt = hasmntopt(mnt, "rtdev")) && fsrt[5] == '=')
-		fsrt += 6;
-
-	/* Do this only after we've finished processing mount options */
-	if (fslog) {
-		fslog = strndup(fslog, strcspn(fslog, " ,"));
-		if (!fslog)
-			goto out_nomem;
-	}
-	if (fsrt) {
-		fsrt = strndup(fsrt, strcspn(fsrt, " ,"));
-		if (!fsrt) {
-			free(fslog);
-			goto out_nomem;
-		}
-	}
-	*logp = fslog;
-	*rtp = fsrt;
-
-	return 0;
-
-out_nomem:
-	*logp = NULL;
-	*rtp = NULL;
-	fprintf(stderr, _("%s: unable to extract mount options for \"%s\"\n"),
-		progname, mnt->mnt_dir);
-	return ENOMEM;
-}
-
-/*
- * If *path is NULL, initialize the fs table with all xfs mount points in mtab
- * If *path is specified, search for that path in mtab
- *
- * Everything - path, devices, and mountpoints - are boiled down to realpath()
- * for comparison, but fs_table is populated with what comes from getmntent.
- */
-static int
-fs_table_initialise_mounts(
-	char		*path)
-{
-	struct mntent	*mnt;
-	FILE		*mtp;
-	char		*fslog, *fsrt;
-	int		error, found;
-	char		rpath[PATH_MAX], rmnt_fsname[PATH_MAX], rmnt_dir[PATH_MAX];
-
-	error = found = 0;
-	fslog = fsrt = NULL;
-
-	if (!mtab_file) {
-		mtab_file = PROC_MOUNTS;
-		if (access(mtab_file, R_OK) != 0)
-			mtab_file = MOUNTED;
-	}
-
-	if ((mtp = setmntent(mtab_file, "r")) == NULL)
-		return ENOENT;
-
-	/* Use realpath to resolve symlinks, relative paths, etc */
-	if (path)
-		if (!realpath(path, rpath))
-			return errno;
-
-	while ((mnt = getmntent(mtp)) != NULL) {
-		if (!realpath(mnt->mnt_dir, rmnt_dir))
-			continue;
-		if (!realpath(mnt->mnt_fsname, rmnt_fsname))
-			continue;
-
-		if (path &&
-		    ((strcmp(rpath, rmnt_dir) != 0) &&
-		     (strcmp(rpath, rmnt_fsname) != 0)))
-			continue;
-		if (fs_extract_mount_options(mnt, &fslog, &fsrt))
-			continue;
-		(void) fs_table_insert(mnt->mnt_dir, 0, FS_MOUNT_POINT,
-					mnt->mnt_fsname, fslog, fsrt);
-		if (path) {
-			found = 1;
-			break;
-		}
-	}
-	endmntent(mtp);
-
-	if (path && !found)
-		error = ENXIO;
-
-	return error;
-}
-
-#elif defined(HAVE_GETMNTINFO)
-#include <sys/mount.h>
-
-/*
- * If *path is NULL, initialize the fs table with all xfs mount points in mtab
- * If *path is specified, search for that path in mtab
- *
- * Everything - path, devices, and mountpoints - are boiled down to realpath()
- * for comparison, but fs_table is populated with what comes from getmntinfo.
- */
-static int
-fs_table_initialise_mounts(
-	char		*path)
-{
-	struct statfs	*stats;
-	int		i, count, error, found;
-	char		rpath[PATH_MAX], rmntfromname[PATH_MAX], rmntonname[PATH_MAX];
-
-	error = found = 0;
-	if ((count = getmntinfo(&stats, 0)) < 0) {
-		fprintf(stderr, _("%s: getmntinfo() failed: %s\n"),
-				progname, strerror(errno));
-		return 0;
-	}
-
-	/* Use realpath to resolve symlinks, relative paths, etc */
-	if (path)
-		if (!realpath(path, rpath))
-			return errno;
-
-	for (i = 0; i < count; i++) {
-		if (!realpath(stats[i].f_mntfromname, rmntfromname))
-			continue;
-		if (!realpath(stats[i].f_mntonname, rmntonname))
-			continue;
-
-		if (path &&
-		    ((strcmp(rpath, rmntonname) != 0) &&
-		     (strcmp(rpath, rmntfromname) != 0)))
-			continue;
-		/* TODO: external log and realtime device? */
-		(void) fs_table_insert(stats[i].f_mntonname, 0,
-					FS_MOUNT_POINT, stats[i].f_mntfromname,
-					NULL, NULL);
-		if (path) {
-			found = 1;
-			break;
-		}
-	}
-	if (path && !found)
-		error = ENXIO;
-
-	return error;
-}
-
-#else
-# error "How do I extract info about mounted filesystems on this platform?"
-#endif
-
-/*
- * Given a directory, match it up to a filesystem mount point.
- */
-static struct fs_path *
-fs_mount_point_from_path(
-	const char	*dir)
-{
-	fs_cursor_t	cursor;
-	fs_path_t	*fs;
-	dev_t		dev = 0;
-
-	if (fs_device_number(dir, &dev))
-		return NULL;
-
-	fs_cursor_initialise(NULL, FS_MOUNT_POINT, &cursor);
-	while ((fs = fs_cursor_next_entry(&cursor))) {
-		if (fs->fs_datadev == dev)
-			break;
-	}
-	return fs;
-}
-
-static void
-fs_table_insert_mount(
-	char		*mount)
-{
-	int		error;
-
-	error = fs_table_initialise_mounts(mount);
-	if (error)
-		fprintf(stderr, _("%s: cannot setup path for mount %s: %s\n"),
-			progname, mount, strerror(error));
-}
-
-static int
-fs_table_initialise_projects(
-	char		*project)
-{
-	fs_project_path_t *path;
-	fs_path_t	*fs;
-	prid_t		prid = 0;
-	int		error = 0, found = 0;
-
-	if (project)
-		prid = prid_from_string(project);
-
-	setprpathent();
-	while ((path = getprpathent()) != NULL) {
-		if (project && prid != path->pp_prid)
-			continue;
-		fs = fs_mount_point_from_path(path->pp_pathname);
-		if (!fs) {
-			fprintf(stderr, _("%s: cannot find mount point for path `%s': %s\n"),
-					progname, path->pp_pathname, strerror(errno));
-			continue;
-		}
-		(void) fs_table_insert(path->pp_pathname, path->pp_prid,
-					FS_PROJECT_PATH, fs->fs_name,
-					NULL, NULL);
-		if (project) {
-			found = 1;
-			break;
-		}
-	}
-	endprpathent();
-
-	if (project && !found)
-		error = ENOENT;
-
-	return error;
-}
-
-static void
-fs_table_insert_project(
-	char		*project)
-{
-	int		error;
-
-	error = fs_table_initialise_projects(project);
-	if (error)
-		fprintf(stderr, _("%s: cannot setup path for project %s: %s\n"),
-			progname, project, strerror(error));
-}
-
-/*
- * Initialize fs_table to contain the given set of mount points and
- * projects.  If mount_count is zero, mounts is ignored and the
- * table is populated with mounted filesystems.  If project_count is
- * zero, projects is ignored and the table is populated with all
- * projects defined in the projects file.
- */
-void
-fs_table_initialise(
-	int	mount_count,
-	char	*mounts[],
-	int	project_count,
-	char	*projects[])
-{
-	int	error;
-	int	i;
-
-	if (mount_count) {
-		for (i = 0; i < mount_count; i++)
-			fs_table_insert_mount(mounts[i]);
-	} else {
-		error = fs_table_initialise_mounts(NULL);
-		if (error)
-			goto out_error;
-	}
-	if (project_count) {
-		for (i = 0; i < project_count; i++)
-			fs_table_insert_project(projects[i]);
-	} else {
-		error = fs_table_initialise_projects(NULL);
-		if (error)
-			goto out_error;
-	}
-
-	return;
-
-out_error:
-	fprintf(stderr, _("%s: cannot initialise path table: %s\n"),
-		progname, strerror(error));
-}
-
-void
-fs_table_insert_project_path(
-	char		*dir,
-	prid_t		prid)
-{
-	fs_path_t	*fs;
-	int		error = 0;
-
-	fs = fs_mount_point_from_path(dir);
-	if (fs)
-		error = fs_table_insert(dir, prid, FS_PROJECT_PATH,
-					fs->fs_name, NULL, NULL);
-	else
-		error = ENOENT;
-
-	if (error) {
-		fprintf(stderr, _("%s: cannot setup path for project dir %s: %s\n"),
-				progname, dir, strerror(error));
-		exit(1);
-	}
-}
diff --git a/libxcmd/projects.c b/libxcmd/projects.c
deleted file mode 100644
index c9e863d..0000000
--- a/libxcmd/projects.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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 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.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "project.h"
-
-#define PROJID		"/etc/projid"
-#define PROJECT_PATHS	"/etc/projects"
-char *projid_file;
-char *projects_file;
-
-static FILE *projects;
-static fs_project_t p;
-static char projects_buffer[512];
-
-static FILE *project_paths;
-static fs_project_path_t pp;
-static char project_paths_buffer[1024];
-
-void
-setprfiles(void)
-{
-	if (!projid_file)
-		projid_file = PROJID;
-	if (!projects_file)
-		projects_file = PROJECT_PATHS;
-}
-
-void
-setprent(void)
-{
-	setprfiles();
-	projects = fopen(projid_file, "r");
-}
-
-void
-setprpathent(void)
-{
-	setprfiles();
-	project_paths = fopen(projects_file, "r");
-}
-
-void
-endprent(void)
-{
-	if (projects)
-		fclose(projects);
-	projects = NULL;
-}
-
-void
-endprpathent(void)
-{
-	if (project_paths)
-		fclose(project_paths);
-	project_paths = NULL;
-}
-
-fs_project_t *
-getprent(void)
-{
-	char	*idstart, *idend;
-	size_t	size = sizeof(projects_buffer) - 1;
-
-	if (!projects)
-		return NULL;
-	for (;;) {
-		if (!fgets(projects_buffer, size, projects))
-			break;
-		/*
-		 * /etc/projid file format -- "name:id\n", ignore "^#..."
-		 */
-		if (projects_buffer[0] == '#')
-			continue;
-		idstart = strchr(projects_buffer, ':');
-		if (!idstart)
-			continue;
-		if ((idstart + 1) - projects_buffer >= size)
-			continue;
-		idend = strchr(idstart+1, ':');
-		if (idend)
-			*idend = '\0';
-		*idstart = '\0';
-		p.pr_prid = atoi(idstart+1);
-		p.pr_name = &projects_buffer[0];
-		return &p;
-	}
-
-	return NULL;
-}
-
-fs_project_t *
-getprnam(
-	char		*name)
-{
-	fs_project_t	*p = NULL;
-
-	setprent();
-	while ((p = getprent()) != NULL)
-		if (strcmp(p->pr_name, name) == 0)
-			break;
-	endprent();
-	return p;
-}
-
-fs_project_t *
-getprprid(
-	prid_t		prid)
-{
-	fs_project_t	*p = NULL;
-
-	setprent();
-	while ((p = getprent()) != NULL)
-		if (p->pr_prid == prid)
-			break;
-	endprent();
-	return p;
-}
-
-fs_project_path_t *
-getprpathent(void)
-{
-	char		*nmstart, *nmend;
-	size_t		size = sizeof(project_paths_buffer) - 1;
-
-	if (!project_paths)
-		return NULL;
-	for (;;) {
-		if (!fgets(project_paths_buffer, size, project_paths))
-			break;
-		/*
-		 * /etc/projects format -- "id:pathname\n", ignore "^#..."
-		 */
-		if (project_paths_buffer[0] == '#')
-			continue;
-		nmstart = strchr(project_paths_buffer, ':');
-		if (!nmstart)
-			continue;
-		if ((nmstart + 1) - project_paths_buffer >= size)
-			continue;
-		nmend = strchr(nmstart + 1, '\n');
-		if (nmend)
-			*nmend = '\0';
-		*nmstart = '\0';
-		pp.pp_pathname = nmstart + 1;
-		pp.pp_prid = atoi(&project_paths_buffer[0]);
-		return &pp;
-	}
-
-	return NULL;
-}
-
-
-int
-getprojid(
-	const char	*name,
-	int		fd,
-	prid_t		*projid)
-{
-	struct fsxattr	fsx;
-
-	if (xfsctl(name, fd, FS_IOC_FSGETXATTR, &fsx)) {
-		perror("FS_IOC_FSGETXATTR");
-		return -1;
-	}
-	*projid = fsx.fsx_projid;
-	return 0;
-}
-
-int
-setprojid(
-	const char	*name,
-	int		fd,
-	prid_t		projid)
-{
-	struct fsxattr	fsx;
-	int		error;
-
-	if ((error = xfsctl(name, fd, FS_IOC_FSGETXATTR, &fsx)) == 0) {
-		fsx.fsx_projid = projid;
-		error = xfsctl(name, fd, FS_IOC_FSSETXATTR, &fsx);
-	}
-	return error;
-}
diff --git a/quota/Makefile b/quota/Makefile
index 9c6411e..120af2e 100644
--- a/quota/Makefile
+++ b/quota/Makefile
@@ -14,8 +14,8 @@ CFILES += $(PKG_PLATFORM).c
 PCFILES = darwin.c freebsd.c irix.c linux.c
 LSRCFILES = $(shell echo $(PCFILES) | sed -e "s/$(PKG_PLATFORM).c//g")
 
-LLDLIBS = $(LIBXCMD)
-LTDEPENDENCIES = $(LIBXCMD)
+LLDLIBS = $(LIBXCMD) $(LIBFROG)
+LTDEPENDENCIES = $(LIBXCMD) $(LIBFROG)
 LLDFLAGS = -static
 
 ifeq ($(ENABLE_READLINE),yes)
diff --git a/spaceman/Makefile b/spaceman/Makefile
index 95ec3c0..8b31030 100644
--- a/spaceman/Makefile
+++ b/spaceman/Makefile
@@ -9,8 +9,8 @@ LTCOMMAND = xfs_spaceman
 HFILES = init.h space.h
 CFILES = init.c file.c prealloc.c trim.c
 
-LLDLIBS = $(LIBXCMD)
-LTDEPENDENCIES = $(LIBXCMD)
+LLDLIBS = $(LIBXCMD) $(LIBFROG)
+LTDEPENDENCIES = $(LIBXCMD) $(LIBFROG)
 LLDFLAGS = -static
 
 ifeq ($(ENABLE_READLINE),yes)


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 10/12] libfrog: add missing function fs_table_destroy
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
                   ` (8 preceding siblings ...)
  2017-11-17 20:14 ` [PATCH 09/12] libfrog: move paths.c " Darrick J. Wong
@ 2017-11-17 20:14 ` Darrick J. Wong
  2017-11-17 20:14 ` [PATCH 11/12] libhandle: add missing destructor Darrick J. Wong
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:14 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Add a function to tear down the fs_table when we're done messing with paths.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 libfrog/paths.c |   20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)


diff --git a/libfrog/paths.c b/libfrog/paths.c
index b767e9d..62b4eda 100644
--- a/libfrog/paths.c
+++ b/libfrog/paths.c
@@ -206,6 +206,26 @@ fs_table_insert(
 	return error;
 }
 
+/* Remove all the cached entries in the fs table. */
+void
+fs_table_destroy(void)
+{
+	int		i;
+	struct fs_path	*fsp;
+
+	for (i = 0, fsp = fs_table; i < fs_count; i++, fsp++) {
+		free(fsp->fs_name);
+		free(fsp->fs_dir);
+		free(fsp->fs_log);
+		free(fsp->fs_rt);
+	}
+
+	fs_count = 0;
+	xfs_fs_count = 0;
+	free(fs_table);
+	fs_table = NULL;
+}
+
 /*
  * Table iteration (cursor-based) interfaces
  */


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 11/12] libhandle: add missing destructor
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
                   ` (9 preceding siblings ...)
  2017-11-17 20:14 ` [PATCH 10/12] libfrog: add missing function fs_table_destroy Darrick J. Wong
@ 2017-11-17 20:14 ` Darrick J. Wong
  2017-11-17 20:14 ` [PATCH 12/12] xfs_repair: remove old workqueue implementation in favor of libfrog code Darrick J. Wong
  2017-12-04 23:44 ` [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Eric Sandeen
  12 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:14 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Make it so that we can tear down the file descriptor hash table.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 include/handle.h   |    2 ++
 libhandle/handle.c |   13 +++++++++++++
 2 files changed, 15 insertions(+)


diff --git a/include/handle.h b/include/handle.h
index ee69a11..49f1441 100644
--- a/include/handle.h
+++ b/include/handle.h
@@ -50,6 +50,8 @@ extern int  parentpaths_by_handle(void *__hanp, size_t __hlen,
 extern int  fssetdm_by_handle (void *__hanp, size_t __hlen,
 			       struct fsdmidata *__fsdmi);
 
+void fshandle_destroy(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libhandle/handle.c b/libhandle/handle.c
index 00127b3..e6971dd 100644
--- a/libhandle/handle.c
+++ b/libhandle/handle.c
@@ -62,6 +62,19 @@ struct fdhash {
 
 static struct fdhash *fdhash_head = NULL;
 
+void
+fshandle_destroy(void)
+{
+	struct fdhash	*nexth;
+	struct fdhash	*h = fdhash_head;
+
+	while (h) {
+		nexth = h->fnxt;
+		free(h);
+		h = nexth;
+	}
+}
+
 int
 path_to_fshandle(
 	char		*path,		/* input,  path to convert */


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 12/12] xfs_repair: remove old workqueue implementation in favor of libfrog code
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
                   ` (10 preceding siblings ...)
  2017-11-17 20:14 ` [PATCH 11/12] libhandle: add missing destructor Darrick J. Wong
@ 2017-11-17 20:14 ` Darrick J. Wong
  2017-12-04 23:44 ` [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Eric Sandeen
  12 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-17 20:14 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Now that we've made a generic workqueue in libfrog, we can remove the
implementation in xfs_repair and turn the old functions into do_error
wrappers.  There are no functional changes in this patch, though some of
the names and types have changed.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 repair/phase3.c   |   16 +++----
 repair/phase4.c   |   26 ++++++-----
 repair/phase6.c   |    4 +-
 repair/phase7.c   |    9 ++--
 repair/prefetch.c |   20 ++++----
 repair/prefetch.h |    4 +-
 repair/scan.c     |   16 +++----
 repair/slab.c     |    4 +-
 repair/threads.c  |  125 +++++++----------------------------------------------
 repair/threads.h  |   38 +++-------------
 10 files changed, 74 insertions(+), 188 deletions(-)


diff --git a/repair/phase3.c b/repair/phase3.c
index 17b1c28..8ebe1ae 100644
--- a/repair/phase3.c
+++ b/repair/phase3.c
@@ -66,7 +66,7 @@ process_agi_unlinked(
 
 static void
 process_ag_func(
-	work_queue_t		*wq,
+	struct workqueue	*wq,
 	xfs_agnumber_t 		agno,
 	void			*arg)
 {
@@ -76,7 +76,7 @@ process_ag_func(
 	 */
 	wait_for_inode_prefetch(arg);
 	do_log(_("        - agno = %d\n"), agno);
-	process_aginodes(wq->mp, arg, agno, 1, 0, 1);
+	process_aginodes(wq->wq_ctx, arg, agno, 1, 0, 1);
 	blkmap_free_final();
 	cleanup_inode_prefetch(arg);
 }
@@ -90,13 +90,13 @@ process_ags(
 
 static void
 do_uncertain_aginodes(
-	work_queue_t	*wq,
-	xfs_agnumber_t	agno,
-	void		*arg)
+	struct workqueue	*wq,
+	xfs_agnumber_t		agno,
+	void			*arg)
 {
-	int		*count = arg;
+	int			*count = arg;
 
-	*count = process_uncertain_aginodes(wq->mp, agno);
+	*count = process_uncertain_aginodes(wq->wq_ctx, agno);
 
 #ifdef XR_INODE_TRACE
 	fprintf(stderr,
@@ -114,7 +114,7 @@ phase3(
 {
 	int			i, j;
 	int			*counts;
-	work_queue_t		wq;
+	struct workqueue	wq;
 
 	do_log(_("Phase 3 - for each AG...\n"));
 	if (!no_modify)
diff --git a/repair/phase4.c b/repair/phase4.c
index cc17ec0..0a02b7d 100644
--- a/repair/phase4.c
+++ b/repair/phase4.c
@@ -134,13 +134,13 @@ quota_sb_check(xfs_mount_t *mp)
 
 static void
 process_ag_func(
-	work_queue_t		*wq,
+	struct workqueue	*wq,
 	xfs_agnumber_t 		agno,
 	void			*arg)
 {
 	wait_for_inode_prefetch(arg);
 	do_log(_("        - agno = %d\n"), agno);
-	process_aginodes(wq->mp, arg, agno, 0, 1, 0);
+	process_aginodes(wq->wq_ctx, arg, agno, 0, 1, 0);
 	blkmap_free_final();
 	cleanup_inode_prefetch(arg);
 
@@ -169,23 +169,23 @@ _("unable to finish adding attr/data fork reverse-mapping data for AG %u.\n"),
 
 static void
 check_rmap_btrees(
-	work_queue_t	*wq,
+	struct workqueue*wq,
 	xfs_agnumber_t	agno,
 	void		*arg)
 {
 	int		error;
 
-	error = rmap_add_fixed_ag_rec(wq->mp, agno);
+	error = rmap_add_fixed_ag_rec(wq->wq_ctx, agno);
 	if (error)
 		do_error(
 _("unable to add AG %u metadata reverse-mapping data.\n"), agno);
 
-	error = rmap_fold_raw_recs(wq->mp, agno);
+	error = rmap_fold_raw_recs(wq->wq_ctx, agno);
 	if (error)
 		do_error(
 _("unable to merge AG %u metadata reverse-mapping data.\n"), agno);
 
-	error = rmaps_verify_btree(wq->mp, agno);
+	error = rmaps_verify_btree(wq->wq_ctx, agno);
 	if (error)
 		do_error(
 _("%s while checking reverse-mappings"),
@@ -194,13 +194,13 @@ _("%s while checking reverse-mappings"),
 
 static void
 compute_ag_refcounts(
-	work_queue_t	*wq,
+	struct workqueue*wq,
 	xfs_agnumber_t	agno,
 	void		*arg)
 {
 	int		error;
 
-	error = compute_refcounts(wq->mp, agno);
+	error = compute_refcounts(wq->wq_ctx, agno);
 	if (error)
 		do_error(
 _("%s while computing reference count records.\n"),
@@ -209,13 +209,13 @@ _("%s while computing reference count records.\n"),
 
 static void
 process_inode_reflink_flags(
-	struct work_queue	*wq,
+	struct workqueue	*wq,
 	xfs_agnumber_t		agno,
 	void			*arg)
 {
 	int			error;
 
-	error = fix_inode_reflink_flags(wq->mp, agno);
+	error = fix_inode_reflink_flags(wq->wq_ctx, agno);
 	if (error)
 		do_error(
 _("%s while fixing inode reflink flags.\n"),
@@ -224,13 +224,13 @@ _("%s while fixing inode reflink flags.\n"),
 
 static void
 check_refcount_btrees(
-	work_queue_t	*wq,
+	struct workqueue*wq,
 	xfs_agnumber_t	agno,
 	void		*arg)
 {
 	int		error;
 
-	error = check_refcounts(wq->mp, agno);
+	error = check_refcounts(wq->wq_ctx, agno);
 	if (error)
 		do_error(
 _("%s while checking reference counts"),
@@ -241,7 +241,7 @@ static void
 process_rmap_data(
 	struct xfs_mount	*mp)
 {
-	struct work_queue	wq;
+	struct workqueue	wq;
 	xfs_agnumber_t		i;
 
 	if (!rmap_needs_work(mp))
diff --git a/repair/phase6.c b/repair/phase6.c
index f3b8378..b326929 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -3125,7 +3125,7 @@ check_for_orphaned_inodes(
 
 static void
 traverse_function(
-	work_queue_t		*wq,
+	struct workqueue	*wq,
 	xfs_agnumber_t 		agno,
 	void			*arg)
 {
@@ -3154,7 +3154,7 @@ traverse_function(
 
 		for (i = 0; i < XFS_INODES_PER_CHUNK; i++)  {
 			if (inode_isadir(irec, i))
-				process_dir_inode(wq->mp, agno, irec, i);
+				process_dir_inode(wq->wq_ctx, agno, irec, i);
 		}
 	}
 	cleanup_inode_prefetch(pf_args);
diff --git a/repair/phase7.c b/repair/phase7.c
index 4ffb81a..b495ec2 100644
--- a/repair/phase7.c
+++ b/repair/phase7.c
@@ -98,10 +98,11 @@ update_inode_nlinks(
  */
 static void
 do_link_updates(
-	struct work_queue	*wq,
+	struct workqueue	*wq,
 	xfs_agnumber_t		agno,
 	void			*arg)
 {
+	struct xfs_mount	*mp = wq->wq_ctx;
 	ino_tree_node_t		*irec;
 	int			j;
 	uint32_t		nrefs;
@@ -120,8 +121,8 @@ do_link_updates(
 			ASSERT(no_modify || nrefs > 0);
 
 			if (get_inode_disk_nlinks(irec, j) != nrefs)
-				update_inode_nlinks(wq->mp,
-					XFS_AGINO_TO_INO(wq->mp, agno,
+				update_inode_nlinks(wq->wq_ctx,
+					XFS_AGINO_TO_INO(mp, agno,
 						irec->ino_startnum + j),
 					nrefs);
 		}
@@ -135,7 +136,7 @@ phase7(
 	struct xfs_mount	*mp,
 	int			scan_threads)
 {
-	struct work_queue	wq;
+	struct workqueue	wq;
 	int			agno;
 
 	if (!no_modify)
diff --git a/repair/prefetch.c b/repair/prefetch.c
index 4c74b6e..9c68e35 100644
--- a/repair/prefetch.c
+++ b/repair/prefetch.c
@@ -943,11 +943,11 @@ start_inode_prefetch(
  */
 static void
 prefetch_ag_range(
-	struct work_queue	*work,
+	struct workqueue	*work,
 	xfs_agnumber_t		start_ag,
 	xfs_agnumber_t		end_ag,
 	bool			dirs_only,
-	void			(*func)(struct work_queue *,
+	void			(*func)(struct workqueue *,
 					xfs_agnumber_t, void *))
 {
 	int			i;
@@ -967,12 +967,12 @@ struct pf_work_args {
 	xfs_agnumber_t	start_ag;
 	xfs_agnumber_t	end_ag;
 	bool		dirs_only;
-	void		(*func)(struct work_queue *, xfs_agnumber_t, void *);
+	void		(*func)(struct workqueue *, xfs_agnumber_t, void *);
 };
 
 static void
 prefetch_ag_range_work(
-	struct work_queue	*work,
+	struct workqueue	*work,
 	xfs_agnumber_t		unused,
 	void			*args)
 {
@@ -991,14 +991,14 @@ void
 do_inode_prefetch(
 	struct xfs_mount	*mp,
 	int			stride,
-	void			(*func)(struct work_queue *,
+	void			(*func)(struct workqueue *,
 					xfs_agnumber_t, void *),
 	bool			check_cache,
 	bool			dirs_only)
 {
 	int			i;
-	struct work_queue	queue;
-	struct work_queue	*queues;
+	struct workqueue	queue;
+	struct workqueue	*queues;
 	int			queues_started = 0;
 
 	/*
@@ -1008,7 +1008,7 @@ do_inode_prefetch(
 	 * CPU to maximise parallelism of the queue to be processed.
 	 */
 	if (check_cache && !libxfs_bcache_overflowed()) {
-		queue.mp = mp;
+		queue.wq_ctx = mp;
 		create_work_queue(&queue, mp, libxfs_nproc());
 		for (i = 0; i < mp->m_sb.sb_agcount; i++)
 			queue_work(&queue, func, i, NULL);
@@ -1021,7 +1021,7 @@ do_inode_prefetch(
 	 * directly after each AG is queued.
 	 */
 	if (!stride) {
-		queue.mp = mp;
+		queue.wq_ctx = mp;
 		prefetch_ag_range(&queue, 0, mp->m_sb.sb_agcount,
 				  dirs_only, func);
 		return;
@@ -1030,7 +1030,7 @@ do_inode_prefetch(
 	/*
 	 * create one worker thread for each segment of the volume
 	 */
-	queues = malloc(thread_count * sizeof(work_queue_t));
+	queues = malloc(thread_count * sizeof(struct workqueue));
 	for (i = 0; i < thread_count; i++) {
 		struct pf_work_args *wargs;
 
diff --git a/repair/prefetch.h b/repair/prefetch.h
index b837752..8652707 100644
--- a/repair/prefetch.h
+++ b/repair/prefetch.h
@@ -4,7 +4,7 @@
 #include <semaphore.h>
 #include "incore.h"
 
-struct work_queue;
+struct workqueue;
 
 extern int 	do_prefetch;
 
@@ -45,7 +45,7 @@ void
 do_inode_prefetch(
 	struct xfs_mount	*mp,
 	int			stride,
-	void			(*func)(struct work_queue *,
+	void			(*func)(struct workqueue *,
 					xfs_agnumber_t, void *),
 	bool			check_cache,
 	bool			dirs_only);
diff --git a/repair/scan.c b/repair/scan.c
index 22c7331..e4ac4a7 100644
--- a/repair/scan.c
+++ b/repair/scan.c
@@ -2342,7 +2342,7 @@ validate_agi(
  */
 static void
 scan_ag(
-	work_queue_t	*wq,
+	struct workqueue*wq,
 	xfs_agnumber_t	agno,
 	void		*arg)
 {
@@ -2504,13 +2504,13 @@ scan_ags(
 	struct xfs_mount	*mp,
 	int			scan_threads)
 {
-	struct aghdr_cnts *agcnts;
-	uint64_t	fdblocks = 0;
-	uint64_t	icount = 0;
-	uint64_t	ifreecount = 0;
-	uint64_t	usedblocks = 0;
-	xfs_agnumber_t	i;
-	work_queue_t	wq;
+	struct aghdr_cnts	*agcnts;
+	uint64_t		fdblocks = 0;
+	uint64_t		icount = 0;
+	uint64_t		ifreecount = 0;
+	uint64_t		usedblocks = 0;
+	xfs_agnumber_t		i;
+	struct workqueue	wq;
 
 	agcnts = malloc(mp->m_sb.sb_agcount * sizeof(*agcnts));
 	if (!agcnts) {
diff --git a/repair/slab.c b/repair/slab.c
index d47448a..b04c3b8 100644
--- a/repair/slab.c
+++ b/repair/slab.c
@@ -211,7 +211,7 @@ struct qsort_slab {
 
 static void
 qsort_slab_helper(
-	struct work_queue	*wq,
+	struct workqueue	*wq,
 	xfs_agnumber_t		agno,
 	void			*arg)
 {
@@ -231,7 +231,7 @@ qsort_slab(
 	struct xfs_slab		*slab,
 	int (*compare_fn)(const void *, const void *))
 {
-	struct work_queue	wq;
+	struct workqueue	wq;
 	struct xfs_slab_hdr	*hdr;
 	struct qsort_slab	*qs;
 
diff --git a/repair/threads.c b/repair/threads.c
index 631531f..7a7f748 100644
--- a/repair/threads.c
+++ b/repair/threads.c
@@ -6,50 +6,6 @@
 #include "protos.h"
 #include "globals.h"
 
-static void *
-worker_thread(void *arg)
-{
-	work_queue_t	*wq;
-	work_item_t	*wi;
-
-	wq = (work_queue_t*)arg;
-
-	/*
-	 * Loop pulling work from the passed in work queue.
-	 * Check for notification to exit after every chunk of work.
-	 */
-	while (1) {
-		pthread_mutex_lock(&wq->lock);
-
-		/*
-		 * Wait for work.
-		 */
-		while (wq->next_item == NULL && !wq->terminate) {
-			ASSERT(wq->item_count == 0);
-			pthread_cond_wait(&wq->wakeup, &wq->lock);
-		}
-		if (wq->next_item == NULL && wq->terminate) {
-			pthread_mutex_unlock(&wq->lock);
-			break;
-		}
-
-		/*
-		 *  Dequeue work from the head of the list.
-		 */
-		ASSERT(wq->item_count > 0);
-		wi = wq->next_item;
-		wq->next_item = wi->next;
-		wq->item_count--;
-
-		pthread_mutex_unlock(&wq->lock);
-
-		(wi->function)(wi->queue, wi->agno, wi->arg);
-		free(wi);
-	}
-
-	return NULL;
-}
-
 void
 thread_init(void)
 {
@@ -67,85 +23,36 @@ thread_init(void)
 
 void
 create_work_queue(
-	work_queue_t		*wq,
-	xfs_mount_t		*mp,
-	int			nworkers)
+	struct workqueue	*wq,
+	struct xfs_mount	*mp,
+	unsigned int		nworkers)
 {
 	int			err;
-	int			i;
-
-	memset(wq, 0, sizeof(work_queue_t));
 
-	pthread_cond_init(&wq->wakeup, NULL);
-	pthread_mutex_init(&wq->lock, NULL);
-
-	wq->mp = mp;
-	wq->thread_count = nworkers;
-	wq->threads = malloc(nworkers * sizeof(pthread_t));
-	wq->terminate = 0;
-
-	for (i = 0; i < nworkers; i++) {
-		err = pthread_create(&wq->threads[i], NULL, worker_thread, wq);
-		if (err != 0) {
-			do_error(_("cannot create worker threads, error = [%d] %s\n"),
+	err = workqueue_create(wq, mp, nworkers);
+	if (err)
+		do_error(_("cannot create worker threads, error = [%d] %s\n"),
 				err, strerror(err));
-		}
-	}
-
 }
 
 void
 queue_work(
-	work_queue_t	*wq,
-	work_func_t	func,
-	xfs_agnumber_t	agno,
-	void		*arg)
+	struct workqueue	*wq,
+	workqueue_func_t	func,
+	xfs_agnumber_t		agno,
+	void			*arg)
 {
-	work_item_t	*wi;
+	int			err;
 
-	wi = (work_item_t *)malloc(sizeof(work_item_t));
-	if (wi == NULL)
+	err = workqueue_add(wq, func, agno, arg);
+	if (err)
 		do_error(_("cannot allocate worker item, error = [%d] %s\n"),
-			errno, strerror(errno));
-
-	wi->function = func;
-	wi->agno = agno;
-	wi->arg = arg;
-	wi->queue = wq;
-	wi->next = NULL;
-
-	/*
-	 *  Now queue the new work structure to the work queue.
-	 */
-	pthread_mutex_lock(&wq->lock);
-	if (wq->next_item == NULL) {
-		wq->next_item = wi;
-		ASSERT(wq->item_count == 0);
-		pthread_cond_signal(&wq->wakeup);
-	} else {
-		wq->last_item->next = wi;
-	}
-	wq->last_item = wi;
-	wq->item_count++;
-	pthread_mutex_unlock(&wq->lock);
+				err, strerror(err));
 }
 
 void
 destroy_work_queue(
-	work_queue_t	*wq)
+	struct workqueue	*wq)
 {
-	int		i;
-
-	pthread_mutex_lock(&wq->lock);
-	wq->terminate = 1;
-	pthread_mutex_unlock(&wq->lock);
-
-	pthread_cond_broadcast(&wq->wakeup);
-
-	for (i = 0; i < wq->thread_count; i++)
-		pthread_join(wq->threads[i], NULL);
-
-	free(wq->threads);
-	pthread_mutex_destroy(&wq->lock);
-	pthread_cond_destroy(&wq->wakeup);
+	workqueue_destroy(wq);
 }
diff --git a/repair/threads.h b/repair/threads.h
index bb0b8f8..fce520a 100644
--- a/repair/threads.h
+++ b/repair/threads.h
@@ -1,47 +1,25 @@
 #ifndef	_XFS_REPAIR_THREADS_H_
 #define	_XFS_REPAIR_THREADS_H_
 
-void	thread_init(void);
-
-struct  work_queue;
-
-typedef void work_func_t(struct work_queue *, xfs_agnumber_t, void *);
+#include "workqueue.h"
 
-typedef struct work_item {
-	struct work_item	*next;
-	work_func_t		*function;
-	struct work_queue	*queue;
-	xfs_agnumber_t		agno;
-	void			*arg;
-} work_item_t;
-
-typedef struct  work_queue {
-	work_item_t		*next_item;
-	work_item_t		*last_item;
-	int			item_count;
-	int			thread_count;
-	pthread_t		*threads;
-	xfs_mount_t		*mp;
-	pthread_mutex_t		lock;
-	pthread_cond_t		wakeup;
-	int			terminate;
-} work_queue_t;
+void	thread_init(void);
 
 void
 create_work_queue(
-	work_queue_t		*wq,
-	xfs_mount_t		*mp,
-	int			nworkers);
+	struct workqueue	*wq,
+	struct xfs_mount	*mp,
+	unsigned int		nworkers);
 
 void
 queue_work(
-	work_queue_t		*wq,
-	work_func_t 		func,
+	struct workqueue	*wq,
+	workqueue_func_t	func,
 	xfs_agnumber_t 		agno,
 	void			*arg);
 
 void
 destroy_work_queue(
-	work_queue_t		*wq);
+	struct workqueue	*wq);
 
 #endif	/* _XFS_REPAIR_THREADS_H_ */


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 08/12] libfrog: move conversion factors out of libxcmd
  2017-11-17 20:14 ` [PATCH 08/12] libfrog: move conversion factors " Darrick J. Wong
@ 2017-11-27 21:47   ` Eric Sandeen
  2017-11-28  0:31     ` Darrick J. Wong
  2017-11-28  0:45   ` [PATCH v2 " Darrick J. Wong
  2017-11-28  5:59   ` [PATCH v3 " Darrick J. Wong
  2 siblings, 1 reply; 20+ messages in thread
From: Eric Sandeen @ 2017-11-27 21:47 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 11/17/17 2:14 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Move all the conversion functions out of libxcmd since they'll be used
> by scrub, which doesn't have a commandline.

This doesn't actually move anything, it just duplicates it?

-Eric



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 08/12] libfrog: move conversion factors out of libxcmd
  2017-11-27 21:47   ` Eric Sandeen
@ 2017-11-28  0:31     ` Darrick J. Wong
  0 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-28  0:31 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: sandeen, linux-xfs

On Mon, Nov 27, 2017 at 03:47:46PM -0600, Eric Sandeen wrote:
> On 11/17/17 2:14 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Move all the conversion functions out of libxcmd since they'll be used
> > by scrub, which doesn't have a commandline.
> 
> This doesn't actually move anything, it just duplicates it?

rrrgh, the libxcmd/input.c changes fell off... :(

Lemme resend.

--D

> -Eric
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v2 08/12] libfrog: move conversion factors out of libxcmd
  2017-11-17 20:14 ` [PATCH 08/12] libfrog: move conversion factors " Darrick J. Wong
  2017-11-27 21:47   ` Eric Sandeen
@ 2017-11-28  0:45   ` Darrick J. Wong
  2017-11-28  5:59   ` [PATCH v3 " Darrick J. Wong
  2 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-28  0:45 UTC (permalink / raw)
  To: sandeen; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Move all the conversion functions out of libxcmd since they'll be used
by scrub, which doesn't have a commandline.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: actually include all of the patch <cough>
---
 include/convert.h |   39 +++++
 include/input.h   |   15 --
 libfrog/Makefile  |    1 
 libfrog/convert.c |  386 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 libxcmd/input.c   |  375 ---------------------------------------------------
 quota/Makefile    |    4 -
 spaceman/Makefile |    4 -
 7 files changed, 431 insertions(+), 393 deletions(-)
 create mode 100644 include/convert.h
 create mode 100644 libfrog/convert.c

diff --git a/include/convert.h b/include/convert.h
new file mode 100644
index 0000000..cff9778
--- /dev/null
+++ b/include/convert.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __CONVERT_H__
+#define __CONVERT_H__
+
+extern int64_t	cvt_s64(char *s, int base);
+extern int32_t	cvt_s32(char *s, int base);
+extern int16_t	cvt_s16(char *s, int base);
+
+extern uint64_t	cvt_u64(char *s, int base);
+extern uint32_t	cvt_u32(char *s, int base);
+extern uint16_t	cvt_u16(char *s, int base);
+
+extern long long cvtnum(size_t blocksize, size_t sectorsize, char *s);
+extern void cvtstr(double value, char *str, size_t sz);
+extern unsigned long cvttime(char *s);
+
+extern uid_t	uid_from_string(char *user);
+extern gid_t	gid_from_string(char *group);
+extern prid_t	prid_from_string(char *project);
+
+#endif	/* __CONVERT_H__ */
diff --git a/include/input.h b/include/input.h
index 145114b..fe107b9 100644
--- a/include/input.h
+++ b/include/input.h
@@ -22,24 +22,14 @@
 #include <grp.h>
 #include <sys/types.h>
 #include "project.h"
+#include "convert.h"
 #include <stdbool.h>
 
 extern char	**breakline(char *input, int *count);
 extern void	doneline(char *input, char **vec);
 extern char	*fetchline(void);
 
-extern int64_t	cvt_s64(char *s, int base);
-extern int32_t	cvt_s32(char *s, int base);
-extern int16_t	cvt_s16(char *s, int base);
-
-extern uint64_t	cvt_u64(char *s, int base);
-extern uint32_t	cvt_u32(char *s, int base);
-extern uint16_t	cvt_u16(char *s, int base);
-
 extern size_t numlen(uint64_t val, size_t base);
-extern long long cvtnum(size_t blocksize, size_t sectorsize, char *s);
-extern void	cvtstr(double value, char *str, size_t sz);
-extern unsigned long cvttime(char *s);
 
 extern struct timeval tadd(struct timeval t1, struct timeval t2);
 extern struct timeval tsub(struct timeval t1, struct timeval t2);
@@ -53,9 +43,6 @@ enum {
 
 extern void	timestr(struct timeval *tv, char *str, size_t sz, int flags);
 
-extern uid_t	uid_from_string(char *user);
-extern gid_t	gid_from_string(char *group);
-extern prid_t	prid_from_string(char *project);
 extern bool	isdigits_only(const char *str);
 extern int	timespec_from_string(const char *sec, const char *nsec, struct timespec *ts);
 
diff --git a/libfrog/Makefile b/libfrog/Makefile
index bffc346..467362c 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -12,6 +12,7 @@ LT_AGE = 0
 
 CFILES = \
 avl64.c \
+convert.c \
 list_sort.c \
 radix-tree.c \
 topology.c \
diff --git a/libfrog/convert.c b/libfrog/convert.c
new file mode 100644
index 0000000..36e0f59
--- /dev/null
+++ b/libfrog/convert.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "platform_defs.h"
+#include "input.h"
+#include <ctype.h>
+#include <stdbool.h>
+
+size_t
+numlen(
+	uint64_t	val,
+	size_t		base)
+{
+	uint64_t	tmp;
+	size_t		len;
+
+	for (len = 0, tmp = val; tmp > 0; tmp = tmp / base)
+		len++;
+	return len == 0 ? 1 : len;
+}
+
+/*
+ * Convert string to int64_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+int64_t
+cvt_s64(
+	char		*s,
+	int		base)
+{
+	long long	i;
+	char		*sp;
+
+	errno = 0;
+	i = strtoll(s, &sp, base);
+	/*
+	 * If the input would over or underflow, return the clamped
+	 * value and let the user check errno.  If we went all the
+	 * way to the end of the input, return the converted value;
+	 * errno will be zero.
+	 */
+	if (errno || (*sp == '\0' && sp != s))
+		return i;
+
+	/* Not all the input was consumed, return error. */
+	errno = -ERANGE;
+	return INT64_MIN;
+}
+
+/*
+ * Convert string to int32_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+int32_t
+cvt_s32(
+	char		*s,
+	int		base)
+{
+	int64_t		i;
+
+	i = cvt_s64(s, base);
+	if (errno)
+		return i;
+	if (i > INT32_MAX || i < INT32_MIN) {
+		errno = -ERANGE;
+		return INT32_MIN;
+	}
+	return i;
+}
+
+/*
+ * Convert string to int16_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+int16_t
+cvt_s16(
+	char		*s,
+	int		base)
+{
+	int64_t		i;
+
+	i = cvt_s64(s, base);
+	if (errno)
+		return i;
+	if (i > INT16_MAX || i < INT16_MIN) {
+		errno = -ERANGE;
+		return INT16_MIN;
+	}
+	return i;
+}
+
+/*
+ * Convert string to uint64_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+uint64_t
+cvt_u64(
+	char		*s,
+	int		base)
+{
+	long long	i;
+	char		*sp;
+
+	errno = 0;
+	i = strtoll(s, &sp, base);
+	/*
+	 * If the input would over or underflow, return the clamped
+	 * value and let the user check errno.  If we went all the
+	 * way to the end of the input, return the converted value;
+	 * errno will be zero.
+	 */
+	if (errno || (*sp == '\0' && sp != s))
+		return i;
+
+	/* Not all the input was consumed, return error. */
+	errno = -ERANGE;
+	return UINT64_MAX;
+}
+
+/*
+ * Convert string to uint32_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+uint32_t
+cvt_u32(
+	char		*s,
+	int		base)
+{
+	uint64_t	i;
+
+	i = cvt_u64(s, base);
+	if (errno)
+		return i;
+	if (i > UINT32_MAX) {
+		errno = -ERANGE;
+		return UINT32_MAX;
+	}
+	return i;
+}
+
+/*
+ * Convert string to uint16_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+uint16_t
+cvt_u16(
+	char		*s,
+	int		base)
+{
+	uint64_t	i;
+
+	i = cvt_u64(s, base);
+	if (errno)
+		return i;
+	if (i > UINT16_MAX) {
+		errno = -ERANGE;
+		return UINT16_MAX;
+	}
+	return i;
+}
+
+#define EXABYTES(x)	((long long)(x) << 60)
+#define PETABYTES(x)	((long long)(x) << 50)
+#define TERABYTES(x)	((long long)(x) << 40)
+#define GIGABYTES(x)	((long long)(x) << 30)
+#define MEGABYTES(x)	((long long)(x) << 20)
+#define KILOBYTES(x)	((long long)(x) << 10)
+
+long long
+cvtnum(
+	size_t		blocksize,
+	size_t		sectorsize,
+	char		*s)
+{
+	long long	i;
+	char		*sp;
+	int		c;
+
+	i = strtoll(s, &sp, 0);
+	if (i == 0 && sp == s)
+		return -1LL;
+	if (*sp == '\0')
+		return i;
+
+	if (sp[1] != '\0')
+		return -1LL;
+
+	c = tolower(*sp);
+	switch (c) {
+	case 'b':
+		return i * blocksize;
+	case 's':
+		return i * sectorsize;
+	case 'k':
+		return KILOBYTES(i);
+	case 'm':
+		return MEGABYTES(i);
+	case 'g':
+		return GIGABYTES(i);
+	case 't':
+		return TERABYTES(i);
+	case 'p':
+		return PETABYTES(i);
+	case 'e':
+		return  EXABYTES(i);
+	}
+	return -1LL;
+}
+
+#define TO_EXABYTES(x)	((x) / EXABYTES(1))
+#define TO_PETABYTES(x)	((x) / PETABYTES(1))
+#define TO_TERABYTES(x)	((x) / TERABYTES(1))
+#define TO_GIGABYTES(x)	((x) / GIGABYTES(1))
+#define TO_MEGABYTES(x)	((x) / MEGABYTES(1))
+#define TO_KILOBYTES(x)	((x) / KILOBYTES(1))
+
+void
+cvtstr(
+	double		value,
+	char		*str,
+	size_t		size)
+{
+	char		*fmt;
+	int		precise;
+
+	precise = ((double)value * 1000 == (double)(int)value * 1000);
+
+	if (value >= EXABYTES(1)) {
+		fmt = precise ? "%.f EiB" : "%.3f EiB";
+		snprintf(str, size, fmt, TO_EXABYTES(value));
+	} else if (value >= PETABYTES(1)) {
+		fmt = precise ? "%.f PiB" : "%.3f PiB";
+		snprintf(str, size, fmt, TO_PETABYTES(value));
+	} else if (value >= TERABYTES(1)) {
+		fmt = precise ? "%.f TiB" : "%.3f TiB";
+		snprintf(str, size, fmt, TO_TERABYTES(value));
+	} else if (value >= GIGABYTES(1)) {
+		fmt = precise ? "%.f GiB" : "%.3f GiB";
+		snprintf(str, size, fmt, TO_GIGABYTES(value));
+	} else if (value >= MEGABYTES(1)) {
+		fmt = precise ? "%.f MiB" : "%.3f MiB";
+		snprintf(str, size, fmt, TO_MEGABYTES(value));
+	} else if (value >= KILOBYTES(1)) {
+		fmt = precise ? "%.f KiB" : "%.3f KiB";
+		snprintf(str, size, fmt, TO_KILOBYTES(value));
+	} else {
+		snprintf(str, size, "%f bytes", value);
+	}
+}
+
+#define MINUTES_TO_SECONDS(m)	((m) * 60)
+#define HOURS_TO_SECONDS(h)	((h) * MINUTES_TO_SECONDS(60))
+#define DAYS_TO_SECONDS(d)	((d) * HOURS_TO_SECONDS(24))
+#define WEEKS_TO_SECONDS(w)	((w) * DAYS_TO_SECONDS(7))
+
+unsigned long
+cvttime(
+	char		*s)
+{
+	unsigned long	i;
+	char		*sp;
+
+	i = strtoul(s, &sp, 0);
+	if (i == 0 && sp == s)
+		return 0;
+	if (*sp == '\0')
+		return i;
+	if ((*sp == 'm' && sp[1] == '\0') ||
+	    (strcmp(sp, "minutes") == 0) ||
+	    (strcmp(sp, "minute") == 0))
+		return MINUTES_TO_SECONDS(i);
+	if ((*sp == 'h' && sp[1] == '\0') ||
+	    (strcmp(sp, "hours") == 0) ||
+	    (strcmp(sp, "hour") == 0))
+		return HOURS_TO_SECONDS(i);
+	if ((*sp == 'd' && sp[1] == '\0') ||
+	    (strcmp(sp, "days") == 0) ||
+	    (strcmp(sp, "day") == 0))
+		return DAYS_TO_SECONDS(i);
+	if ((*sp == 'w' && sp[1] == '\0') ||
+	    (strcmp(sp, "weeks") == 0) ||
+	    (strcmp(sp, "week") == 0))
+		return WEEKS_TO_SECONDS(i);
+	return 0;
+}
+
+/*
+ * Convert from arbitrary user strings into a numeric ID.
+ * If it's all numeric, we convert that inplace, else we do
+ * the name lookup, and return the found identifier.
+ */
+
+prid_t
+prid_from_string(
+	char		*project)
+{
+	fs_project_t	*prj;
+	unsigned long	prid_long;
+	char		*sp;
+
+	/*
+	 * Allow either a full numeric or a valid projectname, even
+	 * if it starts with a digit.
+	 */
+	prid_long = strtoul(project, &sp, 10);
+	if (*project != '\0' && *sp == '\0') {
+		if ((prid_long == ULONG_MAX && errno == ERANGE)
+				|| (prid_long > (prid_t)-1))
+			return -1;
+		return (prid_t)prid_long;
+	}
+	prj = getprnam(project);
+	if (prj)
+		return prj->pr_prid;
+	return -1;
+}
+
+uid_t
+uid_from_string(
+	char		*user)
+{
+	struct passwd	*pwd;
+	unsigned long	uid_long;
+	char		*sp;
+
+	uid_long = strtoul(user, &sp, 10);
+	if (sp != user && *sp == '\0') {
+		if ((uid_long == ULONG_MAX && errno == ERANGE)
+				|| (uid_long > (uid_t)-1))
+			return -1;
+		return (uid_t)uid_long;
+	}
+	pwd = getpwnam(user);
+	if (pwd)
+		return pwd->pw_uid;
+	return -1;
+}
+
+gid_t
+gid_from_string(
+	char		*group)
+{
+	struct group	*grp;
+	unsigned long	gid_long;
+	char		*sp;
+
+	gid_long = strtoul(group, &sp, 10);
+	if (sp != group && *sp == '\0') {
+		if ((gid_long == ULONG_MAX && errno == ERANGE)
+				|| (gid_long > (gid_t)-1))
+			return -1;
+		return (gid_t)gid_long;
+	}
+	grp = getgrnam(group);
+	if (grp)
+		return grp->gr_gid;
+	return -1;
+}
diff --git a/libxcmd/input.c b/libxcmd/input.c
index 7a69dc1..441bb2f 100644
--- a/libxcmd/input.c
+++ b/libxcmd/input.c
@@ -136,308 +136,6 @@ doneline(
 	free(vec);
 }
 
-size_t
-numlen(
-	uint64_t	val,
-	size_t		base)
-{
-	uint64_t	tmp;
-	size_t		len;
-
-	for (len = 0, tmp = val; tmp > 0; tmp = tmp / base)
-		len++;
-	return len == 0 ? 1 : len;
-}
-
-/*
- * Convert string to int64_t, set errno if the conversion fails or
- * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
- * prior to conversion so you can check for bad inputs by examining
- * errno immediately after the call.
- */
-int64_t
-cvt_s64(
-	char		*s,
-	int		base)
-{
-	long long	i;
-	char		*sp;
-
-	errno = 0;
-	i = strtoll(s, &sp, base);
-	/*
-	 * If the input would over or underflow, return the clamped
-	 * value and let the user check errno.  If we went all the
-	 * way to the end of the input, return the converted value;
-	 * errno will be zero.
-	 */
-	if (errno || (*sp == '\0' && sp != s))
-		return i;
-
-	/* Not all the input was consumed, return error. */
-	errno = -ERANGE;
-	return INT64_MIN;
-}
-
-/*
- * Convert string to int32_t, set errno if the conversion fails or
- * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
- * prior to conversion so you can check for bad inputs by examining
- * errno immediately after the call.
- */
-int32_t
-cvt_s32(
-	char		*s,
-	int		base)
-{
-	int64_t		i;
-
-	i = cvt_s64(s, base);
-	if (errno)
-		return i;
-	if (i > INT32_MAX || i < INT32_MIN) {
-		errno = -ERANGE;
-		return INT32_MIN;
-	}
-	return i;
-}
-
-/*
- * Convert string to int16_t, set errno if the conversion fails or
- * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
- * prior to conversion so you can check for bad inputs by examining
- * errno immediately after the call.
- */
-int16_t
-cvt_s16(
-	char		*s,
-	int		base)
-{
-	int64_t		i;
-
-	i = cvt_s64(s, base);
-	if (errno)
-		return i;
-	if (i > INT16_MAX || i < INT16_MIN) {
-		errno = -ERANGE;
-		return INT16_MIN;
-	}
-	return i;
-}
-
-/*
- * Convert string to uint64_t, set errno if the conversion fails or
- * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
- * prior to conversion so you can check for bad inputs by examining
- * errno immediately after the call.
- */
-uint64_t
-cvt_u64(
-	char		*s,
-	int		base)
-{
-	long long	i;
-	char		*sp;
-
-	errno = 0;
-	i = strtoll(s, &sp, base);
-	/*
-	 * If the input would over or underflow, return the clamped
-	 * value and let the user check errno.  If we went all the
-	 * way to the end of the input, return the converted value;
-	 * errno will be zero.
-	 */
-	if (errno || (*sp == '\0' && sp != s))
-		return i;
-
-	/* Not all the input was consumed, return error. */
-	errno = -ERANGE;
-	return UINT64_MAX;
-}
-
-/*
- * Convert string to uint32_t, set errno if the conversion fails or
- * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
- * prior to conversion so you can check for bad inputs by examining
- * errno immediately after the call.
- */
-uint32_t
-cvt_u32(
-	char		*s,
-	int		base)
-{
-	uint64_t	i;
-
-	i = cvt_u64(s, base);
-	if (errno)
-		return i;
-	if (i > UINT32_MAX) {
-		errno = -ERANGE;
-		return UINT32_MAX;
-	}
-	return i;
-}
-
-/*
- * Convert string to uint16_t, set errno if the conversion fails or
- * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
- * prior to conversion so you can check for bad inputs by examining
- * errno immediately after the call.
- */
-uint16_t
-cvt_u16(
-	char		*s,
-	int		base)
-{
-	uint64_t	i;
-
-	i = cvt_u64(s, base);
-	if (errno)
-		return i;
-	if (i > UINT16_MAX) {
-		errno = -ERANGE;
-		return UINT16_MAX;
-	}
-	return i;
-}
-
-#define EXABYTES(x)	((long long)(x) << 60)
-#define PETABYTES(x)	((long long)(x) << 50)
-#define TERABYTES(x)	((long long)(x) << 40)
-#define GIGABYTES(x)	((long long)(x) << 30)
-#define MEGABYTES(x)	((long long)(x) << 20)
-#define KILOBYTES(x)	((long long)(x) << 10)
-
-long long
-cvtnum(
-	size_t		blocksize,
-	size_t		sectorsize,
-	char		*s)
-{
-	long long	i;
-	char		*sp;
-	int		c;
-
-	i = strtoll(s, &sp, 0);
-	if (i == 0 && sp == s)
-		return -1LL;
-	if (*sp == '\0')
-		return i;
-
-	if (sp[1] != '\0')
-		return -1LL;
-
-	c = tolower(*sp);
-	switch (c) {
-	case 'b':
-		return i * blocksize;
-	case 's':
-		return i * sectorsize;
-	case 'k':
-		return KILOBYTES(i);
-	case 'm':
-		return MEGABYTES(i);
-	case 'g':
-		return GIGABYTES(i);
-	case 't':
-		return TERABYTES(i);
-	case 'p':
-		return PETABYTES(i);
-	case 'e':
-		return  EXABYTES(i);
-	}
-	return -1LL;
-}
-
-#define TO_EXABYTES(x)	((x) / EXABYTES(1))
-#define TO_PETABYTES(x)	((x) / PETABYTES(1))
-#define TO_TERABYTES(x)	((x) / TERABYTES(1))
-#define TO_GIGABYTES(x)	((x) / GIGABYTES(1))
-#define TO_MEGABYTES(x)	((x) / MEGABYTES(1))
-#define TO_KILOBYTES(x)	((x) / KILOBYTES(1))
-
-void
-cvtstr(
-	double		value,
-	char		*str,
-	size_t		size)
-{
-	char		*fmt;
-	int		precise;
-
-	precise = ((double)value * 1000 == (double)(int)value * 1000);
-
-	if (value >= EXABYTES(1)) {
-		fmt = precise ? "%.f EiB" : "%.3f EiB";
-		snprintf(str, size, fmt, TO_EXABYTES(value));
-	} else if (value >= PETABYTES(1)) {
-		fmt = precise ? "%.f PiB" : "%.3f PiB";
-		snprintf(str, size, fmt, TO_PETABYTES(value));
-	} else if (value >= TERABYTES(1)) {
-		fmt = precise ? "%.f TiB" : "%.3f TiB";
-		snprintf(str, size, fmt, TO_TERABYTES(value));
-	} else if (value >= GIGABYTES(1)) {
-		fmt = precise ? "%.f GiB" : "%.3f GiB";
-		snprintf(str, size, fmt, TO_GIGABYTES(value));
-	} else if (value >= MEGABYTES(1)) {
-		fmt = precise ? "%.f MiB" : "%.3f MiB";
-		snprintf(str, size, fmt, TO_MEGABYTES(value));
-	} else if (value >= KILOBYTES(1)) {
-		fmt = precise ? "%.f KiB" : "%.3f KiB";
-		snprintf(str, size, fmt, TO_KILOBYTES(value));
-	} else {
-		snprintf(str, size, "%f bytes", value);
-	}
-}
-
-#define MINUTES_TO_SECONDS(m)	((m) * 60)
-#define HOURS_TO_SECONDS(h)	((h) * MINUTES_TO_SECONDS(60))
-#define DAYS_TO_SECONDS(d)	((d) * HOURS_TO_SECONDS(24))
-#define WEEKS_TO_SECONDS(w)	((w) * DAYS_TO_SECONDS(7))
-
-unsigned long
-cvttime(
-	char		*s)
-{
-	unsigned long	i;
-	char		*sp;
-
-	i = strtoul(s, &sp, 0);
-	if (i == 0 && sp == s)
-		return 0;
-	if (*sp == '\0')
-		return i;
-	if ((*sp == 'm' && sp[1] == '\0') ||
-	    (strcmp(sp, "minutes") == 0) ||
-	    (strcmp(sp, "minute") == 0))
-		return MINUTES_TO_SECONDS(i);
-	if ((*sp == 'h' && sp[1] == '\0') ||
-	    (strcmp(sp, "hours") == 0) ||
-	    (strcmp(sp, "hour") == 0))
-		return HOURS_TO_SECONDS(i);
-	if ((*sp == 'd' && sp[1] == '\0') ||
-	    (strcmp(sp, "days") == 0) ||
-	    (strcmp(sp, "day") == 0))
-		return DAYS_TO_SECONDS(i);
-	if ((*sp == 'w' && sp[1] == '\0') ||
-	    (strcmp(sp, "weeks") == 0) ||
-	    (strcmp(sp, "week") == 0))
-		return WEEKS_TO_SECONDS(i);
-	return 0;
-}
-
-struct timeval
-tadd(struct timeval t1, struct timeval t2)
-{
-	t1.tv_usec += t2.tv_usec;
-	if (t1.tv_usec > 1000000) {
-		t1.tv_usec -= 1000000;
-		t1.tv_sec++;
-	}
-	t1.tv_sec += t2.tv_sec;
-	return t1;
-}
-
 struct timeval
 tsub(struct timeval t1, struct timeval t2)
 {
@@ -513,79 +211,6 @@ timespec_from_string(
 	return 0;
 }
 
-/*
- * Convert from arbitrary user strings into a numeric ID.
- * If it's all numeric, we convert that inplace, else we do
- * the name lookup, and return the found identifier.
- */
-
-prid_t
-prid_from_string(
-	char		*project)
-{
-	fs_project_t	*prj;
-	unsigned long	prid_long;
-	char		*sp;
-
-	/*
-	 * Allow either a full numeric or a valid projectname, even
-	 * if it starts with a digit.
-	 */
-	prid_long = strtoul(project, &sp, 10);
-	if (*project != '\0' && *sp == '\0') {
-		if ((prid_long == ULONG_MAX && errno == ERANGE)
-				|| (prid_long > (prid_t)-1))
-			return -1;
-		return (prid_t)prid_long;
-	}
-	prj = getprnam(project);
-	if (prj)
-		return prj->pr_prid;
-	return -1;
-}
-
-uid_t
-uid_from_string(
-	char		*user)
-{
-	struct passwd	*pwd;
-	unsigned long	uid_long;
-	char		*sp;
-
-	uid_long = strtoul(user, &sp, 10);
-	if (sp != user && *sp == '\0') {
-		if ((uid_long == ULONG_MAX && errno == ERANGE)
-				|| (uid_long > (uid_t)-1))
-			return -1;
-		return (uid_t)uid_long;
-	}
-	pwd = getpwnam(user);
-	if (pwd)
-		return pwd->pw_uid;
-	return -1;
-}
-
-gid_t
-gid_from_string(
-	char		*group)
-{
-	struct group	*grp;
-	unsigned long	gid_long;
-	char		*sp;
-
-	gid_long = strtoul(group, &sp, 10);
-	if (sp != group && *sp == '\0') {
-		if ((gid_long == ULONG_MAX && errno == ERANGE)
-				|| (gid_long > (gid_t)-1))
-			return -1;
-		return (gid_t)gid_long;
-	}
-	grp = getgrnam(group);
-	if (grp)
-		return grp->gr_gid;
-	return -1;
-}
-
 bool isdigits_only(
 	const char *str)
 {
diff --git a/quota/Makefile b/quota/Makefile
index 9c6411e..120af2e 100644
--- a/quota/Makefile
+++ b/quota/Makefile
@@ -14,8 +14,8 @@ CFILES += $(PKG_PLATFORM).c
 PCFILES = darwin.c freebsd.c irix.c linux.c
 LSRCFILES = $(shell echo $(PCFILES) | sed -e "s/$(PKG_PLATFORM).c//g")
 
-LLDLIBS = $(LIBXCMD)
-LTDEPENDENCIES = $(LIBXCMD)
+LLDLIBS = $(LIBXCMD) $(LIBFROG)
+LTDEPENDENCIES = $(LIBXCMD) $(LIBFROG)
 LLDFLAGS = -static
 
 ifeq ($(ENABLE_READLINE),yes)
diff --git a/spaceman/Makefile b/spaceman/Makefile
index 95ec3c0..8b31030 100644
--- a/spaceman/Makefile
+++ b/spaceman/Makefile
@@ -9,8 +9,8 @@ LTCOMMAND = xfs_spaceman
 HFILES = init.h space.h
 CFILES = init.c file.c prealloc.c trim.c
 
-LLDLIBS = $(LIBXCMD)
-LTDEPENDENCIES = $(LIBXCMD)
+LLDLIBS = $(LIBXCMD) $(LIBFROG)
+LTDEPENDENCIES = $(LIBXCMD) $(LIBFROG)
 LLDFLAGS = -static
 
 ifeq ($(ENABLE_READLINE),yes)

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v3 08/12] libfrog: move conversion factors out of libxcmd
  2017-11-17 20:14 ` [PATCH 08/12] libfrog: move conversion factors " Darrick J. Wong
  2017-11-27 21:47   ` Eric Sandeen
  2017-11-28  0:45   ` [PATCH v2 " Darrick J. Wong
@ 2017-11-28  5:59   ` Darrick J. Wong
  2 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-28  5:59 UTC (permalink / raw)
  To: sandeen; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Move all the conversion functions out of libxcmd since they'll be used
by scrub, which doesn't have a commandline.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v3: fix copyright/licensing statements
v2: actually include all of the patch <cough>
---
 include/convert.h |   37 +++++
 include/input.h   |   15 --
 libfrog/Makefile  |    1 
 libfrog/convert.c |  384 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 libxcmd/input.c   |  375 ----------------------------------------------------
 quota/Makefile    |    4 -
 spaceman/Makefile |    4 -
 7 files changed, 427 insertions(+), 393 deletions(-)
 create mode 100644 include/convert.h
 create mode 100644 libfrog/convert.c

diff --git a/include/convert.h b/include/convert.h
new file mode 100644
index 0000000..bb75041
--- /dev/null
+++ b/include/convert.h
@@ -0,0 +1,37 @@
+/*
+ * 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 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __CONVERT_H__
+#define __CONVERT_H__
+
+extern int64_t	cvt_s64(char *s, int base);
+extern int32_t	cvt_s32(char *s, int base);
+extern int16_t	cvt_s16(char *s, int base);
+
+extern uint64_t	cvt_u64(char *s, int base);
+extern uint32_t	cvt_u32(char *s, int base);
+extern uint16_t	cvt_u16(char *s, int base);
+
+extern long long cvtnum(size_t blocksize, size_t sectorsize, char *s);
+extern void cvtstr(double value, char *str, size_t sz);
+extern unsigned long cvttime(char *s);
+
+extern uid_t	uid_from_string(char *user);
+extern gid_t	gid_from_string(char *group);
+extern prid_t	prid_from_string(char *project);
+
+#endif	/* __CONVERT_H__ */
diff --git a/include/input.h b/include/input.h
index 145114b..fe107b9 100644
--- a/include/input.h
+++ b/include/input.h
@@ -22,24 +22,14 @@
 #include <grp.h>
 #include <sys/types.h>
 #include "project.h"
+#include "convert.h"
 #include <stdbool.h>
 
 extern char	**breakline(char *input, int *count);
 extern void	doneline(char *input, char **vec);
 extern char	*fetchline(void);
 
-extern int64_t	cvt_s64(char *s, int base);
-extern int32_t	cvt_s32(char *s, int base);
-extern int16_t	cvt_s16(char *s, int base);
-
-extern uint64_t	cvt_u64(char *s, int base);
-extern uint32_t	cvt_u32(char *s, int base);
-extern uint16_t	cvt_u16(char *s, int base);
-
 extern size_t numlen(uint64_t val, size_t base);
-extern long long cvtnum(size_t blocksize, size_t sectorsize, char *s);
-extern void	cvtstr(double value, char *str, size_t sz);
-extern unsigned long cvttime(char *s);
 
 extern struct timeval tadd(struct timeval t1, struct timeval t2);
 extern struct timeval tsub(struct timeval t1, struct timeval t2);
@@ -53,9 +43,6 @@ enum {
 
 extern void	timestr(struct timeval *tv, char *str, size_t sz, int flags);
 
-extern uid_t	uid_from_string(char *user);
-extern gid_t	gid_from_string(char *group);
-extern prid_t	prid_from_string(char *project);
 extern bool	isdigits_only(const char *str);
 extern int	timespec_from_string(const char *sec, const char *nsec, struct timespec *ts);
 
diff --git a/libfrog/Makefile b/libfrog/Makefile
index bffc346..467362c 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -12,6 +12,7 @@ LT_AGE = 0
 
 CFILES = \
 avl64.c \
+convert.c \
 list_sort.c \
 radix-tree.c \
 topology.c \
diff --git a/libfrog/convert.c b/libfrog/convert.c
new file mode 100644
index 0000000..b36f77e
--- /dev/null
+++ b/libfrog/convert.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2003-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "platform_defs.h"
+#include "input.h"
+#include <ctype.h>
+#include <stdbool.h>
+
+size_t
+numlen(
+	uint64_t	val,
+	size_t		base)
+{
+	uint64_t	tmp;
+	size_t		len;
+
+	for (len = 0, tmp = val; tmp > 0; tmp = tmp / base)
+		len++;
+	return len == 0 ? 1 : len;
+}
+
+/*
+ * Convert string to int64_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+int64_t
+cvt_s64(
+	char		*s,
+	int		base)
+{
+	long long	i;
+	char		*sp;
+
+	errno = 0;
+	i = strtoll(s, &sp, base);
+	/*
+	 * If the input would over or underflow, return the clamped
+	 * value and let the user check errno.  If we went all the
+	 * way to the end of the input, return the converted value;
+	 * errno will be zero.
+	 */
+	if (errno || (*sp == '\0' && sp != s))
+		return i;
+
+	/* Not all the input was consumed, return error. */
+	errno = -ERANGE;
+	return INT64_MIN;
+}
+
+/*
+ * Convert string to int32_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+int32_t
+cvt_s32(
+	char		*s,
+	int		base)
+{
+	int64_t		i;
+
+	i = cvt_s64(s, base);
+	if (errno)
+		return i;
+	if (i > INT32_MAX || i < INT32_MIN) {
+		errno = -ERANGE;
+		return INT32_MIN;
+	}
+	return i;
+}
+
+/*
+ * Convert string to int16_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+int16_t
+cvt_s16(
+	char		*s,
+	int		base)
+{
+	int64_t		i;
+
+	i = cvt_s64(s, base);
+	if (errno)
+		return i;
+	if (i > INT16_MAX || i < INT16_MIN) {
+		errno = -ERANGE;
+		return INT16_MIN;
+	}
+	return i;
+}
+
+/*
+ * Convert string to uint64_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+uint64_t
+cvt_u64(
+	char		*s,
+	int		base)
+{
+	long long	i;
+	char		*sp;
+
+	errno = 0;
+	i = strtoll(s, &sp, base);
+	/*
+	 * If the input would over or underflow, return the clamped
+	 * value and let the user check errno.  If we went all the
+	 * way to the end of the input, return the converted value;
+	 * errno will be zero.
+	 */
+	if (errno || (*sp == '\0' && sp != s))
+		return i;
+
+	/* Not all the input was consumed, return error. */
+	errno = -ERANGE;
+	return UINT64_MAX;
+}
+
+/*
+ * Convert string to uint32_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+uint32_t
+cvt_u32(
+	char		*s,
+	int		base)
+{
+	uint64_t	i;
+
+	i = cvt_u64(s, base);
+	if (errno)
+		return i;
+	if (i > UINT32_MAX) {
+		errno = -ERANGE;
+		return UINT32_MAX;
+	}
+	return i;
+}
+
+/*
+ * Convert string to uint16_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+uint16_t
+cvt_u16(
+	char		*s,
+	int		base)
+{
+	uint64_t	i;
+
+	i = cvt_u64(s, base);
+	if (errno)
+		return i;
+	if (i > UINT16_MAX) {
+		errno = -ERANGE;
+		return UINT16_MAX;
+	}
+	return i;
+}
+
+#define EXABYTES(x)	((long long)(x) << 60)
+#define PETABYTES(x)	((long long)(x) << 50)
+#define TERABYTES(x)	((long long)(x) << 40)
+#define GIGABYTES(x)	((long long)(x) << 30)
+#define MEGABYTES(x)	((long long)(x) << 20)
+#define KILOBYTES(x)	((long long)(x) << 10)
+
+long long
+cvtnum(
+	size_t		blocksize,
+	size_t		sectorsize,
+	char		*s)
+{
+	long long	i;
+	char		*sp;
+	int		c;
+
+	i = strtoll(s, &sp, 0);
+	if (i == 0 && sp == s)
+		return -1LL;
+	if (*sp == '\0')
+		return i;
+
+	if (sp[1] != '\0')
+		return -1LL;
+
+	c = tolower(*sp);
+	switch (c) {
+	case 'b':
+		return i * blocksize;
+	case 's':
+		return i * sectorsize;
+	case 'k':
+		return KILOBYTES(i);
+	case 'm':
+		return MEGABYTES(i);
+	case 'g':
+		return GIGABYTES(i);
+	case 't':
+		return TERABYTES(i);
+	case 'p':
+		return PETABYTES(i);
+	case 'e':
+		return  EXABYTES(i);
+	}
+	return -1LL;
+}
+
+#define TO_EXABYTES(x)	((x) / EXABYTES(1))
+#define TO_PETABYTES(x)	((x) / PETABYTES(1))
+#define TO_TERABYTES(x)	((x) / TERABYTES(1))
+#define TO_GIGABYTES(x)	((x) / GIGABYTES(1))
+#define TO_MEGABYTES(x)	((x) / MEGABYTES(1))
+#define TO_KILOBYTES(x)	((x) / KILOBYTES(1))
+
+void
+cvtstr(
+	double		value,
+	char		*str,
+	size_t		size)
+{
+	char		*fmt;
+	int		precise;
+
+	precise = ((double)value * 1000 == (double)(int)value * 1000);
+
+	if (value >= EXABYTES(1)) {
+		fmt = precise ? "%.f EiB" : "%.3f EiB";
+		snprintf(str, size, fmt, TO_EXABYTES(value));
+	} else if (value >= PETABYTES(1)) {
+		fmt = precise ? "%.f PiB" : "%.3f PiB";
+		snprintf(str, size, fmt, TO_PETABYTES(value));
+	} else if (value >= TERABYTES(1)) {
+		fmt = precise ? "%.f TiB" : "%.3f TiB";
+		snprintf(str, size, fmt, TO_TERABYTES(value));
+	} else if (value >= GIGABYTES(1)) {
+		fmt = precise ? "%.f GiB" : "%.3f GiB";
+		snprintf(str, size, fmt, TO_GIGABYTES(value));
+	} else if (value >= MEGABYTES(1)) {
+		fmt = precise ? "%.f MiB" : "%.3f MiB";
+		snprintf(str, size, fmt, TO_MEGABYTES(value));
+	} else if (value >= KILOBYTES(1)) {
+		fmt = precise ? "%.f KiB" : "%.3f KiB";
+		snprintf(str, size, fmt, TO_KILOBYTES(value));
+	} else {
+		snprintf(str, size, "%f bytes", value);
+	}
+}
+
+#define MINUTES_TO_SECONDS(m)	((m) * 60)
+#define HOURS_TO_SECONDS(h)	((h) * MINUTES_TO_SECONDS(60))
+#define DAYS_TO_SECONDS(d)	((d) * HOURS_TO_SECONDS(24))
+#define WEEKS_TO_SECONDS(w)	((w) * DAYS_TO_SECONDS(7))
+
+unsigned long
+cvttime(
+	char		*s)
+{
+	unsigned long	i;
+	char		*sp;
+
+	i = strtoul(s, &sp, 0);
+	if (i == 0 && sp == s)
+		return 0;
+	if (*sp == '\0')
+		return i;
+	if ((*sp == 'm' && sp[1] == '\0') ||
+	    (strcmp(sp, "minutes") == 0) ||
+	    (strcmp(sp, "minute") == 0))
+		return MINUTES_TO_SECONDS(i);
+	if ((*sp == 'h' && sp[1] == '\0') ||
+	    (strcmp(sp, "hours") == 0) ||
+	    (strcmp(sp, "hour") == 0))
+		return HOURS_TO_SECONDS(i);
+	if ((*sp == 'd' && sp[1] == '\0') ||
+	    (strcmp(sp, "days") == 0) ||
+	    (strcmp(sp, "day") == 0))
+		return DAYS_TO_SECONDS(i);
+	if ((*sp == 'w' && sp[1] == '\0') ||
+	    (strcmp(sp, "weeks") == 0) ||
+	    (strcmp(sp, "week") == 0))
+		return WEEKS_TO_SECONDS(i);
+	return 0;
+}
+
+/*
+ * Convert from arbitrary user strings into a numeric ID.
+ * If it's all numeric, we convert that inplace, else we do
+ * the name lookup, and return the found identifier.
+ */
+
+prid_t
+prid_from_string(
+	char		*project)
+{
+	fs_project_t	*prj;
+	unsigned long	prid_long;
+	char		*sp;
+
+	/*
+	 * Allow either a full numeric or a valid projectname, even
+	 * if it starts with a digit.
+	 */
+	prid_long = strtoul(project, &sp, 10);
+	if (*project != '\0' && *sp == '\0') {
+		if ((prid_long == ULONG_MAX && errno == ERANGE)
+				|| (prid_long > (prid_t)-1))
+			return -1;
+		return (prid_t)prid_long;
+	}
+	prj = getprnam(project);
+	if (prj)
+		return prj->pr_prid;
+	return -1;
+}
+
+uid_t
+uid_from_string(
+	char		*user)
+{
+	struct passwd	*pwd;
+	unsigned long	uid_long;
+	char		*sp;
+
+	uid_long = strtoul(user, &sp, 10);
+	if (sp != user && *sp == '\0') {
+		if ((uid_long == ULONG_MAX && errno == ERANGE)
+				|| (uid_long > (uid_t)-1))
+			return -1;
+		return (uid_t)uid_long;
+	}
+	pwd = getpwnam(user);
+	if (pwd)
+		return pwd->pw_uid;
+	return -1;
+}
+
+gid_t
+gid_from_string(
+	char		*group)
+{
+	struct group	*grp;
+	unsigned long	gid_long;
+	char		*sp;
+
+	gid_long = strtoul(group, &sp, 10);
+	if (sp != group && *sp == '\0') {
+		if ((gid_long == ULONG_MAX && errno == ERANGE)
+				|| (gid_long > (gid_t)-1))
+			return -1;
+		return (gid_t)gid_long;
+	}
+	grp = getgrnam(group);
+	if (grp)
+		return grp->gr_gid;
+	return -1;
+}
diff --git a/libxcmd/input.c b/libxcmd/input.c
index 7a69dc1..441bb2f 100644
--- a/libxcmd/input.c
+++ b/libxcmd/input.c
@@ -136,308 +136,6 @@ doneline(
 	free(vec);
 }
 
-size_t
-numlen(
-	uint64_t	val,
-	size_t		base)
-{
-	uint64_t	tmp;
-	size_t		len;
-
-	for (len = 0, tmp = val; tmp > 0; tmp = tmp / base)
-		len++;
-	return len == 0 ? 1 : len;
-}
-
-/*
- * Convert string to int64_t, set errno if the conversion fails or
- * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
- * prior to conversion so you can check for bad inputs by examining
- * errno immediately after the call.
- */
-int64_t
-cvt_s64(
-	char		*s,
-	int		base)
-{
-	long long	i;
-	char		*sp;
-
-	errno = 0;
-	i = strtoll(s, &sp, base);
-	/*
-	 * If the input would over or underflow, return the clamped
-	 * value and let the user check errno.  If we went all the
-	 * way to the end of the input, return the converted value;
-	 * errno will be zero.
-	 */
-	if (errno || (*sp == '\0' && sp != s))
-		return i;
-
-	/* Not all the input was consumed, return error. */
-	errno = -ERANGE;
-	return INT64_MIN;
-}
-
-/*
- * Convert string to int32_t, set errno if the conversion fails or
- * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
- * prior to conversion so you can check for bad inputs by examining
- * errno immediately after the call.
- */
-int32_t
-cvt_s32(
-	char		*s,
-	int		base)
-{
-	int64_t		i;
-
-	i = cvt_s64(s, base);
-	if (errno)
-		return i;
-	if (i > INT32_MAX || i < INT32_MIN) {
-		errno = -ERANGE;
-		return INT32_MIN;
-	}
-	return i;
-}
-
-/*
- * Convert string to int16_t, set errno if the conversion fails or
- * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
- * prior to conversion so you can check for bad inputs by examining
- * errno immediately after the call.
- */
-int16_t
-cvt_s16(
-	char		*s,
-	int		base)
-{
-	int64_t		i;
-
-	i = cvt_s64(s, base);
-	if (errno)
-		return i;
-	if (i > INT16_MAX || i < INT16_MIN) {
-		errno = -ERANGE;
-		return INT16_MIN;
-	}
-	return i;
-}
-
-/*
- * Convert string to uint64_t, set errno if the conversion fails or
- * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
- * prior to conversion so you can check for bad inputs by examining
- * errno immediately after the call.
- */
-uint64_t
-cvt_u64(
-	char		*s,
-	int		base)
-{
-	long long	i;
-	char		*sp;
-
-	errno = 0;
-	i = strtoll(s, &sp, base);
-	/*
-	 * If the input would over or underflow, return the clamped
-	 * value and let the user check errno.  If we went all the
-	 * way to the end of the input, return the converted value;
-	 * errno will be zero.
-	 */
-	if (errno || (*sp == '\0' && sp != s))
-		return i;
-
-	/* Not all the input was consumed, return error. */
-	errno = -ERANGE;
-	return UINT64_MAX;
-}
-
-/*
- * Convert string to uint32_t, set errno if the conversion fails or
- * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
- * prior to conversion so you can check for bad inputs by examining
- * errno immediately after the call.
- */
-uint32_t
-cvt_u32(
-	char		*s,
-	int		base)
-{
-	uint64_t	i;
-
-	i = cvt_u64(s, base);
-	if (errno)
-		return i;
-	if (i > UINT32_MAX) {
-		errno = -ERANGE;
-		return UINT32_MAX;
-	}
-	return i;
-}
-
-/*
- * Convert string to uint16_t, set errno if the conversion fails or
- * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
- * prior to conversion so you can check for bad inputs by examining
- * errno immediately after the call.
- */
-uint16_t
-cvt_u16(
-	char		*s,
-	int		base)
-{
-	uint64_t	i;
-
-	i = cvt_u64(s, base);
-	if (errno)
-		return i;
-	if (i > UINT16_MAX) {
-		errno = -ERANGE;
-		return UINT16_MAX;
-	}
-	return i;
-}
-
-#define EXABYTES(x)	((long long)(x) << 60)
-#define PETABYTES(x)	((long long)(x) << 50)
-#define TERABYTES(x)	((long long)(x) << 40)
-#define GIGABYTES(x)	((long long)(x) << 30)
-#define MEGABYTES(x)	((long long)(x) << 20)
-#define KILOBYTES(x)	((long long)(x) << 10)
-
-long long
-cvtnum(
-	size_t		blocksize,
-	size_t		sectorsize,
-	char		*s)
-{
-	long long	i;
-	char		*sp;
-	int		c;
-
-	i = strtoll(s, &sp, 0);
-	if (i == 0 && sp == s)
-		return -1LL;
-	if (*sp == '\0')
-		return i;
-
-	if (sp[1] != '\0')
-		return -1LL;
-
-	c = tolower(*sp);
-	switch (c) {
-	case 'b':
-		return i * blocksize;
-	case 's':
-		return i * sectorsize;
-	case 'k':
-		return KILOBYTES(i);
-	case 'm':
-		return MEGABYTES(i);
-	case 'g':
-		return GIGABYTES(i);
-	case 't':
-		return TERABYTES(i);
-	case 'p':
-		return PETABYTES(i);
-	case 'e':
-		return  EXABYTES(i);
-	}
-	return -1LL;
-}
-
-#define TO_EXABYTES(x)	((x) / EXABYTES(1))
-#define TO_PETABYTES(x)	((x) / PETABYTES(1))
-#define TO_TERABYTES(x)	((x) / TERABYTES(1))
-#define TO_GIGABYTES(x)	((x) / GIGABYTES(1))
-#define TO_MEGABYTES(x)	((x) / MEGABYTES(1))
-#define TO_KILOBYTES(x)	((x) / KILOBYTES(1))
-
-void
-cvtstr(
-	double		value,
-	char		*str,
-	size_t		size)
-{
-	char		*fmt;
-	int		precise;
-
-	precise = ((double)value * 1000 == (double)(int)value * 1000);
-
-	if (value >= EXABYTES(1)) {
-		fmt = precise ? "%.f EiB" : "%.3f EiB";
-		snprintf(str, size, fmt, TO_EXABYTES(value));
-	} else if (value >= PETABYTES(1)) {
-		fmt = precise ? "%.f PiB" : "%.3f PiB";
-		snprintf(str, size, fmt, TO_PETABYTES(value));
-	} else if (value >= TERABYTES(1)) {
-		fmt = precise ? "%.f TiB" : "%.3f TiB";
-		snprintf(str, size, fmt, TO_TERABYTES(value));
-	} else if (value >= GIGABYTES(1)) {
-		fmt = precise ? "%.f GiB" : "%.3f GiB";
-		snprintf(str, size, fmt, TO_GIGABYTES(value));
-	} else if (value >= MEGABYTES(1)) {
-		fmt = precise ? "%.f MiB" : "%.3f MiB";
-		snprintf(str, size, fmt, TO_MEGABYTES(value));
-	} else if (value >= KILOBYTES(1)) {
-		fmt = precise ? "%.f KiB" : "%.3f KiB";
-		snprintf(str, size, fmt, TO_KILOBYTES(value));
-	} else {
-		snprintf(str, size, "%f bytes", value);
-	}
-}
-
-#define MINUTES_TO_SECONDS(m)	((m) * 60)
-#define HOURS_TO_SECONDS(h)	((h) * MINUTES_TO_SECONDS(60))
-#define DAYS_TO_SECONDS(d)	((d) * HOURS_TO_SECONDS(24))
-#define WEEKS_TO_SECONDS(w)	((w) * DAYS_TO_SECONDS(7))
-
-unsigned long
-cvttime(
-	char		*s)
-{
-	unsigned long	i;
-	char		*sp;
-
-	i = strtoul(s, &sp, 0);
-	if (i == 0 && sp == s)
-		return 0;
-	if (*sp == '\0')
-		return i;
-	if ((*sp == 'm' && sp[1] == '\0') ||
-	    (strcmp(sp, "minutes") == 0) ||
-	    (strcmp(sp, "minute") == 0))
-		return MINUTES_TO_SECONDS(i);
-	if ((*sp == 'h' && sp[1] == '\0') ||
-	    (strcmp(sp, "hours") == 0) ||
-	    (strcmp(sp, "hour") == 0))
-		return HOURS_TO_SECONDS(i);
-	if ((*sp == 'd' && sp[1] == '\0') ||
-	    (strcmp(sp, "days") == 0) ||
-	    (strcmp(sp, "day") == 0))
-		return DAYS_TO_SECONDS(i);
-	if ((*sp == 'w' && sp[1] == '\0') ||
-	    (strcmp(sp, "weeks") == 0) ||
-	    (strcmp(sp, "week") == 0))
-		return WEEKS_TO_SECONDS(i);
-	return 0;
-}
-
-struct timeval
-tadd(struct timeval t1, struct timeval t2)
-{
-	t1.tv_usec += t2.tv_usec;
-	if (t1.tv_usec > 1000000) {
-		t1.tv_usec -= 1000000;
-		t1.tv_sec++;
-	}
-	t1.tv_sec += t2.tv_sec;
-	return t1;
-}
-
 struct timeval
 tsub(struct timeval t1, struct timeval t2)
 {
@@ -513,79 +211,6 @@ timespec_from_string(
 	return 0;
 }
 
-/*
- * Convert from arbitrary user strings into a numeric ID.
- * If it's all numeric, we convert that inplace, else we do
- * the name lookup, and return the found identifier.
- */
-
-prid_t
-prid_from_string(
-	char		*project)
-{
-	fs_project_t	*prj;
-	unsigned long	prid_long;
-	char		*sp;
-
-	/*
-	 * Allow either a full numeric or a valid projectname, even
-	 * if it starts with a digit.
-	 */
-	prid_long = strtoul(project, &sp, 10);
-	if (*project != '\0' && *sp == '\0') {
-		if ((prid_long == ULONG_MAX && errno == ERANGE)
-				|| (prid_long > (prid_t)-1))
-			return -1;
-		return (prid_t)prid_long;
-	}
-	prj = getprnam(project);
-	if (prj)
-		return prj->pr_prid;
-	return -1;
-}
-
-uid_t
-uid_from_string(
-	char		*user)
-{
-	struct passwd	*pwd;
-	unsigned long	uid_long;
-	char		*sp;
-
-	uid_long = strtoul(user, &sp, 10);
-	if (sp != user && *sp == '\0') {
-		if ((uid_long == ULONG_MAX && errno == ERANGE)
-				|| (uid_long > (uid_t)-1))
-			return -1;
-		return (uid_t)uid_long;
-	}
-	pwd = getpwnam(user);
-	if (pwd)
-		return pwd->pw_uid;
-	return -1;
-}
-
-gid_t
-gid_from_string(
-	char		*group)
-{
-	struct group	*grp;
-	unsigned long	gid_long;
-	char		*sp;
-
-	gid_long = strtoul(group, &sp, 10);
-	if (sp != group && *sp == '\0') {
-		if ((gid_long == ULONG_MAX && errno == ERANGE)
-				|| (gid_long > (gid_t)-1))
-			return -1;
-		return (gid_t)gid_long;
-	}
-	grp = getgrnam(group);
-	if (grp)
-		return grp->gr_gid;
-	return -1;
-}
-
 bool isdigits_only(
 	const char *str)
 {
diff --git a/quota/Makefile b/quota/Makefile
index 9c6411e..120af2e 100644
--- a/quota/Makefile
+++ b/quota/Makefile
@@ -14,8 +14,8 @@ CFILES += $(PKG_PLATFORM).c
 PCFILES = darwin.c freebsd.c irix.c linux.c
 LSRCFILES = $(shell echo $(PCFILES) | sed -e "s/$(PKG_PLATFORM).c//g")
 
-LLDLIBS = $(LIBXCMD)
-LTDEPENDENCIES = $(LIBXCMD)
+LLDLIBS = $(LIBXCMD) $(LIBFROG)
+LTDEPENDENCIES = $(LIBXCMD) $(LIBFROG)
 LLDFLAGS = -static
 
 ifeq ($(ENABLE_READLINE),yes)
diff --git a/spaceman/Makefile b/spaceman/Makefile
index 95ec3c0..8b31030 100644
--- a/spaceman/Makefile
+++ b/spaceman/Makefile
@@ -9,8 +9,8 @@ LTCOMMAND = xfs_spaceman
 HFILES = init.h space.h
 CFILES = init.c file.c prealloc.c trim.c
 
-LLDLIBS = $(LIBXCMD)
-LTDEPENDENCIES = $(LIBXCMD)
+LLDLIBS = $(LIBXCMD) $(LIBFROG)
+LTDEPENDENCIES = $(LIBXCMD) $(LIBFROG)
 LLDFLAGS = -static
 
 ifeq ($(ENABLE_READLINE),yes)

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 06/12] libfrog: create a threaded workqueue
  2017-11-17 20:14 ` [PATCH 06/12] libfrog: create a threaded workqueue Darrick J. Wong
@ 2017-11-28  6:00   ` Darrick J. Wong
  0 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-11-28  6:00 UTC (permalink / raw)
  To: sandeen; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Create a thread pool that queues and runs discrete work items.  This
will be a namespaced version of the pool in repair/threads.c; a
subsequent patch will switch repair over.  xfs_scrub will use the
generic thread pool.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: updated copyright information
---
 include/workqueue.h |   55 ++++++++++++++++
 libfrog/Makefile    |    3 +
 libfrog/workqueue.c |  174 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 231 insertions(+), 1 deletion(-)
 create mode 100644 include/workqueue.h
 create mode 100644 libfrog/workqueue.c

diff --git a/include/workqueue.h b/include/workqueue.h
new file mode 100644
index 0000000..b4b3541
--- /dev/null
+++ b/include/workqueue.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * This code was adapted from repair/threads.h.
+ */
+#ifndef	_WORKQUEUE_H_
+#define	_WORKQUEUE_H_
+
+struct workqueue;
+
+typedef void workqueue_func_t(struct workqueue *wq, uint32_t index, void *arg);
+
+struct workqueue_item {
+	struct workqueue	*queue;
+	struct workqueue_item	*next;
+	workqueue_func_t	*function;
+	void			*arg;
+	uint32_t		index;
+};
+
+struct workqueue {
+	void			*wq_ctx;
+	pthread_t		*threads;
+	struct workqueue_item	*next_item;
+	struct workqueue_item	*last_item;
+	pthread_mutex_t		lock;
+	pthread_cond_t		wakeup;
+	unsigned int		item_count;
+	unsigned int		thread_count;
+	bool			terminate;
+};
+
+int workqueue_create(struct workqueue *wq, void *wq_ctx,
+		unsigned int nr_workers);
+int workqueue_add(struct workqueue *wq, workqueue_func_t fn,
+		uint32_t index, void *arg);
+void workqueue_destroy(struct workqueue *wq);
+
+#endif	/* _WORKQUEUE_H_ */
diff --git a/libfrog/Makefile b/libfrog/Makefile
index 3fd42a4..9a43621 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -14,7 +14,8 @@ CFILES = \
 avl64.c \
 list_sort.c \
 radix-tree.c \
-util.c
+util.c \
+workqueue.c
 
 default: ltdepend $(LTLIBRARY)
 
diff --git a/libfrog/workqueue.c b/libfrog/workqueue.c
new file mode 100644
index 0000000..66e8075
--- /dev/null
+++ b/libfrog/workqueue.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * This code was adapted from repair/threads.c, which (at the time)
+ * did not contain a copyright statement.
+ */
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+#include "workqueue.h"
+
+/* Main processing thread */
+static void *
+workqueue_thread(void *arg)
+{
+	struct workqueue	*wq = arg;
+	struct workqueue_item	*wi;
+
+	/*
+	 * Loop pulling work from the passed in work queue.
+	 * Check for notification to exit after every chunk of work.
+	 */
+	while (1) {
+		pthread_mutex_lock(&wq->lock);
+
+		/*
+		 * Wait for work.
+		 */
+		while (wq->next_item == NULL && !wq->terminate) {
+			assert(wq->item_count == 0);
+			pthread_cond_wait(&wq->wakeup, &wq->lock);
+		}
+		if (wq->next_item == NULL && wq->terminate) {
+			pthread_mutex_unlock(&wq->lock);
+			break;
+		}
+
+		/*
+		 *  Dequeue work from the head of the list.
+		 */
+		assert(wq->item_count > 0);
+		wi = wq->next_item;
+		wq->next_item = wi->next;
+		wq->item_count--;
+
+		pthread_mutex_unlock(&wq->lock);
+
+		(wi->function)(wi->queue, wi->index, wi->arg);
+		free(wi);
+	}
+
+	return NULL;
+}
+
+/* Allocate a work queue and threads. */
+int
+workqueue_create(
+	struct workqueue	*wq,
+	void			*wq_ctx,
+	unsigned int		nr_workers)
+{
+	unsigned int		i;
+	int			err = 0;
+
+	memset(wq, 0, sizeof(*wq));
+	pthread_cond_init(&wq->wakeup, NULL);
+	pthread_mutex_init(&wq->lock, NULL);
+
+	wq->wq_ctx = wq_ctx;
+	wq->thread_count = nr_workers;
+	wq->threads = malloc(nr_workers * sizeof(pthread_t));
+	wq->terminate = false;
+
+	for (i = 0; i < nr_workers; i++) {
+		err = pthread_create(&wq->threads[i], NULL, workqueue_thread,
+				wq);
+		if (err)
+			break;
+	}
+
+	if (err)
+		workqueue_destroy(wq);
+	return err;
+}
+
+/*
+ * Create a work item consisting of a function and some arguments and
+ * schedule the work item to be run via the thread pool.
+ */
+int
+workqueue_add(
+	struct workqueue	*wq,
+	workqueue_func_t	func,
+	uint32_t		index,
+	void			*arg)
+{
+	struct workqueue_item	*wi;
+
+	if (wq->thread_count == 0) {
+		func(wq, index, arg);
+		return 0;
+	}
+
+	wi = malloc(sizeof(struct workqueue_item));
+	if (wi == NULL)
+		return ENOMEM;
+
+	wi->function = func;
+	wi->index = index;
+	wi->arg = arg;
+	wi->queue = wq;
+	wi->next = NULL;
+
+	/* Now queue the new work structure to the work queue. */
+	pthread_mutex_lock(&wq->lock);
+	if (wq->next_item == NULL) {
+		wq->next_item = wi;
+		assert(wq->item_count == 0);
+		pthread_cond_signal(&wq->wakeup);
+	} else {
+		wq->last_item->next = wi;
+	}
+	wq->last_item = wi;
+	wq->item_count++;
+	pthread_mutex_unlock(&wq->lock);
+
+	return 0;
+}
+
+/*
+ * Wait for all pending work items to be processed and tear down the
+ * workqueue.
+ */
+void
+workqueue_destroy(
+	struct workqueue	*wq)
+{
+	unsigned int		i;
+
+	pthread_mutex_lock(&wq->lock);
+	wq->terminate = 1;
+	pthread_mutex_unlock(&wq->lock);
+
+	pthread_cond_broadcast(&wq->wakeup);
+
+	for (i = 0; i < wq->thread_count; i++)
+		pthread_join(wq->threads[i], NULL);
+
+	free(wq->threads);
+	pthread_mutex_destroy(&wq->lock);
+	pthread_cond_destroy(&wq->wakeup);
+	memset(wq, 0, sizeof(*wq));
+}

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 02/12] libfrog: move libxfs_log2_roundup to libfrog
  2017-11-17 20:13 ` [PATCH 02/12] libfrog: move libxfs_log2_roundup to libfrog Darrick J. Wong
@ 2017-12-04 22:01   ` Darrick J. Wong
  0 siblings, 0 replies; 20+ messages in thread
From: Darrick J. Wong @ 2017-12-04 22:01 UTC (permalink / raw)
  To: sandeen; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Move libxfs_log2_roundup to libfrog and remove the 'libxfs_' prefix.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: fix copyright problems
---
 include/libfrog.h |   23 +++++++++++++++++++++++
 include/libxfs.h  |    1 -
 libfrog/Makefile  |    3 ++-
 libfrog/util.c    |   36 ++++++++++++++++++++++++++++++++++++
 libxfs/util.c     |   12 ------------
 mkfs/Makefile     |    5 +++--
 mkfs/maxtrres.c   |    4 ++--
 mkfs/xfs_mkfs.c   |    4 ++--
 repair/Makefile   |    6 +++---
 repair/sb.c       |    4 ++--
 10 files changed, 73 insertions(+), 25 deletions(-)
 create mode 100644 include/libfrog.h
 create mode 100644 libfrog/util.c

diff --git a/include/libfrog.h b/include/libfrog.h
new file mode 100644
index 0000000..2d8055b
--- /dev/null
+++ b/include/libfrog.h
@@ -0,0 +1,23 @@
+/*
+ * 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 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#ifndef __LIBFROG_UTIL_H_
+#define __LIBFROG_UTIL_H_
+
+unsigned int	log2_roundup(unsigned int i);
+
+#endif /* __LIBFROG_UTIL_H_ */
diff --git a/include/libxfs.h b/include/libxfs.h
index abb01cb..e392e52 100644
--- a/include/libxfs.h
+++ b/include/libxfs.h
@@ -165,7 +165,6 @@ extern int	libxfs_log_header(char *, uuid_t *, int, int, int, xfs_lsn_t,
 
 
 /* Shared utility routines */
-extern unsigned int	libxfs_log2_roundup(unsigned int i);
 
 extern int	libxfs_alloc_file_space (struct xfs_inode *, xfs_off_t,
 				xfs_off_t, int, int);
diff --git a/libfrog/Makefile b/libfrog/Makefile
index 231a734..6d9ed07 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -10,7 +10,8 @@ LT_CURRENT = 0
 LT_REVISION = 0
 LT_AGE = 0
 
-CFILES =
+CFILES = \
+util.c
 
 default: ltdepend $(LTLIBRARY)
 
diff --git a/libfrog/util.c b/libfrog/util.c
new file mode 100644
index 0000000..4896e4b
--- /dev/null
+++ b/libfrog/util.c
@@ -0,0 +1,36 @@
+/*
+ * 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 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include "platform_defs.h"
+#include "libfrog.h"
+
+/*
+ * libfrog is a collection of miscellaneous userspace utilities.
+ * It's a library of Funny Random Oddball Gunk <cough>.
+ */
+
+unsigned int
+log2_roundup(unsigned int i)
+{
+	unsigned int	rval;
+
+	for (rval = 0; rval < NBBY * sizeof(i); rval++) {
+		if ((1 << rval) >= i)
+			break;
+	}
+	return rval;
+}
diff --git a/libxfs/util.c b/libxfs/util.c
index 3b11ac4..6d8cb5e 100644
--- a/libxfs/util.c
+++ b/libxfs/util.c
@@ -631,18 +631,6 @@ libxfs_alloc_file_space(
 	return error;
 }
 
-unsigned int
-libxfs_log2_roundup(unsigned int i)
-{
-	unsigned int	rval;
-
-	for (rval = 0; rval < NBBY * sizeof(i); rval++) {
-		if ((1 << rval) >= i)
-			break;
-	}
-	return rval;
-}
-
 /*
  * Wrapper around call to libxfs_ialloc. Takes care of committing and
  * allocating a new transaction as needed.
diff --git a/mkfs/Makefile b/mkfs/Makefile
index c13b903..e2dc1d4 100644
--- a/mkfs/Makefile
+++ b/mkfs/Makefile
@@ -10,8 +10,9 @@ LTCOMMAND = mkfs.xfs
 HFILES =
 CFILES = maxtrres.c proto.c xfs_mkfs.c
 
-LLDLIBS += $(LIBXFS) $(LIBXCMD) $(LIBRT) $(LIBPTHREAD) $(LIBBLKID) $(LIBUUID)
-LTDEPENDENCIES += $(LIBXFS) $(LIBXCMD)
+LLDLIBS += $(LIBXFS) $(LIBXCMD) $(LIBFROG) $(LIBRT) $(LIBPTHREAD) $(LIBBLKID) \
+	$(LIBUUID)
+LTDEPENDENCIES += $(LIBXFS) $(LIBXCMD) $(LIBFROG)
 LLDFLAGS = -static-libtool-libs
 
 default: depend $(LTCOMMAND)
diff --git a/mkfs/maxtrres.c b/mkfs/maxtrres.c
index 04028bf..0fa18c8 100644
--- a/mkfs/maxtrres.c
+++ b/mkfs/maxtrres.c
@@ -23,7 +23,7 @@
  * of sector size, block size, inode size, directory version, and
  * directory block size.
  */
-
+#include "libfrog.h"
 #include "libxfs.h"
 #include "xfs_multidisk.h"
 
@@ -55,7 +55,7 @@ max_trans_res(
 	sbp->sb_blocklog = blocklog;
 	sbp->sb_blocksize = 1 << blocklog;
 	sbp->sb_agblocks = agsize;
-	sbp->sb_agblklog = (uint8_t)libxfs_log2_roundup((unsigned int)agsize);
+	sbp->sb_agblklog = (uint8_t)log2_roundup((unsigned int)agsize);
 	sbp->sb_inodelog = inodelog;
 	sbp->sb_inopblog = blocklog - inodelog;
 	sbp->sb_inodesize = 1 << inodelog;
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index 5bfec03..cade04c 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -15,7 +15,7 @@
  * along with this program; if not, write the Free Software Foundation,
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
-
+#include "libfrog.h"
 #include "libxfs.h"
 #include <ctype.h>
 #include "xfs_multidisk.h"
@@ -2720,7 +2720,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 	memset(mp, 0, sizeof(xfs_mount_t));
 	sbp->sb_blocklog = (uint8_t)blocklog;
 	sbp->sb_sectlog = (uint8_t)sectorlog;
-	sbp->sb_agblklog = (uint8_t)libxfs_log2_roundup((unsigned int)agsize);
+	sbp->sb_agblklog = (uint8_t)log2_roundup((unsigned int)agsize);
 	sbp->sb_agblocks = (xfs_agblock_t)agsize;
 	mp->m_blkbb_log = sbp->sb_blocklog - BBSHIFT;
 	mp->m_sectbb_log = sbp->sb_sectlog - BBSHIFT;
diff --git a/repair/Makefile b/repair/Makefile
index b7e8fd5..4184a70 100644
--- a/repair/Makefile
+++ b/repair/Makefile
@@ -20,9 +20,9 @@ CFILES = agheader.c attr_repair.c avl.c avl64.c bmap.c btree.c \
 	progress.c prefetch.c rmap.c rt.c sb.c scan.c slab.c threads.c \
 	versions.c xfs_repair.c
 
-LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBXCMD) $(LIBUUID) \
-	$(LIBRT) $(LIBPTHREAD) $(LIBBLKID)
-LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG) $(LIBXCMD)
+LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBXCMD) $(LIBFROG) $(LIBUUID) $(LIBRT) \
+	$(LIBPTHREAD) $(LIBBLKID)
+LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG) $(LIBXCMD) $(LIBFROG)
 LLDFLAGS = -static-libtool-libs
 
 default: depend $(LTCOMMAND)
diff --git a/repair/sb.c b/repair/sb.c
index acc9283..f40cdea 100644
--- a/repair/sb.c
+++ b/repair/sb.c
@@ -15,7 +15,7 @@
  * along with this program; if not, write the Free Software Foundation,
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
-
+#include "libfrog.h"
 #include "libxfs.h"
 #include "libxcmd.h"
 #include "libxlog.h"
@@ -399,7 +399,7 @@ verify_sb(char *sb_buf, xfs_sb_t *sb, int is_primary_sb)
 		sb->sb_dblocks < XFS_MIN_DBLOCKS(sb))
 		return(XR_BAD_FS_SIZE_DATA);
 
-	if (sb->sb_agblklog != (uint8_t)libxfs_log2_roundup(sb->sb_agblocks))
+	if (sb->sb_agblklog != (uint8_t)log2_roundup(sb->sb_agblocks))
 		return(XR_BAD_FS_SIZE_DATA);
 
 	if (sb->sb_inodesize < XFS_DINODE_MIN_SIZE                     ||

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 00/12] xfsprogs-4.15: common library for misc. routines
  2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
                   ` (11 preceding siblings ...)
  2017-11-17 20:14 ` [PATCH 12/12] xfs_repair: remove old workqueue implementation in favor of libfrog code Darrick J. Wong
@ 2017-12-04 23:44 ` Eric Sandeen
  12 siblings, 0 replies; 20+ messages in thread
From: Eric Sandeen @ 2017-12-04 23:44 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

Ok, for latest versions of all this:

Reviewed-by: Eric Sandeen <sandeen@redhat.com>

On 11/17/17 2:13 PM, Darrick J. Wong wrote:
> Hi all,
> 
> This patch series refactors miscellaneous data structures and runtime
> support code out of libxfs and libxcmd into a new library to contain all
> of the funny random other gunk ("libfrog") that doesn't fit anywhere
> else.  Initially, libfrog will contain routines for bit manipulation,
> device topology, threaded workqueues, an avl tree, and file path
> detection.  Existing programs are modified to pull from libfrog; the
> future xfs_scrub program will also take advantage of it.
> 
> This whole mess sits atop the libxfs-4.15-sync branch; see the
> djwong-devel branch in my repo[1] for something pullable.
> 
> --D
> 
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=djwong-devel

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2017-12-04 23:44 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-11-17 20:13 [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Darrick J. Wong
2017-11-17 20:13 ` [PATCH 01/12] libfrog: move all the userspace support stuff into a new library Darrick J. Wong
2017-11-17 20:13 ` [PATCH 02/12] libfrog: move libxfs_log2_roundup to libfrog Darrick J. Wong
2017-12-04 22:01   ` [PATCH v2 " Darrick J. Wong
2017-11-17 20:13 ` [PATCH 03/12] libfrog: add bit manipulation functions Darrick J. Wong
2017-11-17 20:13 ` [PATCH 04/12] libfrog: move list_sort out of libxfs Darrick J. Wong
2017-11-17 20:13 ` [PATCH 05/12] libfrog: promote avl64 code from xfs_repair Darrick J. Wong
2017-11-17 20:14 ` [PATCH 06/12] libfrog: create a threaded workqueue Darrick J. Wong
2017-11-28  6:00   ` [PATCH v2 " Darrick J. Wong
2017-11-17 20:14 ` [PATCH 07/12] libfrog: move topology code out of libxcmd Darrick J. Wong
2017-11-17 20:14 ` [PATCH 08/12] libfrog: move conversion factors " Darrick J. Wong
2017-11-27 21:47   ` Eric Sandeen
2017-11-28  0:31     ` Darrick J. Wong
2017-11-28  0:45   ` [PATCH v2 " Darrick J. Wong
2017-11-28  5:59   ` [PATCH v3 " Darrick J. Wong
2017-11-17 20:14 ` [PATCH 09/12] libfrog: move paths.c " Darrick J. Wong
2017-11-17 20:14 ` [PATCH 10/12] libfrog: add missing function fs_table_destroy Darrick J. Wong
2017-11-17 20:14 ` [PATCH 11/12] libhandle: add missing destructor Darrick J. Wong
2017-11-17 20:14 ` [PATCH 12/12] xfs_repair: remove old workqueue implementation in favor of libfrog code Darrick J. Wong
2017-12-04 23:44 ` [PATCH 00/12] xfsprogs-4.15: common library for misc. routines Eric Sandeen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.