@@ -825,19 +825,24 @@ xfs_failaddr_t xfs_da3_blkinfo_verify(struct xfs_buf *bp,
struct xfs_da3_blkinfo *hdr3);
/* We use sha512 for the parent pointer name hash. */
-#define XFS_PARENT_NAME_HASH_SIZE (64)
+#define XFS_PARENT_NAME_SHA512_SIZE (64)
/*
* Parent pointer attribute format definition
*
* The EA name encodes the parent inode number, generation and a collision
- * resistant hash computed from the dirent name. The hash is defined to be:
+ * resistant hash computed from the dirent name. The hash is defined to be
+ * one of the following:
*
- * - The dirent name if it fits within the EA name.
+ * - The dirent name, as long as it does not use the last possible byte of the
+ * EA name space.
*
- * - The sha512 of the child inode generation and the dirent name.
+ * - The truncated dirent name, with the sha512 hash of the child inode
+ * generation number and dirent name. The hash is written at the end of the
+ * EA name.
*
- * The EA value contains the same name as the dirent in the parent directory.
+ * 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;
@@ -845,8 +850,17 @@ struct xfs_parent_name_rec {
__u8 p_namehash[];
} __attribute__((packed));
+/* Maximum size of a parent pointer EA name. */
#define XFS_PARENT_NAME_MAX_SIZE \
- (sizeof(struct xfs_parent_name_rec) + XFS_PARENT_NAME_HASH_SIZE)
+ (MAXNAMELEN - 1)
+
+/* Maximum size of a parent pointer name hash. */
+#define XFS_PARENT_NAME_MAX_HASH_SIZE \
+ (XFS_PARENT_NAME_MAX_SIZE - sizeof(struct xfs_parent_name_rec))
+
+/* Offset of the sha512 hash, if used. */
+#define XFS_PARENT_NAME_SHA512_OFFSET \
+ (XFS_PARENT_NAME_MAX_HASH_SIZE - XFS_PARENT_NAME_SHA512_SIZE)
static inline unsigned int
xfs_parent_name_rec_sizeof(
@@ -57,7 +57,7 @@ xfs_parent_namecheck(
xfs_ino_t p_ino;
if (reclen <= xfs_parent_name_rec_sizeof(0) ||
- reclen > xfs_parent_name_rec_sizeof(XFS_PARENT_NAME_HASH_SIZE))
+ reclen > xfs_parent_name_rec_sizeof(XFS_PARENT_NAME_MAX_HASH_SIZE))
return false;
/* Only one namespace bit allowed. */
@@ -75,10 +75,18 @@ xfs_parent_namecheck(
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 >= MAXNAMELEN - XFS_PARENT_NAME_SHA512_OFFSET)
return false;
if (value == NULL)
@@ -98,7 +106,20 @@ xfs_init_parent_name_rec(
rec->p_ino = cpu_to_be64(dp->i_ino);
rec->p_gen = cpu_to_be32(VFS_IC(dp)->i_generation);
return xfs_parent_namehash(ip, name, rec->p_namehash,
- XFS_PARENT_NAME_HASH_SIZE);
+ XFS_PARENT_NAME_MAX_HASH_SIZE);
+}
+
+/* Compute the number of name bytes that can be encoded in the namehash. */
+static inline unsigned int
+xfs_parent_valuelen_adj(
+ int hashlen)
+{
+ ASSERT(hashlen > 0);
+
+ if (hashlen == XFS_PARENT_NAME_MAX_HASH_SIZE)
+ return XFS_PARENT_NAME_SHA512_OFFSET;
+
+ return hashlen;
}
/*
@@ -125,14 +146,29 @@ xfs_parent_irec_from_disk(
return;
}
- ASSERT(valuelen > 0);
ASSERT(valuelen < MAXNAMELEN);
- valuelen = min(valuelen, MAXNAMELEN);
+ if (irec->hashlen == XFS_PARENT_NAME_MAX_HASH_SIZE) {
+ ASSERT(valuelen > 0);
+ ASSERT(valuelen <= MAXNAMELEN - XFS_PARENT_NAME_SHA512_OFFSET);
- irec->p_namelen = valuelen;
- memcpy(irec->p_name, value, valuelen);
- memset(&irec->p_name[valuelen], 0, sizeof(irec->p_name) - valuelen);
+ valuelen = min_t(int, valuelen,
+ MAXNAMELEN - XFS_PARENT_NAME_SHA512_OFFSET);
+
+ memcpy(irec->p_name, irec->p_namehash,
+ XFS_PARENT_NAME_SHA512_OFFSET);
+ memcpy(&irec->p_name[XFS_PARENT_NAME_SHA512_OFFSET],
+ value, valuelen);
+ irec->p_namelen = XFS_PARENT_NAME_SHA512_OFFSET + valuelen;
+ } else {
+ ASSERT(valuelen == 0);
+
+ memcpy(irec->p_name, irec->p_namehash, irec->hashlen);
+ irec->p_namelen = irec->hashlen;
+ }
+
+ memset(&irec->p_name[irec->p_namelen], 0,
+ sizeof(irec->p_name) - irec->p_namelen);
}
/*
@@ -157,11 +193,15 @@ xfs_parent_irec_to_disk(
ASSERT(*valuelen >= irec->p_namelen);
ASSERT(*valuelen < MAXNAMELEN);
- *valuelen = irec->p_namelen;
+ if (irec->hashlen == XFS_PARENT_NAME_MAX_HASH_SIZE)
+ *valuelen = irec->p_namelen - XFS_PARENT_NAME_SHA512_OFFSET;
+ else
+ *valuelen = 0;
}
- if (value)
- memcpy(value, irec->p_name, irec->p_namelen);
+ if (value && irec->hashlen == XFS_PARENT_NAME_MAX_HASH_SIZE)
+ memcpy(value, irec->p_name + XFS_PARENT_NAME_SHA512_OFFSET,
+ irec->p_namelen - XFS_PARENT_NAME_SHA512_OFFSET);
}
/*
@@ -214,6 +254,7 @@ xfs_parent_add(
{
struct xfs_da_args *args = &parent->args;
int hashlen;
+ unsigned int name_adj;
hashlen = xfs_init_parent_name_rec(&parent->rec, dp, parent_name,
child);
@@ -223,11 +264,13 @@ xfs_parent_add(
args->namelen = xfs_parent_name_rec_sizeof(hashlen);
args->hashval = xfs_da_hashname(args->name, args->namelen);
+ name_adj = xfs_parent_valuelen_adj(hashlen);
+
args->trans = tp;
args->dp = child;
if (parent_name) {
- parent->args.value = (void *)parent_name->name;
- parent->args.valuelen = parent_name->len;
+ parent->args.value = (void *)parent_name->name + name_adj;
+ parent->args.valuelen = parent_name->len - name_adj;
}
return xfs_attr_defer_add(args);
@@ -269,6 +312,7 @@ xfs_parent_replace(
{
struct xfs_da_args *args = &new_parent->args;
int old_hashlen, new_hashlen;
+ int new_name_adj;
old_hashlen = xfs_init_parent_name_rec(&new_parent->old_rec, old_dp,
old_name, child);
@@ -279,6 +323,8 @@ xfs_parent_replace(
if (new_hashlen < 0)
return new_hashlen;
+ new_name_adj = xfs_parent_valuelen_adj(new_hashlen);
+
new_parent->args.name = (const uint8_t *)&new_parent->old_rec;
new_parent->args.namelen = xfs_parent_name_rec_sizeof(old_hashlen);
new_parent->args.new_name = (const uint8_t *)&new_parent->rec;
@@ -286,8 +332,8 @@ xfs_parent_replace(
args->trans = tp;
args->dp = child;
- new_parent->args.value = (void *)new_name->name;
- new_parent->args.valuelen = new_name->len;
+ new_parent->args.value = (void *)new_name->name + new_name_adj;
+ new_parent->args.valuelen = new_name->len - new_name_adj;
args->hashval = xfs_da_hashname(args->name, args->namelen);
return xfs_attr_defer_replace(args);
@@ -331,10 +377,13 @@ xfs_parent_lookup(
struct xfs_parent_scratch *scr)
{
int reclen;
+ int name_adj;
int error;
xfs_parent_irec_to_disk(&scr->rec, &reclen, NULL, NULL, pptr);
+ name_adj = xfs_parent_valuelen_adj(pptr->hashlen);
+
memset(&scr->args, 0, sizeof(struct xfs_da_args));
scr->args.attr_filter = XFS_ATTR_PARENT;
scr->args.dp = ip;
@@ -343,8 +392,8 @@ xfs_parent_lookup(
scr->args.namelen = reclen;
scr->args.op_flags = XFS_DA_OP_OKNOENT;
scr->args.trans = tp;
- scr->args.valuelen = namelen;
- scr->args.value = name;
+ scr->args.valuelen = namelen - name_adj;
+ scr->args.value = name + name_adj;
scr->args.whichfork = XFS_ATTR_FORK;
scr->args.hashval = xfs_da_hashname(scr->args.name, scr->args.namelen);
@@ -353,7 +402,8 @@ xfs_parent_lookup(
if (error)
return error;
- return scr->args.valuelen;
+ memcpy(name, pptr->p_namehash, name_adj);
+ return scr->args.valuelen + name_adj;
}
/*
@@ -369,17 +419,20 @@ xfs_parent_set(
struct xfs_parent_scratch *scr)
{
int reclen;
+ int name_adj;
xfs_parent_irec_to_disk(&scr->rec, &reclen, NULL, NULL, pptr);
+ name_adj = xfs_parent_valuelen_adj(pptr->hashlen);
+
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 = reclen;
- scr->args.valuelen = pptr->p_namelen;
- scr->args.value = (void *)pptr->p_name;
+ scr->args.valuelen = pptr->p_namelen - name_adj;
+ scr->args.value = (void *)pptr->p_name + name_adj;
scr->args.whichfork = XFS_ATTR_FORK;
return xfs_attr_set(&scr->args);
@@ -430,12 +483,16 @@ xfs_parent_namehash(
ASSERT(SHA512_DIGEST_SIZE ==
crypto_shash_digestsize(ip->i_mount->m_sha512));
- if (namehash_len != SHA512_DIGEST_SIZE) {
+ if (namehash_len != XFS_PARENT_NAME_MAX_HASH_SIZE) {
ASSERT(0);
return -EINVAL;
}
- if (name->len < namehash_len) {
+ if (name->len < XFS_PARENT_NAME_MAX_HASH_SIZE) {
+ /*
+ * If the dirent name is shorter than the size of the namehash
+ * field, write it directly into the namehash field.
+ */
memcpy(namehash, name->name, name->len);
memset(namehash + name->len, 0, namehash_len - name->len);
return name->len;
@@ -453,11 +510,17 @@ xfs_parent_namehash(
if (error)
goto out;
- error = sha512_done(&shash, namehash);
+ /*
+ * The sha512 hash of the child gen and dirent name is placed at the
+ * end of the namehash, and as many bytes as will fit are copied from
+ * the dirent name to the start of the namehash.
+ */
+ error = sha512_done(&shash, namehash + XFS_PARENT_NAME_SHA512_OFFSET);
if (error)
goto out;
- error = SHA512_DIGEST_SIZE;
+ memcpy(namehash, name->name, XFS_PARENT_NAME_SHA512_OFFSET);
+ error = XFS_PARENT_NAME_MAX_HASH_SIZE;
out:
sha512_erase(&shash);
return error;
@@ -12,8 +12,8 @@ extern struct kmem_cache *xfs_parent_intent_cache;
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
@@ -24,7 +24,7 @@ struct xfs_parent_name_irec {
xfs_ino_t p_ino;
uint32_t p_gen;
uint8_t hashlen;
- uint8_t p_namehash[XFS_PARENT_NAME_HASH_SIZE];
+ uint8_t p_namehash[XFS_PARENT_NAME_MAX_HASH_SIZE];
/* Attributes of a parent pointer. */
uint8_t p_namelen;
@@ -511,7 +511,7 @@ examine_xattr(
struct file_pptr file_pptr = { };
struct xfs_parent_name_irec irec;
struct xfs_name xname;
- uint8_t namehash[XFS_PARENT_NAME_HASH_SIZE];
+ uint8_t namehash[XFS_PARENT_NAME_MAX_HASH_SIZE];
struct xfs_mount *mp = ip->i_mount;
struct file_scan *fscan = priv;
const struct xfs_parent_name_rec *rec = (const void *)name;
@@ -528,7 +528,7 @@ examine_xattr(
/* 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, namelen, value, valuelen);