[PATCH 1/6] apfsck: check that rdev xfield is only on devices

2 views
Skip to first unread message

Ernesto A. Fernández

unread,
Mar 1, 2019, 12:56:34 PM3/1/19
to linux...@googlegroups.com
The rdev extended field of an inode record is used to store the device
identifier of character and block devices. Check that all files of
these types have it, and that files of other types don't.

Signed-off-by: Ernesto A. Fernández <ernesto.mn...@gmail.com>
---
apfsck/inode.c | 35 ++++++++++++++++++++++++++++++++++-
apfsck/inode.h | 1 +
2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/apfsck/inode.c b/apfsck/inode.c
index ac9bcf6..1497468 100644
--- a/apfsck/inode.c
+++ b/apfsck/inode.c
@@ -146,6 +146,34 @@ static int read_sparse_bytes_xfield(char *xval, int len, struct inode *inode)
}

/**
+ * read_rdev_xfield - Parse and check a device identifier xfield
+ * @xval: pointer to the xfield value
+ * @len: remaining length of the inode value
+ * @inode: struct to receive the results
+ *
+ * Returns the length of the xfield value.
+ */
+static int read_rdev_xfield(char *xval, int len, struct inode *inode)
+{
+ u16 filetype = inode->i_mode & S_IFMT;
+ __le32 *rdev;
+
+ assert(filetype); /* Mode must be set before parsing xfields */
+ if (filetype != S_IFCHR && filetype != S_IFBLK)
+ report("Inode record", "not device but has device identifier.");
+
+ if (len < 4)
+ report("Device ID xfield", "doesn't fit in inode record.");
+ rdev = (__le32 *)xval;
+
+ inode->i_rdev = le32_to_cpu(*rdev);
+ if (!inode->i_rdev)
+ report("Device ID xfield", "null ID in use.");
+
+ return sizeof(*rdev);
+}
+
+/**
* read_dstream_xfield - Parse a dstream xfield and check its consistency
* @xval: pointer to the xfield value
* @len: remaining length of the inode value
@@ -218,9 +246,11 @@ static void parse_inode_xfields(struct apfs_xf_blob *xblob, int len,
break;
case APFS_INO_EXT_TYPE_DOCUMENT_ID:
case APFS_INO_EXT_TYPE_FINDER_INFO:
- case APFS_INO_EXT_TYPE_RDEV:
xlen = 4;
break;
+ case APFS_INO_EXT_TYPE_RDEV:
+ xlen = read_rdev_xfield(xval, len, inode);
+ break;
case APFS_INO_EXT_TYPE_NAME:
xlen = strnlen(xval, len - 1) + 1;
if (xval[xlen - 1] != 0)
@@ -365,4 +395,7 @@ void parse_inode_record(struct apfs_inode_key *key,

parse_inode_xfields((struct apfs_xf_blob *)val->xfields,
len - sizeof(*val), inode);
+
+ if ((filetype == S_IFCHR || filetype == S_IFBLK) && !inode->i_rdev)
+ report("Inode record", "device file with no device ID.");
}
diff --git a/apfsck/inode.h b/apfsck/inode.h
index 1cf1979..65c2a5a 100644
--- a/apfsck/inode.h
+++ b/apfsck/inode.h
@@ -142,6 +142,7 @@ struct inode {
u64 i_size; /* Inode size */
u64 i_alloced_size; /* Inode size, including unused */
u64 i_sparse_bytes; /* Number of sparse bytes */
+ u32 i_rdev; /* Device ID */

/* Inode stats measured by the fsck */
u32 i_child_count; /* Number of children of directory */
--
2.11.0

Ernesto A. Fernández

unread,
Mar 1, 2019, 12:56:48 PM3/1/19
to linux...@googlegroups.com
Verify that the document id in an inode's xfields is not reserved, and
that it's not considered free by the volume superblock.

I have not run into any document ids so far, so this patch in untested.

Signed-off-by: Ernesto A. Fernández <ernesto.mn...@gmail.com>
---
apfsck/inode.c | 28 ++++++++++++++++++++++++++++
apfsck/inode.h | 3 +++
apfsck/super.c | 2 ++
apfsck/super.h | 3 +++
4 files changed, 36 insertions(+)

diff --git a/apfsck/inode.c b/apfsck/inode.c
index 1497468..fc412ca 100644
--- a/apfsck/inode.c
+++ b/apfsck/inode.c
@@ -146,6 +146,32 @@ static int read_sparse_bytes_xfield(char *xval, int len, struct inode *inode)
}

