[PATCH 1/2] apfsck: check consistency of inode flags

2 views
Skip to first unread message

Ernesto A. Fernández

unread,
Mar 10, 2019, 2:26:58 PM3/10/19
to linux...@googlegroups.com
Check that the internal flags of an inode are all valid, and consistent
with each other. Also report if any of them are unsupported, or if they
were set as the result of a crash.

Ignore the finder info flag for now, because it seems to be set in some
inodes that have no corresponding xfield. The next patch will take care
of reporting this as weird.

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

diff --git a/apfsck/inode.c b/apfsck/inode.c
index 54a5274..b3925dd 100644
--- a/apfsck/inode.c
+++ b/apfsck/inode.c
@@ -446,6 +446,36 @@ static void parse_inode_xfields(struct apfs_xf_blob *xblob, int len,
}

/**
+ * check_inode_internal_flags - Check basic consistency of inode flags
+ * @flags: the flags
+ */
+static void check_inode_internal_flags(u64 flags)
+{
+ if ((flags & APFS_VALID_INTERNAL_INODE_FLAGS) != flags)
+ report("Inode record", "invalid flags in use.");
+
+ if ((flags & APFS_INODE_DIR_STATS_ORIGIN) &&
+ !(flags & APFS_INODE_MAINTAIN_DIR_STATS))
+ report("Inode record", "incompatible directory stats flags.");
+ if (flags & APFS_INODE_HAS_RSRC_FORK && flags & APFS_INODE_NO_RSRC_FORK)
+ report("Inode record", "incompatible resource fork flags.");
+
+ if (flags & APFS_INODE_BEING_TRUNCATED)
+ report_crash("Inode internal flags");
+
+ if (flags & APFS_INODE_PINNED_TO_MAIN ||
+ flags & APFS_INODE_PINNED_TO_TIER2 ||
+ flags & APFS_INODE_ALLOCATION_SPILLEDOVER)
+ report_unknown("Fusion drive");
+ if (flags & APFS_INODE_HAS_RSRC_FORK)
+ report_unknown("Resource fork");
+ if (flags & APFS_INODE_MAINTAIN_DIR_STATS)
+ report_unknown("Directory statistics");
+ if (flags & APFS_INODE_IS_APFS_PRIVATE)
+ report_unknown("Private implementation inode");
+}
+
+/**
* check_inode_ids - Check that an inode id is consistent with its parent id
* @ino: inode number
* @parent_ino: parent inode number
@@ -512,6 +542,9 @@ void parse_inode_record(struct apfs_inode_key *key,

check_inode_ids(inode->i_ino, le64_to_cpu(val->parent_id));

+ inode->i_flags = le64_to_cpu(val->internal_flags);
+ check_inode_internal_flags(inode->i_flags);
+
mode = le16_to_cpu(val->mode);
filetype = mode & S_IFMT;

diff --git a/apfsck/inode.h b/apfsck/inode.h
index e6c4dff..ba70b8b 100644
--- a/apfsck/inode.h
+++ b/apfsck/inode.h
@@ -23,6 +23,34 @@ struct apfs_sibling_link_key;
/* Smallest inode number available for user content */
#define APFS_MIN_USER_INO_NUM 16

+/* Inode internal flags */
+#define APFS_INODE_IS_APFS_PRIVATE 0x00000001
+#define APFS_INODE_MAINTAIN_DIR_STATS 0x00000002
+#define APFS_INODE_DIR_STATS_ORIGIN 0x00000004
+#define APFS_INODE_PROT_CLASS_EXPLICIT 0x00000008
+#define APFS_INODE_WAS_CLONED 0x00000010
+#define APFS_INODE_FLAG_UNUSED 0x00000020
+#define APFS_INODE_HAS_SECURITY_EA 0x00000040
+#define APFS_INODE_BEING_TRUNCATED 0x00000080
+#define APFS_INODE_HAS_FINDER_INFO 0x00000100
+#define APFS_INODE_IS_SPARSE 0x00000200
+#define APFS_INODE_WAS_EVER_CLONED 0x00000400
+#define APFS_INODE_ACTIVE_FILE_TRIMMED 0x00000800
+#define APFS_INODE_PINNED_TO_MAIN 0x00001000
+#define APFS_INODE_PINNED_TO_TIER2 0x00002000
+#define APFS_INODE_HAS_RSRC_FORK 0x00004000
+#define APFS_INODE_NO_RSRC_FORK 0x00008000
+#define APFS_INODE_ALLOCATION_SPILLEDOVER 0x00010000
+
+/* Masks for internal flags */
+#define APFS_VALID_INTERNAL_INODE_FLAGS 0x0001ffdf
+#define APFS_INODE_INHERITED_INTERNAL_FLAGS (APFS_INODE_MAINTAIN_DIR_STATS)
+#define APFS_INDOE_CLONED_INTERNAL_FLAGS (APFS_INODE_HAS_RSRC_FORK \
+ | APFS_INODE_NO_RSRC_FORK \
+ | APFS_INODE_HAS_FINDER_INFO)
+#define APFS_INODE_PINNED_MASK (APFS_INODE_PINNED_TO_MAIN \
+ | APFS_INODE_PINNED_TO_TIER2)
+
/* File mode flags */
#define S_IFMT 00170000
#define S_IFSOCK 0140000
@@ -166,6 +194,7 @@ struct inode {
u64 i_size; /* Inode size */
u64 i_alloced_size; /* Inode size, including unused */
u64 i_sparse_bytes; /* Number of sparse bytes */
+ u64 i_flags; /* Internal flags */
u32 i_rdev; /* Device ID */
char *i_name; /* Name of primary link */

--
2.11.0

Ernesto A. Fernández

unread,
Mar 10, 2019, 2:27:09 PM3/10/19
to linux...@googlegroups.com
Some of the internal flags of an inode announce the presence of a given
type of xfield. No xfield exists without its corresponding flag. The
converse doesn't seem to hold for the finder info, so only report its
failures as weird.

Several checks are now performed on the xfield type bitmap, so add two
new helper functions, xbmap_test() and xbmap_set().

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

diff --git a/apfsck/inode.c b/apfsck/inode.c
index b3925dd..27e0956 100644
--- a/apfsck/inode.c
+++ b/apfsck/inode.c
@@ -316,6 +316,49 @@ void check_xfield_flags(u8 flags)
}

