]> www.infradead.org Git - mtd-utils.git/commitdiff
fsck.ubifs: Add fsck support
authorZhihao Cheng <chengzhihao1@huawei.com>
Mon, 11 Nov 2024 09:01:02 +0000 (17:01 +0800)
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>
Mon, 11 Nov 2024 09:32:45 +0000 (10:32 +0100)
Add basic process code for fsck.ubifs. There are following modes for fsck:
 1. normal mode: Check the filesystem, ask user whether or not to fix
    the problem as long as inconsistent data is found during fs checking.
 2. safe mode: Check and safely repair the filesystem, if there are any
    data dropping operations needed by fixing, fsck will fail.
 3. danger mode: Answer 'yes' to all questions. There two sub modes:
    a) Check and repair the filesystem according to TNC, data dropping
       will be reported. If TNC/master/log is corrupted, fsck will fail.
    b) Check and forcedly repair the filesystem according to TNC, turns
       to rebuild filesystem if TNC/master/log is corrupted. Always make
       fsck succeed.
 4. check mode: Make no changes to the filesystem, only check the
    filesystem.
 5. rebuild mode: Scan entire UBI volume to find all nodes, and rebuild
    filesystem, always make fsck success.

Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
.gitignore
ubifs-utils/Makemodule.am
ubifs-utils/common/defs.h
ubifs-utils/fsck.ubifs/.gitignore [new file with mode: 0644]
ubifs-utils/fsck.ubifs/fsck.ubifs.c [new file with mode: 0644]
ubifs-utils/fsck.ubifs/fsck.ubifs.h [new file with mode: 0644]
ubifs-utils/libubifs/budget.c
ubifs-utils/libubifs/debug.c
ubifs-utils/libubifs/super.c
ubifs-utils/libubifs/ubifs.h

index 8c51ee5ff8f3fd23a2d8b41e7f532edf3a42b0e3..3eb66c143ae7195e72249fcdeb24ae1f76130684 100644 (file)
@@ -30,6 +30,7 @@ flash_otp_lock
 flash_otp_write
 flash_unlock
 flashcp
+fsck.ubifs
 ftl_check
 ftl_format
 jffs2dump
index 9840c6f6d6e488fc16221518e53f2466751b0614..9f4bd429e0da7dccc941eea65759ca9b77bf355d 100644 (file)
@@ -76,8 +76,21 @@ mkfs_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUI
 mkfs_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
        -I$(top_srcdir)/ubi-utils/include -I$(top_srcdir)/ubifs-utils/common -I $(top_srcdir)/ubifs-utils/libubifs
 
+fsck_ubifs_SOURCES = \
+       $(common_SOURCES) \
+       $(libubifs_SOURCES) \
+       ubifs-utils/fsck.ubifs/fsck.ubifs.h \
+       ubifs-utils/fsck.ubifs/fsck.ubifs.c
+
+fsck_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) \
+                  $(DUMP_STACK_LD) -lm -lpthread
+fsck_ubifs_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) $(ZSTD_CFLAGS) $(UUID_CFLAGS) $(LIBSELINUX_CFLAGS) \
+       -I$(top_srcdir)/ubi-utils/include -I$(top_srcdir)/ubifs-utils/common -I $(top_srcdir)/ubifs-utils/libubifs \
+       -I$(top_srcdir)/ubifs-utils/fsck.ubifs
+
 EXTRA_DIST += ubifs-utils/common/README ubifs-utils/libubifs/README
 
 dist_sbin_SCRIPTS = ubifs-utils/mount.ubifs
 
 sbin_PROGRAMS += mkfs.ubifs
+sbin_PROGRAMS += fsck.ubifs
index db32ea4acac3a7dec8d864f9494efc473ea2cdc4..7ff1771674d9537884a8cba4e59c8fbb75e7cfb0 100644 (file)
@@ -23,8 +23,9 @@ extern struct ubifs_info info_;
 #include "common.h"
 
 #define MKFS_PROGRAM_NAME "mkfs.ubifs"
+#define FSCK_PROGRAM_NAME "fsck.ubifs"
 
