xfs: replace parent pointer diroffset with full dirent name

As I've mentioned in past comments on the parent pointers patchset, the
proposed ondisk parent pointer format presents a major difficulty for
online directory repair.  This difficulty derives from encoding the
directory offset of the dirent that the parent pointer is mirroring.
Recall that parent pointers are stored in extended attributes:

    (parent_ino, parent_gen, diroffset) -> (dirent_name)

If the directory is rebuilt, the offsets of the new directory entries
must match the diroffset encoded in the parent pointer, or the
filesystem becomes inconsistent.  This is difficult to guarantee in the
process of performing a repair and in the long terms is totally
pointless because the the parent pointer code doesn't use the diroffset,
nor does the online fsck code.  IOWs, this design decision increases the
complexity of repair solely to work around the xattr code being unable
to store a full dirent name in the xattr name or match on xattr values.

However, xattrs can now perform lookups on both the name and value of an
xattr.  This means we can redefine the parent pointer format like this:

For dirent names shorter than 243 bytes:

    (parent_ino, parent_gen, dirent_name) -> NULL

For dirent names longer than 243 bytes:

    (parent_ino, parent_gen, dirent_name[0:242]) -> (dirent_name[243:255])

and this works because a parent pointer lookup uses NVLOOKUP, which will
read those last twelve bytes of the dirent name if need be.

Replace the diroffset with this new format, thereby eliminating the need
for directory repair to update all the parent pointers after rebuilding
the directory.  We haven't merged parent pointers, so changes to the
ondisk format are easy.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
diff --git a/db/attr.c b/db/attr.c
index 8ea7b36..c77f79b 100644
--- a/db/attr.c
+++ b/db/attr.c
@@ -20,6 +20,7 @@
 static int	attr_leaf_name_local_count(void *obj, int startoff);
 static int	attr_leaf_name_local_name_count(void *obj, int startoff);
 static int	attr_leaf_name_pptr_count(void *obj, int startoff);
+static int	attr_leaf_name_pptr_dnamelen(void *obj, int startoff);
 static int	attr_leaf_name_local_value_count(void *obj, int startoff);
 static int	attr_leaf_name_local_value_offset(void *obj, int startoff,
 						  int idx);
@@ -125,8 +126,8 @@
 	  attr_leaf_name_pptr_count, FLD_COUNT, TYP_INODE },
 	{ "parent_gen", FLDT_UINT32D, OI(PPOFF(p_gen)),
 	  attr_leaf_name_pptr_count, FLD_COUNT, TYP_NONE },
-	{ "parent_diroffset", FLDT_UINT32D, OI(PPOFF(p_diroffset)),
-	  attr_leaf_name_pptr_count, FLD_COUNT, TYP_NONE },
+	{ "parent_dname", FLDT_CHARNS, OI(PPOFF(p_dname)),
+	  attr_leaf_name_pptr_dnamelen, FLD_COUNT, TYP_NONE },
 	{ "value", FLDT_CHARNS, attr_leaf_name_local_value_offset,
 	  attr_leaf_name_local_value_count, FLD_COUNT|FLD_OFFSET, TYP_NONE },
 	{ "valueblk", FLDT_UINT32X, OI(LVOFF(valueblk)),
@@ -303,6 +304,30 @@
 }
 
 static int
+__attr_leaf_name_pptr_dnamelen(
+	struct xfs_attr_leafblock	*leaf,
+	struct xfs_attr_leaf_entry      *e,
+	int				i)
+{
+	struct xfs_attr_leaf_name_local	*lname;
+
+	if (e->flags & XFS_ATTR_PARENT) {
+		lname = xfs_attr3_leaf_name_local(leaf, i);
+		return xfs_parent_name_dnamelen(lname->namelen);
+	}
+	return 0;
+}
+
+static int
+attr_leaf_name_pptr_dnamelen(
+	void				*obj,
+	int				startoff)
+{
+	return attr_leaf_entry_walk(obj, startoff,
+			__attr_leaf_name_pptr_dnamelen);
+}
+
+static int
 __attr_leaf_name_local_name_count(
 	struct xfs_attr_leafblock	*leaf,
 	struct xfs_attr_leaf_entry      *e,
diff --git a/db/attrshort.c b/db/attrshort.c
index 7c8ac48..77c4756 100644
--- a/db/attrshort.c
+++ b/db/attrshort.c
@@ -14,6 +14,7 @@
 
 static int	attr_sf_entry_name_count(void *obj, int startoff);
 static int	attr_sf_entry_pptr_count(void *obj, int startoff);
+static int	attr_sf_entry_pptr_dnamelen(void *obj, int startoff);
 static int	attr_sf_entry_value_count(void *obj, int startoff);
 static int	attr_sf_entry_value_offset(void *obj, int startoff, int idx);
 static int	attr_shortform_list_count(void *obj, int startoff);
@@ -56,8 +57,8 @@
 	  FLD_COUNT, TYP_INODE },
 	{ "parent_gen", FLDT_UINT32D, OI(PPOFF(p_gen)), attr_sf_entry_pptr_count,
 	  FLD_COUNT, TYP_NONE },
-	{ "parent_diroffset", FLDT_UINT32D, OI(PPOFF(p_diroffset)),
-	   attr_sf_entry_pptr_count, FLD_COUNT, TYP_NONE },
+	{ "parent_dname", FLDT_CHARNS, OI(PPOFF(p_dname)),
+	   attr_sf_entry_pptr_dnamelen, FLD_COUNT, TYP_NONE },
 	{ "value", FLDT_CHARNS, attr_sf_entry_value_offset,
 	  attr_sf_entry_value_count, FLD_COUNT|FLD_OFFSET, TYP_NONE },
 	{ NULL }
@@ -78,6 +79,20 @@
 }
 
 static int
+attr_sf_entry_pptr_dnamelen(
+	void				*obj,
+	int				startoff)
+{
+	struct xfs_attr_sf_entry	*e;
+
+	ASSERT(bitoffs(startoff) == 0);
+	e = (struct xfs_attr_sf_entry *)((char *)obj + byteize(startoff));
+	if (e->flags & XFS_ATTR_PARENT)
+		return xfs_parent_name_dnamelen(e->namelen);
+	return 0;
+}
+
+static int
 attr_sf_entry_name_count(
 	void				*obj,
 	int				startoff)
diff --git a/db/metadump.c b/db/metadump.c
index 4be2399..2e47f8e 100644
--- a/db/metadump.c
+++ b/db/metadump.c
@@ -740,14 +740,12 @@
 #define rol32(x,y)		(((x) << (y)) | ((x) >> (32 - (y))))
 
 static inline unsigned char
-random_filename_char(xfs_ino_t	ino)
+random_filename_char(void)
 {
 	static unsigned char filename_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 						"abcdefghijklmnopqrstuvwxyz"
 						"0123456789-_";
 
-	if (ino)
-		return filename_alphabet[ino % (sizeof filename_alphabet - 1)];
 	return filename_alphabet[random() % (sizeof filename_alphabet - 1)];
 }
 
@@ -817,7 +815,6 @@
  */
 static void
 obfuscate_name(
-	xfs_ino_t	ino,
 	xfs_dahash_t	hash,
 	size_t		name_len,
 	unsigned char	*name)
@@ -845,7 +842,7 @@
 	 * Accumulate its new hash value as we go.
 	 */
 	for (i = 0; i < name_len - 5; i++) {
-		*newp = random_filename_char(ino);
+		*newp = random_filename_char();
 		new_hash = *newp ^ rol32(new_hash, 7);
 		newp++;
 	}
@@ -1210,10 +1207,13 @@
 	/* Obfuscate the name (if possible) */
 
 	hash = libxfs_da_hashname(name, namelen);
