Often enough, there is no clear way to decide if something should be
reported as corruption.
Some filesystems created by Apple software have been found to violate
the specification in small ways (e.g., the volume file count). This is
most likely not corruption, but I have no idea what can be done to
improve the check and I don't want to drop it.
Filesystems that have recently crashed will have some features that
should never be seen under normal use. It may be useful to report when
they appear, to check if the module is unmounting cleanly.
Not all features of APFS will be supported in the near future, by either
the module or the fsck tool. If any of them appear to show up after the
filesystem was used on linux, that's clearly something that should be
reported, even if it's technically not corruption.
To deal with this consistently, distinguish these three types of checks
and add command line options to let the user decide which ones to run.
Signed-off-by: Ernesto A. Fernández <
ernesto.mn...@gmail.com>
---
apfsck/apfsck.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
apfsck/apfsck.h | 9 ++++++++
apfsck/inode.c | 2 +-
apfsck/super.c | 4 ++--
4 files changed, 76 insertions(+), 6 deletions(-)
diff --git a/apfsck/apfsck.c b/apfsck/apfsck.c
index 7f1ecf2..bfc767b 100644
--- a/apfsck/apfsck.c
+++ b/apfsck/apfsck.c
@@ -10,10 +10,12 @@
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
+#include <unistd.h>
#include "apfsck.h"
#include "super.h"
int fd;
+unsigned int options;
/**
* usage - Print usage information and exit
@@ -21,7 +23,7 @@ int fd;
*/
static void usage(char *path)
{
- fprintf(stderr, "usage: %s device\n", path);
+ fprintf(stderr, "usage: %s [-cuw] device\n", path);
exit(1);
}
@@ -50,6 +52,44 @@ __attribute__((noreturn, format(printf, 2, 3))) void report(const char *context,
}
/**
+ * report_crash - Report that a crash was discovered and exit
+ * @context: structure with signs of a crash
+ *
+ * Does nothing unless the -c cli option was used.
+ */
+void report_crash(const char *context)
+{
+ if (options & OPT_REPORT_CRASH)
+ report(context, "the filesystem was not unmounted cleanly.");
+}
+
+/**
+ * report_unknown - Report the presence of unknown features and exit
+ * @feature: the unsupported feature
+ *
+ * Does nothing unless the -u cli option was used.
+ */
+void report_unknown(const char *feature)
+{
+ if (options & OPT_REPORT_UNKNOWN)
+ report(feature, "not supported.");
+}
+
+/**
+ * report_weird - Report unexplained inconsistencies and exit
+ * @context: structure where the inconsistency was found
+ *
+ * Does nothing unless the -w cli option was used. This function should
+ * be called when the specification, and common sense, appear to be in
+ * contradiction with the behaviour of actual filesystems.
+ */
+void report_weird(const char *context)
+{
+ if (options & OPT_REPORT_WEIRD)
+ report(context, "odd inconsistency (may not be corruption).");
+}
+
+/**
* parse_filesystem - Parse the filesystem looking for corruption
*/
static void parse_filesystem(void)
@@ -61,9 +101,30 @@ int main(int argc, char *argv[])
{
char *filename;
- if (argc != 2)
+ while (1) {
+ int opt = getopt(argc, argv, "cuw");
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'c':
+ options |= OPT_REPORT_CRASH;
+ break;
+ case 'u':
+ options |= OPT_REPORT_UNKNOWN;
+ break;
+ case 'w':
+ options |= OPT_REPORT_WEIRD;
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+
+ if (optind >= argc)
usage(argv[0]);
- filename = argv[1];
+ filename = argv[optind];
fd = open(filename, O_RDONLY);
if (fd == -1) {
diff --git a/apfsck/apfsck.h b/apfsck/apfsck.h
index 8baf66a..b1b37f8 100644
--- a/apfsck/apfsck.h
+++ b/apfsck/apfsck.h
@@ -8,11 +8,20 @@
#define _APFSCK_H
/* Declarations for global variables */
+extern unsigned int options; /* Command line options */
extern struct super_block *sb; /* Filesystem superblock */
extern struct volume_superblock *vsb; /* Volume superblock */
extern int fd; /* File descriptor for the device */
+/* Option flags */
+#define OPT_REPORT_CRASH 1 /* Report on-disk signs of a past crash */
+#define OPT_REPORT_UNKNOWN 2 /* Report unknown or unsupported features */
+#define OPT_REPORT_WEIRD 4 /* Report issues that may not be corruption */
+
extern __attribute__((noreturn, format(printf, 2, 3)))
void report(const char *context, const char *message, ...);
+extern void report_crash(const char *context);
+extern void report_unknown(const char *feature);
+extern void report_weird(const char *context);
#endif /* _APFSCK_H */
diff --git a/apfsck/inode.c b/apfsck/inode.c
index cce1e87..cbbb393 100644
--- a/apfsck/inode.c
+++ b/apfsck/inode.c
@@ -365,7 +365,7 @@ static void parse_inode_xfields(struct apfs_xf_blob *xblob, int len,
xlen = 16;
break;
case APFS_INO_EXT_TYPE_PREV_FSIZE:
- printf("Inode xfield: filesystem has crashed.\n");
+ report_crash("Inode xfield");
if (xflags != 0)
report("Previous size xfield", "wrong flags.");
case APFS_INO_EXT_TYPE_SNAP_XID:
diff --git a/apfsck/super.c b/apfsck/super.c
index e7063ec..6bfdb5a 100644
--- a/apfsck/super.c
+++ b/apfsck/super.c
@@ -123,7 +123,7 @@ static void map_main_super(void)
report("Checkpoint descriptors", "latest is missing.");
/* TODO: the latest checkpoint and block zero are somehow different? */
if (sb->s_xid != le64_to_cpu(msb_raw->nx_o.o_xid))
- report("Block zero", "filesystem was not unmounted cleanly.");
+ report_crash("Block zero");
munmap(msb_raw, sb->s_blocksize);
}
@@ -208,7 +208,7 @@ void parse_super(void)
if (le64_to_cpu(vsb_raw->apfs_num_files) !=
vsb->v_file_count)
/* Sometimes this is off by one. TODO: why? */
- printf("Bad file count, may not be corruption.\n");
+ report_weird("File count in volume superblock");
if (le64_to_cpu(vsb_raw->apfs_num_directories) !=
vsb->v_dir_count)
report("Volume superblock", "bad directory count.");
--
2.11.0