/**
+ * read_document_id_xfield - Parse and check a document id xfield
+ * @xval: pointer to the xfield value
+ * @len: remaining length of the inode value
+ * @inode: inode structure
+ *
+ * Returns the length of the xfield value.
+ */
+static int read_document_id_xfield(char *xval, int len, struct inode *inode)
+{
+ __le32 *id_raw;
+ u32 id;
+
+ if (len < 4)
+ report("Document id xfield", "doesn't fit in inode record.");
+ id_raw = (__le32 *)xval;
+ id = le32_to_cpu(*id_raw);
+
+ if (id < APFS_MIN_DOC_ID)
+ report("Document id xfield", "invalid id in use.");
+ if (id >= vsb->v_next_doc_id)
+ report("Document id xfield", "free id in use.");
+
+ return sizeof(*id_raw);
+}
+
+/**
* read_rdev_xfield - Parse and check a device identifier xfield
* @xval: pointer to the xfield value
* @len: remaining length of the inode value
@@ -245,6 +271,8 @@ static void parse_inode_xfields(struct apfs_xf_blob *xblob, int len,
xlen = read_sparse_bytes_xfield(xval, len, inode);
break;
case APFS_INO_EXT_TYPE_DOCUMENT_ID:
+ xlen = read_document_id_xfield(xval, len, inode);
+ break;
case APFS_INO_EXT_TYPE_FINDER_INFO:
xlen = 4;
break;
diff --git a/apfsck/inode.h b/apfsck/inode.h
index 65c2a5a..ec274dc 100644
--- a/apfsck/inode.h
+++ b/apfsck/inode.h
@@ -77,6 +77,9 @@ struct apfs_inode_val {
#define APFS_INO_EXT_TYPE_SPARSE_BYTES 13
#define APFS_INO_EXT_TYPE_RDEV 14

+/* Constants for extended fields */
+#define APFS_MIN_DOC_ID 3 /* Smallest not reserved document id */
+
/*
* Structure used to store the number and size of extended fields of an inode
*/
diff --git a/apfsck/super.c b/apfsck/super.c
index 7cc8647..e7063ec 100644
--- a/apfsck/super.c
+++ b/apfsck/super.c
@@ -153,6 +153,8 @@ static struct apfs_superblock *map_volume_super(int vol,

if (le32_to_cpu(vsb->v_raw->apfs_magic) != APFS_MAGIC)
report("Volume superblock", "wrong magic.");
+
+ vsb->v_next_doc_id = le32_to_cpu(vsb->v_raw->apfs_next_doc_id);
return vsb->v_raw;
}

diff --git a/apfsck/super.h b/apfsck/super.h
index 133d7c2..b69257c 100644
--- a/apfsck/super.h
+++ b/apfsck/super.h
@@ -267,6 +267,9 @@ struct volume_superblock {
u64 v_symlink_count; /* Number of symlinks */
u64 v_special_count; /* Number of other filesystem objects */

+ /* Volume information read from the on-disk structure */
+ u32 v_next_doc_id; /* Next document identifier to be assigned */
+
struct object v_obj; /* Object holding the volume sb */
};

--
2.11.0

Ernesto A. Fernández

unread,
Mar 1, 2019, 12:57:01 PM3/1/19
to linux...@googlegroups.com
An extended field of type PREV_FSIZE will only appear if the filesystem
suffered a crash. Don't treat this as corruption for now, just print
the information.