-	if (xfs_has_parent(mp))
-		obfuscate_name(ino, hash, namelen, name);
-	else
-		obfuscate_name(0, hash, namelen, name);
+	/*
+	 * On filesystems with directory parent pointers, there's no good way
+	 * to obfuscate dirent names now that we encode them into the parent
+	 * pointers because we can't rehash the xattr key to reflect this.
+	 */
+	if (!xfs_has_parent(mp) || !ino)
+		obfuscate_name(hash, namelen, name);
 
 	/*
 	 * Make sure the name is not something already seen.  If we
@@ -1326,7 +1326,7 @@
 			/* last (or single) component */
 			namelen = strnlen((char *)comp, len);
 			hash = libxfs_da_hashname(comp, namelen);
-			obfuscate_name(0, hash, namelen, comp);
+			obfuscate_name(hash, namelen, comp);
 			break;
 		}
 		namelen = slash - (char *)comp;
@@ -1337,7 +1337,7 @@
 			continue;
 		}
 		hash = libxfs_da_hashname(comp, namelen);
-		obfuscate_name(0, hash, namelen, comp);
+		obfuscate_name(hash, namelen, comp);
 		comp += namelen + 1;
 		len -= namelen + 1;
 	}
@@ -1412,16 +1412,11 @@
 			break;
 		}
 
-		if (obfuscate) {
-			if (asfep->flags & XFS_ATTR_PARENT) {
-				generate_obfuscated_name(cur_ino, asfep->valuelen,
-					 &asfep->nameval[asfep->namelen]);
-			} else {
-				generate_obfuscated_name(0, asfep->namelen,
-							 &asfep->nameval[0]);
-				memset(&asfep->nameval[asfep->namelen], 'v',
-				       asfep->valuelen);
-			}
+		if (obfuscate && !(asfep->flags & XFS_ATTR_PARENT)) {
+			generate_obfuscated_name(0, asfep->namelen,
+					&asfep->nameval[0]);
+			memset(&asfep->nameval[asfep->namelen], 'v',
+					asfep->valuelen);
 		}
 
 		asfep = (struct xfs_attr_sf_entry *)((char *)asfep +
@@ -1808,9 +1803,6 @@
 			zlen = xfs_attr_leaf_entsize_local(nlen, vlen) -
 				(sizeof(xfs_attr_leaf_name_local_t) - 1 +
 				 nlen + vlen);
-			if (obfuscate && (entry->flags & XFS_ATTR_PARENT))
-				generate_obfuscated_name(cur_ino, vlen,
-						&local->nameval[nlen]);
 			if (zero_stale_data)
 				memset(&local->nameval[nlen + vlen], 0, zlen);
 		} else {
diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
index c07b816..dd56928 100644
--- a/libxfs/xfs_da_format.h
+++ b/libxfs/xfs_da_format.h
@@ -827,14 +827,43 @@
 /*
  * Parent pointer attribute format definition
  *
- * EA name encodes the parent inode number, generation and the offset of
- * the dirent that points to the child inode. The EA value contains the
- * same name as the dirent in the parent directory.
+ * The EA name encodes the parent inode number, generation and as much of the
+ * dirent name as fits.  In other words, it contains up to 243 bytes of the
+ * dirent name.
+ *
+ * The EA value contains however much of the dirent name that does not fit in
+ * the EA name.
  */
 struct xfs_parent_name_rec {
 	__be64  p_ino;
 	__be32  p_gen;
-	__be32  p_diroffset;
-};
+	__u8	p_dname[];
+} __attribute__((packed));
+
+/* Maximum size of a parent pointer EA name. */
+#define XFS_PARENT_NAME_MAX_SIZE \
+	(MAXNAMELEN - 1)
+
+/* Maximum number of dirent name bytes stored in p_dname. */
+#define XFS_PARENT_MAX_DNAME_SIZE \
+	(XFS_PARENT_NAME_MAX_SIZE - sizeof(struct xfs_parent_name_rec))
+
+/* Maximum number of dirent name bytes stored in the xattr value. */
+#define XFS_PARENT_MAX_DNAME_VALUELEN \
+	sizeof(struct xfs_parent_name_rec)
+
+static inline unsigned int
+xfs_parent_name_rec_sizeof(
+	unsigned int		dnamelen)
+{
+	return sizeof(struct xfs_parent_name_rec) + dnamelen;
+}
+
+static inline unsigned int
+xfs_parent_name_dnamelen(
+	unsigned int		rec_sizeof)
+{
+	return rec_sizeof - sizeof(struct xfs_parent_name_rec);
+}
 
 #endif /* __XFS_DA_FORMAT_H__ */
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index d7e0610..d861947 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -769,7 +769,7 @@
 struct xfs_getparents_rec {
 	__u64		gpr_ino;	/* Inode number */
 	__u32		gpr_gen;	/* Inode generation */
-	__u32		gpr_diroffset;	/* Directory offset */
+	__u32		gpr_rsvd2;	/* Reserved */
 	__u64		gpr_rsvd;	/* Reserved */
 	__u8		gpr_name[];	/* File name and null terminator */
 };
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
index 11c3603..188ec9c 100644
--- a/libxfs/xfs_parent.c
+++ b/libxfs/xfs_parent.c
@@ -55,9 +55,12 @@
 	unsigned int				attr_flags)
 {
 	xfs_ino_t				p_ino;
-	xfs_dir2_dataptr_t			p_diroffset;
 
-	if (reclen != sizeof(struct xfs_parent_name_rec))
+	if (!(attr_flags & XFS_ATTR_PARENT))
+		return false;
+
+	if (reclen <= sizeof(struct xfs_parent_name_rec) ||
+	    reclen > XFS_PARENT_NAME_MAX_SIZE)
 		return false;
 
 	/* Only one namespace bit allowed. */
@@ -68,10 +71,6 @@
 	if (!xfs_verify_ino(mp, p_ino))
 		return false;
 
-	p_diroffset = be32_to_cpu(rec->p_diroffset);
-	if (p_diroffset > XFS_DIR2_MAX_DATAPTR)
-		return false;
-
 	return true;
 }
 
@@ -79,10 +78,18 @@
 bool
 xfs_parent_valuecheck(
 	struct xfs_mount		*mp,
+	size_t				namelen,
 	const void			*value,
 	size_t				valuelen)
 {
-	if (valuelen == 0 || valuelen >= MAXNAMELEN)
+	if (namelen > XFS_PARENT_NAME_MAX_SIZE)
+		return false;
+
+	if (namelen < XFS_PARENT_NAME_MAX_SIZE && valuelen != 0)
+		return false;
+
+	if (namelen == XFS_PARENT_NAME_MAX_SIZE &&
+	    valuelen > XFS_PARENT_MAX_DNAME_VALUELEN)
 		return false;
 
 	if (value == NULL)
@@ -91,19 +98,25 @@
 	return true;
 }
 
