Parse all dstream id records to retrieve their refcount, and check that
it matches the actual number of references for the stream.
A few special cases exist. Streams with no references have no id
record, and neither do xattrs, which always have a single reference.
apfsck/btree.c | 3 +++
apfsck/extents.c | 39 +++++++++++++++++++++++++++++++++++++--
apfsck/extents.h | 15 ++++++++++++++-
apfsck/inode.c | 3 ++-
apfsck/inode.h | 7 -------
apfsck/xattr.c | 3 ++-
6 files changed, 58 insertions(+), 12 deletions(-)
diff --git a/apfsck/btree.c b/apfsck/btree.c
index e6ddd48..e092ba3 100644
--- a/apfsck/btree.c
+++ b/apfsck/btree.c
@@ -519,6 +519,9 @@ static void parse_cat_record(void *key, void *val, int len)
case APFS_TYPE_SIBLING_MAP:
parse_sibling_map_record(key, val, len);
break;
+ case APFS_TYPE_DSTREAM_ID:
+ parse_dstream_id_record(key, val, len);
+ break;
default:
break;
}
diff --git a/apfsck/extents.c b/apfsck/extents.c
index baf6c06..9d372e3 100644
--- a/apfsck/extents.c
+++ b/apfsck/extents.c
@@ -32,8 +32,22 @@ struct dstream **alloc_dstream_table(void)
*/
static void check_dstream_stats(struct dstream *dstream)
{
- if (!dstream->d_obj_type) /* The dstream structure was never seen */
- report_unknown("File extent without inode or xattr");
+ if (dstream->d_obj_type == APFS_TYPE_XATTR) {
+ if (dstream->d_seen || dstream->d_references != 1)
+ report("Data stream", "xattrs can't be cloned.");
+ } else {
+ if (!dstream->d_seen && dstream->d_references)
+ report("Data stream", "missing reference count.");
+ if (dstream->d_seen && !dstream->d_references)
+ report("Data stream", "unnecessary reference count.");
+ if (dstream->d_refcnt != dstream->d_references)
+ report("Data stream", "bad reference count.");
+ }
+
+ if (!dstream->d_references)
+ /* No dstream structure to report the length */
+ return;
+
if (dstream->d_bytes < dstream->d_size)
report("Data stream", "some extents are missing.");
if (dstream->d_bytes != dstream->d_alloced_size)
@@ -133,3 +147,24 @@ void parse_extent_record(struct apfs_file_extent_key *key,
if (!le64_to_cpu(val->phys_block_num)) /* This is a hole */
dstream->d_sparse_bytes += length;
}
+
+/**
+ * parse_dstream_id_record - Parse a dstream id 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_dstream_id_record(struct apfs_dstream_id_key *key,
+ struct apfs_dstream_id_val *val, int len)
+{
+ struct dstream *dstream;
+
+ if (len != sizeof(*val))
+ report("Dstream id record", "wrong size of value.");
+
+ dstream = get_dstream(cat_cnid(&key->hdr), vsb->v_dstream_table);
+ dstream->d_seen = true;
+ dstream->d_refcnt = le32_to_cpu(val->refcnt);
+}
diff --git a/apfsck/extents.h b/apfsck/extents.h
index 69d7f0e..7c42ba5 100644
--- a/apfsck/extents.h
+++ b/apfsck/extents.h
@@ -10,6 +10,7 @@
#include "types.h"
struct apfs_file_extent_key;
+struct apfs_dstream_id_key;
/* File extent records */
#define APFS_FILE_EXTENT_LEN_MASK 0x00ffffffffffffffULL
@@ -25,6 +26,13 @@ struct apfs_file_extent_val {
__le64 crypto_id;
} __packed;
+/*
+ * Structure of a data stream record
+ */
+struct apfs_dstream_id_val {
+ __le32 refcnt;
+} __packed;
+
#define DSTREAM_TABLE_BUCKETS 512 /* So the hash table array fits in 4k */
/*
@@ -33,14 +41,17 @@ struct apfs_file_extent_val {
struct dstream {
u64 d_id; /* Id of the dstream */
u8 d_obj_type; /* Type of the owner objects */
+ bool d_seen; /* Has the dstream record been seen? */
- /* Dstream stats read from the dstream structure */
+ /* Dstream stats read from the dstream structures */
u64 d_size; /* Dstream size */
u64 d_alloced_size; /* Dstream size, including unused */
+ u32 d_refcnt; /* Reference count */
/* Dstream stats measured by the fsck */
u64 d_bytes; /* Size of the extents read so far */
u64 d_sparse_bytes; /* Size of the holes read so far */
+ u32 d_references; /* Number of references to dstream */
struct dstream *d_next; /* Next dstream in linked list */
};
@@ -50,5 +61,7 @@ extern void free_dstream_table(struct dstream **table);
extern struct dstream *get_dstream(u64 ino, struct dstream **table);
extern void parse_extent_record(struct apfs_file_extent_key *key,
struct apfs_file_extent_val *val, int len);
+extern void parse_dstream_id_record(struct apfs_dstream_id_key *key,
+ struct apfs_dstream_id_val *val, int len);
#endif /* _EXTENTS_H */
diff --git a/apfsck/inode.c b/apfsck/inode.c
index 0aa89b4..6dedd6c 100644
--- a/apfsck/inode.c
+++ b/apfsck/inode.c
@@ -311,7 +311,7 @@ static int read_dstream_xfield(char *xval, int len, struct inode *inode)
alloced_size = le64_to_cpu(dstream_raw->alloced_size);
dstream = get_dstream(inode->i_private_id, vsb->v_dstream_table);
- if (dstream->d_obj_type) {
+ if (dstream->d_references) {
/* A dstream structure for this id has already been seen */
if (dstream->d_obj_type != APFS_TYPE_INODE)
report("Dstream xfield", "shared by inode and xattr.");
@@ -327,6 +327,7 @@ static int read_dstream_xfield(char *xval, int len, struct inode *inode)
dstream->d_alloced_size = alloced_size;
}
+ dstream->d_references++;
return sizeof(*dstream_raw);
}
diff --git a/apfsck/inode.h b/apfsck/inode.h
index a9bcb0c..fb4f33d 100644
--- a/apfsck/inode.h
+++ b/apfsck/inode.h
@@ -139,13 +139,6 @@ struct apfs_x_field {
} __packed;
/*
- * Structure of a data stream record
- */
-struct apfs_dstream_id_val {
- __le32 refcnt;
-} __packed;
-
-/*
* Structure used to store information about a data stream
*/
struct apfs_dstream {
diff --git a/apfsck/xattr.c b/apfsck/xattr.c
index bf4d0a1..00d1dbd 100644
--- a/apfsck/xattr.c
+++ b/apfsck/xattr.c
@@ -44,7 +44,7 @@ static void parse_xattr_dstream(struct apfs_xattr_dstream *xstream)
alloced_size = le64_to_cpu(dstream_raw->alloced_size);
dstream = get_dstream(id, vsb->v_dstream_table);
- if (dstream->d_obj_type) {
+ if (dstream->d_references) {
/* A dstream structure for this id has already been seen */
if (dstream->d_obj_type != APFS_TYPE_XATTR)
report("Xattr dstream", "shared by inode and xattr.");
@@ -59,6 +59,7 @@ static void parse_xattr_dstream(struct apfs_xattr_dstream *xstream)
dstream->d_size = size;
dstream->d_alloced_size = alloced_size;
}
+ dstream->d_references++;
}
/**
--
2.11.0