Signed-off-by: Ernesto A. Fernández <ernesto.mn...@gmail.com>
---
apfsck/inode.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/apfsck/inode.c b/apfsck/inode.c
index fc412ca..eae78d3 100644
--- a/apfsck/inode.c
+++ b/apfsck/inode.c
@@ -262,9 +262,10 @@ static void parse_inode_xfields(struct apfs_xf_blob *xblob, int len,
case APFS_INO_EXT_TYPE_FS_UUID:
xlen = 16;
break;
+ case APFS_INO_EXT_TYPE_PREV_FSIZE:
+ printf("Inode xfield: filesystem has crashed.\n");
case APFS_INO_EXT_TYPE_SNAP_XID:
case APFS_INO_EXT_TYPE_DELTA_TREE_OID:
- case APFS_INO_EXT_TYPE_PREV_FSIZE:
xlen = 8;
break;
case APFS_INO_EXT_TYPE_SPARSE_BYTES:
--
2.11.0

Ernesto A. Fernández

unread,
Mar 1, 2019, 12:57:15 PM3/1/19
to linux...@googlegroups.com
If an inode has hard links, each one of them must have its own sibling
link record; the dentry will point to this record in its xfields, and
the name and parent will match. To run all these checks, keep both
xfields and sibling records in a linked list for each inode.

Signed-off-by: Ernesto A. Fernández <ernesto.mn...@gmail.com>
---
apfsck/btree.c | 3 ++
apfsck/dir.c | 38 ++++++++++++++--
apfsck/inode.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
apfsck/inode.h | 34 ++++++++++++++-
4 files changed, 205 insertions(+), 5 deletions(-)

diff --git a/apfsck/btree.c b/apfsck/btree.c
index 6724a40..f2200cf 100644
--- a/apfsck/btree.c
+++ b/apfsck/btree.c
@@ -509,6 +509,9 @@ static void parse_cat_record(void *key, void *val, int len)
case APFS_TYPE_FILE_EXTENT:
parse_extent_record(key, val, len);
break;
+ case APFS_TYPE_SIBLING_LINK:
+ parse_sibling_record(key, val, len);
+ break;
default:
break;
}
diff --git a/apfsck/dir.c b/apfsck/dir.c
index a252327..c14ac87 100644
--- a/apfsck/dir.c
+++ b/apfsck/dir.c
@@ -11,19 +11,43 @@
#include "super.h"