-/* Initializes a xfs_parent_name_rec to be stored as an attribute name */
-static inline void
+/*
+ * Initializes a xfs_parent_name_rec to be stored as an attribute name.
+ * Returns the number of name bytes stored in p_dname.
+ */
+static inline int
 xfs_init_parent_name_rec(
 	struct xfs_parent_name_rec	*rec,
-	const struct xfs_inode		*ip,
-	uint32_t			p_diroffset)
+	const struct xfs_inode		*dp,
+	const struct xfs_name		*name,
+	struct xfs_inode		*ip)
 {
-	xfs_ino_t			p_ino = ip->i_ino;
-	uint32_t			p_gen = VFS_IC(ip)->i_generation;
+	int				dnamelen;
 
-	rec->p_ino = cpu_to_be64(p_ino);
-	rec->p_gen = cpu_to_be32(p_gen);
-	rec->p_diroffset = cpu_to_be32(p_diroffset);
+	rec->p_ino = cpu_to_be64(dp->i_ino);
+	rec->p_gen = cpu_to_be32(VFS_IC(dp)->i_generation);
+
+	dnamelen = min_t(int, name->len, XFS_PARENT_MAX_DNAME_SIZE);
+	memcpy(rec->p_dname, name->name, dnamelen);
+	return dnamelen;
 }
 
 /*
@@ -114,53 +127,58 @@
 xfs_parent_irec_from_disk(
 	struct xfs_parent_name_irec	*irec,
 	const struct xfs_parent_name_rec *rec,
+	int				reclen,
 	const void			*value,
 	int				valuelen)
 {
+	int				dnamelen;
+
 	irec->p_ino = be64_to_cpu(rec->p_ino);
 	irec->p_gen = be32_to_cpu(rec->p_gen);
-	irec->p_diroffset = be32_to_cpu(rec->p_diroffset);
 
 	if (!value) {
 		irec->p_namelen = 0;
 		return;
 	}
 
-	ASSERT(valuelen > 0);
-	ASSERT(valuelen < MAXNAMELEN);
+	ASSERT(valuelen <= XFS_PARENT_MAX_DNAME_VALUELEN);
 
-	valuelen = min(valuelen, MAXNAMELEN);
-
-	irec->p_namelen = valuelen;
-	memcpy(irec->p_name, value, valuelen);
-	memset(&irec->p_name[valuelen], 0, sizeof(irec->p_name) - valuelen);
+	dnamelen = xfs_parent_name_dnamelen(reclen);
+	irec->p_namelen = dnamelen + valuelen;
+	memcpy(irec->p_name, rec->p_dname, dnamelen);
+	if (valuelen > 0)
+		memcpy(irec->p_name + dnamelen, value, valuelen);
 }
 
 /*
- * Convert an incore parent_name record to its ondisk format.  If @value or
- * @valuelen are NULL, they will not be written to.
+ * Convert an incore parent_name record to its ondisk format.  If @valuelen is
+ * NULL, neither it nor @value will be written to.
  */
