[packages/xfsprogs] - rel 3; important fixes from git (these will be in 3.2.1)
arekm
arekm at pld-linux.org
Tue Jul 8 10:07:30 CEST 2014
commit 3d585810b809dcbcd72815e22177f65e1cec8853
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date: Tue Jul 8 10:07:25 2014 +0200
- rel 3; important fixes from git (these will be in 3.2.1)
xfsprogs-git.patch | 27180 +++++++++++++++++++++++++++++++++++++++++++++++++++
xfsprogs.spec | 6 +-
2 files changed, 27184 insertions(+), 2 deletions(-)
---
diff --git a/xfsprogs.spec b/xfsprogs.spec
index fa7a906..1c280a3 100644
--- a/xfsprogs.spec
+++ b/xfsprogs.spec
@@ -6,12 +6,13 @@ Summary: Tools for the XFS filesystem
Summary(pl.UTF-8): Narzędzia do systemu plików XFS
Name: xfsprogs
Version: 3.2.0
-Release: 2
+Release: 3
License: LGPL v2.1 (libhandle), GPL v2 (the rest)
Group: Applications/System
Source0: ftp://linux-xfs.sgi.com/projects/xfs/cmd_tars/%{name}-%{version}.tar.gz
# Source0-md5: 400047b2f6af87c0345b8f0cc00c13db
Source1: xfs_lsprojid
+Patch100: %{name}-git.patch
Patch0: %{name}-miscfix-v2.patch
Patch1: %{name}-pl.po-update.patch
Patch2: %{name}-repair-tcmalloc.patch
@@ -93,8 +94,9 @@ Biblioteki statyczne do XFS.
%prep
%setup -q
+%patch100 -p1
%patch0 -p1
-%patch1 -p1
+#%patch1 -p1
%{?with_tcmalloc:%patch2 -p1}
%patch3 -p1
diff --git a/xfsprogs-git.patch b/xfsprogs-git.patch
new file mode 100644
index 0000000..4b4f73a
--- /dev/null
+++ b/xfsprogs-git.patch
@@ -0,0 +1,27180 @@
+diff --git a/copy/xfs_copy.c b/copy/xfs_copy.c
+index 71adb57..7ce5ec9 100644
+--- a/copy/xfs_copy.c
++++ b/copy/xfs_copy.c
+@@ -217,25 +217,6 @@ handle_error:
+ }
+
+ void
+-killall(void)
+-{
+- int i;
+-
+- /* only the parent gets to kill things */
+-
+- if (getpid() != parent_pid)
+- return;
+-
+- for (i = 0; i < num_targets; i++) {
+- if (target[i].state == ACTIVE) {
+- /* kill up target threads */
+- pthread_kill(target[i].pid, SIGKILL);
+- pthread_mutex_unlock(&targ[i].wait);
+- }
+- }
+-}
+-
+-void
+ handler(int sig)
+ {
+ pid_t pid;
+@@ -400,8 +381,7 @@ read_wbuf(int fd, wbuf *buf, xfs_mount_t *mp)
+ if (buf->length > buf->size) {
+ do_warn(_("assert error: buf->length = %d, buf->size = %d\n"),
+ buf->length, buf->size);
+- killall();
+- abort();
++ exit(1);
+ }
+
+ if ((res = read(fd, buf->data, buf->length)) < 0) {
+@@ -594,11 +574,6 @@ main(int argc, char **argv)
+
+ parent_pid = getpid();
+
+- if (atexit(killall)) {
+- do_log(_("%s: couldn't register atexit function.\n"), progname);
+- die_perror();
+- }
+-
+ /* open up source -- is it a file? */
+
+ open_flags = O_RDONLY;
+@@ -725,7 +700,7 @@ main(int argc, char **argv)
+ if (source_blocksize > source_sectorsize) {
+ /* get number of leftover sectors in last block of ag header */
+
+- tmp_residue = ((XFS_AGFL_DADDR(mp) + 1) * source_sectorsize)
++ tmp_residue = ((XFS_AGFL_DADDR(mp) + 1) * BBSIZE)
+ % source_blocksize;
+ first_residue = (tmp_residue == 0) ? 0 :
+ source_blocksize - tmp_residue;
+@@ -738,10 +713,10 @@ main(int argc, char **argv)
+ exit(1);
+ }
+
+- first_agbno = (((XFS_AGFL_DADDR(mp) + 1) * source_sectorsize)
++ first_agbno = (((XFS_AGFL_DADDR(mp) + 1) * BBSIZE)
+ + first_residue) / source_blocksize;
+ ASSERT(first_agbno != 0);
+- ASSERT( ((((XFS_AGFL_DADDR(mp) + 1) * source_sectorsize)
++ ASSERT(((((XFS_AGFL_DADDR(mp) + 1) * BBSIZE)
+ + first_residue) % source_blocksize) == 0);
+
+ /* now open targets */
+@@ -1177,9 +1152,6 @@ main(int argc, char **argv)
+ }
+
+ check_errors();
+- killall();
+- pthread_exit(NULL);
+- /*NOTREACHED*/
+ return 0;
+ }
+
+diff --git a/db/Makefile b/db/Makefile
+index 9485b82..bae6154 100644
+--- a/db/Makefile
++++ b/db/Makefile
+@@ -14,7 +14,7 @@ HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
+ io.h malloc.h metadump.h output.h print.h quit.h sb.h sig.h strvec.h \
+ text.h type.h write.h attrset.h symlink.h
+ CFILES = $(HFILES:.h=.c)
+-LSRCFILES = xfs_admin.sh xfs_check.sh xfs_ncheck.sh xfs_metadump.sh
++LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
+
+ LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
+ LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG)
+@@ -38,7 +38,6 @@ install: default
+ $(INSTALL) -m 755 -d $(PKG_SBIN_DIR)
+ $(LTINSTALL) -m 755 $(LTCOMMAND) $(PKG_SBIN_DIR)
+ $(INSTALL) -m 755 xfs_admin.sh $(PKG_SBIN_DIR)/xfs_admin
+- $(INSTALL) -m 755 xfs_check.sh $(PKG_SBIN_DIR)/xfs_check
+ $(INSTALL) -m 755 xfs_ncheck.sh $(PKG_SBIN_DIR)/xfs_ncheck
+ $(INSTALL) -m 755 xfs_metadump.sh $(PKG_SBIN_DIR)/xfs_metadump
+ install-dev:
+diff --git a/db/agi.c b/db/agi.c
+index 398bdbb..6f167ac 100644
+--- a/db/agi.c
++++ b/db/agi.c
+@@ -57,6 +57,8 @@ const field_t agi_flds[] = {
+ { "uuid", FLDT_UUID, OI(OFF(uuid)), C1, 0, TYP_NONE },
+ { "lsn", FLDT_UINT64X, OI(OFF(lsn)), C1, 0, TYP_NONE },
+ { "crc", FLDT_CRC, OI(OFF(crc)), C1, 0, TYP_NONE },
++ { "free_root", FLDT_AGBLOCK, OI(OFF(free_root)), C1, 0, TYP_INOBT },
++ { "free_level", FLDT_UINT32D, OI(OFF(free_level)), C1, 0, TYP_NONE },
+ { NULL }
+ };
+
+diff --git a/db/attrset.c b/db/attrset.c
+index 762b3bf..ea5261a 100644
+--- a/db/attrset.c
++++ b/db/attrset.c
+@@ -170,7 +170,7 @@ attr_set_f(
+ out:
+ mp->m_flags &= ~LIBXFS_MOUNT_COMPAT_ATTR;
+ if (ip)
+- libxfs_iput(ip, 0);
++ IRELE(ip);
+ if (value)
+ free(value);
+ return 0;
+@@ -244,6 +244,6 @@ attr_remove_f(
+ out:
+ mp->m_flags &= ~LIBXFS_MOUNT_COMPAT_ATTR;
+ if (ip)
+- libxfs_iput(ip, 0);
++ IRELE(ip);
+ return 0;
+ }
+diff --git a/db/btblock.c b/db/btblock.c
+index 1ea0cff..cdb8b1d 100644
+--- a/db/btblock.c
++++ b/db/btblock.c
+@@ -60,6 +60,12 @@ struct xfs_db_btree {
+ sizeof(xfs_inobt_rec_t),
+ sizeof(__be32),
+ },
++ { XFS_FIBT_MAGIC,
++ XFS_BTREE_SBLOCK_LEN,
++ sizeof(xfs_inobt_key_t),
++ sizeof(xfs_inobt_rec_t),
++ sizeof(__be32),
++ },
+ { XFS_BMAP_CRC_MAGIC,
+ XFS_BTREE_LBLOCK_CRC_LEN,
+ sizeof(xfs_bmbt_key_t),
+@@ -84,6 +90,12 @@ struct xfs_db_btree {
+ sizeof(xfs_inobt_rec_t),
+ sizeof(__be32),
+ },
++ { XFS_FIBT_CRC_MAGIC,
++ XFS_BTREE_SBLOCK_CRC_LEN,
++ sizeof(xfs_inobt_key_t),
++ sizeof(xfs_inobt_rec_t),
++ sizeof(__be32),
++ },
+ { 0,
+ },
+ };
+diff --git a/db/metadump.c b/db/metadump.c
+index 09bb85a..38cd441 100644
+--- a/db/metadump.c
++++ b/db/metadump.c
+@@ -1917,6 +1917,7 @@ scanfunc_ino(
+ xfs_inobt_ptr_t *pp;
+ int i;
+ int numrecs;
++ int finobt = *(int *) arg;
+
+ numrecs = be16_to_cpu(block->bb_numrecs);
+
+@@ -1928,6 +1929,14 @@ scanfunc_ino(
+ typtab[btype].name, agno, agbno);
+ numrecs = mp->m_inobt_mxr[0];
+ }
++
++ /*
++ * Only copy the btree blocks for the finobt. The inobt scan
++ * copies the inode chunks.
++ */
++ if (finobt)
++ return 1;
++
+ rp = XFS_INOBT_REC_ADDR(mp, block, 1);
+ for (i = 0; i < numrecs; i++, rp++) {
+ if (!copy_inode_chunk(agno, rp))
+@@ -1967,6 +1976,7 @@ copy_inodes(
+ {
+ xfs_agblock_t root;
+ int levels;
++ int finobt = 0;
+
+ root = be32_to_cpu(agi->agi_root);
+ levels = be32_to_cpu(agi->agi_level);
+@@ -1985,7 +1995,20 @@ copy_inodes(
+ return 1;
+ }
+
+- return scan_btree(agno, root, levels, TYP_INOBT, agi, scanfunc_ino);
++ if (!scan_btree(agno, root, levels, TYP_INOBT, &finobt, scanfunc_ino))
++ return 0;
++
++ if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
++ root = be32_to_cpu(agi->agi_free_root);
++ levels = be32_to_cpu(agi->agi_free_level);
++
++ finobt = 1;
++ if (!scan_btree(agno, root, levels, TYP_INOBT, &finobt,
++ scanfunc_ino))
++ return 0;
++ }
++
++ return 1;
+ }
+
+ static int
+diff --git a/db/write.c b/db/write.c
+index ca8bd0f..0157a44 100644
+--- a/db/write.c
++++ b/db/write.c
+@@ -565,7 +565,7 @@ convert_arg(
+ return NULL;
+
+ /* Does the value fit into the range of the destination bitfield? */
+- if ((val >> bit_length) > 0)
++ if (bit_length < 64 && (val >> bit_length) > 0)
+ return NULL;
+ /*
+ * If the length of the field is not a multiple of a byte, push
+diff --git a/db/xfs_check.sh b/db/xfs_check.sh
+deleted file mode 100755
+index 553da63..0000000
+--- a/db/xfs_check.sh
++++ /dev/null
+@@ -1,42 +0,0 @@
+-#!/bin/sh -f
+-#
+-# Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+-#
+-
+-OPTS=" "
+-DBOPTS=" "
+-USAGE="Usage: xfs_check [-fsvV] [-l logdev] [-i ino]... [-b bno]... special"
+-
+-echo "xfs_check is deprecated and scheduled for removal in June 2014."
+-echo "Please use xfs_repair -n <dev> instead."
+-
+-while getopts "b:fi:l:stvV" c
+-do
+- case $c in
+- s) OPTS=$OPTS"-s ";;
+- t) OPTS=$OPTS"-t ";;
+- v) OPTS=$OPTS"-v ";;
+- i) OPTS=$OPTS"-i "$OPTARG" ";;
+- b) OPTS=$OPTS"-b "$OPTARG" ";;
+- f) DBOPTS=$DBOPTS" -f";;
+- l) DBOPTS=$DBOPTS" -l "$OPTARG" ";;
+- V) xfs_db -p xfs_check -V
+- status=$?
+- exit $status
+- ;;
+- \?) echo $USAGE 1>&2
+- exit 2
+- ;;
+- esac
+-done
+-set -- extra $@
+-shift $OPTIND
+-case $# in
+- 1) xfs_db$DBOPTS -F -i -p xfs_check -c "check$OPTS" $1
+- status=$?
+- ;;
+- *) echo $USAGE 1>&2
+- exit 2
+- ;;
+-esac
+-exit $status
+diff --git a/fsr/xfs_fsr.c b/fsr/xfs_fsr.c
+index 3818f02..48629fd 100644
+--- a/fsr/xfs_fsr.c
++++ b/fsr/xfs_fsr.c
+@@ -554,6 +554,8 @@ fsrallfs(char *mtab, int howlong, char *leftofffile)
+ fsrprintf(_("could not read %s, starting with %s\n"),
+ leftofffile, *fs->dev);
+ } else {
++ /* Ensure the buffer we read is null terminated */
++ buf[SMBUFSZ-1] = '\0';
+ for (fs = fsbase; fs < fsend; fs++) {
+ fsname = fs->dev;
+ if ((strncmp(buf,fsname,strlen(fsname)) == 0)
+@@ -1203,14 +1205,20 @@ out:
+ * We already are pretty sure we can and want to
+ * defragment the file. Create the tmp file, copy
+ * the data (maintaining holes) and call the kernel
+- * extent swap routinte.
++ * extent swap routine.
++ *
++ * Return values:
++ * -1: Some error was encountered
++ * 0: Successfully defragmented the file
++ * 1: No change / No Error
+ */
+ static int
+ packfile(char *fname, char *tname, int fd,
+ xfs_bstat_t *statp, struct fsxattr *fsxp)
+ {
+- int tfd;
++ int tfd = -1;
+ int srval;
++ int retval = -1; /* Failure is the default */
+ int nextents, extent, cur_nextents, new_nextents;
+ unsigned blksz_dio;
+ unsigned dio_min;
+@@ -1218,7 +1226,7 @@ packfile(char *fname, char *tname, int fd,
+ static xfs_swapext_t sx;
+ struct xfs_flock64 space;
+ off64_t cnt, pos;
+- void *fbuf;
++ void *fbuf = NULL;
+ int ct, wc, wc_b4;
+ char ffname[SMBUFSZ];
+ int ffd = -1;
+@@ -1234,7 +1242,8 @@ packfile(char *fname, char *tname, int fd,
+ if (cur_nextents == 1 || cur_nextents <= nextents) {
+ if (vflag)
+ fsrprintf(_("%s already fully defragmented.\n"), fname);
+- return 1; /* indicates no change/no error */
++ retval = 1; /* indicates no change/no error */
++ goto out;
+ }
+
+ if (dflag)
+@@ -1246,15 +1255,14 @@ packfile(char *fname, char *tname, int fd,
+ if (vflag)
+ fsrprintf(_("could not open tmp file: %s: %s\n"),
+ tname, strerror(errno));
+- return -1;
++ goto out;
+ }
+ unlink(tname);
+
+ /* Setup extended attributes */
+ if (fsr_setup_attr_fork(fd, tfd, statp) != 0) {
+ fsrprintf(_("failed to set ATTR fork on tmp: %s:\n"), tname);
+- close(tfd);
+- return -1;
++ goto out;
+ }
+
+ /* Setup extended inode flags, project identifier, etc */
+@@ -1262,15 +1270,13 @@ packfile(char *fname, char *tname, int fd,
+ if (ioctl(tfd, XFS_IOC_FSSETXATTR, fsxp) < 0) {
+ fsrprintf(_("could not set inode attrs on tmp: %s\n"),
+ tname);
+- close(tfd);
+- return -1;
++ goto out;
+ }
+ }
+
+ if ((ioctl(tfd, XFS_IOC_DIOINFO, &dio)) < 0 ) {
+ fsrprintf(_("could not get DirectIO info on tmp: %s\n"), tname);
+- close(tfd);
+- return -1;
++ goto out;
+ }
+
+ dio_min = dio.d_miniosz;
+@@ -1292,8 +1298,7 @@ packfile(char *fname, char *tname, int fd,
+
+ if (!(fbuf = (char *)memalign(dio.d_mem, blksz_dio))) {
+ fsrprintf(_("could not allocate buf: %s\n"), tname);
+- close(tfd);
+- return -1;
++ goto out;
+ }
+
+ if (nfrags) {
+@@ -1304,9 +1309,7 @@ packfile(char *fname, char *tname, int fd,
+ if ((ffd = open(ffname, openopts, 0666)) < 0) {
+ fsrprintf(_("could not open fragfile: %s : %s\n"),
+ ffname, strerror(errno));
+- close(tfd);
+- free(fbuf);
+- return -1;
++ goto out;
+ }
+ unlink(ffname);
+ }
+@@ -1322,7 +1325,11 @@ packfile(char *fname, char *tname, int fd,
+ fsrprintf(_("could not trunc tmp %s\n"),
+ tname);
+ }
+- lseek64(tfd, outmap[extent].bmv_length, SEEK_CUR);
++ if (lseek64(tfd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
++ fsrprintf(_("could not lseek in tmpfile: %s : %s\n"),
++ tname, strerror(errno));
++ goto out;
++ }
+ continue;
+ } else if (outmap[extent].bmv_length == 0) {
+ /* to catch holes at the beginning of the file */
+@@ -1336,21 +1343,19 @@ packfile(char *fname, char *tname, int fd,
+ if (ioctl(tfd, XFS_IOC_RESVSP64, &space) < 0) {
+ fsrprintf(_("could not pre-allocate tmp space:"
+ " %s\n"), tname);
+- close(tfd);
+- free(fbuf);
+- return -1;
++ goto out;
++ }
++ if (lseek64(tfd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
++ fsrprintf(_("could not lseek in tmpfile: %s : %s\n"),
++ tname, strerror(errno));
++ goto out;
+ }
+- lseek64(tfd, outmap[extent].bmv_length, SEEK_CUR);
+ }
+ } /* end of space allocation loop */
+
+ if (lseek64(tfd, 0, SEEK_SET)) {
+ fsrprintf(_("Couldn't rewind on temporary file\n"));
+- close(tfd);
+- if (ffd != -1)
+- close(ffd);
+- free(fbuf);
+- return -1;
++ goto out;
+ }
+
+ /* Check if the temporary file has fewer extents */
+@@ -1360,19 +1365,24 @@ packfile(char *fname, char *tname, int fd,
+ if (cur_nextents <= new_nextents) {
+ if (vflag)
+ fsrprintf(_("No improvement will be made (skipping): %s\n"), fname);
+- free(fbuf);
+- close(tfd);
+- if (ffd != -1)
+- close(ffd);
+- return 1; /* no change/no error */
++ retval = 1; /* no change/no error */
++ goto out;
+ }
+
+ /* Loop through block map copying the file. */
+ for (extent = 0; extent < nextents; extent++) {
+ pos = outmap[extent].bmv_offset;
+ if (outmap[extent].bmv_block == -1) {
+- lseek64(tfd, outmap[extent].bmv_length, SEEK_CUR);
+- lseek64(fd, outmap[extent].bmv_length, SEEK_CUR);
++ if (lseek64(tfd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
++ fsrprintf(_("could not lseek in tmpfile: %s : %s\n"),
++ tname, strerror(errno));
++ goto out;
++ }
++ if (lseek64(fd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
++ fsrprintf(_("could not lseek in file: %s : %s\n"),
++ fname, strerror(errno));
++ goto out;
++ }
+ continue;
+ } else if (outmap[extent].bmv_length == 0) {
+ /* to catch holes at the beginning of the file */
+@@ -1435,11 +1445,7 @@ packfile(char *fname, char *tname, int fd,
+ tname);
+ }
+ }
+- free(fbuf);
+- close(tfd);
+- if (ffd != -1)
+- close(ffd);
+- return -1;
++ goto out;
+ }
+ if (nfrags) {
+ /* Do a matching write to the tmp file */
+@@ -1452,12 +1458,16 @@ packfile(char *fname, char *tname, int fd,
+ }
+ }
+ }
+- ftruncate64(tfd, statp->bs_size);
+- if (ffd != -1)
+- close(ffd);
+- fsync(tfd);
+-
+- free(fbuf);
++ if (ftruncate64(tfd, statp->bs_size) < 0) {
++ fsrprintf(_("could not truncate tmpfile: %s : %s\n"),
++ fname, strerror(errno));
++ goto out;
++ }
++ if (fsync(tfd) < 0) {
++ fsrprintf(_("could not fsync tmpfile: %s : %s\n"),
++ fname, strerror(errno));
++ goto out;
++ }
+
+ sx.sx_stat = *statp; /* struct copy */
+ sx.sx_version = XFS_SX_VERSION;
+@@ -1471,8 +1481,7 @@ packfile(char *fname, char *tname, int fd,
+ if (vflag)
+ fsrprintf(_("failed to fchown tmpfile %s: %s\n"),
+ tname, strerror(errno));
+- close(tfd);
+- return -1;
++ goto out;
+ }
+
+ /* Swap the extents */
+@@ -1494,8 +1503,7 @@ packfile(char *fname, char *tname, int fd,
+ fsrprintf(_("XFS_IOC_SWAPEXT failed: %s: %s\n"),
+ fname, strerror(errno));
+ }
+- close(tfd);
+- return -1;
++ goto out;
+ }
+
+ /* Report progress */
+@@ -1504,8 +1512,15 @@ packfile(char *fname, char *tname, int fd,
+ cur_nextents, new_nextents,
+ (new_nextents <= nextents ? "DONE" : " " ),
+ fname);
+- close(tfd);
+- return 0;
++ retval = 0;
++
++out:
++ free(fbuf);
++ if (tfd != -1)
++ close(tfd);
++ if (ffd != -1)
++ close(ffd);
++ return retval;
+ }
+
+ char *
+diff --git a/growfs/xfs_growfs.c b/growfs/xfs_growfs.c
+index 77da707..8e611b6 100644
+--- a/growfs/xfs_growfs.c
++++ b/growfs/xfs_growfs.c
+@@ -56,12 +56,13 @@ report_info(
+ int projid32bit,
+ int crcs_enabled,
+ int cimode,
+- int ftype_enabled)
++ int ftype_enabled,
++ int finobt_enabled)
+ {
+ printf(_(
+ "meta-data=%-22s isize=%-6u agcount=%u, agsize=%u blks\n"
+ " =%-22s sectsz=%-5u attr=%u, projid32bit=%u\n"
+- " =%-22s crc=%u\n"
++ " =%-22s crc=%-8u finobt=%u\n"
+ "data =%-22s bsize=%-6u blocks=%llu, imaxpct=%u\n"
+ " =%-22s sunit=%-6u swidth=%u blks\n"
+ "naming =version %-14u bsize=%-6u ascii-ci=%d ftype=%d\n"
+@@ -71,7 +72,7 @@ report_info(
+
+ mntpoint, geo.inodesize, geo.agcount, geo.agblocks,
+ "", geo.sectsize, attrversion, projid32bit,
+- "", crcs_enabled,
++ "", crcs_enabled, finobt_enabled,
+ "", geo.blocksize, (unsigned long long)geo.datablocks,
+ geo.imaxpct,
+ "", geo.sunit, geo.swidth,
+@@ -123,6 +124,7 @@ main(int argc, char **argv)
+ int projid32bit;
+ int crcs_enabled;
+ int ftype_enabled = 0;
++ int finobt_enabled; /* free inode btree */
+
+ progname = basename(argv[0]);
+ setlocale(LC_ALL, "");
+@@ -244,11 +246,12 @@ main(int argc, char **argv)
+ projid32bit = geo.flags & XFS_FSOP_GEOM_FLAGS_PROJID32 ? 1 : 0;
+ crcs_enabled = geo.flags & XFS_FSOP_GEOM_FLAGS_V5SB ? 1 : 0;
+ ftype_enabled = geo.flags & XFS_FSOP_GEOM_FLAGS_FTYPE ? 1 : 0;
++ finobt_enabled = geo.flags & XFS_FSOP_GEOM_FLAGS_FINOBT ? 1 : 0;
+ if (nflag) {
+ report_info(geo, datadev, isint, logdev, rtdev,
+ lazycount, dirversion, logversion,
+ attrversion, projid32bit, crcs_enabled, ci,
+- ftype_enabled);
++ ftype_enabled, finobt_enabled);
+ exit(0);
+ }
+
+@@ -285,7 +288,8 @@ main(int argc, char **argv)
+
+ report_info(geo, datadev, isint, logdev, rtdev,
+ lazycount, dirversion, logversion,
+- attrversion, projid32bit, crcs_enabled, ci, ftype_enabled);
++ attrversion, projid32bit, crcs_enabled, ci, ftype_enabled,
++ finobt_enabled);
+
+ ddsize = xi.dsize;
+ dlsize = ( xi.logBBsize? xi.logBBsize :
+diff --git a/include/libxfs.h b/include/libxfs.h
+index 9c10957..45a924f 100644
+--- a/include/libxfs.h
++++ b/include/libxfs.h
+@@ -480,7 +480,6 @@ typedef struct xfs_inode_log_item {
+ unsigned int ili_fields; /* fields to be logged */
+ unsigned int ili_last_fields; /* fields when flushed*/
+ xfs_inode_log_format_t ili_format; /* logged structure */
+- int ili_lock_flags;
+ } xfs_inode_log_item_t;
+
+ typedef struct xfs_buf_log_item {
+@@ -535,9 +534,7 @@ extern xfs_buf_t *libxfs_trans_getsb (xfs_trans_t *, xfs_mount_t *, int);
+
+ extern int libxfs_trans_iget (xfs_mount_t *, xfs_trans_t *, xfs_ino_t,
+ uint, uint, struct xfs_inode **);
+-extern void libxfs_trans_iput(xfs_trans_t *, struct xfs_inode *, uint);
+ extern void libxfs_trans_ijoin (xfs_trans_t *, struct xfs_inode *, uint);
+-extern void libxfs_trans_ihold (xfs_trans_t *, struct xfs_inode *);
+ extern void libxfs_trans_ijoin_ref(xfs_trans_t *, struct xfs_inode *, int);
+ extern void libxfs_trans_log_inode (xfs_trans_t *, struct xfs_inode *,
+ uint);
+@@ -656,7 +653,9 @@ extern int libxfs_iflush_int (xfs_inode_t *, xfs_buf_t *);
+ /* Inode Cache Interfaces */
+ extern int libxfs_iget (xfs_mount_t *, xfs_trans_t *, xfs_ino_t,
+ uint, xfs_inode_t **, xfs_daddr_t);
+-extern void libxfs_iput (xfs_inode_t *, uint);
++extern void libxfs_iput (xfs_inode_t *);
++
++#define IRELE(ip) libxfs_iput(ip)
+
+ /* Shared utility routines */
+ extern unsigned int libxfs_log2_roundup(unsigned int i);
+@@ -760,6 +759,7 @@ bool xfs_dinode_verify(struct xfs_mount *mp, xfs_ino_t ino,
+ /* xfs_sb.h */
+ #define libxfs_mod_sb xfs_mod_sb
+ #define libxfs_sb_from_disk xfs_sb_from_disk
++#define libxfs_sb_quota_from_disk xfs_sb_quota_from_disk
+ #define libxfs_sb_to_disk xfs_sb_to_disk
+
+ /* xfs_symlink.h */
+diff --git a/include/xfs_ag.h b/include/xfs_ag.h
+index 0fdd410..2531658 100644
+--- a/include/xfs_ag.h
++++ b/include/xfs_ag.h
+@@ -166,24 +166,30 @@ typedef struct xfs_agi {
+ __be32 agi_pad32;
+ __be64 agi_lsn; /* last write sequence */
+
++ __be32 agi_free_root; /* root of the free inode btree */
++ __be32 agi_free_level;/* levels in free inode btree */
++
+ /* structure must be padded to 64 bit alignment */
+ } xfs_agi_t;
+
+ #define XFS_AGI_CRC_OFF offsetof(struct xfs_agi, agi_crc)
+
+-#define XFS_AGI_MAGICNUM 0x00000001
+-#define XFS_AGI_VERSIONNUM 0x00000002
+-#define XFS_AGI_SEQNO 0x00000004
+-#define XFS_AGI_LENGTH 0x00000008
+-#define XFS_AGI_COUNT 0x00000010
+-#define XFS_AGI_ROOT 0x00000020
+-#define XFS_AGI_LEVEL 0x00000040
+-#define XFS_AGI_FREECOUNT 0x00000080
+-#define XFS_AGI_NEWINO 0x00000100
+-#define XFS_AGI_DIRINO 0x00000200
+-#define XFS_AGI_UNLINKED 0x00000400
+-#define XFS_AGI_NUM_BITS 11
+-#define XFS_AGI_ALL_BITS ((1 << XFS_AGI_NUM_BITS) - 1)
++#define XFS_AGI_MAGICNUM (1 << 0)
++#define XFS_AGI_VERSIONNUM (1 << 1)
++#define XFS_AGI_SEQNO (1 << 2)
++#define XFS_AGI_LENGTH (1 << 3)
++#define XFS_AGI_COUNT (1 << 4)
++#define XFS_AGI_ROOT (1 << 5)
++#define XFS_AGI_LEVEL (1 << 6)
++#define XFS_AGI_FREECOUNT (1 << 7)
++#define XFS_AGI_NEWINO (1 << 8)
++#define XFS_AGI_DIRINO (1 << 9)
++#define XFS_AGI_UNLINKED (1 << 10)
++#define XFS_AGI_NUM_BITS_R1 11 /* end of the 1st agi logging region */
++#define XFS_AGI_ALL_BITS_R1 ((1 << XFS_AGI_NUM_BITS_R1) - 1)
++#define XFS_AGI_FREE_ROOT (1 << 11)
++#define XFS_AGI_FREE_LEVEL (1 << 12)
++#define XFS_AGI_NUM_BITS_R2 13
+
+ /* disk block (xfs_daddr_t) in the AG */
+ #define XFS_AGI_DADDR(mp) ((xfs_daddr_t)(2 << (mp)->m_sectbb_log))
+diff --git a/include/xfs_btree.h b/include/xfs_btree.h
+index 6afe0b2..2590d40 100644
+--- a/include/xfs_btree.h
++++ b/include/xfs_btree.h
+@@ -37,6 +37,7 @@ extern kmem_zone_t *xfs_btree_cur_zone;
+ #define XFS_BTNUM_CNT ((xfs_btnum_t)XFS_BTNUM_CNTi)
+ #define XFS_BTNUM_BMAP ((xfs_btnum_t)XFS_BTNUM_BMAPi)
+ #define XFS_BTNUM_INO ((xfs_btnum_t)XFS_BTNUM_INOi)
++#define XFS_BTNUM_FINO ((xfs_btnum_t)XFS_BTNUM_FINOi)
+
+ /*
+ * For logging record fields.
+@@ -67,6 +68,7 @@ do { \
+ case XFS_BTNUM_CNT: __XFS_BTREE_STATS_INC(abtc, stat); break; \
+ case XFS_BTNUM_BMAP: __XFS_BTREE_STATS_INC(bmbt, stat); break; \
+ case XFS_BTNUM_INO: __XFS_BTREE_STATS_INC(ibt, stat); break; \
++ case XFS_BTNUM_FINO: __XFS_BTREE_STATS_INC(fibt, stat); break; \
+ case XFS_BTNUM_MAX: ASSERT(0); /* fucking gcc */ ; break; \
+ } \
+ } while (0)
+@@ -80,6 +82,7 @@ do { \
+ case XFS_BTNUM_CNT: __XFS_BTREE_STATS_ADD(abtc, stat, val); break; \
+ case XFS_BTNUM_BMAP: __XFS_BTREE_STATS_ADD(bmbt, stat, val); break; \
+ case XFS_BTNUM_INO: __XFS_BTREE_STATS_ADD(ibt, stat, val); break; \
++ case XFS_BTNUM_FINO: __XFS_BTREE_STATS_ADD(fibt, stat, val); break; \
+ case XFS_BTNUM_MAX: ASSERT(0); /* fucking gcc */ ; break; \
+ } \
+ } while (0)
+diff --git a/include/xfs_format.h b/include/xfs_format.h
+index 77f6b8b..758052f 100644
+--- a/include/xfs_format.h
++++ b/include/xfs_format.h
+@@ -202,6 +202,8 @@ typedef __be32 xfs_alloc_ptr_t;
+ */
+ #define XFS_IBT_MAGIC 0x49414254 /* 'IABT' */
+ #define XFS_IBT_CRC_MAGIC 0x49414233 /* 'IAB3' */
++#define XFS_FIBT_MAGIC 0x46494254 /* 'FIBT' */
++#define XFS_FIBT_CRC_MAGIC 0x46494233 /* 'FIB3' */
+
+ typedef __uint64_t xfs_inofree_t;
+ #define XFS_INODES_PER_CHUNK (NBBY * sizeof(xfs_inofree_t))
+@@ -244,7 +246,17 @@ typedef __be32 xfs_inobt_ptr_t;
+ * block numbers in the AG.
+ */
+ #define XFS_IBT_BLOCK(mp) ((xfs_agblock_t)(XFS_CNT_BLOCK(mp) + 1))
+-#define XFS_PREALLOC_BLOCKS(mp) ((xfs_agblock_t)(XFS_IBT_BLOCK(mp) + 1))
++#define XFS_FIBT_BLOCK(mp) ((xfs_agblock_t)(XFS_IBT_BLOCK(mp) + 1))
++
++/*
++ * The first data block of an AG depends on whether the filesystem was formatted
++ * with the finobt feature. If so, account for the finobt reserved root btree
++ * block.
++ */
++#define XFS_PREALLOC_BLOCKS(mp) \
++ (xfs_sb_version_hasfinobt(&((mp)->m_sb)) ? \
++ XFS_FIBT_BLOCK(mp) + 1 : \
++ XFS_IBT_BLOCK(mp) + 1)
+
+
+
+diff --git a/include/xfs_fs.h b/include/xfs_fs.h
+index 554fd66..59c40fc 100644
+--- a/include/xfs_fs.h
++++ b/include/xfs_fs.h
+@@ -238,6 +238,7 @@ typedef struct xfs_fsop_resblks {
+ #define XFS_FSOP_GEOM_FLAGS_LAZYSB 0x4000 /* lazy superblock counters */
+ #define XFS_FSOP_GEOM_FLAGS_V5SB 0x8000 /* version 5 superblock */
+ #define XFS_FSOP_GEOM_FLAGS_FTYPE 0x10000 /* inode directory types */
++#define XFS_FSOP_GEOM_FLAGS_FINOBT 0x20000 /* free inode btree */
+
+
+ /*
+diff --git a/include/xfs_ialloc.h b/include/xfs_ialloc.h
+index a8f76a5..c8ac0a4 100644
+--- a/include/xfs_ialloc.h
++++ b/include/xfs_ialloc.h
+@@ -89,7 +89,7 @@ xfs_difree(
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_ino_t inode, /* inode to be freed */
+ struct xfs_bmap_free *flist, /* extents to free */
+- int *delete, /* set if inode cluster was deleted */
++ int *deleted, /* set if inode cluster was deleted */
+ xfs_ino_t *first_ino); /* first inode in deleted cluster */
+
+ /*
+diff --git a/include/xfs_ialloc_btree.h b/include/xfs_ialloc_btree.h
+index f38b220..d7ebea7 100644
+--- a/include/xfs_ialloc_btree.h
++++ b/include/xfs_ialloc_btree.h
+@@ -58,7 +58,8 @@ struct xfs_mount;
+ ((index) - 1) * sizeof(xfs_inobt_ptr_t)))
+
+ extern struct xfs_btree_cur *xfs_inobt_init_cursor(struct xfs_mount *,
+- struct xfs_trans *, struct xfs_buf *, xfs_agnumber_t);
++ struct xfs_trans *, struct xfs_buf *, xfs_agnumber_t,
++ xfs_btnum_t);
+ extern int xfs_inobt_maxrecs(struct xfs_mount *, int, int);
+
+ #endif /* __XFS_IALLOC_BTREE_H__ */
+diff --git a/include/xfs_sb.h b/include/xfs_sb.h
+index f7b2fe7..950d1ea 100644
+--- a/include/xfs_sb.h
++++ b/include/xfs_sb.h
+@@ -587,7 +587,9 @@ xfs_sb_has_compat_feature(
+ return (sbp->sb_features_compat & feature) != 0;
+ }
+
+-#define XFS_SB_FEAT_RO_COMPAT_ALL 0
++#define XFS_SB_FEAT_RO_COMPAT_FINOBT (1 << 0) /* free inode btree */
++#define XFS_SB_FEAT_RO_COMPAT_ALL \
++ (XFS_SB_FEAT_RO_COMPAT_FINOBT)
+ #define XFS_SB_FEAT_RO_COMPAT_UNKNOWN ~XFS_SB_FEAT_RO_COMPAT_ALL
+ static inline bool
+ xfs_sb_has_ro_compat_feature(
+@@ -641,6 +643,12 @@ static inline int xfs_sb_version_hasftype(struct xfs_sb *sbp)
+ (sbp->sb_features2 & XFS_SB_VERSION2_FTYPE));
+ }
+
++static inline int xfs_sb_version_hasfinobt(xfs_sb_t *sbp)
++{
++ return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5) &&
++ (sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_FINOBT);
++}
++
+ /*
+ * end of superblock version macros
+ */
+diff --git a/include/xfs_trans_space.h b/include/xfs_trans_space.h
+index 7d2c920..a7d1721 100644
+--- a/include/xfs_trans_space.h
++++ b/include/xfs_trans_space.h
+@@ -47,7 +47,9 @@
+ #define XFS_DIRREMOVE_SPACE_RES(mp) \
+ XFS_DAREMOVE_SPACE_RES(mp, XFS_DATA_FORK)
+ #define XFS_IALLOC_SPACE_RES(mp) \
+- (XFS_IALLOC_BLOCKS(mp) + (mp)->m_in_maxlevels - 1)
++ (XFS_IALLOC_BLOCKS(mp) + \
++ (xfs_sb_version_hasfinobt(&mp->m_sb) ? 2 : 1 * \
++ ((mp)->m_in_maxlevels - 1)))
+
+ /*
+ * Space reservation values for various transactions.
+@@ -82,5 +84,8 @@
+ (XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
+ #define XFS_SYMLINK_SPACE_RES(mp,nl,b) \
+ (XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl) + (b))
++#define XFS_IFREE_SPACE_RES(mp) \
++ (xfs_sb_version_hasfinobt(&mp->m_sb) ? (mp)->m_in_maxlevels : 0)
++
+
+ #endif /* __XFS_TRANS_SPACE_H__ */
+diff --git a/include/xfs_types.h b/include/xfs_types.h
+index 82bbc34..65c6e66 100644
+--- a/include/xfs_types.h
++++ b/include/xfs_types.h
+@@ -134,7 +134,7 @@ typedef enum {
+
+ typedef enum {
+ XFS_BTNUM_BNOi, XFS_BTNUM_CNTi, XFS_BTNUM_BMAPi, XFS_BTNUM_INOi,
+- XFS_BTNUM_MAX
++ XFS_BTNUM_FINOi, XFS_BTNUM_MAX
+ } xfs_btnum_t;
+
+ struct xfs_name {
+diff --git a/libxfs/init.c b/libxfs/init.c
+index 0924948..06458e5 100644
+--- a/libxfs/init.c
++++ b/libxfs/init.c
+@@ -778,9 +778,9 @@ void
+ libxfs_rtmount_destroy(xfs_mount_t *mp)
+ {
+ if (mp->m_rsumip)
+- libxfs_iput(mp->m_rsumip, 0);
++ IRELE(mp->m_rsumip);
+ if (mp->m_rbmip)
+- libxfs_iput(mp->m_rbmip, 0);
++ IRELE(mp->m_rbmip);
+ mp->m_rsumip = mp->m_rbmip = NULL;
+ }
+
+diff --git a/libxfs/linux.c b/libxfs/linux.c
+index 2e07d54..8d1a117 100644
+--- a/libxfs/linux.c
++++ b/libxfs/linux.c
+@@ -141,10 +141,20 @@ platform_findsizes(char *path, int fd, long long *sz, int *bsz)
+ exit(1);
+ }
+ if ((st.st_mode & S_IFMT) == S_IFREG) {
++ struct xfs_fsop_geom_v1 geom = { 0 };
++
+ *sz = (long long)(st.st_size >> 9);
+- *bsz = BBSIZE;
+- if (BBSIZE > max_block_alignment)
+- max_block_alignment = BBSIZE;
++ if (ioctl(fd, XFS_IOC_FSGEOMETRY_V1, &geom) < 0) {
++ /*
++ * fall back to BBSIZE; mkfs might fail if there's a
++ * size mismatch between the image & the host fs...
++ */
++ *bsz = BBSIZE;
++ } else
++ *bsz = geom.sectsize;
++
++ if (*bsz > max_block_alignment)
++ max_block_alignment = *bsz;
+ return;
+ }
+
+diff --git a/libxfs/rdwr.c b/libxfs/rdwr.c
+index 57c12c1..0294c98 100644
+--- a/libxfs/rdwr.c
++++ b/libxfs/rdwr.c
+@@ -19,6 +19,37 @@
+ #include <xfs/libxfs.h>
+ #include "init.h"
+
++/*
++ * Important design/architecture note:
++ *
++ * The userspace code that uses the buffer cache is much less constrained than
++ * the kernel code. The userspace code is pretty nasty in places, especially
++ * when it comes to buffer error handling. Very little of the userspace code
++ * outside libxfs clears bp->b_error - very little code even checks it - so the
++ * libxfs code is tripping on stale errors left by the userspace code.
++ *
++ * We can't clear errors or zero buffer contents in libxfs_getbuf-* like we do
++ * in the kernel, because those functions are used by the libxfs_readbuf_*
++ * functions and hence need to leave the buffers unchanged on cache hits. This
++ * is actually the only way to gather a write error from a libxfs_writebuf()
++ * call - you need to get the buffer again so you can check bp->b_error field -
++ * assuming that the buffer is still in the cache when you check, that is.
++ *
++ * This is very different to the kernel code which does not release buffers on a
++ * write so we can wait on IO and check errors. The kernel buffer cache also
++ * guarantees a buffer of a known initial state from xfs_buf_get() even on a
++ * cache hit.
++ *
++ * IOWs, userspace is behaving quite differently to the kernel and as a result
++ * it leaks errors from reads, invalidations and writes through
++ * libxfs_getbuf/libxfs_readbuf.
++ *
++ * The result of this is that until the userspace code outside libxfs is cleaned
++ * up, functions that release buffers from userspace control (i.e
++ * libxfs_writebuf/libxfs_putbuf) need to zero bp->b_error to prevent
++ * propagation of stale errors into future buffer operations.
++ */
++
+ #define BDSTRAT_SIZE (256 * 1024)
+
+ #define IO_BCOMPARE_CHECK
+@@ -632,6 +663,12 @@ libxfs_putbuf(xfs_buf_t *bp)
+ pthread_mutex_unlock(&bp->b_lock);
+ }
+ }
++ /*
++ * ensure that any errors on this use of the buffer don't carry
++ * over to the next user.
++ */
++ bp->b_error = 0;
++
+ cache_node_put(libxfs_bcache, (struct cache_node *)bp);
+ }
+
+@@ -907,10 +944,10 @@ libxfs_writebufr(xfs_buf_t *bp)
+ }
+
+ #ifdef IO_DEBUG
+- printf("%lx: %s: wrote %u bytes, blkno=%llu(%llu), %p\n",
++ printf("%lx: %s: wrote %u bytes, blkno=%llu(%llu), %p, error %d\n",
+ pthread_self(), __FUNCTION__, bp->b_bcount,
+ (long long)LIBXFS_BBTOOFF64(bp->b_bn),
+- (long long)bp->b_bn, bp);
++ (long long)bp->b_bn, bp, error);
+ #endif
+ if (!error) {
+ bp->b_flags |= LIBXFS_B_UPTODATE;
+@@ -928,6 +965,7 @@ libxfs_writebuf_int(xfs_buf_t *bp, int flags)
+ * subsequent reads after this write from seeing stale errors.
+ */
+ bp->b_error = 0;
++ bp->b_flags &= ~LIBXFS_B_STALE;
+ bp->b_flags |= (LIBXFS_B_DIRTY | flags);
+ return 0;
+ }
+@@ -946,6 +984,7 @@ libxfs_writebuf(xfs_buf_t *bp, int flags)
+ * subsequent reads after this write from seeing stale errors.
+ */
+ bp->b_error = 0;
++ bp->b_flags &= ~LIBXFS_B_STALE;
+ bp->b_flags |= (LIBXFS_B_DIRTY | flags);
+ libxfs_putbuf(bp);
+ return 0;
+@@ -1103,7 +1142,7 @@ libxfs_idestroy(xfs_inode_t *ip)
+ }
+
+ void
+-libxfs_iput(xfs_inode_t *ip, uint lock_flags)
++libxfs_iput(xfs_inode_t *ip)
+ {
+ if (ip->i_itemp)
+ kmem_zone_free(xfs_ili_zone, ip->i_itemp);
+diff --git a/libxfs/trans.c b/libxfs/trans.c
+index c443863..13d21b2 100644
+--- a/libxfs/trans.c
++++ b/libxfs/trans.c
+@@ -110,7 +110,7 @@ libxfs_trans_roll(
+ /*
+ * Commit the current transaction.
+ * If this commit failed, then it'd just unlock those items that
+- * are not marked ihold. That also means that a filesystem shutdown
++ * are marked to be released. That also means that a filesystem shutdown
+ * is in progress. The caller takes the responsibility to cancel
+ * the duplicate transaction that gets returned.
+ */
+@@ -248,27 +248,6 @@ libxfs_trans_iget(
+ }
+
+ void
+-libxfs_trans_iput(
+- xfs_trans_t *tp,
+- xfs_inode_t *ip,
+- uint lock_flags)
+-{
+- xfs_inode_log_item_t *iip;
+-
+- if (tp == NULL) {
+- libxfs_iput(ip, lock_flags);
+- return;
+- }
+-
+- ASSERT(ip->i_transp == tp);
+- iip = ip->i_itemp;
+- ASSERT(iip != NULL);
+- xfs_trans_del_item(&iip->ili_item);
+-
+- libxfs_iput(ip, lock_flags);
+-}
+-
+-void
+ libxfs_trans_ijoin(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip,
+@@ -301,7 +280,6 @@ libxfs_trans_ijoin_ref(
+ ASSERT(ip->i_itemp != NULL);
+
+ xfs_trans_ijoin(tp, ip, lock_flags);
+- ip->i_itemp->ili_lock_flags = lock_flags;
+
+ #ifdef XACT_DEBUG
+ fprintf(stderr, "ijoin_ref'd inode %llu, transaction %p\n", ip->i_ino, tp);
+@@ -309,21 +287,6 @@ libxfs_trans_ijoin_ref(
+ }
+
+ void
+-libxfs_trans_ihold(
+- xfs_trans_t *tp,
+- xfs_inode_t *ip)
+-{
+- ASSERT(ip->i_transp == tp);
+- ASSERT(ip->i_itemp != NULL);
+-
+- ip->i_itemp->ili_lock_flags = 1;
+-
+-#ifdef XACT_DEBUG
+- fprintf(stderr, "ihold'd inode %llu, transaction %p\n", ip->i_ino, tp);
+-#endif
+-}
+-
+-void
+ libxfs_trans_inode_alloc_buf(
+ xfs_trans_t *tp,
+ xfs_buf_t *bp)
+@@ -702,7 +665,7 @@ inode_item_done(
+ if (!(iip->ili_fields & XFS_ILOG_ALL)) {
+ ip->i_transp = NULL; /* disassociate from transaction */
+ iip->ili_flags = 0; /* reset all flags */
+- goto ili_done;
++ return;
+ }
+
+ /*
+@@ -712,7 +675,7 @@ inode_item_done(
+ if (error) {
+ fprintf(stderr, _("%s: warning - imap_to_bp failed (%d)\n"),
+ progname, error);
+- goto ili_done;
++ return;
+ }
+
+ XFS_BUF_SET_FSPRIVATE(bp, iip);
+@@ -720,7 +683,7 @@ inode_item_done(
+ if (error) {
+ fprintf(stderr, _("%s: warning - iflush_int failed (%d)\n"),
+ progname, error);
+- goto ili_done;
++ return;
+ }
+
+ ip->i_transp = NULL; /* disassociate from transaction */
+@@ -728,16 +691,9 @@ inode_item_done(
+ XFS_BUF_SET_FSPRIVATE2(bp, NULL); /* remove xact ptr */
+ libxfs_writebuf(bp, 0);
+ #ifdef XACT_DEBUG
+- fprintf(stderr, "flushing dirty inode %llu, buffer %p (hold=%u)\n",
+- ip->i_ino, bp, iip->ili_lock_flags);
++ fprintf(stderr, "flushing dirty inode %llu, buffer %p\n",
++ ip->i_ino, bp);
+ #endif
+-ili_done:
+- if (iip->ili_lock_flags) {
+- iip->ili_lock_flags = 0;
+- return;
+- }
+- /* free the inode */
+- libxfs_iput(ip, 0);
+ }
+
+ static void
+@@ -818,10 +774,6 @@ inode_item_unlock(
+ ip->i_transp = NULL;
+
+ iip->ili_flags = 0;
+- if (!iip->ili_lock_flags)
+- libxfs_iput(ip, 0);
+- else
+- iip->ili_lock_flags = 0;
+ }
+
+ /*
+diff --git a/libxfs/util.c b/libxfs/util.c
+index 4c40324..9504e33 100644
+--- a/libxfs/util.c
++++ b/libxfs/util.c
+@@ -595,7 +595,6 @@ libxfs_alloc_file_space(
+ break;
+ }
+ xfs_trans_ijoin(tp, ip, 0);
+- xfs_trans_ihold(tp, ip);
+
+ xfs_bmap_init(&free_list, &firstfsb);
+ error = xfs_bmapi_write(tp, ip, startoffset_fsb, allocatesize_fsb,
+diff --git a/libxfs/xfs.h b/libxfs/xfs.h
+index 5a21590..30a316d 100644
+--- a/libxfs/xfs.h
++++ b/libxfs/xfs.h
+@@ -292,7 +292,6 @@ roundup_64(__uint64_t x, __uint32_t y)
+ #define xfs_trans_get_buf libxfs_trans_get_buf
+ #define xfs_trans_getsb libxfs_trans_getsb
+ #define xfs_trans_iget libxfs_trans_iget
+-#define xfs_trans_ihold libxfs_trans_ihold
+ #define xfs_trans_ijoin libxfs_trans_ijoin
+ #define xfs_trans_ijoin_ref libxfs_trans_ijoin_ref
+ #define xfs_trans_init libxfs_trans_init
+diff --git a/libxfs/xfs_attr_remote.c b/libxfs/xfs_attr_remote.c
+index 5cf5c73..08b983b 100644
+--- a/libxfs/xfs_attr_remote.c
++++ b/libxfs/xfs_attr_remote.c
+@@ -85,7 +85,7 @@ xfs_attr3_rmt_verify(
+ if (be32_to_cpu(rmt->rm_bytes) > fsbsize - sizeof(*rmt))
+ return false;
+ if (be32_to_cpu(rmt->rm_offset) +
+- be32_to_cpu(rmt->rm_bytes) >= XATTR_SIZE_MAX)
++ be32_to_cpu(rmt->rm_bytes) > XATTR_SIZE_MAX)
+ return false;
+ if (rmt->rm_owner == 0)
+ return false;
+diff --git a/libxfs/xfs_btree.c b/libxfs/xfs_btree.c
+index 9be4abd..cc823f5 100644
+--- a/libxfs/xfs_btree.c
++++ b/libxfs/xfs_btree.c
+@@ -27,9 +27,10 @@ kmem_zone_t *xfs_btree_cur_zone;
+ * Btree magic numbers.
+ */
+ static const __uint32_t xfs_magics[2][XFS_BTNUM_MAX] = {
+- { XFS_ABTB_MAGIC, XFS_ABTC_MAGIC, XFS_BMAP_MAGIC, XFS_IBT_MAGIC },
++ { XFS_ABTB_MAGIC, XFS_ABTC_MAGIC, XFS_BMAP_MAGIC, XFS_IBT_MAGIC,
++ XFS_FIBT_MAGIC },
+ { XFS_ABTB_CRC_MAGIC, XFS_ABTC_CRC_MAGIC,
+- XFS_BMAP_CRC_MAGIC, XFS_IBT_CRC_MAGIC }
++ XFS_BMAP_CRC_MAGIC, XFS_IBT_CRC_MAGIC, XFS_FIBT_CRC_MAGIC }
+ };
+ #define xfs_btree_magic(cur) \
+ xfs_magics[!!((cur)->bc_flags & XFS_BTREE_CRC_BLOCKS)][cur->bc_btnum]
+@@ -1099,6 +1100,7 @@ xfs_btree_set_refs(
+ xfs_buf_set_ref(bp, XFS_ALLOC_BTREE_REF);
+ break;
+ case XFS_BTNUM_INO:
++ case XFS_BTNUM_FINO:
+ xfs_buf_set_ref(bp, XFS_INO_BTREE_REF);
+ break;
+ case XFS_BTNUM_BMAP:
+diff --git a/libxfs/xfs_da_btree.c b/libxfs/xfs_da_btree.c
+index b70454e..b731b54 100644
+--- a/libxfs/xfs_da_btree.c
++++ b/libxfs/xfs_da_btree.c
+@@ -2582,7 +2582,8 @@ xfs_da_get_buf(
+ mapp, nmap, 0);
+ error = bp ? bp->b_error : XFS_ERROR(EIO);
+ if (error) {
+- xfs_trans_brelse(trans, bp);
++ if (bp)
++ xfs_trans_brelse(trans, bp);
+ goto out_free;
+ }
+
+diff --git a/libxfs/xfs_ialloc.c b/libxfs/xfs_ialloc.c
+index c19d84a..5462c54 100644
+--- a/libxfs/xfs_ialloc.c
++++ b/libxfs/xfs_ialloc.c
+@@ -88,6 +88,66 @@ xfs_inobt_get_rec(
+ }
+
+ /*
++ * Insert a single inobt record. Cursor must already point to desired location.
++ */
++STATIC int
++xfs_inobt_insert_rec(
++ struct xfs_btree_cur *cur,
++ __int32_t freecount,
++ xfs_inofree_t free,
++ int *stat)
++{
++ cur->bc_rec.i.ir_freecount = freecount;
++ cur->bc_rec.i.ir_free = free;
++ return xfs_btree_insert(cur, stat);
++}
++
++/*
++ * Insert records describing a newly allocated inode chunk into the inobt.
++ */
++STATIC int
++xfs_inobt_insert(
++ struct xfs_mount *mp,
++ struct xfs_trans *tp,
++ struct xfs_buf *agbp,
++ xfs_agino_t newino,
++ xfs_agino_t newlen,
++ xfs_btnum_t btnum)
++{
++ struct xfs_btree_cur *cur;
++ struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
++ xfs_agnumber_t agno = be32_to_cpu(agi->agi_seqno);
++ xfs_agino_t thisino;
++ int i;
++ int error;
++
++ cur = xfs_inobt_init_cursor(mp, tp, agbp, agno, btnum);
++
++ for (thisino = newino;
++ thisino < newino + newlen;
++ thisino += XFS_INODES_PER_CHUNK) {
++ error = xfs_inobt_lookup(cur, thisino, XFS_LOOKUP_EQ, &i);
++ if (error) {
++ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
++ return error;
++ }
++ ASSERT(i == 0);
++
++ error = xfs_inobt_insert_rec(cur, XFS_INODES_PER_CHUNK,
++ XFS_INOBT_ALL_FREE, &i);
++ if (error) {
++ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
++ return error;
++ }
++ ASSERT(i == 1);
++ }
++
++ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
++
++ return 0;
++}
++
++/*
+ * Verify that the number of free inodes in the AGI is correct.
+ */
+ #ifdef DEBUG
+@@ -286,13 +346,10 @@ xfs_ialloc_ag_alloc(
+ {
+ xfs_agi_t *agi; /* allocation group header */
+ xfs_alloc_arg_t args; /* allocation argument structure */
+- xfs_btree_cur_t *cur; /* inode btree cursor */
+ xfs_agnumber_t agno;
+ int error;
+- int i;
+ xfs_agino_t newino; /* new first inode's number */
+ xfs_agino_t newlen; /* new number of inodes */
+- xfs_agino_t thisino; /* current inode number, for loop */
+ int isaligned = 0; /* inode allocation at stripe unit */
+ /* boundary */
+ struct xfs_perag *pag;
+@@ -430,29 +487,19 @@ xfs_ialloc_ag_alloc(
+ agi->agi_newino = cpu_to_be32(newino);
+
+ /*
+- * Insert records describing the new inode chunk into the btree.
++ * Insert records describing the new inode chunk into the btrees.
+ */
+- cur = xfs_inobt_init_cursor(args.mp, tp, agbp, agno);
+- for (thisino = newino;
+- thisino < newino + newlen;
+- thisino += XFS_INODES_PER_CHUNK) {
+- cur->bc_rec.i.ir_startino = thisino;
+- cur->bc_rec.i.ir_freecount = XFS_INODES_PER_CHUNK;
+- cur->bc_rec.i.ir_free = XFS_INOBT_ALL_FREE;
+- error = xfs_btree_lookup(cur, XFS_LOOKUP_EQ, &i);
+- if (error) {
+- xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+- return error;
+- }
+- ASSERT(i == 0);
+- error = xfs_btree_insert(cur, &i);
+- if (error) {
+- xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
++ error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen,
++ XFS_BTNUM_INO);
++ if (error)
++ return error;
++
++ if (xfs_sb_version_hasfinobt(&args.mp->m_sb)) {
++ error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen,
++ XFS_BTNUM_FINO);
++ if (error)
+ return error;
+- }
+- ASSERT(i == 1);
+ }
+- xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+ /*
+ * Log allocation group header fields
+ */
+@@ -652,7 +699,7 @@ xfs_ialloc_get_rec(
+ * available.
+ */
+ STATIC int
+-xfs_dialloc_ag(
++xfs_dialloc_ag_slow(
+ struct xfs_trans *tp,
+ struct xfs_buf *agbp,
+ xfs_ino_t parent,
+@@ -678,7 +725,7 @@ xfs_dialloc_ag(
+ ASSERT(pag->pagi_freecount > 0);
+
+ restart_pagno:
+- cur = xfs_inobt_init_cursor(mp, tp, agbp, agno);
++ cur = xfs_inobt_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_INO);
+ /*
+ * If pagino is 0 (this is the root inode allocation) use newino.
+ * This must work because we've just allocated some.
+@@ -910,6 +957,215 @@ error0:
+ return error;
+ }
+
++STATIC int
++xfs_dialloc_ag(
++ struct xfs_trans *tp,
++ struct xfs_buf *agbp,
++ xfs_ino_t parent,
++ xfs_ino_t *inop)
++{
++ struct xfs_mount *mp = tp->t_mountp;
++ struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
++ xfs_agnumber_t agno = be32_to_cpu(agi->agi_seqno);
++ xfs_agnumber_t pagno = XFS_INO_TO_AGNO(mp, parent);
++ xfs_agino_t pagino = XFS_INO_TO_AGINO(mp, parent);
++ struct xfs_perag *pag;
++ struct xfs_btree_cur *cur;
++ struct xfs_btree_cur *tcur;
++ struct xfs_inobt_rec_incore rec;
++ struct xfs_inobt_rec_incore trec;
++ xfs_ino_t ino;
++ int error;
++ int offset;
++ int i, j;
++
++ if (!xfs_sb_version_hasfinobt(&mp->m_sb))
++ return xfs_dialloc_ag_slow(tp, agbp, parent, inop);
++
++ pag = xfs_perag_get(mp, agno);
++
++ /*
++ * If pagino is 0 (this is the root inode allocation) use newino.
++ * This must work because we've just allocated some.
++ */
++ if (!pagino)
++ pagino = be32_to_cpu(agi->agi_newino);
++
++ cur = xfs_inobt_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_FINO);
++
++ error = xfs_check_agi_freecount(cur, agi);
++ if (error)
++ goto error_cur;
++
++ if (agno == pagno) {
++ /*
++ * We're in the same AG as the parent inode so allocate the
++ * closest inode to the parent.
++ */
++ error = xfs_inobt_lookup(cur, pagino, XFS_LOOKUP_LE, &i);
++ if (error)
++ goto error_cur;
++ if (i == 1) {
++ error = xfs_inobt_get_rec(cur, &rec, &i);
++ if (error)
++ goto error_cur;
++ XFS_WANT_CORRUPTED_GOTO(i == 1, error_cur);
++
++ /*
++ * See if we've landed in the parent inode record. The
++ * finobt only tracks chunks with at least one free
++ * inode, so record existence is enough.
++ */
++ if (pagino >= rec.ir_startino &&
++ pagino < (rec.ir_startino + XFS_INODES_PER_CHUNK))
++ goto alloc_inode;
++ }
++
++ error = xfs_btree_dup_cursor(cur, &tcur);
++ if (error)
++ goto error_cur;
++
++ error = xfs_inobt_lookup(tcur, pagino, XFS_LOOKUP_GE, &j);
++ if (error)
++ goto error_tcur;
++ if (j == 1) {
++ error = xfs_inobt_get_rec(tcur, &trec, &j);
++ if (error)
++ goto error_tcur;
++ XFS_WANT_CORRUPTED_GOTO(j == 1, error_tcur);
++ }
++
++ if (i == 1 && j == 1) {
++ if ((pagino - rec.ir_startino + XFS_INODES_PER_CHUNK - 1) >
++ (trec.ir_startino - pagino)) {
++ rec = trec;
++ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
++ cur = tcur;
++ } else {
++ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
++ }
++ } else if (j == 1) {
++ rec = trec;
++ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
++ cur = tcur;
++ } else {
++ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
++ }
++ } else {
++ /*
++ * Different AG from the parent inode. Check the record for the
++ * most recently allocated inode.
++ */
++ if (agi->agi_newino != cpu_to_be32(NULLAGINO)) {
++ error = xfs_inobt_lookup(cur, agi->agi_newino,
++ XFS_LOOKUP_EQ, &i);
++ if (error)
++ goto error_cur;
++ if (i == 1) {
++ error = xfs_inobt_get_rec(cur, &rec, &i);
++ if (error)
++ goto error_cur;
++ XFS_WANT_CORRUPTED_GOTO(i == 1, error_cur);
++ goto alloc_inode;
++ }
++ }
++
++ /*
++ * Allocate the first inode available in the AG.
++ */
++ error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &i);
++ if (error)
++ goto error_cur;
++ XFS_WANT_CORRUPTED_GOTO(i == 1, error_cur);
++
++ error = xfs_inobt_get_rec(cur, &rec, &i);
++ if (error)
++ goto error_cur;
++ XFS_WANT_CORRUPTED_GOTO(i == 1, error_cur);
++ }
++
++alloc_inode:
++ offset = xfs_lowbit64(rec.ir_free);
++ ASSERT(offset >= 0);
++ ASSERT(offset < XFS_INODES_PER_CHUNK);
++ ASSERT((XFS_AGINO_TO_OFFSET(mp, rec.ir_startino) %
++ XFS_INODES_PER_CHUNK) == 0);
++ ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino + offset);
++
++ /*
++ * Modify or remove the finobt record.
++ */
++ rec.ir_free &= ~XFS_INOBT_MASK(offset);
++ rec.ir_freecount--;
++ if (rec.ir_freecount)
++ error = xfs_inobt_update(cur, &rec);
++ else
++ error = xfs_btree_delete(cur, &i);
++ if (error)
++ goto error_cur;
++
++ /*
++ * Lookup and modify the equivalent record in the inobt.
++ */
++ tcur = xfs_inobt_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_INO);
++
++ error = xfs_check_agi_freecount(tcur, agi);
++ if (error)
++ goto error_tcur;
++
++ error = xfs_inobt_lookup(tcur, rec.ir_startino, XFS_LOOKUP_EQ, &i);
++ if (error)
++ goto error_tcur;
++ XFS_WANT_CORRUPTED_GOTO(i == 1, error_tcur);
++
++ error = xfs_inobt_get_rec(tcur, &trec, &i);
++ if (error)
++ goto error_tcur;
++ XFS_WANT_CORRUPTED_GOTO(i == 1, error_tcur);
++ ASSERT((XFS_AGINO_TO_OFFSET(mp, trec.ir_startino) %
++ XFS_INODES_PER_CHUNK) == 0);
++
++ trec.ir_free &= ~XFS_INOBT_MASK(offset);
++ trec.ir_freecount--;
++
++ XFS_WANT_CORRUPTED_GOTO((rec.ir_free == trec.ir_free) &&
++ (rec.ir_freecount == trec.ir_freecount),
++ error_tcur);
++
++ error = xfs_inobt_update(tcur, &trec);
++ if (error)
++ goto error_tcur;
++
++ /*
++ * Update the perag and superblock.
++ */
++ be32_add_cpu(&agi->agi_freecount, -1);
++ xfs_ialloc_log_agi(tp, agbp, XFS_AGI_FREECOUNT);
++ pag->pagi_freecount--;
++
++ xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, -1);
++
++ error = xfs_check_agi_freecount(tcur, agi);
++ if (error)
++ goto error_tcur;
++ error = xfs_check_agi_freecount(cur, agi);
++ if (error)
++ goto error_tcur;
++
++ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
++ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
++ xfs_perag_put(pag);
++ *inop = ino;
++ return 0;
++
++error_tcur:
++ xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
++error_cur:
++ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
++ xfs_perag_put(pag);
++ return error;
++}
++
+ /*
+ * Allocate an inode on disk.
+ *
+@@ -1069,78 +1325,34 @@ out_error:
+ return XFS_ERROR(error);
+ }
+
+-/*
+- * Free disk inode. Carefully avoids touching the incore inode, all
+- * manipulations incore are the caller's responsibility.
+- * The on-disk inode is not changed by this operation, only the
+- * btree (free inode mask) is changed.
+- */
+-int
+-xfs_difree(
+- xfs_trans_t *tp, /* transaction pointer */
+- xfs_ino_t inode, /* inode to be freed */
+- xfs_bmap_free_t *flist, /* extents to free */
+- int *delete, /* set if inode cluster was deleted */
+- xfs_ino_t *first_ino) /* first inode in deleted cluster */
++STATIC int
++xfs_difree_inobt(
++ struct xfs_mount *mp,
++ struct xfs_trans *tp,
++ struct xfs_buf *agbp,
++ xfs_agino_t agino,
++ struct xfs_bmap_free *flist,
++ int *deleted,
++ xfs_ino_t *first_ino,
++ struct xfs_inobt_rec_incore *orec)
+ {
+- /* REFERENCED */
+- xfs_agblock_t agbno; /* block number containing inode */
+- xfs_buf_t *agbp; /* buffer containing allocation group header */
+- xfs_agino_t agino; /* inode number relative to allocation group */
+- xfs_agnumber_t agno; /* allocation group number */
+- xfs_agi_t *agi; /* allocation group header */
+- xfs_btree_cur_t *cur; /* inode btree cursor */
+- int error; /* error return value */
+- int i; /* result code */
+- int ilen; /* inodes in an inode cluster */
+- xfs_mount_t *mp; /* mount structure for filesystem */
+- int off; /* offset of inode in inode chunk */
+- xfs_inobt_rec_incore_t rec; /* btree record */
+- struct xfs_perag *pag;
++ struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
++ xfs_agnumber_t agno = be32_to_cpu(agi->agi_seqno);
++ struct xfs_perag *pag;
++ struct xfs_btree_cur *cur;
++ struct xfs_inobt_rec_incore rec;
++ int ilen;
++ int error;
++ int i;
++ int off;
+
+- mp = tp->t_mountp;
+-
+- /*
+- * Break up inode number into its components.
+- */
+- agno = XFS_INO_TO_AGNO(mp, inode);
+- if (agno >= mp->m_sb.sb_agcount) {
+- xfs_warn(mp, "%s: agno >= mp->m_sb.sb_agcount (%d >= %d).",
+- __func__, agno, mp->m_sb.sb_agcount);
+- ASSERT(0);
+- return XFS_ERROR(EINVAL);
+- }
+- agino = XFS_INO_TO_AGINO(mp, inode);
+- if (inode != XFS_AGINO_TO_INO(mp, agno, agino)) {
+- xfs_warn(mp, "%s: inode != XFS_AGINO_TO_INO() (%llu != %llu).",
+- __func__, (unsigned long long)inode,
+- (unsigned long long)XFS_AGINO_TO_INO(mp, agno, agino));
+- ASSERT(0);
+- return XFS_ERROR(EINVAL);
+- }
+- agbno = XFS_AGINO_TO_AGBNO(mp, agino);
+- if (agbno >= mp->m_sb.sb_agblocks) {
+- xfs_warn(mp, "%s: agbno >= mp->m_sb.sb_agblocks (%d >= %d).",
+- __func__, agbno, mp->m_sb.sb_agblocks);
+- ASSERT(0);
+- return XFS_ERROR(EINVAL);
+- }
+- /*
+- * Get the allocation group header.
+- */
+- error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
+- if (error) {
+- xfs_warn(mp, "%s: xfs_ialloc_read_agi() returned error %d.",
+- __func__, error);
+- return error;
+- }
+- agi = XFS_BUF_TO_AGI(agbp);
+ ASSERT(agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC));
+- ASSERT(agbno < be32_to_cpu(agi->agi_length));
++ ASSERT(XFS_AGINO_TO_AGBNO(mp, agino) < be32_to_cpu(agi->agi_length));
++
+ /*
+ * Initialize the cursor.
+ */
+- cur = xfs_inobt_init_cursor(mp, tp, agbp, agno);
++ cur = xfs_inobt_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_INO);
+
+ error = xfs_check_agi_freecount(cur, agi);
+ if (error)
+@@ -1180,7 +1392,7 @@ xfs_difree(
+ if (!(mp->m_flags & XFS_MOUNT_IKEEP) &&
+ (rec.ir_freecount == XFS_IALLOC_INODES(mp))) {
+
+- *delete = 1;
++ *deleted = 1;
+ *first_ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino);
+
+ /*
+@@ -1208,7 +1420,7 @@ xfs_difree(
+ agno, XFS_INO_TO_AGBNO(mp,rec.ir_startino)),
+ XFS_IALLOC_BLOCKS(mp), flist, mp);
+ } else {
+- *delete = 0;
++ *deleted = 0;
+
+ error = xfs_inobt_update(cur, &rec);
+ if (error) {
+@@ -1232,6 +1444,7 @@ xfs_difree(
+ if (error)
+ goto error0;
+
++ *orec = rec;
+ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+ return 0;
+
+@@ -1240,6 +1453,189 @@ error0:
+ return error;
+ }
+
++/*
++ * Free an inode in the free inode btree.
++ */
++STATIC int
++xfs_difree_finobt(
++ struct xfs_mount *mp,
++ struct xfs_trans *tp,
++ struct xfs_buf *agbp,
++ xfs_agino_t agino,
++ struct xfs_inobt_rec_incore *ibtrec) /* inobt record */
++{
++ struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
++ xfs_agnumber_t agno = be32_to_cpu(agi->agi_seqno);
++ struct xfs_btree_cur *cur;
++ struct xfs_inobt_rec_incore rec;
++ int offset = agino - ibtrec->ir_startino;
++ int error;
++ int i;
++
++ cur = xfs_inobt_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_FINO);
++
++ error = xfs_inobt_lookup(cur, ibtrec->ir_startino, XFS_LOOKUP_EQ, &i);
++ if (error)
++ goto error;
++ if (i == 0) {
++ /*
++ * If the record does not exist in the finobt, we must have just
++ * freed an inode in a previously fully allocated chunk. If not,
++ * something is out of sync.
++ */
++ XFS_WANT_CORRUPTED_GOTO(ibtrec->ir_freecount == 1, error);
++
++ error = xfs_inobt_insert_rec(cur, ibtrec->ir_freecount,
++ ibtrec->ir_free, &i);
++ if (error)
++ goto error;
++ ASSERT(i == 1);
++
++ goto out;
++ }
++
++ /*
++ * Read and update the existing record.
++ */
++ error = xfs_inobt_get_rec(cur, &rec, &i);
++ if (error)
++ goto error;
++ XFS_WANT_CORRUPTED_GOTO(i == 1, error);
++
++ rec.ir_free |= XFS_INOBT_MASK(offset);
++ rec.ir_freecount++;
++
++ XFS_WANT_CORRUPTED_GOTO((rec.ir_free == ibtrec->ir_free) &&
++ (rec.ir_freecount == ibtrec->ir_freecount),
++ error);
++
++ /*
++ * The content of inobt records should always match between the inobt
++ * and finobt. The lifecycle of records in the finobt is different from
++ * the inobt in that the finobt only tracks records with at least one
++ * free inode. This is to optimize lookup for inode allocation purposes.
++ * The following checks determine whether to update the existing record or
++ * remove it entirely.
++ */
++
++ if (rec.ir_freecount == XFS_IALLOC_INODES(mp) &&
++ !(mp->m_flags & XFS_MOUNT_IKEEP)) {
++ /*
++ * If all inodes are free and we're in !ikeep mode, the entire
++ * inode chunk has been deallocated. Remove the record from the
++ * finobt.
++ */
++ error = xfs_btree_delete(cur, &i);
++ if (error)
++ goto error;
++ ASSERT(i == 1);
++ } else {
++ /*
++ * The existing finobt record was modified and has a combination
++ * of allocated and free inodes or is completely free and ikeep
++ * is enabled. Update the record.
++ */
++ error = xfs_inobt_update(cur, &rec);
++ if (error)
++ goto error;
++ }
++
++out:
++ error = xfs_check_agi_freecount(cur, agi);
++ if (error)
++ goto error;
++
++ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
++ return 0;
++
++error:
++ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
++ return error;
++}
++
++/*
++ * Free disk inode. Carefully avoids touching the incore inode, all
++ * manipulations incore are the caller's responsibility.
++ * The on-disk inode is not changed by this operation, only the
++ * btree (free inode mask) is changed.
++ */
++int
++xfs_difree(
++ struct xfs_trans *tp, /* transaction pointer */
++ xfs_ino_t inode, /* inode to be freed */
++ struct xfs_bmap_free *flist, /* extents to free */
++ int *deleted,/* set if inode cluster was deleted */
++ xfs_ino_t *first_ino)/* first inode in deleted cluster */
++{
++ /* REFERENCED */
++ xfs_agblock_t agbno; /* block number containing inode */
++ struct xfs_buf *agbp; /* buffer for allocation group header */
++ xfs_agino_t agino; /* allocation group inode number */
++ xfs_agnumber_t agno; /* allocation group number */
++ int error; /* error return value */
++ struct xfs_mount *mp; /* mount structure for filesystem */
++ struct xfs_inobt_rec_incore rec;/* btree record */
++
++ mp = tp->t_mountp;
++
++ /*
++ * Break up inode number into its components.
++ */
++ agno = XFS_INO_TO_AGNO(mp, inode);
++ if (agno >= mp->m_sb.sb_agcount) {
++ xfs_warn(mp, "%s: agno >= mp->m_sb.sb_agcount (%d >= %d).",
++ __func__, agno, mp->m_sb.sb_agcount);
++ ASSERT(0);
++ return XFS_ERROR(EINVAL);
++ }
++ agino = XFS_INO_TO_AGINO(mp, inode);
++ if (inode != XFS_AGINO_TO_INO(mp, agno, agino)) {
++ xfs_warn(mp, "%s: inode != XFS_AGINO_TO_INO() (%llu != %llu).",
++ __func__, (unsigned long long)inode,
++ (unsigned long long)XFS_AGINO_TO_INO(mp, agno, agino));
++ ASSERT(0);
++ return XFS_ERROR(EINVAL);
++ }
++ agbno = XFS_AGINO_TO_AGBNO(mp, agino);
++ if (agbno >= mp->m_sb.sb_agblocks) {
++ xfs_warn(mp, "%s: agbno >= mp->m_sb.sb_agblocks (%d >= %d).",
++ __func__, agbno, mp->m_sb.sb_agblocks);
++ ASSERT(0);
++ return XFS_ERROR(EINVAL);
++ }
++ /*
++ * Get the allocation group header.
++ */
++ error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
++ if (error) {
++ xfs_warn(mp, "%s: xfs_ialloc_read_agi() returned error %d.",
++ __func__, error);
++ return error;
++ }
++
++ /*
++ * Fix up the inode allocation btree.
++ */
++ error = xfs_difree_inobt(mp, tp, agbp, agino, flist, deleted, first_ino,
++ &rec);
++ if (error)
++ goto error0;
++
++ /*
++ * Fix up the free inode btree.
++ */
++ if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
++ error = xfs_difree_finobt(mp, tp, agbp, agino, &rec);
++ if (error)
++ goto error0;
++ }
++
++ return 0;
++
++error0:
++ return error;
++}
++
+ STATIC int
+ xfs_imap_lookup(
+ struct xfs_mount *mp,
+@@ -1271,7 +1667,7 @@ xfs_imap_lookup(
+ * we have a record, we need to ensure it contains the inode number
+ * we are looking up.
+ */
+- cur = xfs_inobt_init_cursor(mp, tp, agbp, agno);
++ cur = xfs_inobt_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_INO);
+ error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_LE, &i);
+ if (!error) {
+ if (i)
+@@ -1482,6 +1878,8 @@ xfs_ialloc_log_agi(
+ offsetof(xfs_agi_t, agi_newino),
+ offsetof(xfs_agi_t, agi_dirino),
+ offsetof(xfs_agi_t, agi_unlinked),
++ offsetof(xfs_agi_t, agi_free_root),
++ offsetof(xfs_agi_t, agi_free_level),
+ sizeof(xfs_agi_t)
+ };
+ #ifdef DEBUG
+@@ -1491,14 +1889,39 @@ xfs_ialloc_log_agi(
+ ASSERT(agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC));
+ #endif
+ /*
+- * Compute byte offsets for the first and last fields.
++ * The growth of the agi buffer over time now requires that we interpret
++ * the buffer as two logical regions delineated at the end of the unlinked
++ * list. This is due to the size of the hash table and its location in the
++ * middle of the agi.
++ *
++ * For example, a request to log a field before agi_unlinked and a field
++ * after agi_unlinked could cause us to log the entire hash table and use
++ * an excessive amount of log space. To avoid this behavior, log the
++ * region up through agi_unlinked in one call and the region after
++ * agi_unlinked through the end of the structure in another.
+ */
+- xfs_btree_offsets(fields, offsets, XFS_AGI_NUM_BITS, &first, &last);
++ xfs_trans_buf_set_type(tp, bp, XFS_BLFT_AGI_BUF);
++
+ /*
+- * Log the allocation group inode header buffer.
++ * Compute byte offsets for the first and last fields in the first
++ * region and log agi buffer. This only logs up through agi_unlinked.
+ */
+- xfs_trans_buf_set_type(tp, bp, XFS_BLFT_AGI_BUF);
+- xfs_trans_log_buf(tp, bp, first, last);
++ if (fields & XFS_AGI_ALL_BITS_R1) {
++ xfs_btree_offsets(fields, offsets, XFS_AGI_NUM_BITS_R1,
++ &first, &last);
++ xfs_trans_log_buf(tp, bp, first, last);
++ }
++
++ /*
++ * Mask off the bits in the first region and calculate the first and last
++ * field offsets for any bits in the second region.
++ */
++ fields &= ~XFS_AGI_ALL_BITS_R1;
++ if (fields) {
++ xfs_btree_offsets(fields, offsets, XFS_AGI_NUM_BITS_R2,
++ &first, &last);
++ xfs_trans_log_buf(tp, bp, first, last);
++ }
+ }
+
+ #ifdef DEBUG
+diff --git a/libxfs/xfs_ialloc_btree.c b/libxfs/xfs_ialloc_btree.c
+index 0a29d73..c337389 100644
+--- a/libxfs/xfs_ialloc_btree.c
++++ b/libxfs/xfs_ialloc_btree.c
+@@ -30,7 +30,8 @@ xfs_inobt_dup_cursor(
+ struct xfs_btree_cur *cur)
+ {
+ return xfs_inobt_init_cursor(cur->bc_mp, cur->bc_tp,
+- cur->bc_private.a.agbp, cur->bc_private.a.agno);
++ cur->bc_private.a.agbp, cur->bc_private.a.agno,
++ cur->bc_btnum);
+ }
+
+ STATIC void
+@@ -47,6 +48,21 @@ xfs_inobt_set_root(
+ xfs_ialloc_log_agi(cur->bc_tp, agbp, XFS_AGI_ROOT | XFS_AGI_LEVEL);
+ }
+
++STATIC void
++xfs_finobt_set_root(
++ struct xfs_btree_cur *cur,
++ union xfs_btree_ptr *nptr,
++ int inc) /* level change */
++{
++ struct xfs_buf *agbp = cur->bc_private.a.agbp;
++ struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
++
++ agi->agi_free_root = nptr->s;
++ be32_add_cpu(&agi->agi_free_level, inc);
++ xfs_ialloc_log_agi(cur->bc_tp, agbp,
++ XFS_AGI_FREE_ROOT | XFS_AGI_FREE_LEVEL);
++}
++
+ STATIC int
+ xfs_inobt_alloc_block(
+ struct xfs_btree_cur *cur,
+@@ -154,6 +170,17 @@ xfs_inobt_init_ptr_from_cur(
+ ptr->s = agi->agi_root;
+ }
+
++STATIC void
++xfs_finobt_init_ptr_from_cur(
++ struct xfs_btree_cur *cur,
++ union xfs_btree_ptr *ptr)
++{
++ struct xfs_agi *agi = XFS_BUF_TO_AGI(cur->bc_private.a.agbp);
++
++ ASSERT(cur->bc_private.a.agno == be32_to_cpu(agi->agi_seqno));
++ ptr->s = agi->agi_free_root;
++}
++
+ STATIC __int64_t
+ xfs_inobt_key_diff(
+ struct xfs_btree_cur *cur,
+@@ -184,6 +211,7 @@ xfs_inobt_verify(
+ */
+ switch (block->bb_magic) {
+ case cpu_to_be32(XFS_IBT_CRC_MAGIC):
++ case cpu_to_be32(XFS_FIBT_CRC_MAGIC):
+ if (!xfs_sb_version_hascrc(&mp->m_sb))
+ return false;
+ if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_uuid))
+@@ -195,6 +223,7 @@ xfs_inobt_verify(
+ return false;
+ /* fall through */
+ case cpu_to_be32(XFS_IBT_MAGIC):
++ case cpu_to_be32(XFS_FIBT_MAGIC):
+ break;
+ default:
+ return 0;
+@@ -371,6 +400,28 @@ static const struct xfs_btree_ops xfs_inobt_ops = {
+ #endif
+ };
+
++static const struct xfs_btree_ops xfs_finobt_ops = {
++ .rec_len = sizeof(xfs_inobt_rec_t),
++ .key_len = sizeof(xfs_inobt_key_t),
++
++ .dup_cursor = xfs_inobt_dup_cursor,
++ .set_root = xfs_finobt_set_root,
++ .alloc_block = xfs_inobt_alloc_block,
++ .free_block = xfs_inobt_free_block,
++ .get_minrecs = xfs_inobt_get_minrecs,
++ .get_maxrecs = xfs_inobt_get_maxrecs,
++ .init_key_from_rec = xfs_inobt_init_key_from_rec,
++ .init_rec_from_key = xfs_inobt_init_rec_from_key,
++ .init_rec_from_cur = xfs_inobt_init_rec_from_cur,
++ .init_ptr_from_cur = xfs_finobt_init_ptr_from_cur,
++ .key_diff = xfs_inobt_key_diff,
++ .buf_ops = &xfs_inobt_buf_ops,
++#if defined(DEBUG) || defined(XFS_WARN)
++ .keys_inorder = xfs_inobt_keys_inorder,
++ .recs_inorder = xfs_inobt_recs_inorder,
++#endif
++};
++
+ /*
+ * Allocate a new inode btree cursor.
+ */
+@@ -379,7 +430,8 @@ xfs_inobt_init_cursor(
+ struct xfs_mount *mp, /* file system mount point */
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_buf *agbp, /* buffer for agi structure */
+- xfs_agnumber_t agno) /* allocation group number */
++ xfs_agnumber_t agno, /* allocation group number */
++ xfs_btnum_t btnum) /* ialloc or free ino btree */
+ {
+ struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
+ struct xfs_btree_cur *cur;
+@@ -388,11 +440,17 @@ xfs_inobt_init_cursor(
+
+ cur->bc_tp = tp;
+ cur->bc_mp = mp;
+- cur->bc_nlevels = be32_to_cpu(agi->agi_level);
+- cur->bc_btnum = XFS_BTNUM_INO;
++ cur->bc_btnum = btnum;
++ if (btnum == XFS_BTNUM_INO) {
++ cur->bc_nlevels = be32_to_cpu(agi->agi_level);
++ cur->bc_ops = &xfs_inobt_ops;
++ } else {
++ cur->bc_nlevels = be32_to_cpu(agi->agi_free_level);
++ cur->bc_ops = &xfs_finobt_ops;
++ }
++
+ cur->bc_blocklog = mp->m_sb.sb_blocklog;
+
+- cur->bc_ops = &xfs_inobt_ops;
+ if (xfs_sb_version_hascrc(&mp->m_sb))
+ cur->bc_flags |= XFS_BTREE_CRC_BLOCKS;
+
+diff --git a/libxfs/xfs_sb.c b/libxfs/xfs_sb.c
+index 7ee4612..ea89367 100644
+--- a/libxfs/xfs_sb.c
++++ b/libxfs/xfs_sb.c
+@@ -408,6 +408,8 @@ xfs_sb_from_disk(
+ to->sb_features_incompat = be32_to_cpu(from->sb_features_incompat);
+ to->sb_features_log_incompat =
+ be32_to_cpu(from->sb_features_log_incompat);
++ /* crc is only used on disk, not in memory; just init to 0 here. */
++ to->sb_crc = 0;
+ to->sb_pad = 0;
+ to->sb_pquotino = be64_to_cpu(from->sb_pquotino);
+ to->sb_lsn = be64_to_cpu(from->sb_lsn);
+@@ -485,6 +487,9 @@ xfs_sb_to_disk(
+ if (!fields)
+ return;
+
++ /* We should never write the crc here, it's updated in the IO path */
++ fields &= ~XFS_SB_CRC;
++
+ xfs_sb_quota_to_disk(to, from, &fields);
+ while (fields) {
+ f = (xfs_sb_field_t)xfs_lowbit64((__uint64_t)fields);
+diff --git a/libxfs/xfs_trans_resv.c b/libxfs/xfs_trans_resv.c
+index 1e59fad..870d4fc 100644
+--- a/libxfs/xfs_trans_resv.c
++++ b/libxfs/xfs_trans_resv.c
+@@ -81,6 +81,37 @@ xfs_calc_inode_res(
+ }
+
+ /*
++ * The free inode btree is a conditional feature and the log reservation
++ * requirements differ slightly from that of the traditional inode allocation
++ * btree. The finobt tracks records for inode chunks with at least one free inode.
++ * Therefore, a record can be removed from the tree for an inode allocation or
++ * free and the associated merge reservation is unconditional. This also covers
++ * the possibility of a split on record insertion.
++ *
++ * the free inode btree: max depth * block size
++ * the free inode btree entry: block size
++ *
++ * TODO: is the modify res really necessary? covered by the merge/split res?
++ * This seems to be the pattern of ifree, but not create_resv_alloc. Why?
++ */
++STATIC uint
++xfs_calc_finobt_res(
++ struct xfs_mount *mp,
++ int modify)
++{
++ uint res;
++
++ if (!xfs_sb_version_hasfinobt(&mp->m_sb))
++ return 0;
++
++ res = xfs_calc_buf_res(mp->m_in_maxlevels, XFS_FSB_TO_B(mp, 1));
++ if (modify)
++ res += (uint)XFS_FSB_TO_B(mp, 1);
++
++ return res;
++}
++
++/*
+ * Various log reservation values.
+ *
+ * These are based on the size of the file system block because that is what
+@@ -250,6 +281,7 @@ xfs_calc_remove_reservation(
+ * the superblock for the nlink flag: sector size
+ * the directory btree: (max depth + v2) * dir block size
+ * the directory inode's bmap btree: (max depth + v2) * block size
++ * the finobt
+ */
+ STATIC uint
+ xfs_calc_create_resv_modify(
+@@ -258,7 +290,8 @@ xfs_calc_create_resv_modify(
+ return xfs_calc_inode_res(mp, 2) +
+ xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
+ (uint)XFS_FSB_TO_B(mp, 1) +
+- xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1));
++ xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1)) +
++ xfs_calc_finobt_res(mp, 1);
+ }
+
+ /*
+@@ -268,6 +301,7 @@ xfs_calc_create_resv_modify(
+ * the inode blocks allocated: XFS_IALLOC_BLOCKS * blocksize
+ * the inode btree: max depth * blocksize
+ * the allocation btrees: 2 trees * (max depth - 1) * block size
++ * the finobt
+ */
+ STATIC uint
+ xfs_calc_create_resv_alloc(
+@@ -278,7 +312,8 @@ xfs_calc_create_resv_alloc(
+ xfs_calc_buf_res(XFS_IALLOC_BLOCKS(mp), XFS_FSB_TO_B(mp, 1)) +
+ xfs_calc_buf_res(mp->m_in_maxlevels, XFS_FSB_TO_B(mp, 1)) +
+ xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
+- XFS_FSB_TO_B(mp, 1));
++ XFS_FSB_TO_B(mp, 1)) +
++ xfs_calc_finobt_res(mp, 0);
+ }
+
+ STATIC uint
+@@ -296,6 +331,7 @@ __xfs_calc_create_reservation(
+ * the superblock for the nlink flag: sector size
+ * the inode btree: max depth * blocksize
+ * the allocation btrees: 2 trees * (max depth - 1) * block size
++ * the finobt
+ */
+ STATIC uint
+ xfs_calc_icreate_resv_alloc(
+@@ -305,7 +341,8 @@ xfs_calc_icreate_resv_alloc(
+ mp->m_sb.sb_sectsize +
+ xfs_calc_buf_res(mp->m_in_maxlevels, XFS_FSB_TO_B(mp, 1)) +
+ xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
+- XFS_FSB_TO_B(mp, 1));
++ XFS_FSB_TO_B(mp, 1)) +
++ xfs_calc_finobt_res(mp, 0);
+ }
+
+ STATIC uint
+@@ -359,6 +396,7 @@ xfs_calc_symlink_reservation(
+ * the on disk inode before ours in the agi hash list: inode cluster size
+ * the inode btree: max depth * blocksize
+ * the allocation btrees: 2 trees * (max depth - 1) * block size
++ * the finobt
+ */
+ STATIC uint
+ xfs_calc_ifree_reservation(
+@@ -374,7 +412,8 @@ xfs_calc_ifree_reservation(
+ xfs_calc_buf_res(2 + XFS_IALLOC_BLOCKS(mp) +
+ mp->m_in_maxlevels, 0) +
+ xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
+- XFS_FSB_TO_B(mp, 1));
++ XFS_FSB_TO_B(mp, 1)) +
++ xfs_calc_finobt_res(mp, 1);
+ }
+
+ /*
+diff --git a/man/man5/xfs.5 b/man/man5/xfs.5
+index 0f490f0..5e47c4c 100644
+--- a/man/man5/xfs.5
++++ b/man/man5/xfs.5
+@@ -1,6 +1,6 @@
+ .TH xfs 5
+ .SH NAME
+-xfs \- layout of the XFS filesystem
++xfs \- layout and mount options for the XFS filesystem
+ .SH DESCRIPTION
+ An XFS filesystem can reside on a regular disk partition or on a
+ logical volume.
+@@ -98,9 +98,210 @@ and by-handle (see
+ .BR open_by_handle (3))
+ interfaces.
+ .SH MOUNT OPTIONS
+-Refer to the
++The following XFS-specific mount options may be used when mounting
++an XFS filesystem. Other generic options may be used as well; refer to the
+ .BR mount (8)
+-manual entry for descriptions of the individual XFS mount options.
++manual page for more details.
++.TP
++.B allocsize=size
++Sets the buffered I/O end-of-file preallocation size when
++doing delayed allocation writeout. Valid values for this
++option are page size (typically 4KiB) through to 1GiB,
++inclusive, in power-of-2 increments.
++.sp
++The default behavior is for dynamic end-of-file
++preallocation size, which uses a set of heuristics to
++optimise the preallocation size based on the current
++allocation patterns within the file and the access patterns
++to the file. Specifying a fixed allocsize value turns off
++the dynamic behavior.
++.TP
++.BR attr2 | noattr2
++The options enable/disable an "opportunistic" improvement to
++be made in the way inline extended attributes are stored
++on-disk. When the new form is used for the first time when
++attr2 is selected (either when setting or removing extended
++attributes) the on-disk superblock feature bit field will be
++updated to reflect this format being in use.
++.sp
++The default behavior is determined by the on-disk feature
++bit indicating that attr2 behavior is active. If either
++mount option it set, then that becomes the new default used
++by the filesystem.
++.sp
++CRC enabled filesystems always use the attr2 format, and so
++will reject the noattr2 mount option if it is set.
++.TP
++.BR barrier | nobarrier
++Enables/disables the use of block layer write barriers for
++writes into the journal and for data integrity operations.
++This allows for drive level write caching to be enabled, for
++devices that support write barriers.
++.sp
++Barriers are enabled by default.
++.TP
++.BR discard | nodiscard
++Enable/disable the issuing of commands to let the block
++device reclaim space freed by the filesystem. This is
++useful for SSD devices, thinly provisioned LUNs and virtual
++machine images, but may have a performance impact.
++.sp
++Note: It is currently recommended that you use the fstrim
++application to discard unused blocks rather than the discard
++mount option because the performance impact of this option
++is quite severe. For this reason, nodiscard is the default.
++.TP
++.BR grpid | bsdgroups | nogrpid | sysvgroups
++These options define what group ID a newly created file
++gets. When grpid is set, it takes the group ID of the
++directory in which it is created; otherwise it takes the
++fsgid of the current process, unless the directory has the
++setgid bit set, in which case it takes the gid from the
++parent directory, and also gets the setgid bit set if it is
++a directory itself.
++.TP
++.B filestreams
++Make the data allocator use the filestreams allocation mode
++across the entire filesystem rather than just on directories
++configured to use it.
++.TP
++.BR ikeep | noikeep
++When ikeep is specified, XFS does not delete empty inode
++clusters and keeps them around on disk. When noikeep is
++specified, empty inode clusters are returned to the free
++space pool. noikeep is the default.
++.TP
++.BR inode32 | inode64
++When inode32 is specified, it indicates that XFS limits
++inode creation to locations which will not result in inode
++numbers with more than 32 bits of significance.
++.sp
++When inode64 is specified, it indicates that XFS is allowed
++to create inodes at any location in the filesystem,
++including those which will result in inode numbers occupying
++more than 32 bits of significance.
++.sp
++inode32 is provided for backwards compatibility with older
++systems and applications, since 64 bits inode numbers might
++cause problems for some applications that cannot handle
++large inode numbers. If applications are in use which do
++not handle inode numbers bigger than 32 bits, the inode32
++option should be specified.
++.sp
++For kernel v3.7 and later, inode64 is the default.
++.TP
++.BR largeio | nolargeio
++If "nolargeio" is specified, the optimal I/O reported in
++st_blksize by stat(2) will be as small as possible to allow
++user applications to avoid inefficient read/modify/write
++I/O. This is typically the page size of the machine, as
++this is the granularity of the page cache.
++.sp
++If "largeio" specified, a filesystem that was created with a
++"swidth" specified will return the "swidth" value (in bytes)
++in st_blksize. If the filesystem does not have a "swidth"
++specified but does specify an "allocsize" then "allocsize"
++(in bytes) will be returned instead. Otherwise the behavior
++is the same as if "nolargeio" was specified. nolargeio
++is the default.
++.TP
++.B logbufs=value
++Set the number of in-memory log buffers. Valid numbers
++range from 2\(en8 inclusive.
++.sp
++The default value is 8 buffers.
++.sp
++If the memory cost of 8 log buffers is too high on small
++systems, then it may be reduced at some cost to performance
++on metadata intensive workloads. The logbsize option below
++controls the size of each buffer and so is also relevant to
++this case.
++.TP
++.B logbsize=value
++Set the size of each in-memory log buffer. The size may be
++specified in bytes, or in kibibytes (KiB) with a "k" suffix.
++Valid sizes for version 1 and version 2 logs are 16384 (value=16k)
++and 32768 (value=32k). Valid sizes for version 2 logs also
++include 65536 (value=64k), 131072 (value=128k) and 262144 (value=256k). The
++logbsize must be an integer multiple of the log
++stripe unit configured at mkfs time.
++.sp
++The default value for version 1 logs is 32768, while the
++default value for version 2 logs is MAX(32768, log_sunit).
++.TP
++.BR logdev=device and rtdev=device
++Use an external log (metadata journal) and/or real-time device.
++An XFS filesystem has up to three parts: a data section, a log
++section, and a real-time section. The real-time section is
++optional, and the log section can be separate from the data
++section or contained within it.
++.TP
++.B noalign
++Data allocations will not be aligned at stripe unit
++boundaries. This is only relevant to filesystems created
++with non-zero data alignment parameters (sunit, swidth) by
++mkfs.
++.TP
++.B norecovery
++The filesystem will be mounted without running log recovery.
++If the filesystem was not cleanly unmounted, it is likely to
++be inconsistent when mounted in "norecovery" mode.
++Some files or directories may not be accessible because of this.
++Filesystems mounted "norecovery" must be mounted read-only or
++the mount will fail.
++.TP
++.B nouuid
++Don't check for double mounted file systems using the file
++system uuid. This is useful to mount LVM snapshot volumes,
++and often used in combination with "norecovery" for mounting
++read-only snapshots.
++.TP
++.B noquota
++Forcibly turns off all quota accounting and enforcement
++within the filesystem.
++.TP
++.B uquota/usrquota/uqnoenforce/quota
++User disk quota accounting enabled, and limits (optionally)
++enforced. Refer to xfs_quota(8) for further details.
++.TP
++.B gquota/grpquota/gqnoenforce
++Group disk quota accounting enabled and limits (optionally)
++enforced. Refer to xfs_quota(8) for further details.
++.TP
++.B pquota/prjquota/pqnoenforce
++Project disk quota accounting enabled and limits (optionally)
++enforced. Refer to xfs_quota(8) for further details.
++.TP
++.BR sunit=value " and " swidth=value
++Used to specify the stripe unit and width for a RAID device
++or a stripe volume. "value" must be specified in 512-byte
++block units. These options are only relevant to filesystems
++that were created with non-zero data alignment parameters.
++.sp
++The sunit and swidth parameters specified must be compatible
++with the existing filesystem alignment characteristics. In
++general, that means the only valid changes to sunit are
++increasing it by a power-of-2 multiple. Valid swidth values
++are any integer multiple of a valid sunit value.
++.sp
++Typically the only time these mount options are necessary if
++after an underlying RAID device has had it's geometry
++modified, such as adding a new disk to a RAID5 lun and
++reshaping it.
++.TP
++.B swalloc
++Data allocations will be rounded up to stripe width boundaries
++when the current end of file is being extended and the file
++size is larger than the stripe width size.
++.TP
++.B wsync
++When specified, all filesystem namespace operations are
++executed synchronously. This ensures that when the namespace
++operation (create, unlink, etc) completes, the change to the
++namespace is on stable storage. This is useful in HA setups
++where failover must not result in clients seeing
++inconsistent namespace presentation during or after a
++failover event.
+ .SH SEE ALSO
+ .BR xfsctl (3),
+ .BR mount (8),
+diff --git a/man/man8/mkfs.xfs.8 b/man/man8/mkfs.xfs.8
+index 8184e10..ad9ff3d 100644
+--- a/man/man8/mkfs.xfs.8
++++ b/man/man8/mkfs.xfs.8
+@@ -7,6 +7,9 @@ mkfs.xfs \- construct an XFS filesystem
+ .B \-b
+ .I block_size
+ ] [
++.B \-m
++.I global_metadata_options
++] [
+ .B \-d
+ .I data_section_options
+ ] [
+@@ -125,6 +128,48 @@ The default value is 4096 bytes (4 KiB), the minimum is 512, and the
+ maximum is 65536 (64 KiB).
+ XFS on Linux currently only supports pagesize or smaller blocks.
+ .TP
++.BI \-m " global_metadata_options"
++These options specify metadata format options that either apply to the entire
++filesystem or aren't easily characterised by a specific functionality group. The
++valid
++.I global_metadata_options
++are:
++.RS 1.2i
++.TP
++.BI crc= value
++This is used to create a filesystem which maintains and checks CRC information
++in all metadata objects on disk. The value is either 0 to disable the feature,
++or 1 to enable the use of CRCs.
++.IP
++CRCs enable enhanced error detection due to hardware issues, whilst the format
++changes also improves crash recovery algorithms and the ability of various tools
++to validate and repair metadata corruptions when they are found. The CRC
++algorithm used is CRC32c, so the overhead is dependent on CPU architecture as
++some CPUs have hardware acceleration of this algorithm. Typically the overhead
++of calculating and checking the CRCs is not noticable in normal operation.
++.IP
++By default,
++.B mkfs.xfs
++will not enable metadata CRCs.
++.TP
++.BI finobt= value
++This option enables the use of a separate free inode btree index in each
++allocation group. The value is either 0 to disable the feature, or 1 to create
++a free inode btree in each allocation group.
++.IP
++The free inode btree mirrors the existing allocated inode btree index which
++indexes both used and free inodes. The free inode btree does not index used
++inodes, allowing faster, more consistent inode allocation performance as
++filesystems age.
++.IP
++By default,
++.B mkfs.xfs
++will not create free inode btrees. This feature is also currently only available
++for filesystems created with the
++.B \-m crc=1
++option set.
++.RE
++.TP
+ .BI \-d " data_section_options"
+ These options specify the location, size, and other parameters of the
+ data section of the filesystem. The valid
+diff --git a/man/man8/xfs_check.8 b/man/man8/xfs_check.8
+deleted file mode 100644
+index 23027ca..0000000
+--- a/man/man8/xfs_check.8
++++ /dev/null
+@@ -1,203 +0,0 @@
+-.TH xfs_check 8
+-.SH NAME
+-xfs_check \- check XFS filesystem consistency
+-.SH SYNOPSIS
+-.B xfs_check
+-[
+-.B \-i
+-.I ino
+-] ... [
+-.B \-b
+-.I bno
+-] ... [
+-.B \-f
+-] [
+-.B \-s
+-] [
+-.B \-v
+-] [
+-.B \-l
+-.I logdev
+-]
+-.I device
+-.br
+-.B xfs_check \-V
+-.SH DESCRIPTION
+-.B xfs_check
+-checks whether an XFS filesystem is consistent.
+-It is normally run only when there is reason to believe that the
+-filesystem has a consistency problem.
+-The filesystem to be checked is specified by the
+-.I device
+-argument, which should be the disk or volume device for the filesystem.
+-Filesystems stored in files can also be checked, using the
+-.B \-f
+-flag. The filesystem should normally be unmounted or read-only
+-during the execution of
+-.BR xfs_check .
+-Otherwise, spurious problems are reported.
+-.PP
+-Note that
+-.B xfs_check
+-is deprecated and scheduled for removal in June 2014. Please use
+-.BR xfs_repair " " \-n
+-instead.
+-.SH
+-OPTIONS
+-.TP
+-.B \-f
+-Specifies that the filesystem image to be processed is stored in a
+-regular file at
+-.I device
+-(see the
+-.BR mkfs.xfs "(8) " \-d
+-.I file
+-option). This might happen if an image copy
+-of a filesystem has been made into an ordinary file.
+-.TP
+-.BI \-l " logdev"
+-Specifies the device where the filesystem's external log resides.
+-Only for those filesystems which use an external log. See the
+-.BR mkfs.xfs "(8) " \-l
+-option, and refer to
+-.BR xfs (5)
+-for a detailed description of the XFS log.
+-.TP
+-.B \-s
+-Specifies that only serious errors should be reported.
+-Serious errors are those that make it impossible to find major data
+-structures in the filesystem. This option can be used to cut down the
+-amount of output when there is a serious problem, when the output
+-might make it difficult to see what the real problem is.
+-.TP
+-.B \-v
+-Specifies verbose output; it is impossibly long for a
+-reasonably-sized filesystem.
+-This option is intended for internal use only.
+-.TP
+-.BI \-i " ino"
+-Specifies verbose behavior for the specified inode
+-.IR ino .
+-For instance, it can be used to locate all the blocks
+-associated with a given inode.
+-.TP
+-.BI \-b " bno"
+-Specifies verbose behavior for the specific filesystem block at
+-.IR bno .
+-For instance, it can be used to determine what a specific block
+-is used for. The block number is a "file system block number".
+-Conversion between disk addresses (i.e. addresses reported by
+-.BR xfs_bmap (8))
+-and file system blocks may be accomplished using
+-.BR xfs_db "(8)'s " convert
+-command.
+-.TP
+-.B \-V
+-Prints the version number and exits.
+-.PP
+-Any output that is produced when
+-.B xfs_check
+-is not run in verbose mode indicates that the filesystem has an
+-inconsistency. The filesystem can be repaired using either
+-.BR xfs_repair (8)
+-to fix the filesystem in place, or by using
+-.BR xfsdump (8)
+-and
+-.BR mkfs.xfs (8)
+-to dump the filesystem, make a new filesystem, then use
+-.BR xfsrestore (8)
+-to restore the data onto the new filesystem.
+-Note that xfsdump may fail on a corrupt filesystem.
+-However, if the filesystem is mountable, xfsdump can
+-be used to try and save important data before
+-repairing the filesystem with xfs_repair.
+-If the filesystem is not mountable though, xfs_repair is
+-the only viable option.
+-.SH DIAGNOSTICS
+-If the filesystem is completely corrupt, a core dump might
+-be produced instead of the message
+-.RS
+-.I device
+-.B is not a valid filesystem
+-.RE
+-.PP
+-If the filesystem is very large (has many files) then
+-.B xfs_check
+-might run out of memory. In this case the message
+-.RS
+-.B out of memory
+-.RE
+-is printed.
+-.PP
+-The following is a description of the most likely problems and the associated
+-messages.
+-Most of the diagnostics produced are only meaningful with an understanding
+-of the structure of the filesystem.
+-.TP
+-.BI "agf_freeblks " n ", counted " m " in ag " a
+-The freeblocks count in the allocation group header for allocation group
+-.I a
+-doesn't match the number of blocks counted free.
+-.TP
+-.BI "agf_longest " n ", counted " m " in ag " a
+-The longest free extent in the allocation group header for allocation group
+-.I a
+-doesn't match the longest free extent found in the allocation group.
+-.TP
+-.BI "agi_count " n ", counted " m " in ag " a
+-The allocated inode count in the allocation group header for allocation group
+-.I a
+-doesn't match the number of inodes counted in the allocation group.
+-.TP
+-.BI "agi_freecount " n ", counted " m " in ag " a
+-The free inode count in the allocation group header for allocation group
+-.I a
+-doesn't match the number of inodes counted free in the allocation group.
+-.TP
+-.BI "block " a/b " expected inum 0 got " i
+-The block number is specified as a pair
+-(allocation group number, block in the allocation group).
+-The block is used multiple times (shared), between multiple inodes.
+-This message usually follows a message of the next type.
+-.TP
+-.BI "block " a/b " expected type unknown got " y
+-The block is used multiple times (shared).
+-.TP
+-.BI "block " a/b " type unknown not expected
+-The block is unaccounted for (not in the freelist and not in use).
+-.TP
+-.BI "link count mismatch for inode " nnn " (name " xxx "), nlink " m ", counted " n
+-The inode has a bad link count (number of references in directories).
+-.TP
+-.BI "rtblock " b " expected inum 0 got " i
+-The block is used multiple times (shared), between multiple inodes.
+-This message usually follows a message of the next type.
+-.TP
+-.BI "rtblock " b " expected type unknown got " y
+-The real-time block is used multiple times (shared).
+-.TP
+-.BI "rtblock " b " type unknown not expected
+-The real-time block is unaccounted for (not in the freelist and not in use).
+-.TP
+-.BI "sb_fdblocks " n ", counted " m
+-The number of free data blocks recorded
+-in the superblock doesn't match the number counted free in the filesystem.
+-.TP
+-.BI "sb_frextents " n ", counted " m
+-The number of free real-time extents recorded
+-in the superblock doesn't match the number counted free in the filesystem.
+-.TP
+-.BI "sb_icount " n ", counted " m
+-The number of allocated inodes recorded
+-in the superblock doesn't match the number allocated in the filesystem.
+-.TP
+-.BI "sb_ifree " n ", counted " m
+-The number of free inodes recorded
+-in the superblock doesn't match the number free in the filesystem.
+-.SH SEE ALSO
+-.BR mkfs.xfs (8),
+-.BR xfsdump (8),
+-.BR xfsrestore (8),
+-.BR xfs_ncheck (8),
+-.BR xfs_repair (8),
+-.BR xfs (5).
+diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
+index 7a43a2c..4d8d4ff 100644
+--- a/man/man8/xfs_db.8
++++ b/man/man8/xfs_db.8
+@@ -38,8 +38,7 @@ commands may be run interactively (the default) or as arguments
+ on the command line. Multiple
+ .B \-c
+ arguments may be given. The commands are run in the sequence given,
+-then the program exits. This is the mechanism used to implement
+-.BR xfs_check (8).
++then the program exits.
+ .TP
+ .B \-f
+ Specifies that the filesystem image to be processed is stored in a
+@@ -56,14 +55,11 @@ an ordinary file with
+ .B \-F
+ Specifies that we want to continue even if the superblock magic is not
+ correct. For use in
+-.BR xfs_check
+-and
+ .BR xfs_metadump .
+ .TP
+ .B \-i
+ Allows execution on a mounted filesystem, provided it is mounted read-only.
+-Useful for shell scripts such as
+-.BR xfs_check (8),
++Useful for shell scripts
+ which must only operate on filesystems in a guaranteed consistent state
+ (either unmounted or mounted read-only). These semantics are slightly
+ different to that of the
+@@ -204,9 +200,7 @@ command can be given, presumably with different arguments than the previous one.
+ Get block usage and check filesystem consistency.
+ The information is saved for use by a subsequent
+ .BR blockuse ", " ncheck ", or " blocktrash
+-command. See
+-.BR xfs_check (8)
+-for more information.
++command.
+ .RS 1.0i
+ .TP 0.4i
+ .B \-b
+@@ -244,7 +238,7 @@ Trashing occurs to randomly selected bits in the chosen blocks.
+ This command is available only in debugging versions of
+ .BR xfs_db .
+ It is useful for testing
+-.BR xfs_repair "(8) and " xfs_check (8).
++.BR xfs_repair "(8).
+ .RS 1.0i
+ .TP 0.4i
+ .BR \-0 " | " -1 " | " -2 " | " -3
+@@ -1856,12 +1850,60 @@ and printable ASCII chars.
+ Many messages can come from the
+ .B check
+ .RB ( blockget )
+-command; these are documented in
+-.BR xfs_check (8).
++command.
++If the filesystem is completely corrupt, a core dump might
++be produced instead of the message
++.RS
++.I device
++.B is not a valid filesystem
++.RE
++.PP
++If the filesystem is very large (has many files) then
++.B check
++might run out of memory. In this case the message
++.RS
++.B out of memory
++.RE
++is printed.
++.PP
++The following is a description of the most likely problems and the associated
++messages.
++Most of the diagnostics produced are only meaningful with an understanding
++of the structure of the filesystem.
++.TP
++.BI "agf_freeblks " n ", counted " m " in ag " a
++The freeblocks count in the allocation group header for allocation group
++.I a
++doesn't match the number of blocks counted free.
++.TP
++.BI "agf_longest " n ", counted " m " in ag " a
++The longest free extent in the allocation group header for allocation group
++.I a
++doesn't match the longest free extent found in the allocation group.
++.TP
++.BI "agi_count " n ", counted " m " in ag " a
++The allocated inode count in the allocation group header for allocation group
++.I a
++doesn't match the number of inodes counted in the allocation group.
++.TP
++.BI "agi_freecount " n ", counted " m " in ag " a
++The free inode count in the allocation group header for allocation group
++.I a
++doesn't match the number of inodes counted free in the allocation group.
++.TP
++.BI "block " a/b " expected inum 0 got " i
++The block number is specified as a pair
++(allocation group number, block in the allocation group).
++The block is used multiple times (shared), between multiple inodes.
++This message usually follows a message of the next type.
++.TP
++.BI "block " a/b " expected type unknown got " y
++The block is used multiple times (shared).
++.TP
++.BI "block " a/b " type unknown not expected
+ .SH SEE ALSO
+ .BR mkfs.xfs (8),
+ .BR xfs_admin (8),
+-.BR xfs_check (8),
+ .BR xfs_copy (8),
+ .BR xfs_logprint (8),
+ .BR xfs_metadump (8),
+diff --git a/man/man8/xfs_mdrestore.8 b/man/man8/xfs_mdrestore.8
+index 51297c4..0957d81 100644
+--- a/man/man8/xfs_mdrestore.8
++++ b/man/man8/xfs_mdrestore.8
+@@ -48,7 +48,6 @@ returns an exit code of 0 if all the metadata is successfully restored or
+ .SH SEE ALSO
+ .BR xfs_metadump (8),
+ .BR xfs_repair (8),
+-.BR xfs_check (8),
+ .BR xfs (5)
+ .SH BUGS
+ Email bug reports to
+diff --git a/man/man8/xfs_ncheck.8 b/man/man8/xfs_ncheck.8
+index 4099772..5ae72b2 100644
+--- a/man/man8/xfs_ncheck.8
++++ b/man/man8/xfs_ncheck.8
+@@ -63,13 +63,12 @@ May be given multiple times to select multiple inode numbers.
+ Prints the version number and exits.
+ .PP
+ If the filesystem is seriously corrupted, or very busy and looks
+-like it is corrupt, a message of the form that would be generated by
+-.BR xfs_check (8)
+-may appear.
++like it is corrupt, a message of the form that would be generated by the
++.BR xfs_db (8)
++"check" command may appear.
+ .PP
+ .B xfs_ncheck
+ is only useful with XFS filesystems.
+ .SH SEE ALSO
+ .BR mkfs.xfs (8),
+-.BR xfs_check (8),
+ .BR xfs (5).
+diff --git a/man/man8/xfs_repair.8 b/man/man8/xfs_repair.8
+index b7c2d8c..0394c50 100644
+--- a/man/man8/xfs_repair.8
++++ b/man/man8/xfs_repair.8
+@@ -548,6 +548,5 @@ fixed and/or improved.
+ .BR mkfs.xfs (8),
+ .BR umount (8),
+ .BR xfs_admin (8),
+-.BR xfs_check (8),
+ .BR xfs_metadump (8),
+ .BR xfs (5).
+diff --git a/mkfs/proto.c b/mkfs/proto.c
+index 5a47e27..72068f0 100644
+--- a/mkfs/proto.c
++++ b/mkfs/proto.c
+@@ -197,7 +197,6 @@ rsvfile(
+ tp = libxfs_trans_alloc(mp, 0);
+
+ libxfs_trans_ijoin(tp, ip, 0);
+- libxfs_trans_ihold(tp, ip);
+
+ ip->i_d.di_mode &= ~S_ISUID;
+
+@@ -464,7 +463,6 @@ parseproto(
+ libxfs_trans_ijoin(tp, pip, 0);
+ xname.type = XFS_DIR3_FT_REG_FILE;
+ newdirent(mp, tp, pip, &xname, ip->i_ino, &first, &flist);
+- libxfs_trans_ihold(tp, pip);
+ break;
+
+ case IF_RESERVED: /* pre-allocated space only */
+@@ -481,7 +479,6 @@ parseproto(
+
+ xname.type = XFS_DIR3_FT_REG_FILE;
+ newdirent(mp, tp, pip, &xname, ip->i_ino, &first, &flist);
+- libxfs_trans_ihold(tp, pip);
+ libxfs_trans_log_inode(tp, ip, flags);
+
+ error = libxfs_bmap_finish(&tp, &flist, &committed);
+@@ -489,6 +486,7 @@ parseproto(
+ fail(_("Pre-allocated file creation failed"), error);
+ libxfs_trans_commit(tp, 0);
+ rsvfile(mp, ip, llen);
++ IRELE(ip);
+ return;
+
+ case IF_BLOCK:
+@@ -503,7 +501,6 @@ parseproto(
+ libxfs_trans_ijoin(tp, pip, 0);
+ xname.type = XFS_DIR3_FT_BLKDEV;
+ newdirent(mp, tp, pip, &xname, ip->i_ino, &first, &flist);
+- libxfs_trans_ihold(tp, pip);
+ flags |= XFS_ILOG_DEV;
+ break;
+
+@@ -518,7 +515,6 @@ parseproto(
+ libxfs_trans_ijoin(tp, pip, 0);
+ xname.type = XFS_DIR3_FT_CHRDEV;
+ newdirent(mp, tp, pip, &xname, ip->i_ino, &first, &flist);
+- libxfs_trans_ihold(tp, pip);
+ flags |= XFS_ILOG_DEV;
+ break;
+
+@@ -531,7 +527,6 @@ parseproto(
+ libxfs_trans_ijoin(tp, pip, 0);
+ xname.type = XFS_DIR3_FT_FIFO;
+ newdirent(mp, tp, pip, &xname, ip->i_ino, &first, &flist);
+- libxfs_trans_ihold(tp, pip);
+ break;
+ case IF_SYMLINK:
+ buf = getstr(pp);
+@@ -545,7 +540,6 @@ parseproto(
+ libxfs_trans_ijoin(tp, pip, 0);
+ xname.type = XFS_DIR3_FT_SYMLINK;
+ newdirent(mp, tp, pip, &xname, ip->i_ino, &first, &flist);
+- libxfs_trans_ihold(tp, pip);
+ break;
+ case IF_DIRECTORY:
+ getres(tp, 0);
+@@ -565,7 +559,6 @@ parseproto(
+ newdirent(mp, tp, pip, &xname, ip->i_ino,
+ &first, &flist);
+ pip->i_d.di_nlink++;
+- libxfs_trans_ihold(tp, pip);
+ libxfs_trans_log_inode(tp, pip, XFS_ILOG_CORE);
+ }
+ newdirectory(mp, tp, ip, pip);
+@@ -573,7 +566,6 @@ parseproto(
+ error = libxfs_bmap_finish(&tp, &flist, &committed);
+ if (error)
+ fail(_("Directory creation failed"), error);
+- libxfs_trans_ihold(tp, ip);
+ libxfs_trans_commit(tp, 0);
+ /*
+ * RT initialization. Do this here to ensure that
+@@ -590,7 +582,7 @@ parseproto(
+ break;
+ parseproto(mp, ip, fsxp, pp, name);
+ }
+- libxfs_iput(ip, 0);
++ IRELE(ip);
+ return;
+ default:
+ ASSERT(0);
+@@ -603,6 +595,7 @@ parseproto(
+ error);
+ }
+ libxfs_trans_commit(tp, 0);
++ IRELE(ip);
+ }
+
+ void
+@@ -665,7 +658,6 @@ rtinit(
+ *(__uint64_t *)&rbmip->i_d.di_atime = 0;
+ libxfs_trans_log_inode(tp, rbmip, XFS_ILOG_CORE);
+ libxfs_mod_sb(tp, XFS_SB_RBMINO);
+- libxfs_trans_ihold(tp, rbmip);
+ mp->m_rbmip = rbmip;
+ error = libxfs_inode_alloc(&tp, NULL, S_IFREG, 1, 0,
+ &creds, &fsxattrs, &rsumip);
+@@ -676,7 +668,6 @@ rtinit(
+ rsumip->i_d.di_size = mp->m_rsumsize;
+ libxfs_trans_log_inode(tp, rsumip, XFS_ILOG_CORE);
+ libxfs_mod_sb(tp, XFS_SB_RSUMINO);
+- libxfs_trans_ihold(tp, rsumip);
+ libxfs_trans_commit(tp, 0);
+ mp->m_rsumip = rsumip;
+ /*
+@@ -689,7 +680,6 @@ rtinit(
+ res_failed(i);
+
+ libxfs_trans_ijoin(tp, rbmip, 0);
+- libxfs_trans_ihold(tp, rbmip);
+ bno = 0;
+ xfs_bmap_init(&flist, &first);
+ while (bno < mp->m_sb.sb_rbmblocks) {
+@@ -726,7 +716,6 @@ rtinit(
+ if (i)
+ res_failed(i);
+ libxfs_trans_ijoin(tp, rsumip, 0);
+- libxfs_trans_ihold(tp, rsumip);
+ bno = 0;
+ xfs_bmap_init(&flist, &first);
+ while (bno < nsumblocks) {
+@@ -762,7 +751,6 @@ rtinit(
+ if (i)
+ res_failed(i);
+ libxfs_trans_ijoin(tp, rbmip, 0);
+- libxfs_trans_ihold(tp, rbmip);
+ xfs_bmap_init(&flist, &first);
+ ebno = XFS_RTMIN(mp->m_sb.sb_rextents,
+ bno + NBBY * mp->m_sb.sb_blocksize);
+diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
+index 37c05a9..c85258a 100644
+--- a/mkfs/xfs_mkfs.c
++++ b/mkfs/xfs_mkfs.c
+@@ -183,6 +183,8 @@ char *sopts[] = {
+ char *mopts[] = {
+ #define M_CRC 0
+ "crc",
++#define M_FINOBT 1
++ "finobt",
+ NULL
+ };
+
+@@ -455,11 +457,30 @@ static void get_topology(
+ int force_overwrite)
+ {
+ if (!xi->disfile) {
+- const char *dfile = xi->volname ? xi->volname : xi->dname;
++ char *dfile = xi->volname ? xi->volname : xi->dname;
++ struct stat statbuf;
+
+- blkid_get_topology(dfile, &ft->dsunit, &ft->dswidth,
+- &ft->lsectorsize, &ft->psectorsize,
+- force_overwrite);
++ /*
++ * If our target is a regular file, and xi->disfile isn't
++ * set (i.e. no "-d file" invocation), 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 (!stat(dfile, &statbuf) && S_ISREG(statbuf.st_mode)) {
++ int fd;
++ long long dummy;
++
++ fd = open(dfile, O_RDONLY);
++ if (fd >= 0) {
++ platform_findsizes(dfile, fd, &dummy,
++ &ft->lsectorsize);
++ close(fd);
++ }
++ } else {
++ blkid_get_topology(dfile, &ft->dsunit, &ft->dswidth,
++ &ft->lsectorsize, &ft->psectorsize,
++ force_overwrite);
++ }
+ }
+
+ if (xi->rtname && !xi->risfile) {
+@@ -962,6 +983,7 @@ main(
+ struct fs_topology ft;
+ int lazy_sb_counters;
+ int crcs_enabled;
++ int finobt;
+
+ progname = basename(argv[0]);
+ setlocale(LC_ALL, "");
+@@ -995,6 +1017,7 @@ main(
+ worst_freelist = 0;
+ lazy_sb_counters = 1;
+ crcs_enabled = 0;
++ finobt = 0;
+ memset(&fsx, 0, sizeof(fsx));
+
+ memset(&xi, 0, sizeof(xi));
+@@ -1486,6 +1509,14 @@ _("cannot specify both crc and ftype\n"));
+ usage();
+ }
+ break;
++ case M_FINOBT:
++ if (!value || *value == '\0')
++ reqval('m', mopts, M_CRC);
++ c = atoi(value);
++ if (c < 0 || c > 1)
++ illegal(value, "m finobt");
++ finobt = c;
++ break;
+ default:
+ unknown('m', value);
+ }
+@@ -1827,6 +1858,16 @@ _("32 bit Project IDs always enabled on CRC enabled filesytems\n"));
+ }
+ }
+
++ /*
++ * The kernel doesn't currently support crc=0,finobt=1 filesystems.
++ * Catch it here, disable finobt and warn the user.
++ */
++ if (finobt && !crcs_enabled) {
++ fprintf(stderr,
++_("warning: finobt not supported without CRC support, disabled.\n"));
++ finobt = 0;
++ }
++
+ if (nsflag || nlflag) {
+ if (dirblocksize < blocksize ||
+ dirblocksize > XFS_MAX_BLOCKSIZE) {
+@@ -2413,6 +2454,30 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
+ mp->m_blkbb_log = sbp->sb_blocklog - BBSHIFT;
+ mp->m_sectbb_log = sbp->sb_sectlog - BBSHIFT;
+
++ /*
++ * sb_versionnum and finobt flags must be set before we use
++ * XFS_PREALLOC_BLOCKS().
++ */
++ sbp->sb_features2 = XFS_SB_VERSION2_MKFS(crcs_enabled, lazy_sb_counters,
++ attrversion == 2, !projid16bit, 0,
++ (!crcs_enabled && dirftype));
++ sbp->sb_versionnum = XFS_SB_VERSION_MKFS(crcs_enabled, iaflag,
++ dsunit != 0,
++ logversion == 2, attrversion == 1,
++ (sectorsize != BBSIZE ||
++ lsectorsize != BBSIZE),
++ nci, sbp->sb_features2 != 0);
++ /*
++ * Due to a structure alignment issue, sb_features2 ended up in one
++ * of two locations, the second "incorrect" location represented by
++ * the sb_bad_features2 field. To avoid older kernels mounting
++ * filesystems they shouldn't, set both field to the same value.
++ */
++ sbp->sb_bad_features2 = sbp->sb_features2;
++
++ if (finobt)
++ sbp->sb_features_ro_compat = XFS_SB_FEAT_RO_COMPAT_FINOBT;
++
+ if (loginternal) {
+ /*
+ * Readjust the log size to fit within an AG if it was sized
+@@ -2475,7 +2540,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
+ printf(_(
+ "meta-data=%-22s isize=%-6d agcount=%lld, agsize=%lld blks\n"
+ " =%-22s sectsz=%-5u attr=%u, projid32bit=%u\n"
+- " =%-22s crc=%u\n"
++ " =%-22s crc=%-8u finobt=%u\n"
+ "data =%-22s bsize=%-6u blocks=%llu, imaxpct=%u\n"
+ " =%-22s sunit=%-6u swidth=%u blks\n"
+ "naming =version %-14u bsize=%-6u ascii-ci=%d ftype=%d\n"
+@@ -2484,7 +2549,7 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
+ "realtime =%-22s extsz=%-6d blocks=%lld, rtextents=%lld\n"),
+ dfile, isize, (long long)agcount, (long long)agsize,
+ "", sectorsize, attrversion, !projid16bit,
+- "", crcs_enabled,
++ "", crcs_enabled, finobt,
+ "", blocksize, (long long)dblocks, imaxpct,
+ "", dsunit, dswidth,
+ dirversion, dirblocksize, nci, dirftype,
+@@ -2553,23 +2618,6 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
+ sbp->sb_logsectsize = 0;
+ }
+
+- sbp->sb_features2 = XFS_SB_VERSION2_MKFS(crcs_enabled, lazy_sb_counters,
+- attrversion == 2, !projid16bit, 0,
+- (!crcs_enabled && dirftype));
+- sbp->sb_versionnum = XFS_SB_VERSION_MKFS(crcs_enabled, iaflag,
+- dsunit != 0,
+- logversion == 2, attrversion == 1,
+- (sectorsize != BBSIZE ||
+- lsectorsize != BBSIZE),
+- nci, sbp->sb_features2 != 0);
+- /*
+- * Due to a structure alignment issue, sb_features2 ended up in one
+- * of two locations, the second "incorrect" location represented by
+- * the sb_bad_features2 field. To avoid older kernels mounting
+- * filesystems they shouldn't, set both field to the same value.
+- */
+- sbp->sb_bad_features2 = sbp->sb_features2;
+-
+ if (force_overwrite)
+ zero_old_xfs_structures(&xi, sbp);
+
+@@ -2726,6 +2774,10 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
+ agi->agi_count = 0;
+ agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
+ agi->agi_level = cpu_to_be32(1);
++ if (finobt) {
++ agi->agi_free_root = cpu_to_be32(XFS_FIBT_BLOCK(mp));
++ agi->agi_free_level = cpu_to_be32(1);
++ }
+ agi->agi_freecount = 0;
+ agi->agi_newino = cpu_to_be32(NULLAGINO);
+ agi->agi_dirino = cpu_to_be32(NULLAGINO);
+@@ -2851,6 +2903,26 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
+ xfs_btree_init_block(mp, buf, XFS_IBT_MAGIC, 0, 0,
+ agno, 0);
+ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
++
++ /*
++ * Free INO btree root block
++ */
++ if (!finobt)
++ continue;
++
++ buf = libxfs_getbuf(mp->m_ddev_targp,
++ XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
++ bsize);
++ buf->b_ops = &xfs_inobt_buf_ops;
++ block = XFS_BUF_TO_BLOCK(buf);
++ memset(block, 0, blocksize);
++ if (xfs_sb_version_hascrc(&mp->m_sb))
++ xfs_btree_init_block(mp, buf, XFS_FIBT_CRC_MAGIC, 0, 0,
++ agno, XFS_BTREE_CRC_BLOCKS);
++ else
++ xfs_btree_init_block(mp, buf, XFS_FIBT_MAGIC, 0, 0,
++ agno, 0);
++ libxfs_writebuf(buf, LIBXFS_EXIT_ON_FAILURE);
+ }
+
+ /*
+@@ -3087,7 +3159,7 @@ usage( void )
+ {
+ fprintf(stderr, _("Usage: %s\n\
+ /* blocksize */ [-b log=n|size=num]\n\
+-/* metadata */ [-m crc=[0|1]\n\
++/* metadata */ [-m crc=0|1,finobt=0|1]\n\
+ /* data subvol */ [-d agcount=n,agsize=n,file,name=xxx,size=num,\n\
+ (sunit=value,swidth=value|su=num,sw=num|noalign),\n\
+ sectlog=n|sectsize=num\n\
+diff --git a/po/pl.po b/po/pl.po
+index ebd1884..80a09a8 100644
+--- a/po/pl.po
++++ b/po/pl.po
+@@ -1,13 +1,13 @@
+ # Polish translation for xfsprogs.
+ # This file is distributed under the same license as the xfsprogs package.
+-# Jakub Bogusz <qboosh at pld-linux.org>, 2006-2012.
++# Jakub Bogusz <qboosh at pld-linux.org>, 2006-2014.
+ #
+ msgid ""
+ msgstr ""
+-"Project-Id-Version: xfsprogs 3.1.8\n"
++"Project-Id-Version: xfsprogs 3.2.0\n"
+ "Report-Msgid-Bugs-To: \n"
+-"POT-Creation-Date: 2012-03-04 06:24+0100\n"
+-"PO-Revision-Date: 2012-03-04 07:30+0100\n"
++"POT-Creation-Date: 2014-05-21 18:34+0200\n"
++"PO-Revision-Date: 2014-05-21 18:41+0200\n"
+ "Last-Translator: Jakub Bogusz <qboosh at pld-linux.org>\n"
+ "Language-Team: Polish <translation-team-pl at lists.sourceforge.net>\n"
+ "Language: pl\n"
+@@ -15,6611 +15,6890 @@ msgstr ""
+ "Content-Type: text/plain; charset=UTF-8\n"
+ "Content-Transfer-Encoding: 8bit\n"
+
+-#: .././rtcp/xfs_rtcp.c:30
++#: .././copy/xfs_copy.c:102
+ #, c-format
+-msgid "%s [-e extsize] [-p] source target\n"
+-msgstr "%s [-e rozm_fragmentu] [-p] źródło cel\n"
++msgid "Check logfile \"%s\" for more details\n"
++msgstr "Więcej szczegółów w pliku logu \"%s\"\n"
+
+-#: .././rtcp/xfs_rtcp.c:55 .././repair/xfs_repair.c:317 .././quota/init.c:131
+-#: .././mkfs/xfs_mkfs.c:1623 .././logprint/logprint.c:196 .././io/init.c:183
+-#: .././growfs/xfs_growfs.c:182 .././fsr/xfs_fsr.c:302
+-#: .././estimate/xfs_estimate.c:141 .././db/init.c:93 .././copy/xfs_copy.c:543
++#: .././copy/xfs_copy.c:108
+ #, c-format
+-msgid "%s version %s\n"
+-msgstr "%s wersja %s\n"
++msgid "%s: could not write to logfile \"%s\".\n"
++msgstr "%s: nie udało się zapisać pliku logu \"%s\".\n"
+
+-#: .././rtcp/xfs_rtcp.c:69
++#: .././copy/xfs_copy.c:111
+ #, c-format
+-msgid "%s: must specify files to copy\n"
+-msgstr "%s: trzeba podać pliki do skopiowania\n"
++msgid "Aborting XFS copy -- logfile error -- reason: %s\n"
++msgstr "Przerwano XFS copy - błąd pliku logu - przyczyna: %s\n"
+
+-#: .././rtcp/xfs_rtcp.c:84
+-#, c-format
+-msgid "%s: stat64 of %s failed\n"
+-msgstr "%s: stat64 na %s nie powiodło się\n"
++#: .././copy/xfs_copy.c:126 .././copy/xfs_copy.c:286 .././copy/xfs_copy.c:566
++#: .././copy/xfs_copy.c:573
++msgid "Aborting XFS copy - reason"
++msgstr "Przerwano XFS copy - przyczyna"
+
+-#: .././rtcp/xfs_rtcp.c:91
+-#, c-format
+-msgid "%s: final argument is not directory\n"
+-msgstr "%s: ostatni argument nie jest katalogiem\n"
++#: .././copy/xfs_copy.c:140
++msgid "THE FOLLOWING COPIES FAILED TO COMPLETE\n"
++msgstr "NASTĘPUJĄCYCH KOPII NIE UDAŁO SIĘ UKOŃCZYĆ\n"
+
+-#: .././rtcp/xfs_rtcp.c:138
+-#, c-format
+-msgid "%s: failed stat64 on %s: %s\n"
+-msgstr "%s: nie udało się wykonać stat64 na %s: %s\n"
++#: .././copy/xfs_copy.c:144
++msgid "write error"
++msgstr "błąd zapisu"
+
+-#: .././rtcp/xfs_rtcp.c:159
+-#, c-format
+-msgid "%s: %s filesystem has no realtime partition\n"
+-msgstr "%s: system plików %s nie ma partycji realtime\n"
++#: .././copy/xfs_copy.c:146
++msgid "lseek64 error"
++msgstr "błąd lseek64"
+
+-#: .././rtcp/xfs_rtcp.c:180 .././rtcp/xfs_rtcp.c:208
++#: .././copy/xfs_copy.c:147
+ #, c-format
+-msgid "%s: open of %s failed: %s\n"
+-msgstr "%s: otwarcie %s nie powiodło się: %s\n"
++msgid " at offset %lld\n"
++msgstr " pod offsetem %lld\n"
+
+-#: .././rtcp/xfs_rtcp.c:197
++#: .././copy/xfs_copy.c:151
+ #, c-format
+-msgid "%s: set attributes on %s failed: %s\n"
+-msgstr "%s: ustawienie atrybutów dla %s nie powiodło się: %s\n"
++msgid "All copies completed.\n"
++msgstr "Wszystkie kopie ukończone.\n"
+
+-#: .././rtcp/xfs_rtcp.c:215
++#: .././copy/xfs_copy.c:154
+ #, c-format
+-msgid "%s: get attributes of %s failed: %s\n"
+-msgstr "%s: pobranie atrybutów %s nie powiodło się: %s\n"
++msgid "See \"%s\" for more details.\n"
++msgstr "Więcej szczegółów w \"%s\".\n"
+
+-#: .././rtcp/xfs_rtcp.c:225 .././rtcp/xfs_rtcp.c:260
++#: .././copy/xfs_copy.c:255
+ #, c-format
+-msgid "%s: %s is not a realtime file.\n"
+-msgstr "%s: %s nie jest plikiem realtime.\n"
++msgid "%s: write error on target %d \"%s\" at offset %lld\n"
++msgstr "%s: błąd zapisu przy celu %d \"%s\" pod offsetem %lld\n"
+
+-#: .././rtcp/xfs_rtcp.c:234
++#: .././copy/xfs_copy.c:260
+ #, c-format
+-msgid "%s: %s file extent size is %d, instead of %d.\n"
+-msgstr "%s: plik %s ma rozmiar ekstentu %d zamiast %d.\n"
++msgid "%s: lseek64 error on target %d \"%s\" at offset %lld\n"
++msgstr "%s: błąd lseek64 przy celu %d \"%s\" pod offsetem %lld\n"
+
+-#: .././rtcp/xfs_rtcp.c:246 .././rtcp/xfs_rtcp.c:269
++#: .././copy/xfs_copy.c:266
+ #, c-format
+-msgid "%s: open of %s source failed: %s\n"
+-msgstr "%s: otwarcie źródła %s nie powiodło się: %s\n"
++msgid "Aborting target %d - reason"
++msgstr "Przerywano zapis celu %d - przyczyna"
+
+-#: .././rtcp/xfs_rtcp.c:283
+-#, c-format
+-msgid "%s: couldn't get direct I/O information: %s\n"
+-msgstr "%s: nie udało się uzyskać informacji o bezpośrednim we/wy: %s\n"
++#: .././copy/xfs_copy.c:270
++msgid "Aborting XFS copy - no more targets.\n"
++msgstr "Przerwano XFS copy - nie ma więcej celów.\n"
+
+-#: .././rtcp/xfs_rtcp.c:293
++#: .././copy/xfs_copy.c:281
+ #, c-format
+-msgid "%s: extent size %d not a multiple of %d.\n"
+-msgstr "%s: rozmiar ekstentu %d nie jest wielokrotnością %d.\n"
++msgid "%s: thread %d died unexpectedly, target \"%s\" incomplete\n"
++msgstr "%s: wątek %d zmarł nieoczekiwanie, cel \"%s\" niekompletny\n"
+
+-#: .././rtcp/xfs_rtcp.c:307
++#: .././copy/xfs_copy.c:283
+ #, c-format
+-msgid "The size of %s is not a multiple of %d.\n"
+-msgstr "Rozmiar %s nie jest wielokrotnością %d.\n"
++msgid "%s: offset was probably %lld\n"
++msgstr "%s: offset prawdopodobnie %lld\n"
+
+-#: .././rtcp/xfs_rtcp.c:310
++#: .././copy/xfs_copy.c:294
+ #, c-format
+-msgid "%s will be padded to %lld bytes.\n"
+-msgstr "%s: zostanie dopełniony do %lld bajtów.\n"
++msgid "%s: Unknown child died (should never happen!)\n"
++msgstr "%s: Nieznany potomek zmarł (nie powinno się zdarzyć!)\n"
+
+-#: .././rtcp/xfs_rtcp.c:316
++#: .././copy/xfs_copy.c:304
+ #, c-format
+-msgid "Use the -p option to pad %s to a size which is a multiple of %d bytes.\n"
+-msgstr "Można użyć opcji -p do dopełnienia %s do rozmiaru będącego wielokrotnością %d bajtów.\n"
++msgid "Usage: %s [-bdV] [-L logfile] source target [target ...]\n"
++msgstr "Składnia: %s [-bdV] [-L plik_logu] źródło cel [cel ...]\n"
+
+-#: .././rtcp/xfs_rtcp.c:358
++#: .././copy/xfs_copy.c:386
+ #, c-format
+-msgid "%s: write error: %s\n"
+-msgstr "%s: błąd zapisu: %s\n"
++msgid "%s: lseek64 failure at offset %lld\n"
++msgstr "%s: niepowodzenie lseek64 pod offsetem %lld\n"
+
+-#: .././rtcp/xfs_rtcp.c:386
++#: .././copy/xfs_copy.c:401
+ #, c-format
+-msgid "%s: could not open %s: %s\n"
+-msgstr "%s: nie udało się otworzyć %s: %s\n"
++msgid "assert error: buf->length = %d, buf->size = %d\n"
++msgstr "błąd zapewnienia: buf->length = %d, buf->size = %d\n"
+
+-#: .././repair/xfs_repair.c:81
++#: .././copy/xfs_copy.c:408
+ #, c-format
+-msgid ""
+-"Usage: %s [options] device\n"
+-"\n"
+-"Options:\n"
+-" -f The device is a file\n"
+-" -L Force log zeroing. Do this as a last resort.\n"
+-" -l logdev Specifies the device where the external log resides.\n"
+-" -m maxmem Maximum amount of memory to be used in megabytes.\n"
+-" -n No modify mode, just checks the filesystem for damage.\n"
+-" -P Disables prefetching.\n"
+-" -r rtdev Specifies the device where the realtime section resides.\n"
+-" -v Verbose output.\n"
+-" -c subopts Change filesystem parameters - use xfs_admin.\n"
+-" -o subopts Override default behaviour, refer to man page.\n"
+-" -t interval Reporting interval in minutes.\n"
+-" -d Repair dangerously.\n"
+-" -V Reports version and exits.\n"
+-msgstr ""
+-"Składnia: %s [opcje] urządzenie\n"
+-"\n"
+-"Opcje:\n"
+-" -f Urządzenie jest plikiem\n"
+-" -L Wymuszenie wyzerowania logu. Wykonywać tylko w ostateczności.\n"
+-" -l urz_logu Określenie urządzenia z zewnętrznym logiem.\n"
+-" -m maks_pam Maksymalna ilość pamięci do użycia w megabajtach.\n"
+-" -n Tryb bez modyfikacji, tylko sprawdzenie systemu plików.\n"
+-" -P Wyłączenie prefetch.\n"
+-" -r urz_rt Określenie urządzenia z sekcją realtime.\n"
+-" -v Szczegółowe wyjście.\n"
+-" -c podopcje Zmiana parametrów systemu plików przy użyciu xfs_admina.\n"
+-" -o podopcje Zmiana domyślnego zachowania, więcej na stronie manuala.\n"
+-" -t czas Okres informowania o postępach w minutach.\n"
+-" -d Naprawianie w sposób niebezpieczny.\n"
+-" -V Wypisanie informacji o wersji i zakończenie.\n"
+-
+-#: .././repair/xfs_repair.c:107
+-msgid "no error"
+-msgstr "brak błędu"
+-
+-#: .././repair/xfs_repair.c:108
+-msgid "bad magic number"
+-msgstr "błędna liczba magiczna"
+-
+-#: .././repair/xfs_repair.c:109
+-msgid "bad blocksize field"
+-msgstr "błędne pole blocksize"
+-
+-#: .././repair/xfs_repair.c:110
+-msgid "bad blocksize log field"
+-msgstr "błędne pole logu blocksize"
+-
+-#: .././repair/xfs_repair.c:111
+-msgid "bad or unsupported version"
+-msgstr "błędna lub nie obsługiwana wersja"
+-
+-#: .././repair/xfs_repair.c:113
+-msgid "filesystem mkfs-in-progress bit set"
+-msgstr "ustawiony bit mkfs-in-progress systemu plików"
++msgid "%s: read failure at offset %lld\n"
++msgstr "%s: błąd odczytu pod offsetem %lld\n"
+
+-#: .././repair/xfs_repair.c:115
+-msgid "inconsistent filesystem geometry information"
+-msgstr "niespójne informacje o geometrii systemu plików"
++#: .././copy/xfs_copy.c:438
++msgid "ag header buffer invalid!\n"
++msgstr "błędny bufor nagłówka ag!\n"
+
+-#: .././repair/xfs_repair.c:117
+-msgid "bad inode size or inconsistent with number of inodes/block"
+-msgstr "błędny rozmiar i-węzła lub niespójność z liczbą i-węzłów/blok"
++#: .././copy/xfs_copy.c:546 .././db/init.c:94 .././estimate/xfs_estimate.c:144
++#: .././fsr/xfs_fsr.c:300 .././growfs/xfs_growfs.c:180 .././io/init.c:190
++#: .././logprint/logprint.c:203 .././mkfs/xfs_mkfs.c:1672
++#: .././quota/init.c:131 .././repair/xfs_repair.c:319 .././rtcp/xfs_rtcp.c:55
++#, c-format
++msgid "%s version %s\n"
++msgstr "%s wersja %s\n"
+
+-#: .././repair/xfs_repair.c:118
+-msgid "bad sector size"
+-msgstr "błędny rozmiar sektora"
++#: .././copy/xfs_copy.c:564
++#, c-format
++msgid "%s: couldn't open log file \"%s\"\n"
++msgstr "%s: nie udało się otworzyć pliku logu \"%s\"\n"
+
+-#: .././repair/xfs_repair.c:120
+-msgid "AGF geometry info conflicts with filesystem geometry"
+-msgstr "informacje o geometrii AGF są w konflikcie z geometrią systemu plików"
++#: .././copy/xfs_copy.c:571
++#, c-format
++msgid "%s: couldn't set up logfile stream\n"
++msgstr "%s: nie udało się ustanowić strumienia pliku logu\n"
+
+-#: .././repair/xfs_repair.c:122
+-msgid "AGI geometry info conflicts with filesystem geometry"
+-msgstr "informacje o geometrii AGI są w konflikcie z geometrią systemu plików"
++#: .././copy/xfs_copy.c:583
++msgid "Couldn't allocate target array\n"
++msgstr "Nie udało się przydzielić tablicy celów\n"
+
+-#: .././repair/xfs_repair.c:124
+-msgid "AG superblock geometry info conflicts with filesystem geometry"
+-msgstr "informacje o geometrii superbloku AG są w konflikcie z geometrią systemu plików"
++#: .././copy/xfs_copy.c:598
++#, c-format
++msgid "%s: couldn't register atexit function.\n"
++msgstr "%s: nie udało się zarejestrować funkcji atexit.\n"
+
+-#: .././repair/xfs_repair.c:125
+-msgid "attempted to perform I/O beyond EOF"
+-msgstr "próbowano wykonać operację we/wy poza końcem pliku"
++#: .././copy/xfs_copy.c:607
++#, c-format
++msgid "%s: couldn't open source \"%s\"\n"
++msgstr "%s: nie udało się otworzyć źródła \"%s\"\n"
+
+-#: .././repair/xfs_repair.c:127
+-msgid "inconsistent filesystem geometry in realtime filesystem component"
+-msgstr "niespójna geometria systemu plików w składniku realtime"
++#: .././copy/xfs_copy.c:613
++#, c-format
++msgid "%s: couldn't stat source \"%s\"\n"
++msgstr "%s: nie udało się wykonać stat na źródle \"%s\"\n"
+
+-#: .././repair/xfs_repair.c:129
+-msgid "maximum indicated percentage of inodes > 100%"
+-msgstr "określono maksymalny procent i-węzłów > 100%"
++#: .././copy/xfs_copy.c:623
++#, c-format
++msgid "%s: Cannot set direct I/O flag on \"%s\".\n"
++msgstr "%s: Nie można ustawić flagi bezpośredniego we/wy na \"%s\".\n"
+
+-#: .././repair/xfs_repair.c:131
+-msgid "inconsistent inode alignment value"
+-msgstr "niespójna wartość wyrównania i-węzła"
++#: .././copy/xfs_copy.c:628
++#, c-format
++msgid "%s: xfsctl on file \"%s\" failed.\n"
++msgstr "%s: xfsctl na pliku \"%s\" nie powiodło się.\n"
+
+-#: .././repair/xfs_repair.c:133
+-msgid "not enough secondary superblocks with matching geometry"
+-msgstr "za mało zapasowych superbloków o pasującej geometrii"
++#: .././copy/xfs_copy.c:651
++#, c-format
++msgid "%s: Warning -- a filesystem is mounted on the source device.\n"
++msgstr "%s: Uwaga - system plików jest podmontowany na urządzeniu źródłowym.\n"
+
+-#: .././repair/xfs_repair.c:135
+-msgid "bad stripe unit in superblock"
+-msgstr "błędna jednostka pasa w superbloku"
++#: .././copy/xfs_copy.c:654
++msgid "\t\tGenerated copies may be corrupt unless the source is\n"
++msgstr "\t\tWygenerowane kopie mogą być uszkodzone o ile źródło nie jest\n"
+
+-#: .././repair/xfs_repair.c:137
+-msgid "bad stripe width in superblock"
+-msgstr "błędna szerokość pasa w superbloku"
++#: .././copy/xfs_copy.c:656
++msgid "\t\tunmounted or mounted read-only. Copy proceeding...\n"
++msgstr "\t\todmontowane lub podmontowane tylko do odczytu. Kopiowanie w trakcie...\n"
+
+-#: .././repair/xfs_repair.c:139
+-msgid "bad shared version number in superblock"
+-msgstr "błędny numer wersji współdzielenia w superbloku"
++#: .././copy/xfs_copy.c:673
++#, c-format
++msgid ""
++"%s: couldn't initialize XFS library\n"
++"%s: Aborting.\n"
++msgstr ""
++"%s: nie udało się zainicjować biblioteki XFS\n"
++"%s: Przerwano.\n"
+
+-#: .././repair/xfs_repair.c:144
++#: .././copy/xfs_copy.c:693
+ #, c-format
+-msgid "bad error code - %d\n"
+-msgstr "błędny kod błędu - %d\n"
++msgid "%s: Cannot yet copy V5 fs without '-d'\n"
++msgstr "%s: Nie można jeszcze kopiować systemu plików V5 bez '-d'\n"
+
+-#: .././repair/xfs_repair.c:152
++#: .././copy/xfs_copy.c:699
+ #, c-format
+-msgid "-%c %s option cannot have a value\n"
+-msgstr "opcja -%c %s nie przyjmuje wartości\n"
++msgid ""
++"%s: %s filesystem failed to initialize\n"
++"%s: Aborting.\n"
++msgstr ""
++"%s: Nie powiodła się inicjalizacja systemu plików %s\n"
++"%s: Przerwano.\n"
+
+-#: .././repair/xfs_repair.c:162 .././mkfs/xfs_mkfs.c:2801
++#: .././copy/xfs_copy.c:703
+ #, c-format
+-msgid "option respecified\n"
+-msgstr "ponownie podana opcja\n"
++msgid ""
++"%s %s filesystem failed to initialize\n"
++"%s: Aborting.\n"
++msgstr ""
++"%s: Nie powiodła się inicjalizacja systemu plików %s\n"
++"%s: Przerwano.\n"
+
+-#: .././repair/xfs_repair.c:169 .././mkfs/xfs_mkfs.c:2810
++#: .././copy/xfs_copy.c:707
+ #, c-format
+-msgid "unknown option -%c %s\n"
+-msgstr "nieznana opcja -%c %s\n"
+-
+-#: .././repair/xfs_repair.c:248
+-msgid "-o bhash option cannot be used with -m option\n"
+-msgstr "opcja -o bhash nie może być użyta wraz z opcją -m\n"
+-
+-#: .././repair/xfs_repair.c:300
+-msgid "-m option cannot be used with -o bhash option\n"
+-msgstr "opcja -m nie może być użyta wraz z opcją -o bhash\n"
++msgid ""
++"%s: %s has an external log.\n"
++"%s: Aborting.\n"
++msgstr ""
++"%s: %s ma zewnętrzny log.\n"
++"%s: Przerwano.\n"
+
+-#: .././repair/xfs_repair.c:342
++#: .././copy/xfs_copy.c:711
+ #, c-format
+ msgid ""
+-"\n"
+-"fatal error -- "
++"%s: %s has a real-time section.\n"
++"%s: Aborting.\n"
+ msgstr ""
+-"\n"
+-"błąd krytyczny - "
++"%s: %s ma sekcję real-time.\n"
++"%s: Przerwano.\n"
+
+-#: .././repair/xfs_repair.c:454
+-#, c-format
+-msgid "sb root inode value %<PRIu64> %sinconsistent with calculated value %u\n"
+-msgstr "wartość i-węzła głównego superbloku %<PRIu64> %sniespójna z obliczoną wartością %u\n"
++#: .././copy/xfs_copy.c:736
++msgid ""
++"Error: filesystem block size is smaller than the disk sectorsize.\n"
++"Aborting XFS copy now.\n"
++msgstr ""
++"Błąd: rozmiar bloku systemu plików jest mniejszy niż rozmiar sektora dysku.\n"
++"Przerwano XFS copy.\n"
+
+-#: .././repair/xfs_repair.c:461
++#: .././copy/xfs_copy.c:757
+ #, c-format
+-msgid "resetting superblock root inode pointer to %u\n"
+-msgstr "przestawiono wskaźnik i-węzła głównego superbloku na %u\n"
++msgid "Creating file %s\n"
++msgstr "Tworzenie pliku %s\n"
+
+-#: .././repair/xfs_repair.c:465
++#: .././copy/xfs_copy.c:775
+ #, c-format
+-msgid "would reset superblock root inode pointer to %u\n"
+-msgstr "wskaźnik i-węzła głównego superbloku zostałby przestawiony na %u\n"
++msgid ""
++"%s: a filesystem is mounted on target device \"%s\".\n"
++"%s cannot copy to mounted filesystems. Aborting\n"
++msgstr ""
++"%s: na urządzeniu docelowym \"%s\" jest podmontowany system plików.\n"
++"%s nie może kopiować na podmontowane systemy plików. Przerwano.\n"
+
+-#: .././repair/xfs_repair.c:477
++#: .././copy/xfs_copy.c:786
+ #, c-format
+-msgid "sb realtime bitmap inode %<PRIu64> %sinconsistent with calculated value %u\n"
+-msgstr "i-węzeł bitmapy realtime superbloku %<PRIu64> %sniespójny z obliczoną wartością %u\n"
++msgid "%s: couldn't open target \"%s\"\n"
++msgstr "%s: nie udało się otworzyć celu \"%s\"\n"
+
+-#: .././repair/xfs_repair.c:484
++#: .././copy/xfs_copy.c:796
+ #, c-format
+-msgid "resetting superblock realtime bitmap ino pointer to %u\n"
+-msgstr "przestawiono wskaźnik i-węzła bitmapy realtime superbloku na %u\n"
++msgid "%s: cannot grow data section.\n"
++msgstr "%s: nie można powiększyć sekcji danych.\n"
+
+-#: .././repair/xfs_repair.c:488
++#: .././copy/xfs_copy.c:804
+ #, c-format
+-msgid "would reset superblock realtime bitmap ino pointer to %u\n"
+-msgstr "wskaźnik i-węzła bitmapy realtime superbloku zostałby przestawiony na %u\n"
++msgid "%s: xfsctl on \"%s\" failed.\n"
++msgstr "%s: xfsctl na \"%s\" nie powiodło się.\n"
+
+-#: .././repair/xfs_repair.c:500
++#: .././copy/xfs_copy.c:823
+ #, c-format
+-msgid "sb realtime summary inode %<PRIu64> %sinconsistent with calculated value %u\n"
+-msgstr "i-węzeł opisu realtime superbloku %<PRIu64> %sniespójny z obliczoną wartością %u\n"
++msgid "%s: failed to write last block\n"
++msgstr "%s: nie udało się zapisać ostatniego bloku\n"
+
+-#: .././repair/xfs_repair.c:507
++#: .././copy/xfs_copy.c:825
+ #, c-format
+-msgid "resetting superblock realtime summary ino pointer to %u\n"
+-msgstr "przestawiono wskaźnik i-węzła opisu realtime superbloku na %u\n"
++msgid "\tIs target \"%s\" too small?\n"
++msgstr "\tCzy cel \"%s\" jest zbyt mały?\n"
+
+-#: .././repair/xfs_repair.c:511
+-#, c-format
+-msgid "would reset superblock realtime summary ino pointer to %u\n"
+-msgstr "wskaźnik i-węzła opisu realtime superbloku zostałby przestawiony na %u\n"
++#: .././copy/xfs_copy.c:835
++msgid "Couldn't initialize global thread mask\n"
++msgstr "Nie udało się zainicjować globalnej maski wątków\n"
+
+-#: .././repair/xfs_repair.c:554
+-msgid ""
+-"Primary superblock would have been modified.\n"
+-"Cannot proceed further in no_modify mode.\n"
+-"Exiting now.\n"
+-msgstr ""
+-"Główny superblok zostałby zmodyfikowany.\n"
+-"Nie można kontynuować w trybie bez modyfikacji.\n"
+-"Zakończono.\n"
++#: .././copy/xfs_copy.c:842
++msgid "Error initializing wbuf 0\n"
++msgstr "Błąd inicjalizacji wbuf 0\n"
+
+-#: .././repair/xfs_repair.c:577
+-msgid ""
+-"Cannot get host filesystem geometry.\n"
+-"Repair may fail if there is a sector size mismatch between\n"
+-"the image and the host filesystem.\n"
+-msgstr ""
+-"Nie można pobrać geometrii systemu plików hosta.\n"
+-"Naprawienie może się nie powieść, jeśli istnieje niespójność rozmiaru\n"
+-"sektora między obrazem a systemem plików hosta.\n"
++#: .././copy/xfs_copy.c:850
++msgid "Error initializing btree buf 1\n"
++msgstr "Błąd inicjalizacji btree buf 1\n"
+
+-#: .././repair/xfs_repair.c:589
+-msgid ""
+-"Sector size on host filesystem larger than image sector size.\n"
+-"Cannot turn off direct IO, so exiting.\n"
+-msgstr ""
+-"Rozmiar sektora na systemie plików hosta większy niż rozmiar sektora obrazu.\n"
+-"Nie można wyłączyć bezpośredniego we/wy - zakończono działanie.\n"
++#: .././copy/xfs_copy.c:855
++msgid "Error creating first semaphore.\n"
++msgstr "Błąd tworzenia pierwszego semafora.\n"
+
+-#: .././repair/xfs_repair.c:599
+-#, c-format
+-msgid "%s: cannot repair this filesystem. Sorry.\n"
+-msgstr "%s: niestety nie można naprawić tego systemu plików.\n"
++#: .././copy/xfs_copy.c:870
++msgid "Couldn't malloc space for thread args\n"
++msgstr "Nie udało się przydzielić miejsca na argumenty wątku\n"
+
+-#: .././repair/xfs_repair.c:624
++#: .././copy/xfs_copy.c:882
+ #, c-format
+-msgid " - reporting progress in intervals of %s\n"
+-msgstr " - informowanie o postępie w odstępach %s\n"
++msgid "Error creating thread mutex %d\n"
++msgstr "Błąd podczas tworzenia sekcji krytycznej %d wątku\n"
+
+-#: .././repair/xfs_repair.c:671
++#: .././copy/xfs_copy.c:899
+ #, c-format
+-msgid " - max_mem = %lu, icount = %<PRIu64>, imem = %<PRIu64>, dblock = %<PRIu64>, dmem = %<PRIu64>\n"
+-msgstr " - max_mem = %lu, icount = %<PRIu64>, imem = %<PRIu64>, dblock = %<PRIu64>, dmem = %<PRIu64>\n"
++msgid "Error creating thread for target %d\n"
++msgstr "Błąd podczas tworzenia wątku dla celu %d\n"
+
+-#: .././repair/xfs_repair.c:684
++#: .././copy/xfs_copy.c:953
+ #, c-format
+-msgid ""
+-"Required memory for repair is greater that the maximum specified\n"
+-"with the -m option. Please increase it to at least %lu.\n"
+-msgstr ""
+-"Pamięć wymagana do naprawy przekracza maksimum określone opcją -m.\n"
+-"Proszę ją zwiększyć do co najmniej %lu.\n"
++msgid "Error: current level %d >= btree levels %d\n"
++msgstr "Błąd: bieżący poziom %d >= poziomów b-drzewa %d\n"
+
+-#: .././repair/xfs_repair.c:689
++#: .././copy/xfs_copy.c:972
+ #, c-format
++msgid "Bad btree magic 0x%x\n"
++msgstr "Niewłaściwa liczba magiczna b-drzewa 0x%x\n"
++
++#: .././copy/xfs_copy.c:999
++msgid "WARNING: source filesystem inconsistent.\n"
++msgstr "UWAGA: źródłowy system plików niespójny.\n"
++
++#: .././copy/xfs_copy.c:1001
++msgid " A leaf btree rec isn't a leaf. Aborting now.\n"
++msgstr " Liść rekordu b-drzewa nie jest liściem. Przerwano.\n"
++
++#: .././db/addr.c:35
++msgid "[field-expression]"
++msgstr "[wyrażenie-pól]"
++
++#: .././db/addr.c:36
++msgid "set current address"
++msgstr "ustawienie bieżącego adresu"
++
++#: .././db/addr.c:42
+ msgid ""
+-"Not enough RAM available for repair to enable prefetching.\n"
+-"This will be _slow_.\n"
+-"You need at least %luMB RAM to run with prefetching enabled.\n"
++"\n"
++" 'addr' uses the given field to set the filesystem address and type\n"
++"\n"
++" Examples:\n"
++"\n"
++" sb\n"
++" a rootino - set the type to inode and set position to the root inode\n"
++" a u.bmx[0].startblock (for inode with blockmap)\n"
++"\n"
+ msgstr ""
+-"Zbyt mało dostępnej pamięci RAM, żeby naprawiać z włączonym prefetch.\n"
+-"To będzie _wolne_.\n"
+-"Do włączenia prefetch potrzeba przynajmniej %luMB RAM.\n"
+-
+-#: .././repair/xfs_repair.c:707
+-#, c-format
+-msgid " - block cache size set to %d entries\n"
+-msgstr " - rozmiar bufora bloku ustawiony na %d wpisów\n"
++"\n"
++" 'addr' wykorzystuje podane pole do ustawienia adresu w systemie plików i typu\n"
++"\n"
++" Przykłady:\n"
++"\n"
++" sb\n"
++" a rootino - ustawienie typu na i-węzeł i pozycji na i-węzeł główny\n"
++" a u.bmx[0].startblock (dla i-węzła z mapą bloków)\n"
++"\n"
+
+-#: .././repair/xfs_repair.c:736
+-msgid "Found unsupported filesystem features. Exiting now.\n"
+-msgstr "Znaleziono nie obsługiwane cechy systemu plików. Zakończono.\n"
++#: .././db/addr.c:72 .././db/attrset.c:86 .././db/attrset.c:189
++#: .././db/print.c:74 .././db/type.c:142 .././db/write.c:101
++msgid "no current type\n"
++msgstr "brak bieżącego typu\n"
+
+-#: .././repair/xfs_repair.c:754
++#: .././db/addr.c:82
+ #, c-format
+-msgid "No modify flag set, skipping phase 5\n"
+-msgstr "Ustawiono flagę braku modyfikacji, pominięto fazę 5\n"
+-
+-#: .././repair/xfs_repair.c:773
+-msgid "Inode allocation btrees are too corrupted, skipping phases 6 and 7\n"
+-msgstr "B-drzewa alokacji i-węzłów są zbyt uszkodzone, pominięto fazy 6 i 7\n"
++msgid "no fields for type %s\n"
++msgstr "brak pól dla typu %s\n"
+
+-#: .././repair/xfs_repair.c:779
+-msgid "Warning: no quota inodes were found. Quotas disabled.\n"
+-msgstr "Uwaga: nie znaleziono i-węzłów limitów (quot). Limity wyłączone.\n"
++#: .././db/addr.c:94
++msgid "array not allowed for addr command\n"
++msgstr "tablica nie jest dozwolona dla polecenia addr\n"
+
+-#: .././repair/xfs_repair.c:782
+-msgid "Warning: no quota inodes were found. Quotas would be disabled.\n"
+-msgstr "Uwaga: nie znaleziono i-węzłów limitów (quot). Limity zostałyby wyłączone.\n"
++#: .././db/addr.c:103
++#, c-format
++msgid "no next type for field %s\n"
++msgstr "brak następnego typu dla pola %s\n"
+
+-#: .././repair/xfs_repair.c:787
+-msgid "Warning: quota inodes were cleared. Quotas disabled.\n"
+-msgstr "Uwaga: i-węzły limitów (quot) były wyczyszczone. Limity wyłączone.\n"
++#: .././db/addr.c:110
++#, c-format
++msgid "no addr function for field %s (type %s)\n"
++msgstr "brak funkcji addr dla pola %s (typu %s)\n"
+
+-#: .././repair/xfs_repair.c:790
+-msgid "Warning: quota inodes would be cleared. Quotas would be disabled.\n"
+-msgstr "Uwaga: i-węzły limitów (quot) zostałyby wyczyszczone. Limity zostałyby wyłączone.\n"
++#: .././db/agf.c:35 .././db/agfl.c:36 .././db/agi.c:35 .././db/sb.c:42
++msgid "[agno]"
++msgstr "[agno]"
+
+-#: .././repair/xfs_repair.c:796
+-msgid ""
+-"Warning: user quota information was cleared.\n"
+-"User quotas can not be enforced until limit information is recreated.\n"
+-msgstr ""
+-"Uwaga: informacje o limitach użytkowników były wyczyszczone.\n"
+-"Limity użytkowników nie mogą być wymuszone do czasu odtworzenia informacji.\n"
++#: .././db/agf.c:36
++msgid "set address to agf header"
++msgstr "ustawienie adresu na nagłówek agf"
+
+-#: .././repair/xfs_repair.c:800
++#: .././db/agf.c:82
+ msgid ""
+-"Warning: user quota information would be cleared.\n"
+-"User quotas could not be enforced until limit information was recreated.\n"
++"\n"
++" set allocation group free block list\n"
++"\n"
++" Example:\n"
++"\n"
++" agf 2 - move location to AGF in 2nd filesystem allocation group\n"
++"\n"
++" Located in the second sector of each allocation group, the AGF\n"
++" contains the root of two different freespace btrees:\n"
<Skipped 23241 lines>
================================================================
---- gitweb:
http://git.pld-linux.org/gitweb.cgi/packages/xfsprogs.git/commitdiff/3d585810b809dcbcd72815e22177f65e1cec8853
More information about the pld-cvs-commit
mailing list