-enum { MKFS_PROGRAM_TYPE = 0 };
+enum { MKFS_PROGRAM_TYPE = 0, FSCK_PROGRAM_TYPE };
 
 enum {
        DUMP_PREFIX_NONE,
diff --git a/ubifs-utils/fsck.ubifs/.gitignore b/ubifs-utils/fsck.ubifs/.gitignore
new file mode 100644 (file)
index 0000000..09d664a
--- /dev/null
@@ -0,0 +1 @@
+/fsck.ubifs
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
new file mode 100644 (file)
index 0000000..721976b
--- /dev/null
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Huawei Technologies Co, Ltd.
+ *
+ * Authors: Zhihao Cheng <chengzhihao1@huawei.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
+
+#include "bitops.h"
+#include "kmem.h"
+#include "ubifs.h"
+#include "defs.h"
+#include "debug.h"
+#include "key.h"
+#include "misc.h"
+#include "fsck.ubifs.h"
+
+/*
+ * Because we copy functions from the kernel, we use a subset of the UBIFS
+ * file-system description object struct ubifs_info.
+ */
+struct ubifs_info info_;
+static struct ubifs_info *c = &info_;
+
+int exit_code = FSCK_OK;
+
+static const char *optstring = "Vrg:abyn";
+
+static const struct option longopts[] = {
+       {"version",            0, NULL, 'V'},
+       {"reserve",            1, NULL, 'r'},
+       {"debug",              1, NULL, 'g'},
+       {"auto",               1, NULL, 'a'},
+       {"rebuild",            1, NULL, 'b'},
+       {"yes",                1, NULL, 'y'},
+       {"nochange",           1, NULL, 'n'},
+       {NULL, 0, NULL, 0}
+};
+
+static const char *helptext =
+"Usage: fsck.ubifs [OPTIONS] ubi_volume\n"
+"Check & repair UBIFS filesystem on a given UBI volume\n\n"
+"Options:\n"
+"-V, --version            Display version information\n"
+"-g, --debug=LEVEL        Display debug information (0 - none, 1 - error message,\n"
+"                         2 - warning message[default], 3 - notice message, 4 - debug message)\n"
+"-a, --auto               Automatic safely repair without droping data (No questions).\n"
+"                         Can not be specified at the same time as the -y or -n options\n"
+"-y, --yes                Assume \"yes\" to all questions. Automatic repair and report dropping data (No questions).\n"
+"                         There are two submodes for this working mode:\n"
+"                           a. default - Fail if TNC/master/log is corrupted. Only -y option is specified\n"
+"                           b. rebuild fs - Turn to rebuild fs if TNC/master/log is corrupted. Specify -b option to make effect\n"
+"                         Can not be specified at the same time as the -a or -n options\n"
+"-b, --rebuild            Forcedly repair the filesystem even by rebuilding filesystem.\n"
+"                         Depends on -y option\n"
+"-n, --nochange           Make no changes to the filesystem, only check filesystem.\n"
+"                         Can not be specified at the same time as the -a or -y options\n"
+"Examples:\n"
+"\t1. Check and repair filesystem from UBI volume /dev/ubi0_0\n"
+"\t   fsck.ubifs /dev/ubi0_0\n"
+"\t2. Only check without modifying filesystem from UBI volume /dev/ubi0_0\n"
+"\t   fsck.ubifs -n /dev/ubi0_0\n"
+"\t3. Check and safely repair filesystem from UBI volume /dev/ubi0_0\n"
+"\t   fsck.ubifs -a /dev/ubi0_0\n"
+"\t4. Check and forcedly repair filesystem from UBI volume /dev/ubi0_0\n"
+"\t   fsck.ubifs -y -b /dev/ubi0_0\n\n";
+
+static inline void usage(void)
+{
+       printf("%s", helptext);
+       exit_code |= FSCK_USAGE;
+       exit(exit_code);
+}
+
+static void get_options(int argc, char *argv[], int *mode)
+{
+       int opt, i, submode = 0;
+       char *endp;
+
+       while (1) {
+               opt = getopt_long(argc, argv, optstring, longopts, &i);
+               if (opt == -1)
+                       break;
+               switch (opt) {
+               case 'V':
+                       common_print_version();
+                       exit(FSCK_OK);
+               case 'g':
+                       c->debug_level = strtol(optarg, &endp, 0);
+                       if (*endp != '\0' || endp == optarg ||
+                           c->debug_level < 0 || c->debug_level > DEBUG_LEVEL) {
+                               log_err(c, 0, "bad debugging level '%s'", optarg);
+                               usage();
+                       }
+                       break;
+               case 'a':
+                       if (*mode != NORMAL_MODE) {
+conflict_opt:
+                               log_err(c, 0, "Only one of the options -a, -n or -y may be specified");
+                               usage();
+                       }
+                       *mode = SAFE_MODE;
+                       break;
+               case 'y':
+                       if (*mode != NORMAL_MODE)
+                               goto conflict_opt;
+                       *mode = DANGER_MODE0;
+                       break;
+               case 'b':
+                       submode = 1;
+                       break;
+               case 'n':
+                       if (*mode != NORMAL_MODE)
+                               goto conflict_opt;
+                       *mode = CHECK_MODE;
+                       c->ro_mount = 1;
+                       break;
+               case 'r':
+                       /* Compatible with FSCK(8). */
+                       break;
+               default:
+                       usage();
+               }
+       }
+
+       if (submode) {
+               if (*mode != DANGER_MODE0) {
+                       log_err(c, 0, "Option -y is not specified when -b is used");
+                       usage();
+               } else
+                       *mode = DANGER_MODE1;
+       }
+
+       if (optind != argc) {
+               c->dev_name = strdup(argv[optind]);
+               if (!c->dev_name) {
+                       log_err(c, errno, "can not allocate dev_name");
+                       exit_code |= FSCK_ERROR;
+                       exit(exit_code);
+               }
+       }
+
+       if (!c->dev_name) {
+               log_err(c, 0, "no ubi_volume specified");
+               usage();
+       }
+}
+
+static void exit_callback(void)
+{
+       if (exit_code & FSCK_NONDESTRUCT)
+               log_out(c, "********** Filesystem was modified **********");
+       if (exit_code & FSCK_UNCORRECTED)
+               log_out(c, "********** WARNING: Filesystem still has errors **********");
+       if (exit_code & ~(FSCK_OK|FSCK_NONDESTRUCT))
+               log_out(c, "FSCK failed, exit code %d", exit_code);
+       else
+               log_out(c, "FSCK success!");
+}
+
+static void fsck_assert_failed(__unused const struct ubifs_info *c)
+{
+       exit_code |= FSCK_ERROR;
+       exit(exit_code);
+}
+
+static void signal_cancel(int sig)
+{
+       ubifs_warn(c, "killed by signo %d", sig);
+       exit_code |= FSCK_CANCELED;
+       exit(exit_code);
+}
+
+static int init_fsck_info(struct ubifs_info *c, int argc, char *argv[])
+{
+       int err = 0, mode = NORMAL_MODE;
+       struct sigaction sa;
+       struct ubifs_fsck_info *fsck = NULL;
+
+       if (atexit(exit_callback)) {
+               log_err(c, errno, "can not set exit callback");
+               return -errno;
+       }
+
+       init_ubifs_info(c, FSCK_PROGRAM_TYPE);
+       get_options(argc, argv, &mode);
+
+       fsck = calloc(1, sizeof(struct ubifs_fsck_info));
+       if (!fsck) {
+               err = -errno;
+               log_err(c, errno, "can not allocate fsck info");
+               goto out_err;
+       }
+
+       c->private = fsck;
+       FSCK(c)->mode = mode;
+       c->assert_failed_cb = fsck_assert_failed;
+
+       memset(&sa, 0, sizeof(struct sigaction));
+       sa.sa_handler = signal_cancel;
+       if (sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, NULL)) {
+               err = -errno;
+               log_err(c, errno, "can not set signal handler");
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       free(fsck);
+       free(c->dev_name);
+       c->dev_name = NULL;
+       return err;
+}
+
+static void destroy_fsck_info(struct ubifs_info *c)
+{
+       free(c->private);
+       c->private = NULL;
+       free(c->dev_name);
+       c->dev_name = NULL;
+}
+
+/*
+ * do_fsck - Check & repair the filesystem.
+ */
+static int do_fsck(void)
+{
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       int err;
+
+       err = init_fsck_info(c, argc, argv);
+       if (err) {
+               exit_code |= FSCK_ERROR;
+               goto out_exit;
+       }
+
+       err = ubifs_open_volume(c, c->dev_name);
+       if (err) {
+               exit_code |= FSCK_ERROR;
+               goto out_destroy_fsck;
+       }
+
+       err = do_fsck();
+
+       ubifs_close_volume(c);
+out_destroy_fsck:
+       destroy_fsck_info(c);
+out_exit:
+       return exit_code;
+}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
new file mode 100644 (file)
index 0000000..531a3ab
--- /dev/null
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Huawei Technologies Co, Ltd.
+ *
+ * Authors: Zhihao Cheng <chengzhihao1@huawei.com>
+ */
+
+#ifndef __FSCK_UBIFS_H__
+#define __FSCK_UBIFS_H__
+
+/* Exit codes used by fsck-type programs */
+#define FSCK_OK                        0       /* No errors */
+#define FSCK_NONDESTRUCT       1       /* File system errors corrected */
+#define FSCK_REBOOT            2       /* System should be rebooted */
+#define FSCK_UNCORRECTED       4       /* File system errors left uncorrected */
+#define FSCK_ERROR             8       /* Operational error */
+#define FSCK_USAGE             16      /* Usage or syntax error */
+#define FSCK_CANCELED          32      /* Aborted with a signal or ^C */
+#define FSCK_LIBRARY           128     /* Shared library error */
+
+/*
+ * There are 6 working modes for fsck:
+ * NORMAL_MODE:        Check the filesystem, ask user whether or not to fix the
+ *             problem as long as inconsistent data is found during checking.
+ * SAFE_MODE:  Check and safely repair the filesystem, if there are any
+ *             data dropping operations needed by fixing, fsck will fail.
+ * DANGER_MODE0:Check and repair the filesystem according to TNC, data dropping
+ *              will be reported. If TNC/master/log is corrupted, fsck will fail.
+ * DANGER_MODE1:Check and forcedly repair the filesystem according to TNC,
+ *             turns to @REBUILD_MODE mode automatically if TNC/master/log is
+ *             corrupted.
+ * REBUILD_MODE:Scan entire UBI volume to find all nodes, and rebuild the
+ *             filesystem, always make fsck success.
+ * CHECK_MODE: Make no changes to the filesystem, only check the filesystem.
+ */
+enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
+       DANGER_MODE1, REBUILD_MODE, CHECK_MODE };
+
+/**
+ * struct ubifs_fsck_info - UBIFS fsck information.
+ * @mode: working mode
+ */
+struct ubifs_fsck_info {
+       int mode;
+};
+
+#define FSCK(c) ((struct ubifs_fsck_info*)c->private)
+
+static inline const char *mode_name(const struct ubifs_info *c)
+{
+       if (!c->private)
+               return "";
+
+       switch (FSCK(c)->mode) {
+       case NORMAL_MODE:
+               return ",normal mode";
+       case SAFE_MODE:
+               return ",safe mode";
+       case DANGER_MODE0:
+               return ",danger mode";
+       case DANGER_MODE1:
+               return ",danger + rebuild mode";
+       case REBUILD_MODE:
+               return ",rebuild mode";
+       case CHECK_MODE:
+               return ",check mode";
+       default:
+               return "";
+       }
+}
+
+#define log_out(c, fmt, ...)                                           \
+       printf("%s[%d] (%s%s): " fmt "\n", c->program_name ? : "noprog",\
+              getpid(), c->dev_name ? : "-", mode_name(c),             \
+              ##__VA_ARGS__)
+
+#define log_err(c, err, fmt, ...) do {                                 \
+       printf("%s[%d][ERROR] (%s%s): %s: " fmt,                        \
+              c->program_name ? : "noprog", getpid(),                  \
+              c->dev_name ? : "-", mode_name(c),                       \
+              __FUNCTION__, ##__VA_ARGS__);                            \
+       if (err)                                                        \
+               printf(" - %s", strerror(err));                         \
+       printf("\n");                                                   \
+} while (0)
+
+/* Exit code for fsck program. */
+extern int exit_code;
+
+#endif
index 54392a9331e5edef7d35e188a1ef5999360142c0..5550c9af7f0db74d39334f0080f21a02ab58bed7 100644 (file)
@@ -201,7 +201,7 @@ long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs)
 static int can_use_rp(__unused struct ubifs_info *c)
 {
        /* Fsck can always use reserved pool. */
-       return c->program_type != MKFS_PROGRAM_TYPE;
+       return c->program_type == FSCK_PROGRAM_TYPE;
 }
 
 /**
index a2109906f38dcf84cca5442f51f10c12fca22a7f..94928da3f1f549c3711823c28a3f3a70927a424f 100644 (file)
@@ -1021,7 +1021,11 @@ void ubifs_assert_failed(struct ubifs_info *c, const char *expr,
 
        /*
         * Different from linux kernel.
-        * There is only one action(readonly) when assertion is failed.
+        * Invoke callback function if there is one, otherwise make filesystem
+        * readonly when assertion is failed.
         */
