Home | History | Annotate | Download | only in pending
      1 /* fsck.c -  check and repair a Linux filesystem
      2  *
      3  * Copyright 2013 Sandeep Sharma <sandeep.jack2756 (at) gmail.com>
      4  * Copyright 2013 Kyungwan Han <asura321 (at) gmail.com>
      5 
      6 USE_FSCK(NEWTOY(fsck, "?t:ANPRTVsC#", TOYFLAG_USR|TOYFLAG_BIN))
      7 
      8 config FSCK
      9   bool "fsck"
     10   default n
     11   help
     12     usage: fsck [-ANPRTV] [-C FD] [-t FSTYPE] [FS_OPTS] [BLOCKDEV]...
     13 
     14     Check and repair filesystems
     15 
     16     -A      Walk /etc/fstab and check all filesystems
     17     -N      Don't execute, just show what would be done
     18     -P      With -A, check filesystems in parallel
     19     -R      With -A, skip the root filesystem
     20     -T      Don't show title on startup
     21     -V      Verbose
     22     -C n    Write status information to specified filedescriptor
     23     -t TYPE List of filesystem types to check
     24 
     25 */
     26 
     27 #define FOR_fsck
     28 #include "toys.h"
     29 #include <mntent.h>
     30 
     31 #define FLAG_WITHOUT_NO_PRFX 1
     32 #define FLAG_WITH_NO_PRFX 2
     33 #define FLAG_DONE 1
     34 
     35 GLOBALS(
     36   int fd_num;
     37   char *t_list;
     38 
     39   struct double_list *devices;
     40   char *arr_flag;
     41   char **arr_type;
     42   int negate;
     43   int sum_status;
     44   int nr_run;
     45   int sig_num;
     46   long max_nr_run;
     47 )
     48 
     49 struct f_sys_info {
     50   char *device, *mountpt, *type, *opts;
     51   int passno, flag;
     52   struct f_sys_info *next;
     53 };
     54 
     55 struct child_list {
     56   struct child_list *next;
     57   pid_t pid;
     58   char *prog_name, *dev_name;
     59 };
     60 
     61 static struct f_sys_info *filesys_info = NULL; //fstab entry list
     62 static struct child_list *c_list = NULL; //fsck.type child list.
     63 
     64 static void kill_all(void)
     65 {
     66   struct child_list *child;
     67 
     68   for (child = c_list; child; child = child->next)
     69     kill(child->pid, SIGTERM);
     70   _exit(0);
     71 }
     72 
     73 static long strtol_range(char *str, int min, int max)
     74 {
     75   char *endptr = NULL;
     76   errno = 0;
     77   long ret_value = strtol(str, &endptr, 10);
     78 
     79   if(errno) perror_exit("Invalid num %s", str);
     80   else if(endptr && (*endptr != '\0' || endptr == str))
     81     perror_exit("Not a valid num %s", str);
     82   if(ret_value >= min && ret_value <= max) return ret_value;
     83   else perror_exit("Number %s is not in valid [%d-%d] Range", str, min, max);
     84 }
     85 
     86 //create fstab entries list.
     87 static struct f_sys_info* create_db(struct mntent *f_info)
     88 {
     89   struct f_sys_info *temp = filesys_info;
     90   if (temp) {
     91     while (temp->next) temp = temp->next;
     92     temp->next = xzalloc(sizeof(struct f_sys_info));
     93     temp = temp->next;
     94   } else filesys_info = temp = xzalloc(sizeof(struct f_sys_info));
     95 
     96   temp->device = xstrdup(f_info->mnt_fsname);
     97   temp->mountpt = xstrdup(f_info->mnt_dir);
     98   if (strchr(f_info->mnt_type, ',')) temp->type = xstrdup("auto");
     99   else  temp->type = xstrdup(f_info->mnt_type);
    100   temp->opts = xstrdup(f_info->mnt_opts);
    101   temp->passno = f_info->mnt_passno;
    102   return temp;
    103 }
    104 
    105 //is we have 'no' or ! before type.
    106 static int is_no_prefix(char **p)
    107 {
    108   int no = 0;
    109 
    110   if ((*p[0] == 'n' && *(*p + 1) == 'o')) no = 2;
    111   else if (*p[0] == '!') no = 1;
    112   *p += no;
    113   return ((no) ? 1 :0);
    114 }
    115 
    116 static void fix_tlist(void)
    117 {
    118   char *p, *s = TT.t_list;
    119   int n = 1, no;
    120 
    121   while ((s = strchr(s, ','))) {
    122     s++;
    123     n++;
    124   }
    125 
    126   TT.arr_flag = xzalloc(n + 1);
    127   TT.arr_type = xzalloc((n + 1) * sizeof(char *));
    128   s = TT.t_list;
    129   n = 0;
    130   while ((p = strsep(&s, ","))) {
    131     no = is_no_prefix(&p);
    132     if (!strcmp(p, "loop")) {
    133       TT.arr_flag[n] = no ? FLAG_WITH_NO_PRFX :FLAG_WITHOUT_NO_PRFX;
    134       TT.negate = no;
    135     } else if (!strncmp(p, "opts=", 5)) {
    136       p+=5;
    137       TT.arr_flag[n] = is_no_prefix(&p) ?FLAG_WITH_NO_PRFX :FLAG_WITHOUT_NO_PRFX;
    138       TT.negate = no;
    139     } else {
    140       if (!n) TT.negate = no;
    141       if (n && TT.negate != no) error_exit("either all or none of the filesystem"
    142           " types passed to -t must be prefixed with 'no' or '!'");
    143     }
    144     TT.arr_type[n++] = p;
    145   }
    146 }
    147 
    148 //ignore these types...
    149 static int ignore_type(char *type)
    150 {
    151   int i = 0;
    152   char *str;
    153   char *ignored_types[] = {
    154     "ignore","iso9660", "nfs","proc",
    155     "sw","swap", "tmpfs","devpts",NULL
    156   };
    157   while ((str = ignored_types[i++])) {
    158     if (!strcmp(str, type)) return 1;
    159   }
    160   return 0;
    161 }
    162 
    163 // return true if has to ignore the filesystem.
    164 static int to_be_ignored(struct f_sys_info *finfo)
    165 {
    166   int i, ret = 0, type_present = 0;
    167 
    168   if (!finfo->passno) return 1; //Ignore with pass num = 0
    169   if (TT.arr_type) {
    170     for (i = 0; TT.arr_type[i]; i++) {
    171       if (!TT.arr_flag[i]) { //it is type of filesys.
    172         type_present = 2;
    173         if (!strcmp(TT.arr_type[i], finfo->type)) ret = 0;
    174         else ret = 1;
    175       } else if (TT.arr_flag[i] == FLAG_WITH_NO_PRFX) { //it is option of filesys
    176         if (hasmntopt((const struct mntent *)finfo, TT.arr_type[i])) return 1;
    177       } else { //FLAG_WITHOUT_NO_PRFX
    178         if (!hasmntopt((const struct mntent *)finfo, TT.arr_type[i])) return 1;
    179       }
    180     }
    181   }
    182   if (ignore_type(finfo->type)) return 1;
    183   if (TT.arr_type && type_present != 2) return 0;
    184   return ((TT.negate) ? !ret : ret);
    185 }
    186 
    187 // find type and execute corresponding fsck.type prog.
    188 static void do_fsck(struct f_sys_info *finfo)
    189 {
    190   struct child_list *child;
    191   char **args;
    192   char *type;
    193   pid_t pid;
    194   int i = 1, j = 0;
    195 
    196   if (strcmp(finfo->type, "auto")) type = finfo->type;
    197   else if (TT.t_list && (TT.t_list[0] != 'n' || TT.t_list[1] != 'o' || TT.t_list[0] != '!')
    198       && strncmp(TT.t_list, "opts=", 5) && strncmp(TT.t_list , "loop", 4)
    199       && !TT.arr_type[1]) type = TT.t_list; //one file sys at cmdline
    200   else type = "auto";
    201 
    202   args = xzalloc((toys.optc + 2 + 1 + 1) * sizeof(char*)); //+1, for NULL, +1 if -C
    203   args[0] = xmprintf("fsck.%s", type);
    204 
    205   if(toys.optflags & FLAG_C) args[i++] = xmprintf("%s %d","-C", TT.fd_num);
    206   while(toys.optargs[j]) {
    207     if(*toys.optargs[j]) args[i++] = xstrdup(toys.optargs[j]);
    208     j++;
    209   }
    210   args[i] = finfo->device;
    211 
    212   TT.nr_run++;
    213   if ((toys.optflags & FLAG_V) || (toys.optflags & FLAG_N)) {
    214     printf("[%s (%d) -- %s]", args[0], TT.nr_run,
    215         finfo->mountpt ? finfo->mountpt : finfo->device);
    216     for (i = 0; args[i]; i++) xprintf(" %s", args[i]);
    217     xputc('\n');
    218   }
    219 
    220   if (toys.optflags & FLAG_N) {
    221     for (j=0;j<i;j++) free(args[i]);
    222     free(args);
    223     return;
    224   } else {
    225     if ((pid = fork()) < 0) {
    226       perror_msg(args[0]);
    227       for (j=0;j<i;j++) free(args[i]);
    228       free(args);
    229       return;
    230     }
    231     if (!pid) xexec(args); //child, executes fsck.type
    232   }
    233 
    234   child = xzalloc(sizeof(struct child_list)); //Parent, add to child list.
    235   child->dev_name = xstrdup(finfo->device);
    236   child->prog_name = args[0];
    237   child->pid = pid;
    238 
    239   if (c_list) {
    240     child->next = c_list;
    241     c_list = child;
    242   } else {
    243     c_list = child;
    244     child->next =NULL;
    245   }
    246 }
    247 
    248 // for_all = 1; wait for all child to exit
    249 // for_all = 0; wait for any one to exit
    250 static int wait_for(int for_all)
    251 {
    252   pid_t pid;
    253   int status = 0, child_exited;
    254   struct child_list *prev, *temp;
    255 
    256   errno = 0;
    257   if (!c_list) return 0;
    258   while ((pid = wait(&status))) {
    259     temp = c_list;
    260     prev = temp;
    261     if (TT.sig_num) kill_all();
    262     child_exited = 0;
    263     if (pid < 0) {
    264       if (errno == EINTR) continue;
    265       else if (errno == ECHILD) break; //No child to wait, break and return status.
    266       else perror_exit("option arg Invalid\n"); //paranoid.
    267     }
    268     while (temp) {
    269       if (temp->pid == pid) {
    270         child_exited = 1;
    271         break;
    272       }
    273       prev = temp;
    274       temp = temp->next;
    275     }
    276     if (child_exited) {
    277       if (WIFEXITED(status)) TT.sum_status |= WEXITSTATUS(status);
    278       else if (WIFSIGNALED(status)) {
    279         TT.sum_status |= 4; //Uncorrected.
    280         if (WTERMSIG(status) != SIGINT)
    281           perror_msg("child Term. by sig: %d\n",(WTERMSIG(status)));
    282         TT.sum_status |= 8; //Operatinal error
    283       } else {
    284         TT.sum_status |= 4; //Uncorrected.
    285         perror_msg("%s %s: status is %x, should never happen\n",
    286             temp->prog_name, temp->dev_name, status);
    287       }
    288       TT.nr_run--;
    289       if (prev == temp) c_list = c_list->next; //first node
    290       else prev->next = temp->next;
    291       free(temp->prog_name);
    292       free(temp->dev_name);
    293       free(temp);
    294       if (!for_all) break;
    295     }
    296   }
    297   return TT.sum_status;
    298 }
    299 
    300 //scan all the fstab entries or -t matches with fstab.
    301 static int scan_all(void)
    302 {
    303   struct f_sys_info *finfo = filesys_info;
    304   int ret = 0, passno;
    305 
    306   if (toys.optflags & FLAG_V) xprintf("Checking all filesystem\n");
    307   while (finfo) {
    308     if (to_be_ignored(finfo)) finfo->flag |= FLAG_DONE;
    309     finfo = finfo->next;
    310   }
    311   finfo = filesys_info;
    312 
    313   if (!(toys.optflags & FLAG_P)) {
    314     while (finfo) {
    315       if (!strcmp(finfo->mountpt, "/")) { // man says: check / in parallel with others if -P is absent.
    316         if ((toys.optflags & FLAG_R) || to_be_ignored(finfo)) {
    317           finfo->flag |= FLAG_DONE;
    318           break;
    319         } else {
    320           do_fsck(finfo);
    321           finfo->flag |= FLAG_DONE;
    322           if (TT.sig_num) kill_all();
    323           if ((ret |= wait_for(1)) > 4) return ret; //destruction in filesys.
    324           break;
    325         }
    326       }
    327       finfo = finfo->next;
    328     }
    329   }
    330   if (toys.optflags & FLAG_R) { // with -PR we choose to skip root.
    331     for (finfo = filesys_info; finfo; finfo = finfo->next) {
    332       if(!strcmp(finfo->mountpt, "/")) finfo->flag |= FLAG_DONE;
    333     }
    334   }
    335   passno = 1;
    336   while (1) {
    337     for (finfo = filesys_info; finfo; finfo = finfo->next)
    338       if (!finfo->flag) break;
    339     if (!finfo) break;
    340 
    341     for (finfo = filesys_info; finfo; finfo = finfo->next) {
    342       if (finfo->flag) continue;
    343       if (finfo->passno == passno) {
    344         do_fsck(finfo);
    345         finfo->flag |= FLAG_DONE;
    346         if ((toys.optflags & FLAG_s) || (TT.nr_run
    347               && (TT.nr_run >= TT.max_nr_run))) ret |= wait_for(0);
    348       }
    349     }
    350     if (TT.sig_num) kill_all();
    351     ret |= wait_for(1);
    352     passno++;
    353   }
    354   return ret;
    355 }
    356 
    357 void record_sig_num(int sig)
    358 {
    359   TT.sig_num = sig;
    360 }
    361 
    362 void fsck_main(void)
    363 {
    364   struct mntent mt;
    365   struct double_list *dev;
    366   struct f_sys_info *finfo;
    367   FILE *fp;
    368   char *tmp, **arg = toys.optargs;
    369 
    370   sigatexit(record_sig_num);
    371   while (*arg) {
    372     if ((**arg == '/') || strchr(*arg, '=')) {
    373       dlist_add(&TT.devices, xstrdup(*arg));
    374       **arg = '\0';
    375     }
    376     arg++;
    377   }
    378   if (toys.optflags & FLAG_t) fix_tlist();
    379   if (!(tmp = getenv("FSTAB_FILE"))) tmp = "/etc/fstab";
    380   if (!(fp = setmntent(tmp, "r"))) perror_exit("setmntent failed:");
    381   while (getmntent_r(fp, &mt, toybuf, 4096)) create_db(&mt);
    382   endmntent(fp);
    383 
    384   if (!(toys.optflags & FLAG_T)) xprintf("fsck ----- (Toybox)\n");
    385 
    386   if ((tmp = getenv("FSCK_MAX_INST")))
    387     TT.max_nr_run = strtol_range(tmp, 0, INT_MAX);
    388   if (!TT.devices || (toys.optflags & FLAG_A)) {
    389     toys.exitval = scan_all();
    390     if (CFG_TOYBOX_FREE) goto free_all;
    391     return; //if CFG_TOYBOX_FREE is not set, exit.
    392   }
    393 
    394   dev = TT.devices;
    395   dev->prev->next = NULL; //break double list to traverse.
    396   for (; dev; dev = dev->next) {
    397     for (finfo = filesys_info; finfo; finfo = finfo->next)
    398       if (!strcmp(finfo->device, dev->data)
    399           || !strcmp(finfo->mountpt, dev->data)) break;
    400     if (!finfo) { //if not present, fill def values.
    401       mt.mnt_fsname = dev->data;
    402       mt.mnt_dir = "";
    403       mt.mnt_type = "auto";
    404       mt.mnt_opts = "";
    405       mt.mnt_passno = -1;
    406       finfo = create_db(&mt);
    407     }
    408     do_fsck(finfo);
    409     finfo->flag |= FLAG_DONE;
    410     if ((toys.optflags & FLAG_s) || (TT.nr_run && (TT.nr_run >= TT.max_nr_run)))
    411       toys.exitval |= wait_for(0);
    412   }
    413   if (TT.sig_num) kill_all();
    414   toys.exitval |= wait_for(1);
    415   finfo = filesys_info;
    416 
    417 free_all:
    418   if (CFG_TOYBOX_FREE) {
    419     struct f_sys_info *finfo, *temp;
    420 
    421     llist_traverse(TT.devices, llist_free_double);
    422     free(TT.arr_type);
    423     free(TT.arr_flag);
    424     for (finfo = filesys_info; finfo;) {
    425       temp = finfo->next;
    426       free(finfo->device);
    427       free(finfo->mountpt);
    428       free(finfo->type);
    429       free(finfo->opts);
    430       free(finfo);
    431       finfo = temp;
    432     }
    433   }
    434 }
    435