-void
+int
 xfs_parent_irec_to_disk(
 	struct xfs_parent_name_rec	*rec,
+	int				*reclen,
 	void				*value,
 	int				*valuelen,
 	const struct xfs_parent_name_irec *irec)
 {
+	int				dnamelen;
+
 	rec->p_ino = cpu_to_be64(irec->p_ino);
 	rec->p_gen = cpu_to_be32(irec->p_gen);
-	rec->p_diroffset = cpu_to_be32(irec->p_diroffset);
+	dnamelen = min_t(int, irec->p_namelen, XFS_PARENT_MAX_DNAME_SIZE);
+	*reclen = xfs_parent_name_rec_sizeof(dnamelen);
+	memcpy(rec->p_dname, irec->p_name, dnamelen);
 
-	if (valuelen) {
-		ASSERT(*valuelen > 0);
-		ASSERT(*valuelen >= irec->p_namelen);
-		ASSERT(*valuelen < MAXNAMELEN);
+	if (!valuelen)
+		return dnamelen;
 
-		*valuelen = irec->p_namelen;
-	}
+	*valuelen = irec->p_namelen - dnamelen;
+	if (*valuelen)
+		memcpy(value, rec->p_dname + XFS_PARENT_MAX_DNAME_SIZE,
+				*valuelen);
 
-	if (value)
-		memcpy(value, irec->p_name, irec->p_namelen);
+	return dnamelen;
 }
 
 /*
@@ -194,9 +212,10 @@
 	parent->args.geo = mp->m_attr_geo;
 	parent->args.whichfork = XFS_ATTR_FORK;
 	parent->args.attr_filter = XFS_ATTR_PARENT;
-	parent->args.op_flags = XFS_DA_OP_OKNOENT | XFS_DA_OP_LOGGED;
+	parent->args.op_flags = XFS_DA_OP_OKNOENT | XFS_DA_OP_LOGGED |
+				XFS_DA_OP_VLOOKUP;
 	parent->args.name = (const uint8_t *)&parent->rec;
-	parent->args.namelen = sizeof(struct xfs_parent_name_rec);
+	parent->args.namelen = 0;
 
 	*parentp = parent;
 	return 0;
@@ -209,20 +228,25 @@
 	struct xfs_parent_defer	*parent,
 	struct xfs_inode	*dp,
 	const struct xfs_name	*parent_name,
-	xfs_dir2_dataptr_t	diroffset,
 	struct xfs_inode	*child)
 {
 	struct xfs_da_args	*args = &parent->args;
+	int			dnamelen;
 
-	xfs_init_parent_name_rec(&parent->rec, dp, diroffset);
+	dnamelen = xfs_init_parent_name_rec(&parent->rec, dp, parent_name,
+			child);
+
+	args->namelen = xfs_parent_name_rec_sizeof(dnamelen);
 	args->hashval = xfs_da_hashname(args->name, args->namelen);
 
 	args->trans = tp;
 	args->dp = child;
-	if (parent_name) {
-		parent->args.value = (void *)parent_name->name;
-		parent->args.valuelen = parent_name->len;
-	}
+
+	parent->args.valuelen = parent_name->len - dnamelen;
+	if (parent->args.valuelen > 0)
+		parent->args.value = (void *)parent_name->name + dnamelen;
+	else
+		parent->args.value = NULL;
 
 	return xfs_attr_defer_add(args);
 }
@@ -231,17 +255,27 @@
 int
 xfs_parent_remove(
 	struct xfs_trans	*tp,
-	struct xfs_inode	*dp,
 	struct xfs_parent_defer	*parent,
-	xfs_dir2_dataptr_t	diroffset,
+	struct xfs_inode	*dp,
+	const struct xfs_name	*name,
 	struct xfs_inode	*child)
 {
 	struct xfs_da_args	*args = &parent->args;
+	int			dnamelen;
 
-	xfs_init_parent_name_rec(&parent->rec, dp, diroffset);
+	dnamelen = xfs_init_parent_name_rec(&parent->rec, dp, name, child);
+
+	args->namelen = xfs_parent_name_rec_sizeof(dnamelen);
 	args->trans = tp;
 	args->dp = child;
 	args->hashval = xfs_da_hashname(args->name, args->namelen);
+
+	parent->args.valuelen = name->len - dnamelen;
+	if (parent->args.valuelen > 0)
+		parent->args.value = (void *)name->name + dnamelen;
+	else
+		parent->args.value = NULL;
+
 	return xfs_attr_defer_remove(args);
 }
 
@@ -251,26 +285,37 @@
 	struct xfs_trans	*tp,
 	struct xfs_parent_defer	*new_parent,
 	struct xfs_inode	*old_dp,
-	xfs_dir2_dataptr_t	old_diroffset,
-	const struct xfs_name	*parent_name,
+	const struct xfs_name	*old_name,
 	struct xfs_inode	*new_dp,
-	xfs_dir2_dataptr_t	new_diroffset,
+	const struct xfs_name	*new_name,
 	struct xfs_inode	*child)
 {
 	struct xfs_da_args	*args = &new_parent->args;
+	int			old_dnamelen, new_dnamelen;
 
-	xfs_init_parent_name_rec(&new_parent->old_rec, old_dp, old_diroffset);
-	xfs_init_parent_name_rec(&new_parent->rec, new_dp, new_diroffset);
+	old_dnamelen = xfs_init_parent_name_rec(&new_parent->old_rec, old_dp,
+			old_name, child);
+	new_dnamelen = xfs_init_parent_name_rec(&new_parent->rec, new_dp,
+			new_name, child);
+
 	new_parent->args.name = (const uint8_t *)&new_parent->old_rec;
-	new_parent->args.namelen = sizeof(struct xfs_parent_name_rec);
+	new_parent->args.namelen = xfs_parent_name_rec_sizeof(old_dnamelen);
 	new_parent->args.new_name = (const uint8_t *)&new_parent->rec;
-	new_parent->args.new_namelen = sizeof(struct xfs_parent_name_rec);
+	new_parent->args.new_namelen = xfs_parent_name_rec_sizeof(new_dnamelen);
 	args->trans = tp;
 	args->dp = child;
 
-	ASSERT(parent_name != NULL);
-	new_parent->args.value = (void *)parent_name->name;
-	new_parent->args.valuelen = parent_name->len;
+	new_parent->args.new_valuelen = new_name->len - new_dnamelen;
+	if (new_parent->args.new_valuelen > 0)
+		new_parent->args.new_value = (void *)new_name->name + new_dnamelen;
+	else
+		new_parent->args.new_value = NULL;
+
+	new_parent->args.valuelen = old_name->len - old_dnamelen;
+	if (new_parent->args.valuelen > 0)
+		new_parent->args.value = (void *)old_name->name + old_dnamelen;
+	else
+		new_parent->args.value = NULL;
 
 	args->hashval = xfs_da_hashname(args->name, args->namelen);
 	return xfs_attr_defer_replace(args);
@@ -300,42 +345,40 @@
 }
 
 /*
- * Look up the @name associated with the parent pointer (@pptr) of @ip.  Caller
- * must hold at least ILOCK_SHARED.  Returns the length of the dirent name, or
- * a negative errno.  The scratchpad need not be initialized.
+ * Look up the @name associated with the parent pointer (@pptr) of @ip.
+ * Caller must hold at least ILOCK_SHARED.  Returns 0 if the pointer is found,
+ * -ENOATTR if there is no match, or a negative errno.  The scratchpad need not
+ *  be initialized.
  */
 int
 xfs_parent_lookup(
 	struct xfs_trans		*tp,
 	struct xfs_inode		*ip,
 	const struct xfs_parent_name_irec *pptr,
-	unsigned char			*name,
-	unsigned int			namelen,
 	struct xfs_parent_scratch	*scr)
 {
-	int				error;
+	int				dnamelen;
+	int				reclen;
 
-	xfs_parent_irec_to_disk(&scr->rec, NULL, NULL, pptr);
+	dnamelen = xfs_parent_irec_to_disk(&scr->rec, &reclen, NULL, NULL, pptr);
 
 	memset(&scr->args, 0, sizeof(struct xfs_da_args));
 	scr->args.attr_filter	= XFS_ATTR_PARENT;
 	scr->args.dp		= ip;
 	scr->args.geo		= ip->i_mount->m_attr_geo;
 	scr->args.name		= (const unsigned char *)&scr->rec;
-	scr->args.namelen	= sizeof(struct xfs_parent_name_rec);
-	scr->args.op_flags	= XFS_DA_OP_OKNOENT;
+	scr->args.namelen	= reclen;
+	scr->args.op_flags	= XFS_DA_OP_OKNOENT | XFS_DA_OP_VLOOKUP;
 	scr->args.trans		= tp;
-	scr->args.valuelen	= namelen;
-	scr->args.value		= name;
+	scr->args.valuelen	= pptr->p_namelen - dnamelen;
 	scr->args.whichfork	= XFS_ATTR_FORK;
 
+	if (scr->args.valuelen)
+		scr->args.value	= (void *)pptr->p_name + dnamelen;
+
 	scr->args.hashval = xfs_da_hashname(scr->args.name, scr->args.namelen);
 
-	error = xfs_attr_get_ilocked(&scr->args);
-	if (error)
-		return error;
-
-	return scr->args.valuelen;
+	return xfs_attr_get_ilocked(&scr->args);
 }
 
 /*
@@ -350,18 +393,24 @@
 	const struct xfs_parent_name_irec *pptr,
 	struct xfs_parent_scratch	*scr)
 {
-	xfs_parent_irec_to_disk(&scr->rec, NULL, NULL, pptr);
+	int				dnamelen;
+	int				reclen;
+
+	dnamelen = xfs_parent_irec_to_disk(&scr->rec, &reclen, NULL, NULL, pptr);
 
 	memset(&scr->args, 0, sizeof(struct xfs_da_args));
 	scr->args.attr_filter	= XFS_ATTR_PARENT;
 	scr->args.dp		= ip;
 	scr->args.geo		= ip->i_mount->m_attr_geo;
 	scr->args.name		= (const unsigned char *)&scr->rec;
-	scr->args.namelen	= sizeof(struct xfs_parent_name_rec);
-	scr->args.valuelen	= pptr->p_namelen;
-	scr->args.value		= (void *)pptr->p_name;
+	scr->args.namelen	= reclen;
+	scr->args.op_flags	= XFS_DA_OP_VLOOKUP;
+	scr->args.valuelen	= pptr->p_namelen - dnamelen;
 	scr->args.whichfork	= XFS_ATTR_FORK;
 
+	if (scr->args.valuelen)
+		scr->args.value	= (void *)pptr->p_name + dnamelen;
+
 	return xfs_attr_set(&scr->args);
 }
 
@@ -377,16 +426,23 @@
 	const struct xfs_parent_name_irec *pptr,
 	struct xfs_parent_scratch	*scr)
 {
-	xfs_parent_irec_to_disk(&scr->rec, NULL, NULL, pptr);
+	int				dnamelen;
+	int				reclen;
+
+	dnamelen = xfs_parent_irec_to_disk(&scr->rec, &reclen, NULL, NULL, pptr);
 
 	memset(&scr->args, 0, sizeof(struct xfs_da_args));
 	scr->args.attr_filter	= XFS_ATTR_PARENT;
 	scr->args.dp		= ip;
 	scr->args.geo		= ip->i_mount->m_attr_geo;
 	scr->args.name		= (const unsigned char *)&scr->rec;
-	scr->args.namelen	= sizeof(struct xfs_parent_name_rec);
-	scr->args.op_flags	= XFS_DA_OP_REMOVE;
+	scr->args.namelen	= reclen;
+	scr->args.op_flags	= XFS_DA_OP_REMOVE | XFS_DA_OP_VLOOKUP;
+	scr->args.valuelen	= pptr->p_namelen - dnamelen;
 	scr->args.whichfork	= XFS_ATTR_FORK;
 
+	if (scr->args.valuelen)
+		scr->args.value	= (void *)pptr->p_name + dnamelen;
+
 	return xfs_attr_set(&scr->args);
 }
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
index a7fc621..0b3e0b9 100644
--- a/libxfs/xfs_parent.h
+++ b/libxfs/xfs_parent.h
@@ -12,8 +12,8 @@
 bool xfs_parent_namecheck(struct xfs_mount *mp,
 		const struct xfs_parent_name_rec *rec, size_t reclen,
 		unsigned int attr_flags);
-bool xfs_parent_valuecheck(struct xfs_mount *mp, const void *value,
-		size_t valuelen);
+bool xfs_parent_valuecheck(struct xfs_mount *mp, size_t namelen,
+		const void *value, size_t valuelen);
 
 /*
  * Incore version of a parent pointer, also contains dirent name so callers
@@ -23,26 +23,30 @@
 	/* Key fields for looking up a particular parent pointer. */
 	xfs_ino_t		p_ino;
 	uint32_t		p_gen;
-	xfs_dir2_dataptr_t	p_diroffset;
-
-	/* Attributes of a parent pointer. */
 	uint8_t			p_namelen;
 	unsigned char		p_name[MAXNAMELEN];
 };
 
 void xfs_parent_irec_from_disk(struct xfs_parent_name_irec *irec,
-		const struct xfs_parent_name_rec *rec,
+		const struct xfs_parent_name_rec *rec, int reclen,
 		const void *value, int valuelen);
-void xfs_parent_irec_to_disk(struct xfs_parent_name_rec *rec, void *value,
-		int *valuelen, const struct xfs_parent_name_irec *irec);
+int xfs_parent_irec_to_disk(struct xfs_parent_name_rec *rec, int *reclen,
+		void *value, int *valuelen,
+		const struct xfs_parent_name_irec *irec);
 
 /*
  * Dynamically allocd structure used to wrap the needed data to pass around
  * the defer ops machinery
  */
 struct xfs_parent_defer {
-	struct xfs_parent_name_rec	rec;
-	struct xfs_parent_name_rec	old_rec;
+	union {
+		struct xfs_parent_name_rec	rec;
+		__u8			dummy1[XFS_PARENT_NAME_MAX_SIZE];
+	};
+	union {
+		struct xfs_parent_name_rec	old_rec;
+		__u8			dummy2[XFS_PARENT_NAME_MAX_SIZE];
+	};
 	struct xfs_da_args		args;
 	bool				have_log;
 };
