123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- /* 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 file descriptor
- -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;
- char *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);
- 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;
- TT.negate = no;
- } else if (!strncmp(p, "opts=", 5)) {
- p+=5;
- TT.arr_flag[n] = is_no_prefix(&p) ?FLAG_WITH_NO_PRFX :FLAG_WITHOUT_NO_PRFX;
- TT.negate = no;
- } 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] = xmprintf("fsck.%s", type);
-
- if(toys.optflags & FLAG_C) args[i++] = xmprintf("%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) {
- for (j=0;j<i;j++) free(args[i]);
- free(args);
- return;
- } else {
- if ((pid = fork()) < 0) {
- perror_msg_raw(*args);
- for (j=0;j<i;j++) free(args[i]);
- free(args);
- 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;
- errno = 0;
- if (!c_list) return 0;
- while ((pid = wait(&status))) {
- temp = c_list;
- prev = temp;
- 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;
- }
- 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) goto free_all;
- return; //if CFG_TOYBOX_FREE is not set, exit.
- }
- 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;
- free_all:
- if (CFG_TOYBOX_FREE) {
- struct f_sys_info *finfo, *temp;
- llist_traverse(TT.devices, llist_free_double);
- 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;
- }
- }
- }
|