• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 file descriptor
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 
kill_all(void)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 
strtol_range(char * str,int min,int max)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.
create_db(struct mntent * f_info)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.
is_no_prefix(char ** p)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 
fix_tlist(void)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...
ignore_type(char * type)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.
to_be_ignored(struct f_sys_info * finfo)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.
do_fsck(struct f_sys_info * finfo)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_raw(*args);
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
wait_for(int for_all)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.
scan_all(void)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 
record_sig_num(int sig)357 void record_sig_num(int sig)
358 {
359   TT.sig_num = sig;
360 }
361 
fsck_main(void)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