@@ -79,15 +83,14 @@
 
 int xfs_parent_add(struct xfs_trans *tp, struct xfs_parent_defer *parent,
 		struct xfs_inode *dp, const struct xfs_name *parent_name,
-		xfs_dir2_dataptr_t diroffset, struct xfs_inode *child);
+		struct xfs_inode *child);
 int xfs_parent_replace(struct xfs_trans *tp,
 		struct xfs_parent_defer *new_parent, struct xfs_inode *old_dp,
-		xfs_dir2_dataptr_t old_diroffset,
-		const struct xfs_name *parent_name, struct xfs_inode *new_ip,
-		xfs_dir2_dataptr_t new_diroffset, struct xfs_inode *child);
-int xfs_parent_remove(struct xfs_trans *tp, struct xfs_inode *dp,
-		struct xfs_parent_defer *parent, xfs_dir2_dataptr_t diroffset,
-		struct xfs_inode *child);
+		const struct xfs_name *old_name, struct xfs_inode *new_ip,
+		const struct xfs_name *new_name, struct xfs_inode *child);
+int xfs_parent_remove(struct xfs_trans *tp,
+		struct xfs_parent_defer *parent, struct xfs_inode *dp,
+		const struct xfs_name *name, struct xfs_inode *child);
 
 void __xfs_parent_cancel(struct xfs_mount *mp, struct xfs_parent_defer *parent);
 
@@ -105,13 +108,16 @@
 
 /* Scratchpad memory so that raw parent operations don't burn stack space. */
 struct xfs_parent_scratch {
-	struct xfs_parent_name_rec	rec;
+	union {
+		struct xfs_parent_name_rec	rec;
+		__u8			dummy1[XFS_PARENT_NAME_MAX_SIZE];
+	};
 	struct xfs_da_args		args;
 };
 
 int xfs_parent_lookup(struct xfs_trans *tp, struct xfs_inode *ip,
-		const struct xfs_parent_name_irec *pptr, unsigned char *name,
-		unsigned int namelen, struct xfs_parent_scratch *scratch);
+		const struct xfs_parent_name_irec *pptr,
+		struct xfs_parent_scratch *scratch);
 
 int xfs_parent_set(struct xfs_inode *ip,
 		const struct xfs_parent_name_irec *pptr,
diff --git a/libxfs/xfs_trans_resv.c b/libxfs/xfs_trans_resv.c
index 0989151..ec48d60 100644
--- a/libxfs/xfs_trans_resv.c
+++ b/libxfs/xfs_trans_resv.c
@@ -424,19 +424,20 @@
 {
 	return sizeof(struct xfs_attri_log_format) +
 			xlog_calc_iovec_len(XATTR_NAME_MAX) +
-			xlog_calc_iovec_len(sizeof(struct xfs_parent_name_rec));
+			xlog_calc_iovec_len(XFS_PARENT_NAME_MAX_SIZE);
 }
 static inline unsigned int xfs_calc_pptr_unlink_overhead(void)
 {
 	return sizeof(struct xfs_attri_log_format) +
-			xlog_calc_iovec_len(sizeof(struct xfs_parent_name_rec));
+			xlog_calc_iovec_len(XFS_PARENT_NAME_MAX_SIZE);
 }
 static inline unsigned int xfs_calc_pptr_replace_overhead(void)
 {
 	return sizeof(struct xfs_attri_log_format) +
 			xlog_calc_iovec_len(XATTR_NAME_MAX) +
 			xlog_calc_iovec_len(XATTR_NAME_MAX) +
-			xlog_calc_iovec_len(sizeof(struct xfs_parent_name_rec));
+			xlog_calc_iovec_len(XFS_PARENT_NAME_MAX_SIZE) +
+			xlog_calc_iovec_len(XFS_PARENT_NAME_MAX_SIZE);
 }
 
 /*
diff --git a/logprint/log_redo.c b/logprint/log_redo.c
index c705bb1..5a3f088 100644
--- a/logprint/log_redo.c
+++ b/logprint/log_redo.c
@@ -684,12 +684,12 @@
 {
 	struct xfs_parent_name_irec	irec;
 
-	libxfs_parent_irec_from_disk(&irec, name_ptr, value_ptr, value_len);
+	libxfs_parent_irec_from_disk(&irec, name_ptr, name_len, value_ptr,
+			value_len);
 
 	printf("PPTR: %s attr_namelen %u attr_valuelen %u\n", tag, name_len, value_len);
-	printf("PPTR: %s parent_ino %llu parent_gen %u diroffset %u namelen %u name '%.*s'\n",
+	printf("PPTR: %s parent_ino %llu parent_gen %u namelen %u name '%.*s'\n",
 			tag, (unsigned long long)irec.p_ino, irec.p_gen,
-			irec.p_diroffset,
 			irec.p_namelen, irec.p_namelen, irec.p_name);
 }
 
diff --git a/man/man3/xfsctl.3 b/man/man3/xfsctl.3
index 7cc9749..42ba3bb 100644
--- a/man/man3/xfsctl.3
+++ b/man/man3/xfsctl.3
@@ -367,7 +367,6 @@
 			p = xfs_ppinfo_to_pp(pi, i);
 			printf("inode		= %llu\\n", (unsigned long long)p->xpp_ino);
 			printf("generation	= %u\\n", (unsigned int)p->xpp_gen);
-			printf("diroffset	= %u\\n", (unsigned int)p->xpp_diroffset);
 			printf("name		= \\"%s\\"\\n\\n", (char *)p->xpp_name);
 		}
 	} while (!pi->pi_flags & XFS_PPTR_OFLAG_DONE);
diff --git a/mkfs/proto.c b/mkfs/proto.c
index b8d7ac9..445fbef 100644
--- a/mkfs/proto.c
+++ b/mkfs/proto.c
@@ -509,7 +509,7 @@
 		libxfs_trans_log_inode(tp, ip, flags);
 		if (parent) {
 			error = -libxfs_parent_add(tp, parent, pip, &xname,
-					offset, ip);
+					ip);
 			if (error)
 				fail(_("committing parent pointers failed."),
 						error);
@@ -602,7 +602,7 @@
 		libxfs_trans_log_inode(tp, ip, flags);
 		if (parent) {
 			error = -libxfs_parent_add(tp, parent, pip, &xname,
-					offset, ip);
+					ip);
 			if (error)
 				fail(_("committing parent pointers failed."),
 						error);
@@ -636,8 +636,7 @@
 	}
 	libxfs_trans_log_inode(tp, ip, flags);
 	if (parent) {
-		error = -libxfs_parent_add(tp, parent, pip, &xname, offset,
-				ip);
+		error = -libxfs_parent_add(tp, parent, pip, &xname, ip);
 		if (error)
 			fail(_("committing parent pointers failed."), error);
 	}
diff --git a/repair/phase6.c b/repair/phase6.c
index 1994162..3fb11df 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -68,7 +68,6 @@
 	struct dir_hash_ent	*nextbyorder;	/* next in order added */
 	xfs_dahash_t		hashval;	/* hash value of name */
 	uint32_t		address;	/* offset of data entry */
-	uint32_t		new_address;	/* new address, if we rebuild */
 	xfs_ino_t		inum;		/* inode num of entry */
 	short			junkit;		/* name starts with / */
 	short			seen;		/* have seen leaf entry */
@@ -226,7 +225,6 @@
 	p->address = addr;
 	p->inum = inum;
 	p->seen = 0;
-	p->new_address = addr;
 
 	/* Set up the name in the region trailing the hash entry. */
 	memcpy(p->namebuf, name, namelen);
@@ -979,7 +977,7 @@
 		do_error(
 		_("can't make %s, createname error %d\n"),
 			ORPHANAGE, error);
-	add_parent_ptr(ip->i_ino, ORPHANAGE, diroffset, pip);
+	add_parent_ptr(ip->i_ino, ORPHANAGE, pip);
 
 	/*
 	 * bump up the link count in the root directory to account
@@ -1169,8 +1167,7 @@
 	}
 
 	if (xfs_has_parent(mp))
-		add_parent_ptr(ino_p->i_ino, xname.name, diroffset,
-				orphanage_ip);
+		add_parent_ptr(ino_p->i_ino, xname.name, orphanage_ip);
 
 	libxfs_irele(ino_p);
 	libxfs_irele(orphanage_ip);
@@ -1341,8 +1338,8 @@
 
 		libxfs_trans_ijoin(tp, ip, 0);
 
-		error = -libxfs_dir_createname(tp, ip, &p->name, p->inum,
-						nres, &p->new_address);
+		error = -libxfs_dir_createname(tp, ip, &p->name, p->inum, nres,
+				NULL);
 		if (error) {
 			do_warn(
 _("name create failed in ino %" PRIu64 " (%d)\n"), ino, error);
@@ -2819,7 +2816,7 @@
 						p->name.name[1] == '.'))))
 			continue;
 
-		add_parent_ptr(p->inum, p->name.name, p->new_address, dp);
+		add_parent_ptr(p->inum, p->name.name, dp);
 	}
 }
 
diff --git a/repair/pptr.c b/repair/pptr.c
index 99a4047..d8c9581 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -119,9 +119,6 @@
 	xfs_ino_t		parent_ino;
 	unsigned int		parent_gen;
 
-	/* dirent offset */
-	xfs_dir2_dataptr_t	diroffset;
-
 	/* dirent name length */
 	unsigned int		namelen;
 
@@ -140,9 +137,6 @@
 	unsigned long long	parent_ino:63;
 	unsigned int		parent_gen;
 
-	/* dirent offset */
-	xfs_dir2_dataptr_t	diroffset;
-
 	/* parent pointer name length */
 	unsigned int		namelen;
 
@@ -193,8 +187,14 @@
 	/* attribute name length */
 	unsigned int		attrnamelen;
 
+	/* attribute value length */
+	unsigned int		attrvaluelen;
+
 	/* cookie for the attribute name */
 	xfblob_cookie		attrname_cookie;
+
+	/* cookie for the attribute value */
+	xfblob_cookie		attrvalue_cookie;
 };
 
 /* Global names storage file. */
