changeset 1119:36a3a6f55154

fsck wrapper from Ashwini Sharma. (Note: this just calls filesystem-specific programs not yet in toybox.)
author Rob Landley <rob@landley.net>
date Sun, 10 Nov 2013 18:38:43 -0600
parents 04f83dae08b4
children 55a533fa5039
files toys/pending/fsck.c
diffstat 1 files changed, 432 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/pending/fsck.c	Sun Nov 10 18:38:43 2013 -0600
@@ -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();
+}