-       ubifs_ro_mode(c, -EINVAL);
+       if (c->assert_failed_cb)
+               c->assert_failed_cb(c);
+       else
+               ubifs_ro_mode(c, -EINVAL);
 }
index 9fa366f3f5b4d2d3d6d7e61d4c15a510e1f26c5c..155489d94dc55471731fe78a7cd43d65e4e15dc6 100644 (file)
@@ -137,6 +137,53 @@ int close_target(struct ubifs_info *c)
        return 0;
 }
 
+/**
+ * ubifs_open_volume - open UBI volume.
+ * @c: the UBIFS file-system description object
+ * @volume_name: the UBI volume name
+ *
+ * Open ubi volume. This function is implemented by open_ubi + open_target.
+ *
+ * Returns %0 in case of success and a negative error code in case of failure.
+ */
+int ubifs_open_volume(struct ubifs_info *c, const char *volume_name)
+{
+       int err;
+
+       err = open_ubi(c, volume_name);
+       if (err) {
+               ubifs_err(c, "cannot open libubi. %s", strerror(errno));
+               return err;
+       }
+
+       err = open_target(c);
+       if (err)
+               close_ubi(c);
+
+       return err;
+}
+
+/**
+ * ubifs_close_volume - close UBI volume.
+ * @c: the UBIFS file-system description object
+ *
+ * Close ubi volume. This function is implemented by close_target + close_ubi.
+ *
+ * Returns %0 in case of success and a negative error code in case of failure.
+ */
+int ubifs_close_volume(struct ubifs_info *c)
+{
+       int err;
+
+       err = close_target(c);
+       if (err)
+               return err;
+
+       close_ubi(c);
+
+       return 0;
+}
+
 /**
  * check_volume_empty - check if the UBI volume is empty.
  * @c: the UBIFS file-system description object
@@ -197,6 +244,11 @@ void init_ubifs_info(struct ubifs_info *c, int program_type)
        case MKFS_PROGRAM_TYPE:
                c->program_name = MKFS_PROGRAM_NAME;
                break;
+       case FSCK_PROGRAM_TYPE:
+               c->program_name = FSCK_PROGRAM_NAME;
+               /* Always check crc for data node. */
+               c->no_chk_data_crc = 0;
+               break;
        default:
                assert(0);
                break;