@@ -220,9 +220,9 @@
 	if (pa->parent_ino > pb->parent_ino)
 		return 1;
 
-	if (pa->diroffset < pb->diroffset)
+	if (pa->name_cookie < pb->name_cookie)
 		return -1;
-	if (pa->diroffset > pb->diroffset)
+	if (pa->name_cookie > pb->name_cookie)
 		return 1;
 
 	return 0;
@@ -241,9 +241,18 @@
 	if (pa->parent_ino > pb->parent_ino)
 		return 1;
 
-	if (pa->diroffset < pb->diroffset)
+	/*
+	 * Push the parent pointer names that we didn't find in the dirent scan
+	 * towards the front of the list so that we delete them first.
+	 */
+	if (!pa->name_in_nameblobs && pb->name_in_nameblobs)
 		return -1;
-	if (pa->diroffset > pb->diroffset)
+	if (pa->name_in_nameblobs && !pb->name_in_nameblobs)
+		return 1;
+
+	if (pa->name_cookie < pb->name_cookie)
+		return -1;
+	if (pa->name_cookie > pb->name_cookie)
 		return 1;
 
 	return 0;
@@ -308,12 +317,11 @@
 	}
 }
 
-/* Remember that @dp has a dirent (@fname, @ino) at @diroffset. */
+/* Remember that @dp has a dirent (@fname, @ino). */
 void
 add_parent_ptr(
 	xfs_ino_t		ino,
 	const unsigned char	*fname,
-	xfs_dir2_dataptr_t	diroffset,
 	struct xfs_inode	*dp)
 {
 	struct xfs_mount	*mp = dp->i_mount;
@@ -321,7 +329,6 @@
 		.child_agino	= XFS_INO_TO_AGINO(mp, ino),
 		.parent_ino	= dp->i_ino,
 		.parent_gen	= VFS_I(dp)->i_generation,
-		.diroffset	= diroffset,
 		.namelen	= strlen(fname),
 	};
 	struct ag_pptrs		*ag_pptrs;
@@ -348,9 +355,9 @@
 				fname, strerror(error));
 
 	dbg_printf(
- _("%s: dp %llu fname '%s' diroffset %u ino %llu cookie 0x%llx\n"),
+ _("%s: dp %llu fname '%s' ino %llu cookie 0x%llx\n"),
 			__func__, (unsigned long long)dp->i_ino, fname,
-			diroffset, (unsigned long long)ino,
+			(unsigned long long)ino,
 			(unsigned long long)ag_pptr.name_cookie);
 }
 
@@ -374,15 +381,15 @@
 			.dp		= ip,
 			.attr_filter	= ga->attr_filter,
 			.namelen	= ga->attrnamelen,
-			.op_flags	= XFS_DA_OP_REMOVE,
+			.op_flags	= XFS_DA_OP_REMOVE | XFS_DA_OP_VLOOKUP,
 		};
 		void			*buf;
 
-		buf = malloc(ga->attrnamelen);
+		buf = malloc(ga->attrnamelen + ga->attrvaluelen);
 		if (!buf)
 			do_error(
  _("allocating %u bytes to remove ino %llu garbage xattr failed: %s\n"),
-					ga->attrnamelen,
+					ga->attrnamelen + ga->attrvaluelen,
 					(unsigned long long)ip->i_ino,
 					strerror(errno));
 
@@ -393,6 +400,14 @@
  _("loading garbage xattr name failed: %s\n"),
 					strerror(error));
 
+		error = -xfblob_load(fscan->garbage_xattr_names,
+				ga->attrvalue_cookie, buf + ga->attrnamelen,
+				ga->attrvaluelen);
+		if (error)
+			do_error(
+ _("loading garbage xattr name failed: %s\n"),
+					strerror(error));
+
 		args.name = buf;
 		error = -libxfs_attr_set(&args);
 		if (error)
@@ -418,11 +433,14 @@
 	struct file_scan	*fscan,
 	unsigned int		attr_filter,
 	const void		*name,
-	unsigned int		namelen)
+	unsigned int		namelen,
+	const void		*value,
+	unsigned int		valuelen)
 {
 	struct garbage_xattr	garbage_xattr = {
 		.attr_filter	= attr_filter,
 		.attrnamelen	= namelen,
+		.attrvaluelen	= valuelen,
 	};
 	struct xfs_mount	*mp = ip->i_mount;
 	int			error;
@@ -464,6 +482,13 @@
 				(unsigned long long)ip->i_ino,
 				strerror(error));
 
+	error = -xfblob_store(fscan->garbage_xattr_names,
+			&garbage_xattr.attrvalue_cookie, value, valuelen);
+	if (error)
+		do_error(_("storing ino %llu garbage xattr failed: %s\n"),
+				(unsigned long long)ip->i_ino,
+				strerror(error));
+
 	error = -slab_add(fscan->garbage_xattr_recs, &garbage_xattr);
 	if (error)
 		do_error(_("storing ino %llu garbage xattr rec failed: %s\n"),
@@ -525,14 +550,13 @@
 
 	/* Does the ondisk parent pointer structure make sense? */
 	if (!xfs_parent_namecheck(mp, rec, namelen, attr_flags) ||
-	    !xfs_parent_valuecheck(mp, value, valuelen))
+	    !xfs_parent_valuecheck(mp, namelen, value, valuelen))
 		goto corrupt;
 
