fsck.c 11 KB

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