/**
+ * xbmap_set - Set an xfield type in the xfield bitmap
+ * @bmap: the xfield bitmap
+ * @type: the extended field type
+ */
+static inline void xbmap_set(u16 *bmap, u8 type)
+{
+ *bmap |= 1 << type;
+}
+
+/**
+ * xbmap_test - Test if an xfield type is present in the xfield bitmap
+ * @bmap: the xfield bitmap
+ * @type: the extended field type
+ */
+static inline bool xbmap_test(u16 bmap, u8 type)
+{
+ return bmap & (1 << type);
+}
+
+/**
+ * check_xfield_inode_flags - Check that xfields are consistent with inode flags
+ * @bmap: bitmap of xfield types seen in the inode
+ * @flags: inode flags
+ */
+static void check_xfield_inode_flags(u16 bmap, u64 flags)
+{
+ if (xbmap_test(bmap, APFS_INO_EXT_TYPE_DIR_STATS_KEY) !=
+ (bool)(flags & APFS_INODE_MAINTAIN_DIR_STATS))
+ report("Inode record", "wrong setting for dir stats flag.");
+ if (xbmap_test(bmap, APFS_INO_EXT_TYPE_SPARSE_BYTES) !=
+ (bool)(flags & APFS_INODE_IS_SPARSE))
+ report("Inode record", "wrong setting for sparse flag.");
+
+ /* Some inodes don't have finder info but still have the flag... */
+ if (xbmap_test(bmap, APFS_INO_EXT_TYPE_FINDER_INFO) &&
+ !(flags & APFS_INODE_HAS_FINDER_INFO))
+ report("Inode record", "wrong setting for finder info flag.");
+ if (!xbmap_test(bmap, APFS_INO_EXT_TYPE_FINDER_INFO) &&
+ (flags & APFS_INODE_HAS_FINDER_INFO))
+ report_weird("Finder info flag in inode record");
+}
+
+/**
* parse_inode_xfields - Parse and check an inode extended fields
* @xblob: pointer to the beginning of the xfields in the inode value
* @len: length of the xfields
@@ -332,8 +375,10 @@ static void parse_inode_xfields(struct apfs_xf_blob *xblob, int len,
int xcount;
int i;

- if (len == 0) /* No extended fields */
+ if (len == 0) { /* No extended fields */
+ check_xfield_inode_flags(type_bitmap, inode->i_flags);
return;
+ }

len -= sizeof(*xblob);
if (len < 0)
@@ -355,7 +400,6 @@ static void parse_inode_xfields(struct apfs_xf_blob *xblob, int len,

for (i = 0; i < le16_to_cpu(xblob->xf_num_exts); ++i) {
int xlen, xpad_len;
- u16 type_flag;
u8 xflags = xfield[i].x_flags;

check_xfield_flags(xflags);
@@ -420,10 +464,9 @@ static void parse_inode_xfields(struct apfs_xf_blob *xblob, int len,
report("Inode xfield", "invalid type.");
}

- type_flag = 1 << xfield[i].x_type;
- if (type_bitmap & type_flag)
+ if (xbmap_test(type_bitmap, xfield[i].x_type))
report("Inode record", "two xfields of the same type.");
- type_bitmap |= type_flag;
+ xbmap_set(&type_bitmap, xfield[i].x_type);

if (xlen != le16_to_cpu(xfield[i].x_size))
report("Inode xfield", "wrong size");
@@ -443,6 +486,8 @@ static void parse_inode_xfields(struct apfs_xf_blob *xblob, int len,

if (len)
report("Inode record", "length of xfields does not add up.");
+
+ check_xfield_inode_flags(type_bitmap, inode->i_flags);
}

/**
--
2.11.0

Reply all
Reply to author
Forward
0 new messages