-	libxfs_parent_irec_from_disk(&irec, rec, value, valuelen);
+	libxfs_parent_irec_from_disk(&irec, rec, namelen, value, valuelen);
 
 	file_pptr.parent_ino = irec.p_ino;
 	file_pptr.parent_gen = irec.p_gen;
-	file_pptr.diroffset = irec.p_diroffset;
 	file_pptr.namelen = irec.p_namelen;
 
 	error = store_file_pptr_name(fscan, &file_pptr, &irec);
@@ -548,16 +572,17 @@
 				(unsigned long long)ip->i_ino, strerror(error));
 
 	dbg_printf(
- _("%s: dp %llu fname '%.*s' namelen %u diroffset %u ino %llu cookie 0x%llx\n"),
+ _("%s: dp %llu fname '%.*s' namelen %u ino %llu cookie 0x%llx\n"),
 			__func__, (unsigned long long)irec.p_ino,
 			irec.p_namelen, (const char *)irec.p_name,
-			irec.p_namelen, irec.p_diroffset,
+			irec.p_namelen,
 			(unsigned long long)ip->i_ino,
 			(unsigned long long)file_pptr.name_cookie);
 	fscan->nr_file_pptrs++;
 	return 0;
 corrupt:
-	record_garbage_xattr(ip, fscan, attr_flags, name, namelen);
+	record_garbage_xattr(ip, fscan, attr_flags, name, namelen, value,
+			valuelen);
 	return 0;
 }
 
@@ -571,13 +596,11 @@
 	struct xfs_parent_name_irec	pptr_rec = {
 		.p_ino			= ag_pptr->parent_ino,
 		.p_gen			= ag_pptr->parent_gen,
-		.p_diroffset		= ag_pptr->diroffset,
 		.p_namelen		= ag_pptr->namelen,
 	};
 	struct xfs_parent_scratch	scratch;
 
 	memcpy(pptr_rec.p_name, name, ag_pptr->namelen);
-
 	return -libxfs_parent_set(ip, &pptr_rec, &scratch);
 }
 
@@ -585,15 +608,17 @@
 static int
 remove_file_pptr(
 	struct xfs_inode		*ip,
-	const struct file_pptr		*file_pptr)
+	const struct file_pptr		*file_pptr,
+	const unsigned char		*name)
 {
 	struct xfs_parent_name_irec	pptr_rec = {
 		.p_ino			= file_pptr->parent_ino,
 		.p_gen			= file_pptr->parent_gen,
-		.p_diroffset		= file_pptr->diroffset,
+		.p_namelen		= file_pptr->namelen,
 	};
 	struct xfs_parent_scratch	scratch;
 
+	memcpy(pptr_rec.p_name, name, file_pptr->namelen);
 	return -libxfs_parent_unset(ip, &pptr_rec, &scratch);
 }
 
@@ -638,13 +663,25 @@
 				strerror(error));
 
 	while ((file_pptr = pop_slab_cursor(cur)) != NULL) {
-		error = remove_file_pptr(ip, file_pptr);
+		unsigned char	name[MAXNAMELEN];
+
+		error = load_file_pptr_name(fscan, file_pptr, name);
 		if (error)
 			do_error(
- _("wiping ino %llu pptr (ino %llu gen 0x%x diroffset %u) failed: %s\n"),
+  _("loading incorrect name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx) failed: %s\n"),
+					(unsigned long long)ip->i_ino,
+					(unsigned long long)file_pptr->parent_ino,
+					file_pptr->parent_gen,
+					(unsigned long long)file_pptr->name_cookie,
+					strerror(error));
+
+		error = remove_file_pptr(ip, file_pptr, name);
+		if (error)
+			do_error(
+ _("wiping ino %llu pptr (ino %llu gen 0x%x) failed: %s\n"),
 				(unsigned long long)ip->i_ino,
 				(unsigned long long)file_pptr->parent_ino,
-				file_pptr->parent_gen, file_pptr->diroffset,
+				file_pptr->parent_gen,
 				strerror(error));
 	}
 
