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 }