While checking the catalog tree, add an entry to a hash table for every
inode record found. For now its only purpose is to verify that inode
numbers are not repeated, but more checks will be added in the future.
Signed-off-by: Ernesto A. Fernández <
ernesto.mn...@gmail.com>
---
apfsck/Makefile | 2 +-
apfsck/btree.c | 31 ++++++++++++--
apfsck/inode.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++
apfsck/inode.h | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
apfsck/key.c | 25 ------------
apfsck/key.h | 25 ++++++++++++
apfsck/super.c | 5 +++
apfsck/super.h | 1 +
8 files changed, 291 insertions(+), 29 deletions(-)
create mode 100644 apfsck/inode.c
create mode 100644 apfsck/inode.h
diff --git a/apfsck/Makefile b/apfsck/Makefile
index d516a3a..a1c595a 100644
--- a/apfsck/Makefile
+++ b/apfsck/Makefile
@@ -1,4 +1,4 @@
-SRCS = apfsck.c btree.c crc32c.c key.c object.c super.c unicode.c
+SRCS = apfsck.c btree.c crc32c.c inode.c key.c object.c super.c unicode.c
OBJS = $(SRCS:.c=.o)
DEPS = $(SRCS:.c=.d)
diff --git a/apfsck/btree.c b/apfsck/btree.c
index b626a57..9028144 100644
--- a/apfsck/btree.c
+++ b/apfsck/btree.c
@@ -11,6 +11,7 @@
#include <sys/mman.h>
#include "apfsck.h"
#include "btree.h"
+#include "inode.h"
#include "key.h"
#include "object.h"
#include "super.h"
@@ -487,6 +488,25 @@ static void node_compare_bmaps(struct node *node)
}
/**
+ * parse_cat_record - Parse a catalog record value and check for corruption
+ * @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.
+ */
+static void parse_cat_record(void *key, void *val, int len)
+{
+ switch (cat_type(key)) {
+ case APFS_TYPE_INODE:
+ parse_inode_record(key, val, len);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
* parse_subtree - Parse a subtree and check for corruption
* @root: root node of the subtree
* @last_key: parent key, that must come before all the keys in this subtree;
@@ -514,6 +534,7 @@ static void parse_subtree(struct node *root, struct key *last_key)
for (i = 0; i < root->records; ++i) {
struct node *child;
void *raw = root->raw;
+ void *raw_key, *raw_val;
int off, len;
u64 child_id;
@@ -521,16 +542,17 @@ static void parse_subtree(struct node *root, struct key *last_key)
if (len > btree->longest_key)
btree->longest_key = len;
bmap_mark_as_used(root->used_key_bmap, off - root->key, len);
+ raw_key = raw + off;
if (btree_is_omap(btree)) {
- read_omap_key(raw + off, len, &curr_key);
+ read_omap_key(raw_key, len, &curr_key);
/* When a key is added, the node is updated */
if (curr_key.number > root->object.xid)
report("Object map",
"node xid is older than key xid.");
} else {
- read_cat_key(raw + off, len, &curr_key);
+ read_cat_key(raw_key, len, &curr_key);
}
if (keycmp(last_key, &curr_key) > 0)
@@ -543,16 +565,19 @@ static void parse_subtree(struct node *root, struct key *last_key)
len = node_locate_data(root, i, &off);
bmap_mark_as_used(root->used_val_bmap, off - root->data, len);
+ raw_val = raw + off;
if (node_is_leaf(root)) {
if (len > btree->longest_val)
btree->longest_val = len;
+ if (!btree_is_omap(btree))
+ parse_cat_record(raw_key, raw_val, len);
continue;
}
if (len != 8)
report("B-tree", "wrong size of nonleaf record value.");
- child_id = le64_to_cpu(*(__le64 *)(raw + off));
+ child_id = le64_to_cpu(*(__le64 *)(raw_val));
child = read_node(child_id, btree);
if (child->level != root->level - 1)
diff --git a/apfsck/inode.c b/apfsck/inode.c
new file mode 100644
index 0000000..552891f
--- /dev/null
+++ b/apfsck/inode.c
@@ -0,0 +1,109 @@
+/*
+ * apfsprogs/apfsck/inode.c
+ *
+ * Copyright (C) 2018 Ernesto A. Fernández <
ernesto.mn...@gmail.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "apfsck.h"
+#include "inode.h"
+#include "key.h"
+#include "super.h"
+#include "types.h"
+
+/**
+ * alloc_inode_table - Allocates and returns an empty inode hash table
+ */
+struct inode **alloc_inode_table(void)
+{
+ struct inode **table;
+
+ table = calloc(INODE_TABLE_BUCKETS, sizeof(*table));
+ if (!table) {
+ perror(NULL);
+ exit(1);
+ }
+ return table;
+}
+
+/**
+ * free_inode_table - Free the inode hash table and all its inodes
+ * @table: table to free
+ */
+void free_inode_table(struct inode **table)
+{
+ struct inode *current;
+ struct inode *next;
+ int i;
+
+ for (i = 0; i < INODE_TABLE_BUCKETS; ++i) {
+ current = table[i];
+ while (current) {
+ next = current->i_next;
+ free(current);
+ current = next;
+ }
+ }
+ free(table);
+}
+
+/**
+ * get_inode - Find or create an inode structure in a hash table
+ * @ino: inode number
+ * @table: the hash table
+ *
+ * Returns the inode structure, after creating it if necessary.
+ */
+static struct inode *get_inode(u64 ino, struct inode **table)
+{
+ int index = ino % INODE_TABLE_BUCKETS; /* Trivial hash function */
+ struct inode **entry_p = table + index;
+ struct inode *entry = *entry_p;
+ struct inode *new;
+
+ /* Inodes are ordered by ino in each linked list */
+ while (entry) {
+ if (ino == entry->i_ino)
+ return entry;
+ if (ino < entry->i_ino)
+ break;
+
+ entry_p = &entry->i_next;
+ entry = *entry_p;
+ }
+
+ new = malloc(sizeof(*new));
+ if (!new) {
+ perror(NULL);
+ exit(1);
+ }
+
+ new->i_seen = false;
+ new->i_ino = ino;
+ new->i_next = entry;
+ *entry_p = new;
+ return new;
+}
+
+/**
+ * parse_inode_record - Parse an inode record value and check for corruption
+ * @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_inode_record(struct apfs_inode_key *key,
+ struct apfs_inode_val *val, int len)
+{
+ struct inode *inode;
+
+ if (len < sizeof(*val))
+ report("Inode record", "value is too small.");
+
+ inode = get_inode(cat_cnid(&key->hdr), vsb->v_inode_table);
+ if (inode->i_seen)
+ report("Catalog", "inode numbers are repeated.");
+ inode->i_seen = true;
+}
diff --git a/apfsck/inode.h b/apfsck/inode.h
new file mode 100644
index 0000000..19e76cc
--- /dev/null
+++ b/apfsck/inode.h
@@ -0,0 +1,122 @@
+/*
+ * apfsprogs/apfsck/inode.h
+ *
+ * Copyright (C) 2018 Ernesto A. Fernández <
ernesto.mn...@gmail.com>
+ */
+
+#ifndef _INODE_H
+#define _INODE_H
+
+#include "types.h"
+
+struct apfs_inode_key;
+
+/* Inode numbers for special inodes */
+#define APFS_INVALID_INO_NUM 0
+
+#define APFS_ROOT_DIR_PARENT 1 /* Root directory parent */
+#define APFS_ROOT_DIR_INO_NUM 2 /* Root directory */
+#define APFS_PRIV_DIR_INO_NUM 3 /* Private directory */
+#define APFS_SNAP_DIR_INO_NUM 6 /* Snapshots metadata */
+
+/* Smallest inode number available for user content */
+#define APFS_MIN_USER_INO_NUM 16
+
+/*
+ * Structure of an inode as stored as a B-tree value
+ */
+struct apfs_inode_val {
+/*00*/ __le64 parent_id;
+ __le64 private_id;
+/*10*/ __le64 create_time;
+ __le64 mod_time;
+ __le64 change_time;
+ __le64 access_time;
+/*30*/ __le64 internal_flags;
+ union {
+ __le32 nchildren;
+ __le32 nlink;
+ };
+ __le32 default_protection_class;
+/*40*/ __le32 write_generation_counter;
+ __le32 bsd_flags;
+ __le32 owner;
+ __le32 group;
+/*50*/ __le16 mode;
+ __le16 pad1;
+ __le64 pad2;
+/*5C*/ u8 xfields[];
+} __packed;
+
+/* Extended field types */
+#define APFS_DREC_EXT_TYPE_SIBLING_ID 1
+
+#define APFS_INO_EXT_TYPE_SNAP_XID 1
+#define APFS_INO_EXT_TYPE_DELTA_TREE_OID 2
+#define APFS_INO_EXT_TYPE_DOCUMENT_ID 3
+#define APFS_INO_EXT_TYPE_NAME 4
+#define APFS_INO_EXT_TYPE_PREV_FSIZE 5
+#define APFS_INO_EXT_TYPE_RESERVED_6 6
+#define APFS_INO_EXT_TYPE_FINDER_INFO 7
+#define APFS_INO_EXT_TYPE_DSTREAM 8
+#define APFS_INO_EXT_TYPE_RESERVED_9 9
+#define APFS_INO_EXT_TYPE_DIR_STATS_KEY 10
+#define APFS_INO_EXT_TYPE_FS_UUID 11
+#define APFS_INO_EXT_TYPE_RESERVED_12 12
+#define APFS_INO_EXT_TYPE_SPARSE_BYTES 13
+#define APFS_INO_EXT_TYPE_RDEV 14
+
+/*
+ * Structure used to store the number and size of extended fields of an inode
+ */
+struct apfs_xf_blob {
+ __le16 xf_num_exts;
+ __le16 xf_used_data;
+ u8 xf_data[];
+} __packed;
+
+/*
+ * Structure used to store an inode's extended field
+ */
+struct apfs_x_field {
+ u8 x_type;
+ u8 x_flags;
+ __le16 x_size;
+} __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 {
+ __le64 size;
+ __le64 alloced_size;
+ __le64 default_crypto_id;
+ __le64 total_bytes_written;
+ __le64 total_bytes_read;
+} __packed;
+
+#define INODE_TABLE_BUCKETS 512 /* So the hash table array fits in 4k */
+
+/*
+ * Inode data in memory
+ */
+struct inode {
+ u64 i_ino; /* Inode number */
+ bool i_seen; /* Has this inode been seen? */
+
+ struct inode *i_next; /* Next inode in linked list */
+};
+
+extern struct inode **alloc_inode_table();
+extern void free_inode_table(struct inode **table);
+extern void parse_inode_record(struct apfs_inode_key *key,
+ struct apfs_inode_val *val, int len);
+
+#endif /* _INODE_H */
diff --git a/apfsck/key.c b/apfsck/key.c
index 192564a..a15e942 100644
--- a/apfsck/key.c
+++ b/apfsck/key.c
@@ -38,31 +38,6 @@ void read_omap_key(void *raw, int size, struct key *key)
}
/**
- * cat_type - Read the record type of a catalog key
- * @key: the raw catalog key
- *
- * The record type is stored in the last byte of the cnid field; this function
- * returns that value.
- */
-static inline int cat_type(struct apfs_key_header *key)
-{
- return (le64_to_cpu(key->obj_id_and_type) & APFS_OBJ_TYPE_MASK)
- >> APFS_OBJ_TYPE_SHIFT;
-}
-
-/**
- * cat_cnid - Read the cnid value on a catalog key
- * @key: the raw catalog key
- *
- * The cnid value shares the its field with the record type. This function
- * masks that part away and returns the result.
- */
-static inline u64 cat_cnid(struct apfs_key_header *key)
-{
- return le64_to_cpu(key->obj_id_and_type) & APFS_OBJ_ID_MASK;
-}
-
-/**
* keycmp - Compare two keys
* @k1, @k2: keys to compare
*
diff --git a/apfsck/key.h b/apfsck/key.h
index 6b1516f..fa550ea 100644
--- a/apfsck/key.h
+++ b/apfsck/key.h
@@ -186,6 +186,31 @@ static inline void init_xattr_key(u64 ino, const char *name, struct key *key)
key->name = name;
}
+/**
+ * cat_type - Read the record type of a catalog key
+ * @key: the raw catalog key
+ *
+ * The record type is stored in the last byte of the cnid field; this function
+ * returns that value.
+ */
+static inline int cat_type(struct apfs_key_header *key)
+{
+ return (le64_to_cpu(key->obj_id_and_type) & APFS_OBJ_TYPE_MASK)
+ >> APFS_OBJ_TYPE_SHIFT;
+}
+
+/**
+ * cat_cnid - Read the cnid value on a catalog key
+ * @key: the raw catalog key
+ *
+ * The cnid value shares the its field with the record type. This function
+ * masks that part away and returns the result.
+ */
+static inline u64 cat_cnid(struct apfs_key_header *key)
+{
+ return le64_to_cpu(key->obj_id_and_type) & APFS_OBJ_ID_MASK;
+}
+
extern int keycmp(struct key *k1, struct key *k2);
extern void read_cat_key(void *raw, int size, struct key *key);
extern void read_omap_key(void *raw, int size, struct key *key);
diff --git a/apfsck/super.c b/apfsck/super.c
index 78f4931..0ec45fe 100644
--- a/apfsck/super.c
+++ b/apfsck/super.c
@@ -10,6 +10,7 @@
#include <sys/mman.h>
#include "apfsck.h"
#include "btree.h"
+#include "inode.h"
#include "object.h"
#include "types.h"
#include "super.h"
@@ -179,6 +180,7 @@ void parse_super(void)
perror(NULL);
exit(1);
}
+ vsb->v_inode_table = alloc_inode_table();
vsb_raw = map_volume_super(vol, vsb);
if (!vsb_raw) {
@@ -194,6 +196,9 @@ void parse_super(void)
le64_to_cpu(vsb_raw->apfs_root_tree_oid),
vsb->v_omap->root);
+ free_inode_table(vsb->v_inode_table);
+ vsb->v_inode_table = NULL;
+
sb->s_volumes[vol] = vsb;
}
diff --git a/apfsck/super.h b/apfsck/super.h
index 9af5fd0..0762d2e 100644
--- a/apfsck/super.h
+++ b/apfsck/super.h
@@ -258,6 +258,7 @@ 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 object v_obj; /* Object holding the volume sb */
};
--
2.11.0