Mercurial > hg > toybox
comparison toys/pending/fsck.c @ 1119:36a3a6f55154 draft
fsck wrapper from Ashwini Sharma. (Note: this just calls filesystem-specific programs not yet in toybox.)
author | Rob Landley <rob@landley.net> |
---|---|
date | Sun, 10 Nov 2013 18:38:43 -0600 |
parents | |
children | 0752b2d58909 |
comparison
equal
deleted
inserted
replaced
1118:04f83dae08b4 | 1119:36a3a6f55154 |
---|---|
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 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 | |
35 GLOBALS( | |
36 int fd_num; | |
37 char *t_list; | |
38 | |
39 struct double_list *devices; | |
40 int *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 | |
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 | |
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. | |
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. | |
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 | |
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) * sizeof(char)); | |
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")) TT.arr_flag[n] = no ? FLAG_WITH_NO_PRFX :FLAG_WITHOUT_NO_PRFX; | |
133 else if (!strncmp(p, "opts=", 5)) { | |
134 p+=5; | |
135 TT.arr_flag[n] = is_no_prefix(&p) ?FLAG_WITH_NO_PRFX :FLAG_WITHOUT_NO_PRFX; | |
136 } | |
137 else { | |
138 if (!n) TT.negate = no; | |
139 if (n && TT.negate != no) error_exit("either all or none of the filesystem" | |
140 " types passed to -t must be prefixed with 'no' or '!'"); | |
141 } | |
142 TT.arr_type[n++] = p; | |
143 } | |
144 } | |
145 | |
146 //ignore these types... | |
147 static int ignore_type(char *type) | |
148 { | |
149 int i = 0; | |
150 char *str; | |
151 char *ignored_types[] = { | |
152 "ignore","iso9660", "nfs","proc", | |
153 "sw","swap", "tmpfs","devpts",NULL | |
154 }; | |
155 while ((str = ignored_types[i++])) { | |
156 if (!strcmp(str, type)) return 1; | |
157 } | |
158 return 0; | |
159 } | |
160 | |
161 // return true if has to ignore the filesystem. | |
162 static int to_be_ignored(struct f_sys_info *finfo) | |
163 { | |
164 int i, ret = 0, type_present = 0; | |
165 | |
166 if (!finfo->passno) return 1; //Ignore with pass num = 0 | |
167 if (TT.arr_type) { | |
168 for (i = 0; TT.arr_type[i]; i++) { | |
169 if (!TT.arr_flag[i]) { //it is type of filesys. | |
170 type_present = 2; | |
171 if (!strcmp(TT.arr_type[i], finfo->type)) ret = 0; | |
172 else ret = 1; | |
173 } else if (TT.arr_flag[i] == FLAG_WITH_NO_PRFX) { //it is option of filesys | |
174 if (hasmntopt((const struct mntent *)finfo, TT.arr_type[i])) return 1; | |
175 } else { //FLAG_WITHOUT_NO_PRFX | |
176 if (!hasmntopt((const struct mntent *)finfo, TT.arr_type[i])) return 1; | |
177 } | |
178 } | |
179 } | |
180 if (ignore_type(finfo->type)) return 1; | |
181 if (TT.arr_type && type_present != 2) return 0; | |
182 return ((TT.negate) ? !ret : ret); | |
183 } | |
184 | |
185 // find type and execute corresponding fsck.type prog. | |
186 static void do_fsck(struct f_sys_info *finfo) | |
187 { | |
188 struct child_list *child; | |
189 char **args; | |
190 char *type; | |
191 pid_t pid; | |
192 int i = 1, j = 0; | |
193 | |
194 if (strcmp(finfo->type, "auto")) type = finfo->type; | |
195 else if (TT.t_list && (TT.t_list[0] != 'n' || TT.t_list[1] != 'o' || TT.t_list[0] != '!') | |
196 && strncmp(TT.t_list, "opts=", 5) && strncmp(TT.t_list , "loop", 4) | |
197 && !TT.arr_type[1]) type = TT.t_list; //one file sys at cmdline | |
198 else type = "auto"; | |
199 | |
200 args = xzalloc((toys.optc + 2 + 1 + 1) * sizeof(char*)); //+1, for NULL, +1 if -C | |
201 args[0] = xmsprintf("fsck.%s", type); | |
202 | |
203 if(toys.optflags & FLAG_C) args[i++] = xmsprintf("%s %d","-C", TT.fd_num); | |
204 while(toys.optargs[j]) { | |
205 if(*toys.optargs[j]) args[i++] = xstrdup(toys.optargs[j]); | |
206 j++; | |
207 } | |
208 args[i] = finfo->device; | |
209 | |
210 TT.nr_run++; | |
211 if ((toys.optflags & FLAG_V) || (toys.optflags & FLAG_N)) { | |
212 printf("[%s (%d) -- %s]", args[0], TT.nr_run, | |
213 finfo->mountpt ? finfo->mountpt : finfo->device); | |
214 for (i = 0; args[i]; i++) xprintf(" %s", args[i]); | |
215 xputc('\n'); | |
216 } | |
217 | |
218 if (toys.optflags & FLAG_N) return; | |
219 else { | |
220 if ((pid = fork()) < 0) { | |
221 perror_msg(args[0]); | |
222 return; | |
223 } | |
224 if (!pid) xexec(args); //child, executes fsck.type | |
225 } | |
226 | |
227 child = xzalloc(sizeof(struct child_list)); //Parent, add to child list. | |
228 child->dev_name = xstrdup(finfo->device); | |
229 child->prog_name = args[0]; | |
230 child->pid = pid; | |
231 | |
232 if (c_list) { | |
233 child->next = c_list; | |
234 c_list = child; | |
235 } else { | |
236 c_list = child; | |
237 child->next =NULL; | |
238 } | |
239 } | |
240 | |
241 // for_all = 1; wait for all child to exit | |
242 // for_all = 0; wait for any one to exit | |
243 static int wait_for(int for_all) | |
244 { | |
245 pid_t pid; | |
246 int status = 0, child_exited; | |
247 struct child_list *prev, *temp = c_list; | |
248 prev = temp; | |
249 | |
250 errno = 0; | |
251 if (!c_list) return 0; | |
252 while ((pid = wait(&status))) { | |
253 if (TT.sig_num) kill_all(); | |
254 child_exited = 0; | |
255 if (pid < 0) { | |
256 if (errno == EINTR) continue; | |
257 else if (errno == ECHILD) break; //No child to wait, break and return status. | |
258 else perror_exit("option arg Invalid\n"); //paranoid. | |
259 } | |
260 while (temp) { | |
261 if (temp->pid == pid) { | |
262 child_exited = 1; | |
263 break; | |
264 } | |
265 prev = temp; | |
266 temp = temp->next; | |
267 } | |
268 if (child_exited) { | |
269 if (WIFEXITED(status)) TT.sum_status |= WEXITSTATUS(status); | |
270 else if (WIFSIGNALED(status)) { | |
271 TT.sum_status |= 4; //Uncorrected. | |
272 if (WTERMSIG(status) != SIGINT) | |
273 perror_msg("child Term. by sig: %d\n",(WTERMSIG(status))); | |
274 TT.sum_status |= 8; //Operatinal error | |
275 } else { | |
276 TT.sum_status |= 4; //Uncorrected. | |
277 perror_msg("%s %s: status is %x, should never happen\n", | |
278 temp->prog_name, temp->dev_name, status); | |
279 } | |
280 TT.nr_run--; | |
281 if (prev == temp) c_list = c_list->next; //first node | |
282 else prev->next = temp->next; | |
283 free(temp->prog_name); | |
284 free(temp->dev_name); | |
285 free(temp); | |
286 if (!for_all) break; | |
287 } | |
288 } | |
289 return TT.sum_status; | |
290 } | |
291 | |
292 //scan all the fstab entries or -t matches with fstab. | |
293 static int scan_all(void) | |
294 { | |
295 struct f_sys_info *finfo = filesys_info; | |
296 int ret = 0, passno; | |
297 | |
298 if (toys.optflags & FLAG_V) xprintf("Checking all filesystem\n"); | |
299 while (finfo) { | |
300 if (to_be_ignored(finfo)) finfo->flag |= FLAG_DONE; | |
301 finfo = finfo->next; | |
302 } | |
303 finfo = filesys_info; | |
304 | |
305 if (!(toys.optflags & FLAG_P)) { | |
306 while (finfo) { | |
307 if (!strcmp(finfo->mountpt, "/")) { // man says: check / in parallel with others if -P is absent. | |
308 if ((toys.optflags & FLAG_R) || to_be_ignored(finfo)) { | |
309 finfo->flag |= FLAG_DONE; | |
310 break; | |
311 } else { | |
312 do_fsck(finfo); | |
313 finfo->flag |= FLAG_DONE; | |
314 if (TT.sig_num) kill_all(); | |
315 if ((ret |= wait_for(1)) > 4) return ret; //destruction in filesys. | |
316 break; | |
317 } | |
318 } | |
319 finfo = finfo->next; | |
320 } | |
321 } | |
322 if (toys.optflags & FLAG_R) { // with -PR we choose to skip root. | |
323 for (finfo = filesys_info; finfo; finfo = finfo->next) { | |
324 if(!strcmp(finfo->mountpt, "/")) finfo->flag |= FLAG_DONE; | |
325 } | |
326 } | |
327 passno = 1; | |
328 while (1) { | |
329 for (finfo = filesys_info; finfo; finfo = finfo->next) | |
330 if (!finfo->flag) break; | |
331 if (!finfo) break; | |
332 | |
333 for (finfo = filesys_info; finfo; finfo = finfo->next) { | |
334 if (finfo->flag) continue; | |
335 if (finfo->passno == passno) { | |
336 do_fsck(finfo); | |
337 finfo->flag |= FLAG_DONE; | |
338 if ((toys.optflags & FLAG_s) || (TT.nr_run | |
339 && (TT.nr_run >= TT.max_nr_run))) ret |= wait_for(0); | |
340 } | |
341 } | |
342 if (TT.sig_num) kill_all(); | |
343 ret |= wait_for(1); | |
344 passno++; | |
345 } | |
346 return ret; | |
347 } | |
348 | |
349 void record_sig_num(int sig) | |
350 { | |
351 TT.sig_num = sig; | |
352 } | |
353 | |
354 static void list_free(void *node) //for satisfying Valgrind | |
355 { | |
356 free(((struct double_list*)node)->data); | |
357 free(node); | |
358 } | |
359 | |
360 static void free_all(void) | |
361 { | |
362 struct f_sys_info *finfo, *temp; | |
363 | |
364 llist_traverse(TT.devices, list_free); | |
365 free(TT.arr_type); | |
366 free(TT.arr_flag); | |
367 for (finfo = filesys_info; finfo;) { | |
368 temp = finfo->next; | |
369 free(finfo->device); | |
370 free(finfo->mountpt); | |
371 free(finfo->type); | |
372 free(finfo->opts); | |
373 free(finfo); | |
374 finfo = temp; | |
375 } | |
376 } | |
377 | |
378 void fsck_main(void) | |
379 { | |
380 struct mntent mt; | |
381 struct double_list *dev; | |
382 struct f_sys_info *finfo; | |
383 FILE *fp; | |
384 char *tmp, **arg = toys.optargs; | |
385 | |
386 sigatexit(record_sig_num); | |
387 while (*arg) { | |
388 if ((**arg == '/') || strchr(*arg, '=')) { | |
389 dlist_add(&TT.devices, xstrdup(*arg)); | |
390 **arg = '\0'; | |
391 } | |
392 arg++; | |
393 } | |
394 if (toys.optflags & FLAG_t) fix_tlist(); | |
395 if (!(tmp = getenv("FSTAB_FILE"))) tmp = "/etc/fstab"; | |
396 if (!(fp = setmntent(tmp, "r"))) perror_exit("setmntent failed:"); | |
397 while (getmntent_r(fp, &mt, toybuf, 4096)) create_db(&mt); | |
398 endmntent(fp); | |
399 | |
400 if (!(toys.optflags & FLAG_T)) xprintf("fsck ----- (Toybox)\n"); | |
401 | |
402 if ((tmp = getenv("FSCK_MAX_INST"))) TT.max_nr_run = strtol_range(tmp, 0, INT_MAX); | |
403 if (!TT.devices || (toys.optflags & FLAG_A)) { | |
404 toys.exitval = scan_all(); | |
405 if (CFG_TOYBOX_FREE) free_all(); | |
406 return; | |
407 } | |
408 | |
409 dev = TT.devices; | |
410 dev->prev->next = NULL; //break double list to traverse. | |
411 for (; dev; dev = dev->next) { | |
412 for (finfo = filesys_info; finfo; finfo = finfo->next) | |
413 if (!strcmp(finfo->device, dev->data) | |
414 || !strcmp(finfo->mountpt, dev->data)) break; | |
415 if (!finfo) { //if not present, fill def values. | |
416 mt.mnt_fsname = dev->data; | |
417 mt.mnt_dir = ""; | |
418 mt.mnt_type = "auto"; | |
419 mt.mnt_opts = ""; | |
420 mt.mnt_passno = -1; | |
421 finfo = create_db(&mt); | |
422 } | |
423 do_fsck(finfo); | |
424 finfo->flag |= FLAG_DONE; | |
425 if ((toys.optflags & FLAG_s) || (TT.nr_run && (TT.nr_run >= TT.max_nr_run))) | |
426 toys.exitval |= wait_for(0); | |
427 } | |
428 if (TT.sig_num) kill_all(); | |
429 toys.exitval |= wait_for(1); | |
430 finfo = filesys_info; | |
431 if (CFG_TOYBOX_FREE) free_all(); | |
432 } |