index 2af9d87b21d948125cb6e241509a9b019afb3bc8..babaae8f4bacdb301531a0b3275f9e57ceb3c385 100644 (file)
@@ -1026,6 +1026,9 @@ struct ubifs_budg_info {
  *
  * @new_ihead_lnum: used by debugging to check @c->ihead_lnum
  * @new_ihead_offs: used by debugging to check @c->ihead_offs
+ *
+ * @private: private information related to specific situation, eg. fsck.
+ * @assert_failed_cb: callback function to handle assertion failure
  */
 struct ubifs_info {
        struct ubifs_sb_node *sup_node;
@@ -1248,6 +1251,9 @@ struct ubifs_info {
 
        int new_ihead_lnum;
        int new_ihead_offs;
+
+       void *private;
+       void (*assert_failed_cb)(const struct ubifs_info *c);
 };
 
 extern atomic_long_t ubifs_clean_zn_cnt;
@@ -1684,6 +1690,8 @@ int open_ubi(struct ubifs_info *c, const char *node);
 void close_ubi(struct ubifs_info *c);
 int open_target(struct ubifs_info *c);
 int close_target(struct ubifs_info *c);
+int ubifs_open_volume(struct ubifs_info *c, const char *volume_name);
+int ubifs_close_volume(struct ubifs_info *c);
 int check_volume_empty(struct ubifs_info *c);
 void init_ubifs_info(struct ubifs_info *c, int program_type);
 int init_constants_early(struct ubifs_info *c);