toybox/toys/pending/fsck.c
<<
>>
Prefs
   1/* fsck.c -  check and repair a Linux filesystem
   2 *
   3 * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
   4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
   5
   6USE_FSCK(NEWTOY(fsck, "?t:ANPRTVsC#", TOYFLAG_USR|TOYFLAG_BIN))
   7
   8config 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
  35GLOBALS(
  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
  49struct f_sys_info {
  50  char *device, *mountpt, *type, *opts;
  51  int passno, flag;
  52  struct f_sys_info *next;
  53};
  54
  55struct child_list {
  56  struct child_list *next;
  57  pid_t pid;
  58  char *prog_name, *dev_name;
  59};
  60
  61static struct f_sys_info *filesys_info = NULL; //fstab entry list
  62static struct child_list *c_list = NULL; //fsck.type child list.
  63
  64static 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
  73static 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.
  87static 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.
 106static 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
 116static 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...
 149static 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.
 164static 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.
 188static 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
 250static 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.
 301static 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
 357void record_sig_num(int sig) 
 358{
 359  TT.sig_num = sig;
 360}
 361
 362void 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
 417free_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