/**
+ * read_sibling_id_xfield - Parse a sibling id xfield and check its consistency
+ * @xval: pointer to the xfield value
+ * @len: remaining length of the dentry value
+ * @sibling_id: on return, the sibling id
+ *
+ * Returns the length of the xfield value.
+ */
+static int read_sibling_id_xfield(char *xval, int len, u64 *sibling_id)
+{
+ __le64 *id_raw;
+
+ if (len < 8)
+ report("Sibling link xfield", "doesn't fit in dentry record.");
+ id_raw = (__le64 *)xval;
+
+ *sibling_id = le64_to_cpu(*id_raw);
+
+ return sizeof(*id_raw);
+}
+
+/**
* parse_dentry_xfields - Parse and check a dentry extended fields
* @xblob: pointer to the beginning of the xfields in the dentry value
* @len: length of the xfields
+ * @sibling_id: on return, the sibling id (0 if none)
*
* Internal consistency of @key must be checked before calling this function.
*/
-static void parse_dentry_xfields(struct apfs_xf_blob *xblob, int len)
+static void parse_dentry_xfields(struct apfs_xf_blob *xblob, int len,
+ u64 *sibling_id)
{
struct apfs_x_field *xfield;
char *xval;
int xcount;
int i;

+ *sibling_id = 0;
if (len == 0) /* No extended fields */
return;

@@ -49,7 +73,7 @@ static void parse_dentry_xfields(struct apfs_xf_blob *xblob, int len)

switch (xfield[i].x_type) {
case APFS_DREC_EXT_TYPE_SIBLING_ID:
- xlen = 8;
+ xlen = read_sibling_id_xfield(xval, len, sibling_id);
break;
default:
report("Dentry xfield", "invalid type.");
@@ -88,7 +112,10 @@ void parse_dentry_record(struct apfs_drec_hashed_key *key,
{
u64 ino, parent_ino;
struct inode *inode, *parent;
+ int namelen = le32_to_cpu(key->name_len_and_hash) & 0x3FFU;
u16 filetype, dtype;
+ u64 sibling_id;
+ struct sibling *sibling;

if (len < sizeof(*val))
report("Dentry record", "value is too small.");
@@ -121,5 +148,10 @@ void parse_dentry_record(struct apfs_drec_hashed_key *key,
inode->i_mode |= dtype << 12;

parse_dentry_xfields((struct apfs_xf_blob *)val->xfields,
- len - sizeof(*val));
+ len - sizeof(*val), &sibling_id);
+
+ if (!sibling_id) /* No sibling record for this dentry */
+ return;
+ sibling = get_sibling(sibling_id, namelen, inode);
+ set_or_check_sibling(parent_ino, namelen, key->name, sibling);
}
diff --git a/apfsck/inode.c b/apfsck/inode.c
index eae78d3..c1da183 100644
--- a/apfsck/inode.c
+++ b/apfsck/inode.c
@@ -61,6 +61,37 @@ struct inode **alloc_inode_table(void)
}

/**
+ * free_sibling_links - Free the sibling links for an inode
+ * @inode: inode to free
+ *
+ * Also checks that the number of listed siblings is correct, and that all
+ * of them had both a record and a dentry xfield.
+ */
+static void free_sibling_links(struct inode *inode)
+{
+ struct sibling *current = inode->i_siblings;
+ struct sibling *next;
+ u32 count = 0;
+
+ while (current) {
+ if (!current->s_checked)
+ report("Catalog", "orphaned or missing sibling link.");
+ next = current->s_next;
+ free(current);
+ current = next;
+ ++count;
+ }
+
+ /* Inodes with one link can have a sibling record, but don't need it */
+ if (inode->i_link_count == 1 && count == 0)
+ return;
+
+ if (count != inode->i_link_count)
+ report("Inode record",
+ "link count inconsistent with sibling records.");
+}
+
+/**
* free_inode_table - Free the inode hash table and all its inodes
* @table: table to free
*
@@ -77,6 +108,7 @@ void free_inode_table(struct inode **table)
current = table[i];
while (current) {
check_inode_stats(current);
+ free_sibling_links(current);

next = current->i_next;
free(current);
@@ -428,3 +460,106 @@ void parse_inode_record(struct apfs_inode_key *key,
if ((filetype == S_IFCHR || filetype == S_IFBLK) && !inode->i_rdev)
report("Inode record", "device file with no device ID.");
}
+
+/**
+ * get_sibling - Find or create a sibling link structure for an inode
+ * @id: sibling id
+ * @namelen: length of the sibling name
+ * @inode: the inode
+ *
+ * Returns the sibling structure, after creating it if necessary.
+ */
+struct sibling *get_sibling(u64 id, int namelen, struct inode *inode)
+{
+ struct sibling **entry_p = &inode->i_siblings;
+ struct sibling *entry = *entry_p;
+ struct sibling *new;
+
+ /* Siblings are ordered by id in the inode's linked list */
+ while (entry) {
+ if (id == entry->s_id)
+ return entry;
+ if (id < entry->s_id)
+ break;
+
+ entry_p = &entry->s_next;
+ entry = *entry_p;
+ }
+
+ new = calloc(1, sizeof(*new) + namelen);
+ if (!new) {
+ perror(NULL);
+ exit(1);
+ }
+
+ new->s_checked = false;
+ new->s_id = id;
+ new->s_next = entry;
+ *entry_p = new;
+ return new;
+}
+
+/**
+ * set_or_check_sibling - Set or check the fields of a sibling structure
+ * @parent_id: parent id
+ * @namelen: length of the name
+ * @name: name of the sibling
+ * @sibling: the sibling structure
+ *
+ * When first called for @sibling, sets the three given fields. On the second
+ * call, checks that they are set to the correct values.
+ */
+void set_or_check_sibling(u64 parent_id, int namelen, u8 *name,
+ struct sibling *sibling)
+{
+ /* Whichever was read first, dentry or sibling, sets the fields */
+ if (!sibling->s_name_len) {
+ sibling->s_name_len = namelen;
+ strcpy((char *)sibling->s_name, (char *)name);
+ sibling->s_parent_ino = parent_id;
+ return;
+ }
+
+ /* Fields already set, check them */
+ if (sibling->s_name_len != namelen)
+ report("Sibling record", "name length doesn't match dentry's.");
+ if (strcmp((char *)sibling->s_name, (char *)name))
+ report("Sibling record", "name doesn't match dentry's.");
+ if (sibling->s_parent_ino != parent_id)
+ report("Sibling record", "parent id doesn't match dentry's.");
+ sibling->s_checked = true;
+}
+
+/**
+ * parse_sibling_record - Parse and check a sibling link record value
+ * @key: pointer to the raw key
+ * @val: pointer to the raw value
+ * @len: length of the raw value
+ *
+ * Internal consistency of @key must be checked before calling this function.
+ */
+void parse_sibling_record(struct apfs_sibling_link_key *key,
+ struct apfs_sibling_val *val, int len)
+{
+ struct inode *inode;
+ struct sibling *sibling;
+ int namelen;
+
+ if (len < sizeof(*val))
+ report("Sibling link record", "value is too small.");
+ namelen = le16_to_cpu(val->name_len);
+
+ if (len != sizeof(*val) + namelen)
+ report("Sibling link record", "wrong size of value.");
+ if (val->name[namelen - 1] != 0)
+ report("Sibling link record", "name lacks NULL-termination.");
+ /* Name length doesn't need checking: it's the same for the dentry */
+
+ inode = get_inode(cat_cnid(&key->hdr), vsb->v_inode_table);
+ if (!inode->i_seen) /* The b-tree keys are in order */
+ report("Sibling link record", "inode is missing");
+
+ sibling = get_sibling(le64_to_cpu(key->sibling_id), namelen, inode);
+ set_or_check_sibling(le64_to_cpu(val->parent_id), namelen, val->name,
+ sibling);
+}
diff --git a/apfsck/inode.h b/apfsck/inode.h
index ec274dc..ba09eb2 100644
--- a/apfsck/inode.h
+++ b/apfsck/inode.h
@@ -10,6 +10,7 @@
#include "types.h"

struct apfs_inode_key;
+struct apfs_sibling_link_key;

/* Inode numbers for special inodes */
#define APFS_INVALID_INO_NUM 0
@@ -126,6 +127,16 @@ struct apfs_dir_stats_val {
__le64 gen_count;
} __packed;

+/*
+ * Structure of the value for a sibling link record. These are used to
+ * list the hard links for a given inode.
+ */
+struct apfs_sibling_val {
+ __le64 parent_id;
+ __le16 name_len;
+ u8 name[0];
+} __packed;
+
#define INODE_TABLE_BUCKETS 512 /* So the hash table array fits in 4k */

/*
@@ -148,17 +159,36 @@ struct inode {
u32 i_rdev; /* Device ID */

/* Inode stats measured by the fsck */
- u32 i_child_count; /* Number of children of directory */
- u32 i_link_count; /* Number of dentries for file */
+ u32 i_child_count; /* Number of children of directory */
+ u32 i_link_count; /* Number of dentries for file */
+ struct sibling *i_siblings; /* Linked list of siblings for inode */

struct inode *i_next; /* Next inode in linked list */
};

+/*
+ * Sibling link data in memory
+ */
+struct sibling {
+ struct sibling *s_next; /* Next sibling in linked list */
+ u64 s_id; /* Sibling id */
+ bool s_checked; /* Has this sibling been checked? */
+
+ u64 s_parent_ino; /* Inode number for parent */
+ u16 s_name_len; /* Name length */
+ u8 s_name[0]; /* Name */
+};
+
extern struct inode **alloc_inode_table();
extern void free_inode_table(struct inode **table);
extern struct inode *get_inode(u64 ino, struct inode **table);
extern void check_inode_ids(u64 ino, u64 parent_ino);
extern void parse_inode_record(struct apfs_inode_key *key,
struct apfs_inode_val *val, int len);
+extern struct sibling *get_sibling(u64 id, int namelen, struct inode *inode);
+extern void set_or_check_sibling(u64 parent_id, int namelen, u8 *name,
+ struct sibling *sibling);
+extern void parse_sibling_record(struct apfs_sibling_link_key *key,
+ struct apfs_sibling_val *val, int len);

#endif /* _INODE_H */
--
2.11.0

Ernesto A. Fernández

unread,
Mar 1, 2019, 12:57:27 PM3/1/19
to linux...@googlegroups.com
When an inode or dentry does not have any extended fields, the length of
the record value will just be the size of the structure. Having an
xfield blob with no xfields makes no sense, so it's most likely not
allowed. Add a check for this.

Signed-off-by: Ernesto A. Fernández <ernesto.mn...@gmail.com>
---
apfsck/dir.c | 3 +++
apfsck/inode.c | 3 +++
2 files changed, 6 insertions(+)

diff --git a/apfsck/dir.c b/apfsck/dir.c
index c14ac87..c937d55 100644
--- a/apfsck/dir.c
+++ b/apfsck/dir.c
@@ -54,7 +54,10 @@ static void parse_dentry_xfields(struct apfs_xf_blob *xblob, int len,
len -= sizeof(*xblob);
if (len < 0)
report("Dentry record", "no room for extended fields.");
+
xcount = le16_to_cpu(xblob->xf_num_exts);
+ if (!xcount)
+ report("Dentry record", "xfield blob has no xfields.");

xfield = (struct apfs_x_field *)xblob->xf_data;
xval = (char *)xfield + xcount * sizeof(xfield[0]);
diff --git a/apfsck/inode.c b/apfsck/inode.c
index c1da183..8772083 100644
--- a/apfsck/inode.c
+++ b/apfsck/inode.c
@@ -275,7 +275,10 @@ static void parse_inode_xfields(struct apfs_xf_blob *xblob, int len,
len -= sizeof(*xblob);
if (len < 0)
report("Inode records", "no room for extended fields.");
+
xcount = le16_to_cpu(xblob->xf_num_exts);
+ if (!xcount)
+ report("Inode record", "xfield blob has no xfields.");

xfield = (struct apfs_x_field *)xblob->xf_data;
xval = (char *)xfield + xcount * sizeof(xfield[0]);
--
2.11.0

Ernesto A. Fernández

unread,
Mar 1, 2019, 12:57:40 PM3/1/19
to linux...@googlegroups.com
A dentry will only use xfields to report its sibling link id, so there
will never be more than one. Add a check for this, and use it to
simplify parse_dentry_xfields().

Signed-off-by: Ernesto A. Fernández <ernesto.mn...@gmail.com>
---
apfsck/dir.c | 51 +++++++++++++--------------------------------------
1 file changed, 13 insertions(+), 38 deletions(-)

diff --git a/apfsck/dir.c b/apfsck/dir.c
index c937d55..b70027e 100644
--- a/apfsck/dir.c
+++ b/apfsck/dir.c
@@ -44,8 +44,7 @@ static void parse_dentry_xfields(struct apfs_xf_blob *xblob, int len,
{
struct apfs_x_field *xfield;
char *xval;
- int xcount;
- int i;
+ int xlen;

*sibling_id = 0;
if (len == 0) /* No extended fields */
@@ -54,52 +53,28 @@ static void parse_dentry_xfields(struct apfs_xf_blob *xblob, int len,
len -= sizeof(*xblob);
if (len < 0)
report("Dentry record", "no room for extended fields.");
-
- xcount = le16_to_cpu(xblob->xf_num_exts);
- if (!xcount)
- report("Dentry record", "xfield blob has no xfields.");
+ if (le16_to_cpu(xblob->xf_num_exts) != 1)
+ report("Dentry record", "bad xfield count.");

xfield = (struct apfs_x_field *)xblob->xf_data;
- xval = (char *)xfield + xcount * sizeof(xfield[0]);
- len -= xcount * sizeof(xfield[0]);
+ xval = (char *)xfield + sizeof(*xfield);
+ len -= sizeof(*xfield);
if (len < 0)
- report("Dentry record", "number of xfields cannot fit.");
+ report("Dentry record", "xfield cannot fit.");

/* The official reference seems to be wrong here */
if (le16_to_cpu(xblob->xf_used_data) != len)
report("Dentry record",
"value size incompatible with xfields.");

- /* TODO: could a dentry actually have more than one xfield? */
- for (i = 0; i < le16_to_cpu(xblob->xf_num_exts); ++i) {
- int xlen, xpad_len;
-
- switch (xfield[i].x_type) {
- case APFS_DREC_EXT_TYPE_SIBLING_ID:
- xlen = read_sibling_id_xfield(xval, len, sibling_id);
- break;
- default:
- report("Dentry xfield", "invalid type.");
- }
-
- if (xlen != le16_to_cpu(xfield[i].x_size))
- report("Dentry xfield", "wrong size");
- len -= xlen;
- xval += xlen;
-
- /* Attribute length is padded with zeroes to a multiple of 8 */
- xpad_len = ROUND_UP(xlen, 8) - xlen;
- len -= xpad_len;
- if (len < 0)
- report("Dentry xfield", "doesn't fit in record value.");
-
- for (; xpad_len; ++xval, --xpad_len)
- if (*xval)
- report("Dentry xfield", "non-zero padding.");
- }
+ if (xfield->x_type != APFS_DREC_EXT_TYPE_SIBLING_ID)
+ report("Dentry xfield", "invalid type.");
+ xlen = read_sibling_id_xfield(xval, len, sibling_id);

- if (len)
- report("Dentry record", "length of xfields does not add up.");
+ if (xlen != le16_to_cpu(xfield->x_size))
+ report("Dentry xfield", "wrong size");
+ if (xlen != len)
+ report("Dentry record", "wrong used space for xfields.");
}

/**
--
2.11.0

Reply all
Reply to author
Forward
0 new messages