aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--toys/pending/fsck.c432
1 files changed, 432 insertions, 0 deletions
diff --git a/toys/pending/fsck.c b/toys/pending/fsck.c
new file mode 100644
index 00000000..1c607ae2
--- /dev/null
+++ b/toys/pending/fsck.c
@@ -0,0 +1,432 @@
+/* fsck.c - check and repair a Linux filesystem
+ *
+ * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ * Copyright 2013 Kyungwan Han <asura321@gmail.com>
+
+USE_FSCK(NEWTOY(fsck, "?t:ANPRTVsC#", TOYFLAG_USR|TOYFLAG_BIN))
+
+config FSCK
+ bool "fsck"
+ default n
+ help
+ Usage: fsck [-ANPRTV] [-C FD] [-t FSTYPE] [FS_OPTS] [BLOCKDEV]...
+
+ Check and repair filesystems
+
+ -A Walk /etc/fstab and check all filesystems
+ -N Don't execute, just show what would be done
+ -P With -A, check filesystems in parallel
+ -R With -A, skip the root filesystem
+ -T Don't show title on startup
+ -V Verbose
+ -C n Write status information to specified filedescriptor
+ -t TYPE List of filesystem types to check
+
+*/
+
+#define FOR_fsck
+#include "toys.h"
+#include <mntent.h>
+
+#define FLAG_WITHOUT_NO_PRFX 1
+#define FLAG_WITH_NO_PRFX 2
+#define FLAG_DONE 1
+
+GLOBALS(
+ int fd_num;
+ char *t_list;
+
+ struct double_list *devices;
+ int *arr_flag;
+ char **arr_type;
+ int negate;
+ int sum_status;
+ int nr_run;
+ int sig_num;
+ long max_nr_run;
+)
+
+struct f_sys_info {
+ char *device, *mountpt, *type, *opts;
+ int passno, flag;
+ struct f_sys_info *next;
+};
+
+struct child_list {
+ struct child_list *next;
+ pid_t pid;
+ char *prog_name, *dev_name;
+};
+
+static struct f_sys_info *filesys_info = NULL; //fstab entry list
+static struct child_list *c_list = NULL; //fsck.type child list.
+
+static void kill_all(void)
+{
+ struct child_list *child;
+
+ for (child = c_list; child; child = child->next)
+ kill(child->pid, SIGTERM);
+ _exit(0);
+}
+
+static long strtol_range(char *str, int min, int max)
+{
+ char *endptr = NULL;
+ errno = 0;
+ long ret_value = strtol(str, &endptr, 10);
+
+ if(errno) perror_exit("Invalid num %s", str);
+ else if(endptr && (*endptr != '\0' || endptr == str))
+ perror_exit("Not a valid num %s", str);
+ if(ret_value >= min && ret_value <= max) return ret_value;
+ else perror_exit("Number %s is not in valid [%d-%d] Range", str, min, max);
+}
+
+//create fstab entries list.
+static struct f_sys_info* create_db(struct mntent *f_info)
+{
+ struct f_sys_info *temp = filesys_info;
+ if (temp) {
+ while (temp->next) temp = temp->next;
+ temp->next = xzalloc(sizeof(struct f_sys_info));
+ temp = temp->next;
+ } else filesys_info = temp = xzalloc(sizeof(struct f_sys_info));
+
+ temp->device = xstrdup(f_info->mnt_fsname);
+ temp->mountpt = xstrdup(f_info->mnt_dir);
+ if (strchr(f_info->mnt_type, ',')) temp->type = xstrdup("auto");
+ else temp->type = xstrdup(f_info->mnt_type);
+ temp->opts = xstrdup(f_info->mnt_opts);
+ temp->passno = f_info->mnt_passno;
+ return temp;
+}
+
+//is we have 'no' or ! before type.
+static int is_no_prefix(char **p)
+{
+ int no = 0;
+
+ if ((*p[0] == 'n' && *(*p + 1) == 'o')) no = 2;
+ else if (*p[0] == '!') no = 1;
+ *p += no;
+ return ((no) ? 1 :0);
+}
+
+static void fix_tlist(void)
+{
+ char *p, *s = TT.t_list;
+ int n = 1, no;
+
+ while ((s = strchr(s, ','))) {
+ s++;
+ n++;
+ }
+
+ TT.arr_flag = xzalloc((n + 1) * sizeof(char));
+ TT.arr_type = xzalloc((n + 1) * sizeof(char *));
+ s = TT.t_list;
+ n = 0;
+ while ((p = strsep(&s, ","))) {
+ no = is_no_prefix(&p);
+ if (!strcmp(p, "loop")) TT.arr_flag[n] = no ? FLAG_WITH_NO_PRFX :FLAG_WITHOUT_NO_PRFX;
+ else if (!strncmp(p, "opts=", 5)) {
+ p+=5;
+ TT.arr_flag[n] = is_no_prefix(&p) ?FLAG_WITH_NO_PRFX :FLAG_WITHOUT_NO_PRFX;
+ }
+ else {
+ if (!n) TT.negate = no;
+ if (n && TT.negate != no) error_exit("either all or none of the filesystem"
+ " types passed to -t must be prefixed with 'no' or '!'");
+ }
+ TT.arr_type[n++] = p;
+ }
+}
+
+//ignore these types...
+static int ignore_type(char *type)
+{
+ int i = 0;
+ char *str;
+ char *ignored_types[] = {
+ "ignore","iso9660", "nfs","proc",
+ "sw","swap", "tmpfs","devpts",NULL
+ };
+ while ((str = ignored_types[i++])) {
+ if (!strcmp(str, type)) return 1;
+ }
+ return 0;
+}
+
+// return true if has to ignore the filesystem.
+static int to_be_ignored(struct f_sys_info *finfo)
+{
+ int i, ret = 0, type_present = 0;
+
+ if (!finfo->passno) return 1; //Ignore with pass num = 0
+ if (TT.arr_type) {
+ for (i = 0; TT.arr_type[i]; i++) {
+ if (!TT.arr_flag[i]) { //it is type of filesys.
+ type_present = 2;
+ if (!strcmp(TT.arr_type[i], finfo->type)) ret = 0;
+ else ret = 1;
+ } else if (TT.arr_flag[i] == FLAG_WITH_NO_PRFX) { //it is option of filesys
+ if (hasmntopt((const struct mntent *)finfo, TT.arr_type[i])) return 1;
+ } else { //FLAG_WITHOUT_NO_PRFX
+ if (!hasmntopt((const struct mntent *)finfo, TT.arr_type[i])) return 1;
+ }
+ }
+ }
+ if (ignore_type(finfo->type)) return 1;
+ if (TT.arr_type && type_present != 2) return 0;
+ return ((TT.negate) ? !ret : ret);
+}
+
+// find type and execute corresponding fsck.type prog.
+static void do_fsck(struct f_sys_info *finfo)
+{
+ struct child_list *child;
+ char **args;
+ char *type;
+ pid_t pid;
+ int i = 1, j = 0;
+
+ if (strcmp(finfo->type, "auto")) type = finfo->type;
+ else if (TT.t_list && (TT.t_list[0] != 'n' || TT.t_list[1] != 'o' || TT.t_list[0] != '!')
+ && strncmp(TT.t_list, "opts=", 5) && strncmp(TT.t_list , "loop", 4)
+ && !TT.arr_type[1]) type = TT.t_list; //one file sys at cmdline
+ else type = "auto";
+
+ args = xzalloc((toys.optc + 2 + 1 + 1) * sizeof(char*)); //+1, for NULL, +1 if -C
+ args[0] = xmsprintf("fsck.%s", type);
+
+ if(toys.optflags & FLAG_C) args[i++] = xmsprintf("%s %d","-C", TT.fd_num);
+ while(toys.optargs[j]) {
+ if(*toys.optargs[j]) args[i++] = xstrdup(toys.optargs[j]);
+ j++;
+ }
+ args[i] = finfo->device;
+
+ TT.nr_run++;
+ if ((toys.optflags & FLAG_V) || (toys.optflags & FLAG_N)) {
+ printf("[%s (%d) -- %s]", args[0], TT.nr_run,
+ finfo->mountpt ? finfo->mountpt : finfo->device);
+ for (i = 0; args[i]; i++) xprintf(" %s", args[i]);
+ xputc('\n');
+ }
+
+ if (toys.optflags & FLAG_N) return;
+ else {
+ if ((pid = fork()) < 0) {
+ perror_msg(args[0]);
+ return;
+ }
+ if (!pid) xexec(args); //child, executes fsck.type
+ }
+
+ child = xzalloc(sizeof(struct child_list)); //Parent, add to child list.
+ child->dev_name = xstrdup(finfo->device);
+ child->prog_name = args[0];
+ child->pid = pid;
+
+ if (c_list) {
+ child->next = c_list;
+ c_list = child;
+ } else {
+ c_list = child;
+ child->next =NULL;
+ }
+}
+
+// for_all = 1; wait for all child to exit
+// for_all = 0; wait for any one to exit
+static int wait_for(int for_all)
+{
+ pid_t pid;
+ int status = 0, child_exited;
+ struct child_list *prev, *temp = c_list;
+ prev = temp;
+
+ errno = 0;
+ if (!c_list) return 0;
+ while ((pid = wait(&status))) {
+ if (TT.sig_num) kill_all();
+ child_exited = 0;
+ if (pid < 0) {
+ if (errno == EINTR) continue;
+ else if (errno == ECHILD) break; //No child to wait, break and return status.
+ else perror_exit("option arg Invalid\n"); //paranoid.
+ }
+ while (temp) {
+ if (temp->pid == pid) {
+ child_exited = 1;
+ break;
+ }
+ prev = temp;
+ temp = temp->next;
+ }
+ if (child_exited) {
+ if (WIFEXITED(status)) TT.sum_status |= WEXITSTATUS(status);
+ else if (WIFSIGNALED(status)) {
+ TT.sum_status |= 4; //Uncorrected.
+ if (WTERMSIG(status) != SIGINT)
+ perror_msg("child Term. by sig: %d\n",(WTERMSIG(status)));
+ TT.sum_status |= 8; //Operatinal error
+ } else {
+ TT.sum_status |= 4; //Uncorrected.
+ perror_msg("%s %s: status is %x, should never happen\n",
+ temp->prog_name, temp->dev_name, status);
+ }
+ TT.nr_run--;
+ if (prev == temp) c_list = c_list->next; //first node
+ else prev->next = temp->next;
+ free(temp->prog_name);
+ free(temp->dev_name);
+ free(temp);
+ if (!for_all) break;
+ }
+ }
+ return TT.sum_status;
+}
+
+//scan all the fstab entries or -t matches with fstab.
+static int scan_all(void)
+{
+ struct f_sys_info *finfo = filesys_info;
+ int ret = 0, passno;
+
+ if (toys.optflags & FLAG_V) xprintf("Checking all filesystem\n");
+ while (finfo) {
+ if (to_be_ignored(finfo)) finfo->flag |= FLAG_DONE;
+ finfo = finfo->next;
+ }
+ finfo = filesys_info;
+
+ if (!(toys.optflags & FLAG_P)) {
+ while (finfo) {
+ if (!strcmp(finfo->mountpt, "/")) { // man says: check / in parallel with others if -P is absent.
+ if ((toys.optflags & FLAG_R) || to_be_ignored(finfo)) {
+ finfo->flag |= FLAG_DONE;
+ break;
+ } else {
+ do_fsck(finfo);
+ finfo->flag |= FLAG_DONE;
+ if (TT.sig_num) kill_all();
+ if ((ret |= wait_for(1)) > 4) return ret; //destruction in filesys.
+ break;
+ }
+ }
+ finfo = finfo->next;
+ }
+ }
+ if (toys.optflags & FLAG_R) { // with -PR we choose to skip root.
+ for (finfo = filesys_info; finfo; finfo = finfo->next) {
+ if(!strcmp(finfo->mountpt, "/")) finfo->flag |= FLAG_DONE;
+ }
+ }
+ passno = 1;
+ while (1) {
+ for (finfo = filesys_info; finfo; finfo = finfo->next)
+ if (!finfo->flag) break;
+ if (!finfo) break;
+
+ for (finfo = filesys_info; finfo; finfo = finfo->next) {
+ if (finfo->flag) continue;
+ if (finfo->passno == passno) {
+ do_fsck(finfo);
+ finfo->flag |= FLAG_DONE;
+ if ((toys.optflags & FLAG_s) || (TT.nr_run
+ && (TT.nr_run >= TT.max_nr_run))) ret |= wait_for(0);
+ }
+ }
+ if (TT.sig_num) kill_all();
+ ret |= wait_for(1);
+ passno++;
+ }
+ return ret;
+}
+
+void record_sig_num(int sig)
+{
+ TT.sig_num = sig;
+}
+
+static void list_free(void *node) //for satisfying Valgrind
+{
+ free(((struct double_list*)node)->data);
+ free(node);
+}
+
+static void free_all(void)
+{
+ struct f_sys_info *finfo, *temp;
+
+ llist_traverse(TT.devices, list_free);
+ free(TT.arr_type);
+ free(TT.arr_flag);
+ for (finfo = filesys_info; finfo;) {
+ temp = finfo->next;
+ free(finfo->device);
+ free(finfo->mountpt);
+ free(finfo->type);
+ free(finfo->opts);
+ free(finfo);
+ finfo = temp;
+ }
+}
+
+void fsck_main(void)
+{
+ struct mntent mt;
+ struct double_list *dev;
+ struct f_sys_info *finfo;
+ FILE *fp;
+ char *tmp, **arg = toys.optargs;
+
+ sigatexit(record_sig_num);
+ while (*arg) {
+ if ((**arg == '/') || strchr(*arg, '=')) {
+ dlist_add(&TT.devices, xstrdup(*arg));
+ **arg = '\0';
+ }
+ arg++;
+ }
+ if (toys.optflags & FLAG_t) fix_tlist();
+ if (!(tmp = getenv("FSTAB_FILE"))) tmp = "/etc/fstab";
+ if (!(fp = setmntent(tmp, "r"))) perror_exit("setmntent failed:");
+ while (getmntent_r(fp, &mt, toybuf, 4096)) create_db(&mt);
+ endmntent(fp);
+
+ if (!(toys.optflags & FLAG_T)) xprintf("fsck ----- (Toybox)\n");
+
+ if ((tmp = getenv("FSCK_MAX_INST"))) TT.max_nr_run = strtol_range(tmp, 0, INT_MAX);
+ if (!TT.devices || (toys.optflags & FLAG_A)) {
+ toys.exitval = scan_all();
+ if (CFG_TOYBOX_FREE) free_all();
+ return;
+ }
+
+ dev = TT.devices;
+ dev->prev->next = NULL; //break double list to traverse.
+ for (; dev; dev = dev->next) {
+ for (finfo = filesys_info; finfo; finfo = finfo->next)
+ if (!strcmp(finfo->device, dev->data)
+ || !strcmp(finfo->mountpt, dev->data)) break;
+ if (!finfo) { //if not present, fill def values.
+ mt.mnt_fsname = dev->data;
+ mt.mnt_dir = "";
+ mt.mnt_type = "auto";
+ mt.mnt_opts = "";
+ mt.mnt_passno = -1;
+ finfo = create_db(&mt);
+ }
+ do_fsck(finfo);
+ finfo->flag |= FLAG_DONE;
+ if ((toys.optflags & FLAG_s) || (TT.nr_run && (TT.nr_run >= TT.max_nr_run)))
+ toys.exitval |= wait_for(0);
+ }
+ if (TT.sig_num) kill_all();
+ toys.exitval |= wait_for(1);
+ finfo = filesys_info;
+ if (CFG_TOYBOX_FREE) free_all();
+}