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