Rework all hash table code to be shared by dstreams and inodes, and
move it into the new htable.c and htable.h files.
Signed-off-by: Ernesto A. Fernández <
ernesto.mn...@gmail.com>
---
apfsck/Makefile | 2 +-
apfsck/dir.c | 4 +--
apfsck/extents.c | 86 ++++++++++++++----------------------------------
apfsck/extents.h | 14 ++++----
apfsck/htable.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++
apfsck/htable.h | 40 +++++++++++++++++++++++
apfsck/inode.c | 99 ++++++++++++++++----------------------------------------
apfsck/inode.h | 15 +++++----
apfsck/super.c | 5 +--
apfsck/super.h | 6 ++--
apfsck/xattr.c | 4 +--
11 files changed, 209 insertions(+), 154 deletions(-)
create mode 100644 apfsck/htable.c
create mode 100644 apfsck/htable.h
diff --git a/apfsck/Makefile b/apfsck/Makefile
index 3beeb38..67f8d21 100644
--- a/apfsck/Makefile
+++ b/apfsck/Makefile
@@ -1,4 +1,4 @@
-SRCS = apfsck.c btree.c crc32c.c dir.c extents.c \
+SRCS = apfsck.c btree.c crc32c.c dir.c extents.c htable.c \
inode.c key.c object.c super.c unicode.c xattr.c
OBJS = $(SRCS:.c=.o)
DEPS = $(SRCS:.c=.d)
diff --git a/apfsck/dir.c b/apfsck/dir.c
index d53589d..fc4de1a 100644
--- a/apfsck/dir.c
+++ b/apfsck/dir.c
@@ -104,7 +104,7 @@ void parse_dentry_record(struct apfs_drec_hashed_key *key,
report("Dentry record", "value is too small.");
ino = le64_to_cpu(val->file_id);
- inode = get_inode(ino, vsb->v_inode_table);
+ inode = get_inode(ino);
inode->i_link_count++;
if (!inode->i_first_name) {
@@ -120,7 +120,7 @@ void parse_dentry_record(struct apfs_drec_hashed_key *key,
parent_ino = cat_cnid(&key->hdr);
check_inode_ids(ino, parent_ino);
if (parent_ino != APFS_ROOT_DIR_PARENT) {
- parent = get_inode(parent_ino, vsb->v_inode_table);
+ parent = get_inode(parent_ino);
if (!parent->i_seen) /* The b-tree keys are in order */
report("Dentry record", "parent inode missing");
if ((parent->i_mode & S_IFMT) != S_IFDIR)
diff --git a/apfsck/extents.c b/apfsck/extents.c
index a99a2f9..c98f7b0 100644
--- a/apfsck/extents.c
+++ b/apfsck/extents.c
@@ -8,25 +8,11 @@
#include <stdio.h>
#include "apfsck.h"
#include "extents.h"
+#include "htable.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;
-}
-
-/**
* check_dstream_stats - Verify the stats gathered by the fsck vs the metadata
* @dstream: dstream structure to check
*/
@@ -55,63 +41,39 @@ static void check_dstream_stats(struct dstream *dstream)
}
/**
- * free_dstream_table - Free the dstream hash table and all its dstreams
+ * free_dstream - Free a dstream structure after performing some final checks
+ * @entry: the entry to free
+ */
+static void free_dstream(union htable_entry *entry)
+{
+ struct dstream *dstream = &entry->dstream;
+
+ check_dstream_stats(dstream);
+ free(entry);
+}
+
+/**
+ * free_dstream_table - Free the dstream hash table and all its dentries
* @table: table to free
*/
-void free_dstream_table(struct dstream **table)
+void free_dstream_table(union htable_entry **table)
{
- struct dstream *current;
- struct dstream *next;
- int i;
-
- for (i = 0; i < DSTREAM_TABLE_BUCKETS; ++i) {
- current = table[i];
- while (current) {
- check_dstream_stats(current);
-
- next = current->d_next;
- free(current);
- current = next;
- }
- }
- free(table);
+ free_htable(table, free_dstream);
}
/**
- * get_dstream - Find or create a dstream structure in a hash table
+ * get_dstream - Find or create a dstream structure in the dstream 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)
+struct dstream *get_dstream(u64 id)
{
- 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);
- }
+ union htable_entry *entry;
- new->d_id = id;
- new->d_next = entry;
- *entry_p = new;
- return new;
+ entry = get_htable_entry(id, sizeof(struct dstream),
+ vsb->v_dstream_table);
+ return &entry->dstream;
}
/**
@@ -142,7 +104,7 @@ void parse_extent_record(struct apfs_file_extent_key *key,
if (flags)
report("Extent record", "no flags should be set.");
- dstream = get_dstream(cat_cnid(&key->hdr), vsb->v_dstream_table);
+ dstream = get_dstream(cat_cnid(&key->hdr));
if (dstream->d_bytes != le64_to_cpu(key->logical_addr))
report("Data stream", "extents are not consecutive.");
dstream->d_bytes += length;
@@ -167,7 +129,7 @@ void parse_dstream_id_record(struct apfs_dstream_id_key *key,
if (len != sizeof(*val))
report("Dstream id record", "wrong size of value.");
- dstream = get_dstream(cat_cnid(&key->hdr), vsb->v_dstream_table);
+ dstream = get_dstream(cat_cnid(&key->hdr));
dstream->d_seen = true;
dstream->d_refcnt = le32_to_cpu(val->refcnt);
}
diff --git a/apfsck/extents.h b/apfsck/extents.h
index 7c42ba5..35229d4 100644
--- a/apfsck/extents.h
+++ b/apfsck/extents.h
@@ -39,7 +39,12 @@ struct apfs_dstream_id_val {
* Dstream data in memory
*/
struct dstream {
- u64 d_id; /* Id of the dstream */
+ /* Hash table entry header (struct htable_entry_header from htable.h) */
+ struct {
+ union htable_entry *d_next;
+ u64 d_id;
+ };
+
u8 d_obj_type; /* Type of the owner objects */
bool d_seen; /* Has the dstream record been seen? */
@@ -52,13 +57,10 @@ struct dstream {
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 */
};
-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 free_dstream_table(union htable_entry **table);
+extern struct dstream *get_dstream(u64 ino);
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,
diff --git a/apfsck/htable.c b/apfsck/htable.c
new file mode 100644
index 0000000..4e0f33d
--- /dev/null
+++ b/apfsck/htable.c
@@ -0,0 +1,88 @@
+/*
+ * apfsprogs/apfsck/htable.c
+ *
+ * Copyright (C) 2019 Ernesto A. Fernández <
ernesto.mn...@gmail.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "apfsck.h"
+#include "htable.h"
+#include "types.h"
+
+/**
+ * alloc_htable - Allocates and returns an empty hash table
+ */
+union htable_entry **alloc_htable(void)
+{
+ union htable_entry **table;
+
+ table = calloc(HTABLE_BUCKETS, sizeof(*table));
+ if (!table) {
+ perror(NULL);
+ exit(1);
+ }
+ return table;
+}
+
+/**
+ * free_htable - Free a hash table and all its entries
+ * @table: the catalog table to free
+ * @free_entry: function that checks and frees an entry
+ */
+void free_htable(union htable_entry **table,
+ void (*free_entry)(union htable_entry *))
+{
+ union htable_entry *current;
+ union htable_entry *next;
+ int i;
+
+ for (i = 0; i < INODE_TABLE_BUCKETS; ++i) {
+ current = table[i];
+ while (current) {
+ next = current->header.h_next;
+ free_entry(current);
+ current = next;
+ }
+ }
+ free(table);
+}
+
+/**
+ * get_htable_entry - Find or create an entry in a hash table
+ * @id: id of the entry
+ * @size: size of the entry
+ * @table: the hash table
+ *
+ * Returns the entry, after creating it if necessary.
+ */
+union htable_entry *get_htable_entry(u64 id, int size,
+ union htable_entry **table)
+{
+ int index = id % INODE_TABLE_BUCKETS; /* Trivial hash function */
+ union htable_entry **entry_p = table + index;
+ union htable_entry *entry = *entry_p;
+ union htable_entry *new;
+
+ /* In each linked list, entries are ordered by id */
+ while (entry) {
+ if (id == entry->header.h_id)
+ return entry;
+ if (id < entry->header.h_id)
+ break;
+
+ entry_p = &entry->header.h_next;
+ entry = *entry_p;
+ }
+
+ new = calloc(1, size);
+ if (!new) {
+ perror(NULL);
+ exit(1);
+ }
+
+ new->header.h_id = id;
+ new->header.h_next = entry;
+ *entry_p = new;
+ return new;
+}
diff --git a/apfsck/htable.h b/apfsck/htable.h
new file mode 100644
index 0000000..b6100c6
--- /dev/null
+++ b/apfsck/htable.h
@@ -0,0 +1,40 @@
+/*
+ * apfsprogs/apfsck/htable.h
+ *
+ * Copyright (C) 2019 Ernesto A. Fernández <
ernesto.mn...@gmail.com>
+ */
+
+#ifndef _HTABLE_H
+#define _HTABLE_H
+
+#include "extents.h"
+#include "inode.h"
+#include "key.h"
+#include "types.h"
+
+#define HTABLE_BUCKETS 512 /* So the hash table array fits in 4k */
+
+/*
+ * Structure of the common header for hash table entries
+ */
+struct htable_entry_header {
+ union htable_entry *h_next; /* Next entry in linked list */
+ u64 h_id; /* Catalog object id of entry */
+};
+
+/*
+ * Generic hash table entry
+ */
+union htable_entry {
+ struct htable_entry_header header; /* Common header */
+ struct inode inode; /* Inode data */
+ struct dstream dstream; /* Dstream data */
+};
+
+extern union htable_entry **alloc_htable();
+extern void free_htable(union htable_entry **table,
+ void (*free_entry)(union htable_entry *));
+extern union htable_entry *get_htable_entry(u64 id, int size,
+ union htable_entry **table);
+
+#endif /* _HTABLE_H */
diff --git a/apfsck/inode.c b/apfsck/inode.c
index 6dedd6c..1f14651 100644
--- a/apfsck/inode.c
+++ b/apfsck/inode.c
@@ -10,6 +10,7 @@
#include <string.h>
#include "apfsck.h"
#include "extents.h"
+#include "htable.h"
#include "inode.h"
#include "key.h"
#include "super.h"
@@ -36,7 +37,7 @@ static void check_inode_stats(struct inode *inode)
report("Inode record", "wrong link count.");
}
- dstream = get_dstream(inode->i_private_id, vsb->v_dstream_table);
+ dstream = get_dstream(inode->i_private_id);
if (dstream->d_sparse_bytes != inode->i_sparse_bytes)
report("Inode record", "wrong count of sparse bytes.");
@@ -53,29 +54,11 @@ static void check_inode_stats(struct inode *inode)
}
/**
- * 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_names - Free all data on an inode's names
* @inode: inode to free
*
* Frees the primary name and all sibling links, but not before running a few
* remaining consistency checks.
- * remaining checking that
- * the number of listed siblings is correct, that all of them had both a record
- * and a dentry xfield, and that the primary link matches the primary name.
*/
static void free_inode_names(struct inode *inode)
{
@@ -124,68 +107,42 @@ static void free_inode_names(struct inode *inode)
}
/**
- * free_inode_table - Free the inode hash table and all its inodes
+ * free_inode - Free an inode structure after performing some final checks
+ * @entry: the entry to free
+ */
+static void free_inode(union htable_entry *entry)
+{
+ struct inode *inode = &entry->inode;
+
+ check_inode_stats(inode);
+ free_inode_names(inode);
+ free(entry);
+}
+
+/**
+ * free_inode_table - Free the inode hash table and all its entries
* @table: table to free
*
* Also performs some consistency checks that can only be done after the whole
* catalog has been parsed.
*/
-void free_inode_table(struct inode **table)
+void free_inode_table(union htable_entry **table)
{
- struct inode *current;
- struct inode *next;
- int i;
-
- for (i = 0; i < INODE_TABLE_BUCKETS; ++i) {
- current = table[i];
- while (current) {
- check_inode_stats(current);
- free_inode_names(current);
-
- next = current->i_next;
- free(current);
- current = next;
- }
- }
- free(table);
+ free_htable(table, free_inode);
}
/**
- * get_inode - Find or create an inode structure in a hash table
- * @ino: inode number
- * @table: the hash table
+ * get_inode - Find or create an inode structure in the inode hash table
+ * @ino: inode number
*
* Returns the inode structure, after creating it if necessary.
*/
-struct inode *get_inode(u64 ino, struct inode **table)
+struct inode *get_inode(u64 ino)
{
- 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;
- }
+ union htable_entry *entry;
- new = calloc(1, 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;
+ entry = get_htable_entry(ino, sizeof(struct inode), vsb->v_inode_table);
+ return &entry->inode;
}
/**
@@ -310,7 +267,7 @@ static int read_dstream_xfield(char *xval, int len, struct inode *inode)
size = le64_to_cpu(dstream_raw->size);
alloced_size = le64_to_cpu(dstream_raw->alloced_size);
- dstream = get_dstream(inode->i_private_id, vsb->v_dstream_table);
+ dstream = get_dstream(inode->i_private_id);
if (dstream->d_references) {
/* A dstream structure for this id has already been seen */
if (dstream->d_obj_type != APFS_TYPE_INODE)
@@ -607,7 +564,7 @@ void parse_inode_record(struct apfs_inode_key *key,
if (len < sizeof(*val))
report("Inode record", "value is too small.");
- inode = get_inode(cat_cnid(&key->hdr), vsb->v_inode_table);
+ inode = get_inode(cat_cnid(&key->hdr));
if (inode->i_seen)
report("Catalog", "inode numbers are repeated.");
inode->i_seen = true;
@@ -758,7 +715,7 @@ void parse_sibling_record(struct apfs_sibling_link_key *key,
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);
+ inode = get_inode(cat_cnid(&key->hdr));
if (!inode->i_seen) /* The b-tree keys are in order */
report("Sibling link record", "inode is missing");
@@ -789,7 +746,7 @@ void parse_sibling_map_record(struct apfs_sibling_map_key *key,
if (len != sizeof(*val))
report("Sibling map record", "wrong size of value.");
- inode = get_inode(le64_to_cpu(val->file_id), vsb->v_inode_table);
+ inode = get_inode(le64_to_cpu(val->file_id));
sibling = get_sibling(cat_cnid(&key->hdr), inode);
sibling->s_mapped = true;
}
diff --git a/apfsck/inode.h b/apfsck/inode.h
index fb4f33d..04f0356 100644
--- a/apfsck/inode.h
+++ b/apfsck/inode.h
@@ -12,6 +12,7 @@
struct apfs_inode_key;
struct apfs_sibling_link_key;
struct apfs_sibling_map_key;
+union htable_entry;
/* Inode numbers for special inodes */
#define APFS_INVALID_INO_NUM 0
@@ -187,7 +188,12 @@ struct apfs_sibling_map_val {
* Inode data in memory
*/
struct inode {
- u64 i_ino; /* Inode number */
+ /* Hash table entry header (struct htable_entry_header from htable.h) */
+ struct {
+ union htable_entry *i_next;
+ u64 i_ino; /* Inode number */
+ };
+
u64 i_private_id; /* Id of the inode's data stream */
bool i_seen; /* Has this inode been seen? */
@@ -208,8 +214,6 @@ struct inode {
u32 i_link_count; /* Number of dentries for file */
char *i_first_name; /* Name of first dentry encountered */
struct sibling *i_siblings; /* Linked list of siblings for inode */
-
- struct inode *i_next; /* Next inode in linked list */
};
/*
@@ -226,9 +230,8 @@ struct sibling {
u8 *s_name; /* In-memory copy of the 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 free_inode_table(union htable_entry **table);
+extern struct inode *get_inode(u64 ino);
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);
diff --git a/apfsck/super.c b/apfsck/super.c
index aba654f..2771c76 100644
--- a/apfsck/super.c
+++ b/apfsck/super.c
@@ -11,6 +11,7 @@
#include "apfsck.h"
#include "btree.h"
#include "extents.h"
+#include "htable.h"
#include "inode.h"
#include "object.h"
#include "types.h"
@@ -183,8 +184,8 @@ void parse_super(void)
perror(NULL);
exit(1);
}
- vsb->v_dstream_table = alloc_dstream_table();
- vsb->v_inode_table = alloc_inode_table();
+ vsb->v_dstream_table = alloc_htable();
+ vsb->v_inode_table = alloc_htable();
vsb_raw = map_volume_super(vol, vsb);
if (!vsb_raw) {
diff --git a/apfsck/super.h b/apfsck/super.h
index 26b332f..7c81698 100644
--- a/apfsck/super.h
+++ b/apfsck/super.h
@@ -10,6 +10,8 @@
#include "types.h"
#include "object.h"
+union htable_entry;
+
/*
* Structure used to store a range of physical blocks
*/
@@ -258,8 +260,8 @@ struct volume_superblock {
struct apfs_superblock *v_raw;
struct btree *v_omap;
struct btree *v_cat;
- struct inode **v_inode_table; /* Hash table of all inodes */
- struct dstream **v_dstream_table; /* Hash table of all dstreams */
+ union htable_entry **v_inode_table; /* Hash table of all inodes */
+ union htable_entry **v_dstream_table; /* Hash table of all dstreams */
/* Volume stats as measured by the fsck */
u64 v_file_count; /* Number of files */
diff --git a/apfsck/xattr.c b/apfsck/xattr.c
index 00d1dbd..d594cd6 100644
--- a/apfsck/xattr.c
+++ b/apfsck/xattr.c
@@ -43,7 +43,7 @@ static void parse_xattr_dstream(struct apfs_xattr_dstream *xstream)
size = le64_to_cpu(dstream_raw->size);
alloced_size = le64_to_cpu(dstream_raw->alloced_size);
- dstream = get_dstream(id, vsb->v_dstream_table);
+ dstream = get_dstream(id);
if (dstream->d_references) {
/* A dstream structure for this id has already been seen */
if (dstream->d_obj_type != APFS_TYPE_XATTR)
@@ -96,7 +96,7 @@ void parse_xattr_record(struct apfs_xattr_key *key,
report("Xattr record", "bad length for embedded data.");
}
- inode = get_inode(cat_cnid(&key->hdr), vsb->v_inode_table);
+ inode = get_inode(cat_cnid(&key->hdr));
if (!inode->i_seen)
report("Catalog", "xattr record with no inode.");
--
2.11.0