While checking the catalog tree, add an entry to a hash table for every
file extent record found. For now its only purpose is to verify that
extents are consecutive, i.e., that each extent begins where the last
one ended. More checks will be added in the future.
The hash table code is essentially the same for extents and inodes, so
I intend to refactor this soon.
apfsck/extents.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
apfsck/extents.h | 15 +++++++++++
apfsck/super.c | 4 +++
apfsck/super.h | 3 ++-
4 files changed, 102 insertions(+), 1 deletion(-)
diff --git a/apfsck/extents.c b/apfsck/extents.c
index 184dbab..a1cf4e8 100644
--- a/apfsck/extents.c
+++ b/apfsck/extents.c
@@ -4,12 +4,87 @@
*/
+#include <stdlib.h>
+#include <stdio.h>
#include "apfsck.h"
#include "extents.h"
#include "key.h"
#include "super.h"
/**
+ * alloc_dstream_table - Allocates and returns an empty dstream hash table
+ */
+struct dstream **alloc_dstream_table(void)
+{
+ struct dstream **table;
+
+ table = calloc(DSTREAM_TABLE_BUCKETS, sizeof(*table));
+ if (!table) {
+ perror(NULL);
+ exit(1);
+ }
+ return table;
+}
+
+/**
+ * free_dstream_table - Free the dstream hash table and all its dstreams
+ * @table: table to free
+ */
+void free_dstream_table(struct dstream **table)
+{
+ struct dstream *current;
+ struct dstream *next;
+ int i;
+
+ for (i = 0; i < DSTREAM_TABLE_BUCKETS; ++i) {
+ current = table[i];
+ while (current) {
+ next = current->d_next;
+ free(current);
+ current = next;
+ }
+ }
+ free(table);
+}
+
+/**
+ * get_dstream - Find or create a dstream structure in a hash table
+ * @id: id of the dstream
+ * @table: the hash table
+ *
+ * Returns the dstream structure, after creating it if necessary.
+ */
+struct dstream *get_dstream(u64 id, struct dstream **table)
+{
+ int index = id % DSTREAM_TABLE_BUCKETS; /* Trivial hash function */
+ struct dstream **entry_p = table + index;
+ struct dstream *entry = *entry_p;
+ struct dstream *new;
+
+ /* Dstreams are ordered by id in each linked list */
+ while (entry) {
+ if (id == entry->d_id)
+ return entry;
+ if (id < entry->d_id)
+ break;
+
+ entry_p = &entry->d_next;
+ entry = *entry_p;
+ }
+
+ new = calloc(1, sizeof(*new));
+ if (!new) {
+ perror(NULL);
+ exit(1);
+ }
+
+ new->d_id = id;
+ new->d_next = entry;
+ *entry_p = new;
+ return new;
+}
+
+/**
* parse_extent_record - Parse an extent record value and check for corruption
* @key: pointer to the raw key
* @val: pointer to the raw value
@@ -20,6 +95,7 @@
void parse_extent_record(struct apfs_file_extent_key *key,
struct apfs_file_extent_val *val, int len)
{
+ struct dstream *dstream;
u64 length, flags;
if (len != sizeof(*val))
@@ -32,4 +108,9 @@ void parse_extent_record(struct apfs_file_extent_key *key,
flags = le64_to_cpu(val->len_and_flags) & APFS_FILE_EXTENT_FLAG_MASK;
if (flags)
report("Extent record", "no flags should be set.");
+
+ dstream = get_dstream(cat_cnid(&key->hdr), vsb->v_dstream_table);
+ if (dstream->d_size != le64_to_cpu(key->logical_addr))
+ report("Data stream", "extents are not consecutive.");
+ dstream->d_size += length;
}
diff --git a/apfsck/extents.h b/apfsck/extents.h
index ec9c0fb..1b838de 100644
--- a/apfsck/extents.h
+++ b/apfsck/extents.h
@@ -25,6 +25,21 @@ struct apfs_file_extent_val {
__le64 crypto_id;
} __packed;
+#define DSTREAM_TABLE_BUCKETS 512 /* So the hash table array fits in 4k */
+
+/*
+ * Dstream data in memory
+ */
+struct dstream {
+ u64 d_id; /* Id of the dstream */
+ u64 d_size; /* Size of the extents read so far */
+
+ struct dstream *d_next; /* Next dstream in linked list */
+};
+
+extern struct dstream **alloc_dstream_table();
+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);
diff --git a/apfsck/super.c b/apfsck/super.c
index 15dfed7..e270fd8 100644
--- a/apfsck/super.c
+++ b/apfsck/super.c
@@ -10,6 +10,7 @@
#include <sys/mman.h>
#include "apfsck.h"
#include "btree.h"
+#include "extents.h"
#include "inode.h"
#include "object.h"
#include "types.h"
@@ -180,6 +181,7 @@ void parse_super(void)
perror(NULL);
exit(1);
}
+ vsb->v_dstream_table = alloc_dstream_table();
vsb->v_inode_table = alloc_inode_table();
vsb_raw = map_volume_super(vol, vsb);
@@ -198,6 +200,8 @@ void parse_super(void)
free_inode_table(vsb->v_inode_table);
vsb->v_inode_table = NULL;
+ free_dstream_table(vsb->v_dstream_table);
+ vsb->v_dstream_table = NULL;
if (le64_to_cpu(vsb_raw->apfs_num_files) !=
vsb->v_file_count)
diff --git a/apfsck/super.h b/apfsck/super.h
index 7e606c8..133d7c2 100644
--- a/apfsck/super.h
+++ b/apfsck/super.h
@@ -258,7 +258,8 @@ struct volume_superblock {
struct apfs_superblock *v_raw;
struct btree *v_omap;
struct btree *v_cat;
- struct inode **v_inode_table; /* Hash table listing the inodes */
+ struct inode **v_inode_table; /* Hash table of all inodes */
+ struct dstream **v_dstream_table; /* Hash table of all dstreams */
/* Volume stats as measured by the fsck */
u64 v_file_count; /* Number of files */
--
2.11.0