@@ -831,8 +831,11 @@ xfs_failaddr_t xfs_da3_blkinfo_verify(struct xfs_buf *bp,
* 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 the
- * sha512 of the child inode generation and the dirent name.
+ * resistant hash computed from the dirent name. The hash is defined to be:
+ *
+ * - The dirent name if it fits within the EA name.
+ *
+ * - The sha512 of the child inode generation and the dirent name.
*
* The EA value contains the same name as the dirent in the parent directory.
*/
@@ -842,4 +845,18 @@ struct xfs_parent_name_rec {
__u8 p_namehash[XFS_PARENT_NAME_HASH_SIZE];
} __attribute__((packed));
+static inline unsigned int
+xfs_parent_name_rec_sizeof(
+ unsigned int hashlen)
+{
+ return offsetof(struct xfs_parent_name_rec, p_namehash) + hashlen;
+}
+
+static inline unsigned int
+xfs_parent_name_hashlen(
+ unsigned int rec_sizeof)
+{
+ return rec_sizeof - offsetof(struct xfs_parent_name_rec, p_namehash);
+}
+
#endif /* __XFS_DA_FORMAT_H__ */
@@ -56,7 +56,8 @@ xfs_parent_namecheck(
{
xfs_ino_t p_ino;
- if (reclen != sizeof(struct xfs_parent_name_rec))
+ if (reclen <= xfs_parent_name_rec_sizeof(0) ||
+ reclen > xfs_parent_name_rec_sizeof(XFS_PARENT_NAME_HASH_SIZE))
return false;
/* Only one namespace bit allowed. */
@@ -108,12 +109,16 @@ void
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)
{
irec->p_ino = be64_to_cpu(rec->p_ino);
irec->p_gen = be32_to_cpu(rec->p_gen);
- memcpy(irec->p_namehash, rec->p_namehash, sizeof(irec->p_namehash));
+ irec->hashlen = xfs_parent_name_hashlen(reclen);
+ memcpy(irec->p_namehash, rec->p_namehash, irec->hashlen);
+ memset(irec->p_namehash + irec->hashlen, 0,
+ sizeof(irec->p_namehash) - irec->hashlen);
if (!value) {
irec->p_namelen = 0;
@@ -137,13 +142,15 @@ xfs_parent_irec_from_disk(
void
xfs_parent_irec_to_disk(
struct xfs_parent_name_rec *rec,
+ int *reclen,
void *value,
int *valuelen,
const struct xfs_parent_name_irec *irec)
{
rec->p_ino = cpu_to_be64(irec->p_ino);
rec->p_gen = cpu_to_be32(irec->p_gen);
- memcpy(rec->p_namehash, irec->p_namehash, sizeof(rec->p_namehash));
+ *reclen = xfs_parent_name_rec_sizeof(irec->hashlen);
+ memcpy(rec->p_namehash, irec->p_namehash, irec->hashlen);
if (valuelen) {
ASSERT(*valuelen > 0);
@@ -206,12 +213,14 @@ xfs_parent_add(
struct xfs_inode *child)
{
struct xfs_da_args *args = &parent->args;
- int error;
+ int hashlen;
- error = xfs_init_parent_name_rec(&parent->rec, dp, parent_name, child);
- if (error)
- return error;
+ hashlen = xfs_init_parent_name_rec(&parent->rec, dp, parent_name,
+ child);
+ if (hashlen < 0)
+ return hashlen;
+ args->namelen = xfs_parent_name_rec_sizeof(hashlen);
args->hashval = xfs_da_hashname(args->name, args->namelen);
args->trans = tp;
@@ -234,12 +243,13 @@ xfs_parent_remove(
struct xfs_inode *child)
{
struct xfs_da_args *args = &parent->args;
- int error;
+ int hashlen;
- error = xfs_init_parent_name_rec(&parent->rec, dp, name, child);
- if (error)
- return error;
+ hashlen = xfs_init_parent_name_rec(&parent->rec, dp, name, child);
+ if (hashlen < 0)
+ return hashlen;
+ args->namelen = xfs_parent_name_rec_sizeof(hashlen);
args->trans = tp;
args->dp = child;
args->hashval = xfs_da_hashname(args->name, args->namelen);
@@ -258,21 +268,21 @@ xfs_parent_replace(
struct xfs_inode *child)
{
struct xfs_da_args *args = &new_parent->args;
- int error;
+ int old_hashlen, new_hashlen;
- error = xfs_init_parent_name_rec(&new_parent->old_rec, old_dp,
+ old_hashlen = xfs_init_parent_name_rec(&new_parent->old_rec, old_dp,
old_name, child);
- if (error)
- return error;
- error = xfs_init_parent_name_rec(&new_parent->rec, new_dp, new_name,
- child);
- if (error)
- return error;
+ if (old_hashlen < 0)
+ return old_hashlen;
+ new_hashlen = xfs_init_parent_name_rec(&new_parent->rec, new_dp,
+ new_name, child);
+ if (new_hashlen < 0)
+ return new_hashlen;
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_hashlen);
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_hashlen);
args->trans = tp;
args->dp = child;
@@ -320,16 +330,17 @@ xfs_parent_lookup(
unsigned int namelen,
struct xfs_parent_scratch *scr)
{
+ int reclen;
int error;
- xfs_parent_irec_to_disk(&scr->rec, NULL, NULL, pptr);
+ 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.namelen = reclen;
scr->args.op_flags = XFS_DA_OP_OKNOENT;
scr->args.trans = tp;
scr->args.valuelen = namelen;
@@ -357,14 +368,16 @@ xfs_parent_set(
const struct xfs_parent_name_irec *pptr,
struct xfs_parent_scratch *scr)
{
- xfs_parent_irec_to_disk(&scr->rec, NULL, NULL, pptr);
+ int reclen;
+
+ 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.namelen = reclen;
scr->args.valuelen = pptr->p_namelen;
scr->args.value = (void *)pptr->p_name;
scr->args.whichfork = XFS_ATTR_FORK;
@@ -384,14 +397,16 @@ xfs_parent_unset(
const struct xfs_parent_name_irec *pptr,
struct xfs_parent_scratch *scr)
{
- xfs_parent_irec_to_disk(&scr->rec, NULL, NULL, pptr);
+ int reclen;
+
+ 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.namelen = reclen;
scr->args.whichfork = XFS_ATTR_FORK;
return xfs_attr_set(&scr->args);
@@ -399,7 +414,7 @@ xfs_parent_unset(
/*
* Compute the parent pointer namehash for the given child file and dirent
- * name.
+ * name. Returns the length of the hash in bytes, or a negative errno.
*/
int
xfs_parent_namehash(
@@ -420,6 +435,12 @@ xfs_parent_namehash(
return -EINVAL;
}
+ if (name->len < namehash_len) {
+ memcpy(namehash, name->name, name->len);
+ memset(namehash + name->len, 0, namehash_len - name->len);
+ return name->len;
+ }
+
error = sha512_init(&shash);
if (error)
goto out;
@@ -436,6 +457,7 @@ xfs_parent_namehash(
if (error)
goto out;
+ error = SHA512_DIGEST_SIZE;
out:
sha512_erase(&shash);
return error;
@@ -451,7 +473,12 @@ xfs_parent_irec_hash(
.name = pptr->p_name,
.len = pptr->p_namelen,
};
+ int hashlen;
- return xfs_parent_namehash(ip, &xname, &pptr->p_namehash,
+ hashlen = xfs_parent_namehash(ip, &xname, &pptr->p_namehash,
sizeof(pptr->p_namehash));
+ if (hashlen < 0)
+ return hashlen;
+ pptr->hashlen = hashlen;
+ return 0;
}
@@ -23,6 +23,7 @@ struct xfs_parent_name_irec {
/* Key fields for looking up a particular parent pointer. */
xfs_ino_t p_ino;
uint32_t p_gen;
+ uint8_t hashlen;
uint8_t p_namehash[XFS_PARENT_NAME_HASH_SIZE];
/* Attributes of a parent pointer. */
@@ -31,10 +32,11 @@ struct xfs_parent_name_irec {
};
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);
+void 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
@@ -144,15 +144,19 @@ xchk_dir_parent_pointer(
{
struct xfs_scrub *sc = sd->sc;
int pptr_namelen;
- int error;
+ int hashlen;
sd->pptr.p_ino = sc->ip->i_ino;
sd->pptr.p_gen = VFS_I(sc->ip)->i_generation;
- error = xfs_parent_namehash(ip, name, &sd->pptr.p_namehash,
+ hashlen = xfs_parent_namehash(ip, name, &sd->pptr.p_namehash,
sizeof(sd->pptr.p_namehash));
- if (error)
- return error;
+ if (hashlen < 0) {
+ xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0,
+ &hashlen);
+ return hashlen;
+ }
+ sd->pptr.hashlen = hashlen;
pptr_namelen = xfs_parent_lookup(sc->tp, ip, &sd->pptr, sd->namebuf,
MAXNAMELEN, &sd->pptr_scratch);
@@ -540,7 +540,7 @@ xrep_dir_scan_parent_pointer(
!xfs_parent_valuecheck(sc->mp, value, valuelen))
return -EFSCORRUPTED;
- xfs_parent_irec_from_disk(&rd->pptr, rec, value, valuelen);
+ xfs_parent_irec_from_disk(&rd->pptr, rec, namelen, value, valuelen);
/* Ignore parent pointers that point back to a different dir. */
if (rd->pptr.p_ino != sc->ip->i_ino ||
@@ -531,6 +531,7 @@ xchk_parent_scan_attr(
struct xfs_inode *dp = NULL;
const struct xfs_parent_name_rec *rec = (const void *)name;
unsigned int lockmode;
+ int hashlen;
int error;
/* Ignore incomplete xattrs */
@@ -552,7 +553,7 @@ xchk_parent_scan_attr(
return -ECANCELED;
}
- xfs_parent_irec_from_disk(&pp->pptr, rec, value, valuelen);
+ xfs_parent_irec_from_disk(&pp->pptr, rec, namelen, value, valuelen);
xname.name = pp->pptr.p_name;
xname.len = pp->pptr.p_namelen;
@@ -561,13 +562,16 @@ xchk_parent_scan_attr(
* Does the namehash in the parent pointer match the actual name?
* If not, there's no point in checking further.
*/
- error = xfs_parent_namehash(sc->ip, &xname, pp->child_namehash,
+ hashlen = xfs_parent_namehash(sc->ip, &xname, pp->child_namehash,
sizeof(pp->child_namehash));
- if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))
- return error;
+ if (hashlen < 0) {
+ xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &hashlen);
+ return hashlen;
+ }
- if (memcmp(pp->pptr.p_namehash, pp->child_namehash,
- sizeof(pp->pptr.p_namehash))) {
+ if (hashlen != pp->pptr.hashlen ||
+ memcmp(pp->pptr.p_namehash, pp->child_namehash,
+ pp->pptr.hashlen)) {
trace_xchk_parent_bad_namehash(sc->ip, pp->pptr.p_ino,
xname.name, xname.len);
xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
@@ -522,7 +522,7 @@ xrep_pptr_dump_tempptr(
else
return -EFSCORRUPTED;
- xfs_parent_irec_from_disk(&rp->pptr, rec, value, valuelen);
+ xfs_parent_irec_from_disk(&rp->pptr, rec, namelen, value, valuelen);
trace_xrep_pptr_dumpname(sc->tempip, &rp->pptr);
@@ -74,7 +74,7 @@ xfs_getparent_listent(
return;
}
- xfs_parent_irec_from_disk(&gp->pptr_irec, (void *)name, value,
+ xfs_parent_irec_from_disk(&gp->pptr_irec, (void *)name, namelen, value,
valuelen);
trace_xfs_getparent_listent(context->dp, ppi, irec);