@@ -665,37 +702,37 @@
 			ag_pptr->namelen);
 	if (error)
 		do_error(
- _("loading missing name for ino %llu parent pointer (ino %llu gen 0x%x diroffset %u namecookie 0x%llx) failed: %s\n"),
+ _("loading missing name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx) failed: %s\n"),
 				(unsigned long long)ip->i_ino,
 				(unsigned long long)ag_pptr->parent_ino,
-				ag_pptr->parent_gen, ag_pptr->diroffset,
+				ag_pptr->parent_gen,
 				(unsigned long long)ag_pptr->name_cookie,
 				strerror(error));
 
 	if (no_modify) {
 		do_warn(
- _("would add missing ino %llu parent pointer (ino %llu gen 0x%x diroffset %u name '%.*s')\n"),
+ _("would add missing ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
 				(unsigned long long)ip->i_ino,
 				(unsigned long long)ag_pptr->parent_ino,
-				ag_pptr->parent_gen, ag_pptr->diroffset,
+				ag_pptr->parent_gen,
 				ag_pptr->namelen, name);
 		return;
 	}
 
 	do_warn(
- _("adding missing ino %llu parent pointer (ino %llu gen 0x%x diroffset %u name '%.*s')\n"),
+ _("adding missing ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
 			(unsigned long long)ip->i_ino,
 			(unsigned long long)ag_pptr->parent_ino,
-			ag_pptr->parent_gen, ag_pptr->diroffset,
+			ag_pptr->parent_gen,
 			ag_pptr->namelen, name);
 
 	error = add_file_pptr(ip, ag_pptr, name);
 	if (error)
 		do_error(
- _("adding ino %llu pptr (ino %llu gen 0x%x diroffset %u name '%.*s') failed: %s\n"),
+ _("adding ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"),
 			(unsigned long long)ip->i_ino,
 			(unsigned long long)ag_pptr->parent_ino,
-			ag_pptr->parent_gen, ag_pptr->diroffset,
+			ag_pptr->parent_gen,
 			ag_pptr->namelen, name, strerror(error));
 }
 
@@ -712,37 +749,37 @@
 	error = load_file_pptr_name(fscan, file_pptr, name);
 	if (error)
 		do_error(
- _("loading incorrect name for ino %llu parent pointer (ino %llu gen 0x%x diroffset %u namecookie 0x%llx) failed: %s\n"),
+ _("loading incorrect name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx) failed: %s\n"),
 				(unsigned long long)ip->i_ino,
 				(unsigned long long)file_pptr->parent_ino,
-				file_pptr->parent_gen, file_pptr->diroffset,
+				file_pptr->parent_gen,
 				(unsigned long long)file_pptr->name_cookie,
 				strerror(error));
 
 	if (no_modify) {
 		do_warn(
- _("would remove bad ino %llu parent pointer (ino %llu gen 0x%x diroffset %u name '%.*s')\n"),
+ _("would remove bad ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
 				(unsigned long long)ip->i_ino,
 				(unsigned long long)file_pptr->parent_ino,
-				file_pptr->parent_gen, file_pptr->diroffset,
+				file_pptr->parent_gen,
 				file_pptr->namelen, name);
 		return;
 	}
 
 	do_warn(
- _("removing bad ino %llu parent pointer (ino %llu gen 0x%x diroffset %u name '%.*s')\n"),
+ _("removing bad ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
 			(unsigned long long)ip->i_ino,
 			(unsigned long long)file_pptr->parent_ino,
-			file_pptr->parent_gen, file_pptr->diroffset,
+			file_pptr->parent_gen,
 			file_pptr->namelen, name);
 
-	error = remove_file_pptr(ip, file_pptr);
+	error = remove_file_pptr(ip, file_pptr, name);
 	if (error)
 		do_error(
- _("removing ino %llu pptr (ino %llu gen 0x%x diroffset %u name '%.*s') failed: %s\n"),
+ _("removing ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"),
 			(unsigned long long)ip->i_ino,
 			(unsigned long long)file_pptr->parent_ino,
-			file_pptr->parent_gen, file_pptr->diroffset,
+			file_pptr->parent_gen,
 			file_pptr->namelen, name, strerror(error));
 }
 
@@ -765,20 +802,20 @@
 			ag_pptr->namelen);
 	if (error)
 		do_error(
- _("loading master-list name for ino %llu parent pointer (ino %llu gen 0x%x diroffset %u namecookie 0x%llx namelen %u) failed: %s\n"),
+ _("loading master-list name for ino %llu parent pointer (ino %llu gen 0x%x  namecookie 0x%llx namelen %u) failed: %s\n"),
 				(unsigned long long)ip->i_ino,
 				(unsigned long long)ag_pptr->parent_ino,
-				ag_pptr->parent_gen, ag_pptr->diroffset,
+				ag_pptr->parent_gen,
 				(unsigned long long)ag_pptr->name_cookie,
 				ag_pptr->namelen, strerror(error));
 
 	error = load_file_pptr_name(fscan, file_pptr, name2);
 	if (error)
 		do_error(
- _("loading file-list name for ino %llu parent pointer (ino %llu gen 0x%x diroffset %u namecookie 0x%llx namelen %u) failed: %s\n"),
+ _("loading file-list name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx namelen %u) failed: %s\n"),
 				(unsigned long long)ip->i_ino,
 				(unsigned long long)file_pptr->parent_ino,
-				file_pptr->parent_gen, file_pptr->diroffset,
+				file_pptr->parent_gen,
 				(unsigned long long)file_pptr->name_cookie,
 				ag_pptr->namelen, strerror(error));
 
@@ -794,42 +831,67 @@
 reset:
 	if (no_modify) {
 		do_warn(
- _("would update ino %llu parent pointer (ino %llu gen 0x%x diroffset %u name '%.*s')\n"),
+ _("would update ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
 				(unsigned long long)ip->i_ino,
 				(unsigned long long)ag_pptr->parent_ino,
-				ag_pptr->parent_gen, ag_pptr->diroffset,
+				ag_pptr->parent_gen,
 				ag_pptr->namelen, name1);
 		return;
 	}
 
 	do_warn(
- _("updating ino %llu parent pointer (ino %llu gen 0x%x diroffset %u name '%.*s')\n"),
+ _("updating ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
 			(unsigned long long)ip->i_ino,
 			(unsigned long long)ag_pptr->parent_ino,
-			ag_pptr->parent_gen, ag_pptr->diroffset,
+			ag_pptr->parent_gen,
 			ag_pptr->namelen, name1);
 
 	if (ag_pptr->parent_gen != file_pptr->parent_gen) {
-		error = remove_file_pptr(ip, file_pptr);
+		error = remove_file_pptr(ip, file_pptr, name2);
 		if (error)
 			do_error(
- _("erasing ino %llu pptr (ino %llu gen 0x%x diroffset %u name '%.*s') failed: %s\n"),
+ _("erasing ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"),
 				(unsigned long long)ip->i_ino,
 				(unsigned long long)file_pptr->parent_ino,
-				file_pptr->parent_gen, file_pptr->diroffset,
+				file_pptr->parent_gen,
 				file_pptr->namelen, name2, strerror(error));
 	}
 
 	error = add_file_pptr(ip, ag_pptr, name1);
 	if (error)
 		do_error(
- _("updating ino %llu pptr (ino %llu gen 0x%x diroffset %u name '%.*s') failed: %s\n"),
+ _("updating ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"),
 			(unsigned long long)ip->i_ino,
 			(unsigned long long)ag_pptr->parent_ino,
-			ag_pptr->parent_gen, ag_pptr->diroffset,
+			ag_pptr->parent_gen,
 			ag_pptr->namelen, name1, strerror(error));
 }
 
+static int
+cmp_file_to_ag_pptr(
+	const struct file_pptr	*fp,
+	const struct ag_pptr	*ap)
+{
+	if (fp->parent_ino > ap->parent_ino)
+		return 1;
+	if (fp->parent_ino < ap->parent_ino)
+		return -1;
+
+	/*
+	 * If this parent pointer wasn't found in the dirent scan, we know it
+	 * should be removed.
+	 */
+	if (!fp->name_in_nameblobs)
+		return -1;
+
+	if (fp->name_cookie < ap->name_cookie)
+		return -1;
+	if (fp->name_cookie > ap->name_cookie)
+		return 1;
+
+	return 0;
+}
+
 /*
  * Make sure that the parent pointers we observed match the ones ondisk.
  *
@@ -895,26 +957,26 @@
 				(unsigned long long)ip->i_ino, strerror(error));
 
 	do {
+		int	cmp_result;
+
 		file_pptr = peek_slab_cursor(fscan->file_pptr_recs_cur);
 
 		dbg_printf(
- _("%s: dp %llu dp_gen 0x%x namelen %u diroffset %u ino %llu namecookie 0x%llx (master)\n"),
+ _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (master)\n"),
 				__func__,
 				(unsigned long long)ag_pptr->parent_ino,
 				ag_pptr->parent_gen,
 				ag_pptr->namelen,
-				ag_pptr->diroffset,
 				(unsigned long long)ip->i_ino,
 				(unsigned long long)ag_pptr->name_cookie);
 
 		if (file_pptr) {
 			dbg_printf(
- _("%s: dp %llu dp_gen 0x%x namelen %u diroffset %u ino %llu namecookie 0x%llx (file)\n"),
+ _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (file)\n"),
 					__func__,
 					(unsigned long long)file_pptr->parent_ino,
 					file_pptr->parent_gen,
 					file_pptr->namelen,
-					file_pptr->diroffset,
 					(unsigned long long)ip->i_ino,
 					(unsigned long long)file_pptr->name_cookie);
 		} else {
@@ -924,9 +986,8 @@
 					(unsigned long long)ip->i_ino);
 		}
 
-		if (!file_pptr ||
-		    file_pptr->parent_ino > ag_pptr->parent_ino ||
-		    file_pptr->diroffset > ag_pptr->diroffset) {
+		cmp_result = file_pptr ? cmp_file_to_ag_pptr(file_pptr, ag_pptr) : 1;
+		if (cmp_result > 0) {
 			/*
 			 * The master pptr list knows about pptrs that are not
 			 * in the ondisk metadata.  Add the missing pptr and
@@ -934,8 +995,7 @@
 			 */
 			add_missing_parent_ptr(ip, fscan, ag_pptr);
 			advance_slab_cursor(fscan->ag_pptr_recs_cur);
-		} else if (file_pptr->parent_ino < ag_pptr->parent_ino ||
-			   file_pptr->diroffset < ag_pptr->diroffset) {
+		} else if (cmp_result < 0) {
 			/*
 			 * The ondisk pptrs mention a link that is not in the
 			 * master list.  Delete the extra pptr and advance only
@@ -959,12 +1019,11 @@
 
 	while ((file_pptr = pop_slab_cursor(fscan->file_pptr_recs_cur))) {
 		dbg_printf(
- _("%s: dp %llu dp_gen 0x%x namelen %u diroffset %u ino %llu namecookie 0x%llx (excess)\n"),
+ _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (excess)\n"),
 				__func__,
 				(unsigned long long)file_pptr->parent_ino,
 				file_pptr->parent_gen,
 				file_pptr->namelen,
-				file_pptr->diroffset,
 				(unsigned long long)ip->i_ino,
 				(unsigned long long)file_pptr->name_cookie);
 
diff --git a/repair/pptr.h b/repair/pptr.h
index d72c1ac..1cf3444 100644
--- a/repair/pptr.h
+++ b/repair/pptr.h
@@ -10,7 +10,7 @@
 void parent_ptr_init(struct xfs_mount *mp);
 
 void add_parent_ptr(xfs_ino_t ino, const unsigned char *fname,
-		xfs_dir2_dataptr_t diroffset, struct xfs_inode *dp);
+		struct xfs_inode *dp);
 
 void check_parent_ptrs(struct xfs_mount *mp);