Annotation of /tags/mkinitrd-6_3_1/busybox/e2fsprogs/fsck.c
Parent Directory | Revision Log
Revision 532 -
(hide annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
Original Path: trunk/mkinitrd-magellan/busybox/e2fsprogs/fsck.c
File MIME type: text/plain
File size: 26807 byte(s)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
Original Path: trunk/mkinitrd-magellan/busybox/e2fsprogs/fsck.c
File MIME type: text/plain
File size: 26807 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | niro | 532 | /* vi: set sw=4 ts=4: */ |
2 | /* | ||
3 | * fsck --- A generic, parallelizing front-end for the fsck program. | ||
4 | * It will automatically try to run fsck programs in parallel if the | ||
5 | * devices are on separate spindles. It is based on the same ideas as | ||
6 | * the generic front end for fsck by David Engel and Fred van Kempen, | ||
7 | * but it has been completely rewritten from scratch to support | ||
8 | * parallel execution. | ||
9 | * | ||
10 | * Written by Theodore Ts'o, <tytso@mit.edu> | ||
11 | * | ||
12 | * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994: | ||
13 | * o Changed -t fstype to behave like with mount when -A (all file | ||
14 | * systems) or -M (like mount) is specified. | ||
15 | * o fsck looks if it can find the fsck.type program to decide | ||
16 | * if it should ignore the fs type. This way more fsck programs | ||
17 | * can be added without changing this front-end. | ||
18 | * o -R flag skip root file system. | ||
19 | * | ||
20 | * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, | ||
21 | * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o. | ||
22 | * | ||
23 | * %Begin-Header% | ||
24 | * This file may be redistributed under the terms of the GNU Public | ||
25 | * License. | ||
26 | * %End-Header% | ||
27 | */ | ||
28 | |||
29 | /* All filesystem specific hooks have been removed. | ||
30 | * If filesystem cannot be determined, we will execute | ||
31 | * "fsck.auto". Currently this also happens if you specify | ||
32 | * UUID=xxx or LABEL=xxx as an object to check. | ||
33 | * Detection code for that is also probably has to be in fsck.auto. | ||
34 | * | ||
35 | * In other words, this is _really_ is just a driver program which | ||
36 | * spawns actual fsck.something for each filesystem to check. | ||
37 | * It doesn't guess filesystem types from on-disk format. | ||
38 | */ | ||
39 | |||
40 | #include "busybox.h" | ||
41 | |||
42 | #define EXIT_OK 0 | ||
43 | #define EXIT_NONDESTRUCT 1 | ||
44 | #define EXIT_DESTRUCT 2 | ||
45 | #define EXIT_UNCORRECTED 4 | ||
46 | #define EXIT_ERROR 8 | ||
47 | #define EXIT_USAGE 16 | ||
48 | #define FSCK_CANCELED 32 /* Aborted with a signal or ^C */ | ||
49 | |||
50 | /* | ||
51 | * Internal structure for mount table entries. | ||
52 | */ | ||
53 | |||
54 | struct fs_info { | ||
55 | struct fs_info *next; | ||
56 | char *device; | ||
57 | char *mountpt; | ||
58 | char *type; | ||
59 | char *opts; | ||
60 | int freq; | ||
61 | int passno; | ||
62 | int flags; | ||
63 | }; | ||
64 | |||
65 | #define FLAG_DONE 1 | ||
66 | #define FLAG_PROGRESS 2 | ||
67 | /* | ||
68 | * Structure to allow exit codes to be stored | ||
69 | */ | ||
70 | struct fsck_instance { | ||
71 | struct fsck_instance *next; | ||
72 | int pid; | ||
73 | int flags; | ||
74 | int exit_status; | ||
75 | time_t start_time; | ||
76 | char *prog; | ||
77 | char *type; | ||
78 | char *device; | ||
79 | char *base_device; /* /dev/hda for /dev/hdaN etc */ | ||
80 | }; | ||
81 | |||
82 | static const char *const ignored_types[] = { | ||
83 | "ignore", | ||
84 | "iso9660", | ||
85 | "nfs", | ||
86 | "proc", | ||
87 | "sw", | ||
88 | "swap", | ||
89 | "tmpfs", | ||
90 | "devpts", | ||
91 | NULL | ||
92 | }; | ||
93 | |||
94 | #if 0 | ||
95 | static const char *const really_wanted[] = { | ||
96 | "minix", | ||
97 | "ext2", | ||
98 | "ext3", | ||
99 | "jfs", | ||
100 | "reiserfs", | ||
101 | "xiafs", | ||
102 | "xfs", | ||
103 | NULL | ||
104 | }; | ||
105 | #endif | ||
106 | |||
107 | #define BASE_MD "/dev/md" | ||
108 | |||
109 | static char **devices; | ||
110 | static char **args; | ||
111 | static int num_devices; | ||
112 | static int num_args; | ||
113 | static int verbose; | ||
114 | |||
115 | #define FS_TYPE_FLAG_NORMAL 0 | ||
116 | #define FS_TYPE_FLAG_OPT 1 | ||
117 | #define FS_TYPE_FLAG_NEGOPT 2 | ||
118 | static char **fs_type_list; | ||
119 | static uint8_t *fs_type_flag; | ||
120 | static smallint fs_type_negated; | ||
121 | |||
122 | static volatile smallint cancel_requested; | ||
123 | static smallint doall; | ||
124 | static smallint noexecute; | ||
125 | static smallint serialize; | ||
126 | static smallint skip_root; | ||
127 | /* static smallint like_mount; */ | ||
128 | static smallint notitle; | ||
129 | static smallint parallel_root; | ||
130 | static smallint force_all_parallel; | ||
131 | |||
132 | /* "progress indicator" code is somewhat buggy and ext[23] specific. | ||
133 | * We should be filesystem agnostic. IOW: there should be a well-defined | ||
134 | * API for fsck.something, NOT ad-hoc hacks in generic fsck. */ | ||
135 | #define DO_PROGRESS_INDICATOR 0 | ||
136 | #if DO_PROGRESS_INDICATOR | ||
137 | static smallint progress; | ||
138 | static int progress_fd; | ||
139 | #endif | ||
140 | |||
141 | static int num_running; | ||
142 | static int max_running; | ||
143 | static char *fstype; | ||
144 | static struct fs_info *filesys_info; | ||
145 | static struct fs_info *filesys_last; | ||
146 | static struct fsck_instance *instance_list; | ||
147 | |||
148 | /* | ||
149 | * Return the "base device" given a particular device; this is used to | ||
150 | * assure that we only fsck one partition on a particular drive at any | ||
151 | * one time. Otherwise, the disk heads will be seeking all over the | ||
152 | * place. If the base device cannot be determined, return NULL. | ||
153 | * | ||
154 | * The base_device() function returns an allocated string which must | ||
155 | * be freed. | ||
156 | */ | ||
157 | #if ENABLE_FEATURE_DEVFS | ||
158 | /* | ||
159 | * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3 | ||
160 | * pathames. | ||
161 | */ | ||
162 | static const char *const devfs_hier[] = { | ||
163 | "host", "bus", "target", "lun", NULL | ||
164 | }; | ||
165 | #endif | ||
166 | |||
167 | static char *base_device(const char *device) | ||
168 | { | ||
169 | char *str, *cp; | ||
170 | #if ENABLE_FEATURE_DEVFS | ||
171 | const char *const *hier; | ||
172 | const char *disk; | ||
173 | int len; | ||
174 | #endif | ||
175 | cp = str = xstrdup(device); | ||
176 | |||
177 | /* Skip over /dev/; if it's not present, give up. */ | ||
178 | if (strncmp(cp, "/dev/", 5) != 0) | ||
179 | goto errout; | ||
180 | cp += 5; | ||
181 | |||
182 | /* | ||
183 | * For md devices, we treat them all as if they were all | ||
184 | * on one disk, since we don't know how to parallelize them. | ||
185 | */ | ||
186 | if (cp[0] == 'm' && cp[1] == 'd') { | ||
187 | cp[2] = 0; | ||
188 | return str; | ||
189 | } | ||
190 | |||
191 | /* Handle DAC 960 devices */ | ||
192 | if (strncmp(cp, "rd/", 3) == 0) { | ||
193 | cp += 3; | ||
194 | if (cp[0] != 'c' || !isdigit(cp[1]) | ||
195 | || cp[2] != 'd' || !isdigit(cp[3])) | ||
196 | goto errout; | ||
197 | cp[4] = 0; | ||
198 | return str; | ||
199 | } | ||
200 | |||
201 | /* Now let's handle /dev/hd* and /dev/sd* devices.... */ | ||
202 | if ((cp[0] == 'h' || cp[0] == 's') && cp[1] == 'd') { | ||
203 | cp += 2; | ||
204 | /* If there's a single number after /dev/hd, skip it */ | ||
205 | if (isdigit(*cp)) | ||
206 | cp++; | ||
207 | /* What follows must be an alpha char, or give up */ | ||
208 | if (!isalpha(*cp)) | ||
209 | goto errout; | ||
210 | cp[1] = 0; | ||
211 | return str; | ||
212 | } | ||
213 | |||
214 | #if ENABLE_FEATURE_DEVFS | ||
215 | /* Now let's handle devfs (ugh) names */ | ||
216 | len = 0; | ||
217 | if (strncmp(cp, "ide/", 4) == 0) | ||
218 | len = 4; | ||
219 | if (strncmp(cp, "scsi/", 5) == 0) | ||
220 | len = 5; | ||
221 | if (len) { | ||
222 | cp += len; | ||
223 | /* | ||
224 | * Now we proceed down the expected devfs hierarchy. | ||
225 | * i.e., .../host1/bus2/target3/lun4/... | ||
226 | * If we don't find the expected token, followed by | ||
227 | * some number of digits at each level, abort. | ||
228 | */ | ||
229 | for (hier = devfs_hier; *hier; hier++) { | ||
230 | len = strlen(*hier); | ||
231 | if (strncmp(cp, *hier, len) != 0) | ||
232 | goto errout; | ||
233 | cp += len; | ||
234 | while (*cp != '/' && *cp != 0) { | ||
235 | if (!isdigit(*cp)) | ||
236 | goto errout; | ||
237 | cp++; | ||
238 | } | ||
239 | cp++; | ||
240 | } | ||
241 | cp[-1] = 0; | ||
242 | return str; | ||
243 | } | ||
244 | |||
245 | /* Now handle devfs /dev/disc or /dev/disk names */ | ||
246 | disk = 0; | ||
247 | if (strncmp(cp, "discs/", 6) == 0) | ||
248 | disk = "disc"; | ||
249 | else if (strncmp(cp, "disks/", 6) == 0) | ||
250 | disk = "disk"; | ||
251 | if (disk) { | ||
252 | cp += 6; | ||
253 | if (strncmp(cp, disk, 4) != 0) | ||
254 | goto errout; | ||
255 | cp += 4; | ||
256 | while (*cp != '/' && *cp != 0) { | ||
257 | if (!isdigit(*cp)) | ||
258 | goto errout; | ||
259 | cp++; | ||
260 | } | ||
261 | *cp = 0; | ||
262 | return str; | ||
263 | } | ||
264 | #endif | ||
265 | errout: | ||
266 | free(str); | ||
267 | return NULL; | ||
268 | } | ||
269 | |||
270 | static void free_instance(struct fsck_instance *p) | ||
271 | { | ||
272 | free(p->prog); | ||
273 | free(p->device); | ||
274 | free(p->base_device); | ||
275 | free(p); | ||
276 | } | ||
277 | |||
278 | static struct fs_info *create_fs_device(const char *device, const char *mntpnt, | ||
279 | const char *type, const char *opts, | ||
280 | int freq, int passno) | ||
281 | { | ||
282 | struct fs_info *fs; | ||
283 | |||
284 | fs = xzalloc(sizeof(*fs)); | ||
285 | fs->device = xstrdup(device); | ||
286 | fs->mountpt = xstrdup(mntpnt); | ||
287 | fs->type = xstrdup(type); | ||
288 | fs->opts = xstrdup(opts ? opts : ""); | ||
289 | fs->freq = freq; | ||
290 | fs->passno = passno; | ||
291 | /*fs->flags = 0; */ | ||
292 | /*fs->next = NULL; */ | ||
293 | |||
294 | if (!filesys_info) | ||
295 | filesys_info = fs; | ||
296 | else | ||
297 | filesys_last->next = fs; | ||
298 | filesys_last = fs; | ||
299 | |||
300 | return fs; | ||
301 | } | ||
302 | |||
303 | static void strip_line(char *line) | ||
304 | { | ||
305 | char *p = line + strlen(line) - 1; | ||
306 | |||
307 | while (*line) { | ||
308 | if (*p != '\n' && *p != '\r') | ||
309 | break; | ||
310 | *p-- = '\0'; | ||
311 | } | ||
312 | } | ||
313 | |||
314 | static char *parse_word(char **buf) | ||
315 | { | ||
316 | char *word, *next; | ||
317 | |||
318 | word = *buf; | ||
319 | if (*word == '\0') | ||
320 | return NULL; | ||
321 | |||
322 | word = skip_whitespace(word); | ||
323 | next = skip_non_whitespace(word); | ||
324 | if (*next) | ||
325 | *next++ = '\0'; | ||
326 | *buf = next; | ||
327 | return word; | ||
328 | } | ||
329 | |||
330 | static void parse_escape(char *word) | ||
331 | { | ||
332 | char *q, c; | ||
333 | const char *p; | ||
334 | |||
335 | if (!word) | ||
336 | return; | ||
337 | |||
338 | for (p = q = word; *p; q++) { | ||
339 | c = *p++; | ||
340 | if (c != '\\') { | ||
341 | *q = c; | ||
342 | } else { | ||
343 | *q = bb_process_escape_sequence(&p); | ||
344 | } | ||
345 | } | ||
346 | *q = '\0'; | ||
347 | } | ||
348 | |||
349 | static int parse_fstab_line(char *line, struct fs_info **ret_fs) | ||
350 | { | ||
351 | char *device, *mntpnt, *type, *opts, *freq, *passno, *cp; | ||
352 | struct fs_info *fs; | ||
353 | |||
354 | *ret_fs = 0; | ||
355 | strip_line(line); | ||
356 | cp = strchr(line, '#'); | ||
357 | if (cp) | ||
358 | *cp = '\0'; /* Ignore everything after the comment char */ | ||
359 | cp = line; | ||
360 | |||
361 | device = parse_word(&cp); | ||
362 | if (!device) return 0; /* Allow blank lines */ | ||
363 | mntpnt = parse_word(&cp); | ||
364 | type = parse_word(&cp); | ||
365 | opts = parse_word(&cp); | ||
366 | freq = parse_word(&cp); | ||
367 | passno = parse_word(&cp); | ||
368 | |||
369 | if (!mntpnt || !type) | ||
370 | return -1; | ||
371 | |||
372 | parse_escape(device); | ||
373 | parse_escape(mntpnt); | ||
374 | parse_escape(type); | ||
375 | parse_escape(opts); | ||
376 | parse_escape(freq); | ||
377 | parse_escape(passno); | ||
378 | |||
379 | if (strchr(type, ',')) | ||
380 | type = NULL; | ||
381 | |||
382 | fs = create_fs_device(device, mntpnt, type ? type : "auto", opts, | ||
383 | freq ? atoi(freq) : -1, | ||
384 | passno ? atoi(passno) : -1); | ||
385 | *ret_fs = fs; | ||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | /* Load the filesystem database from /etc/fstab */ | ||
390 | static void load_fs_info(const char *filename) | ||
391 | { | ||
392 | FILE *f; | ||
393 | int lineno = 0; | ||
394 | int old_fstab = 1; | ||
395 | struct fs_info *fs; | ||
396 | |||
397 | f = fopen_or_warn(filename, "r"); | ||
398 | if (f == NULL) { | ||
399 | /*bb_perror_msg("WARNING: cannot open %s", filename);*/ | ||
400 | return; | ||
401 | } | ||
402 | while (1) { | ||
403 | int r; | ||
404 | char *buf = xmalloc_getline(f); | ||
405 | if (!buf) break; | ||
406 | r = parse_fstab_line(buf, &fs); | ||
407 | free(buf); | ||
408 | lineno++; | ||
409 | if (r < 0) { | ||
410 | bb_error_msg("WARNING: bad format " | ||
411 | "on line %d of %s", lineno, filename); | ||
412 | continue; | ||
413 | } | ||
414 | if (!fs) | ||
415 | continue; | ||
416 | if (fs->passno < 0) | ||
417 | fs->passno = 0; | ||
418 | else | ||
419 | old_fstab = 0; | ||
420 | } | ||
421 | fclose(f); | ||
422 | |||
423 | if (old_fstab) { | ||
424 | fputs("\007" | ||
425 | "WARNING: Your /etc/fstab does not contain the fsck passno field.\n" | ||
426 | "I will kludge around things for you, but you should fix\n" | ||
427 | "your /etc/fstab file as soon as you can.\n\n", stderr); | ||
428 | for (fs = filesys_info; fs; fs = fs->next) { | ||
429 | fs->passno = 1; | ||
430 | } | ||
431 | } | ||
432 | } | ||
433 | |||
434 | /* Lookup filesys in /etc/fstab and return the corresponding entry. */ | ||
435 | static struct fs_info *lookup(char *filesys) | ||
436 | { | ||
437 | struct fs_info *fs; | ||
438 | |||
439 | for (fs = filesys_info; fs; fs = fs->next) { | ||
440 | if (strcmp(filesys, fs->device) == 0 | ||
441 | || (fs->mountpt && strcmp(filesys, fs->mountpt) == 0) | ||
442 | ) | ||
443 | break; | ||
444 | } | ||
445 | |||
446 | return fs; | ||
447 | } | ||
448 | |||
449 | #if DO_PROGRESS_INDICATOR | ||
450 | static int progress_active(void) | ||
451 | { | ||
452 | struct fsck_instance *inst; | ||
453 | |||
454 | for (inst = instance_list; inst; inst = inst->next) { | ||
455 | if (inst->flags & FLAG_DONE) | ||
456 | continue; | ||
457 | if (inst->flags & FLAG_PROGRESS) | ||
458 | return 1; | ||
459 | } | ||
460 | return 0; | ||
461 | } | ||
462 | #endif | ||
463 | |||
464 | |||
465 | /* | ||
466 | * Send a signal to all outstanding fsck child processes | ||
467 | */ | ||
468 | static void kill_all_if_cancel_requested(void) | ||
469 | { | ||
470 | static smallint kill_sent; | ||
471 | |||
472 | struct fsck_instance *inst; | ||
473 | |||
474 | if (!cancel_requested || kill_sent) | ||
475 | return; | ||
476 | |||
477 | for (inst = instance_list; inst; inst = inst->next) { | ||
478 | if (inst->flags & FLAG_DONE) | ||
479 | continue; | ||
480 | kill(inst->pid, SIGTERM); | ||
481 | } | ||
482 | kill_sent = 1; | ||
483 | } | ||
484 | |||
485 | /* | ||
486 | * Wait for one child process to exit; when it does, unlink it from | ||
487 | * the list of executing child processes, and return it. | ||
488 | */ | ||
489 | static struct fsck_instance *wait_one(int flags) | ||
490 | { | ||
491 | int status; | ||
492 | int sig; | ||
493 | struct fsck_instance *inst, *prev; | ||
494 | pid_t pid; | ||
495 | |||
496 | if (!instance_list) | ||
497 | return NULL; | ||
498 | |||
499 | if (noexecute) { | ||
500 | inst = instance_list; | ||
501 | prev = NULL; | ||
502 | #ifdef RANDOM_DEBUG | ||
503 | while (inst->next && (random() & 1)) { | ||
504 | prev = inst; | ||
505 | inst = inst->next; | ||
506 | } | ||
507 | #endif | ||
508 | inst->exit_status = 0; | ||
509 | goto ret_inst; | ||
510 | } | ||
511 | |||
512 | /* | ||
513 | * gcc -Wall fails saving throw against stupidity | ||
514 | * (inst and prev are thought to be uninitialized variables) | ||
515 | */ | ||
516 | inst = prev = NULL; | ||
517 | |||
518 | do { | ||
519 | pid = waitpid(-1, &status, flags); | ||
520 | kill_all_if_cancel_requested(); | ||
521 | if (pid == 0 && (flags & WNOHANG)) | ||
522 | return NULL; | ||
523 | if (pid < 0) { | ||
524 | if (errno == EINTR || errno == EAGAIN) | ||
525 | continue; | ||
526 | if (errno == ECHILD) { | ||
527 | bb_error_msg("wait: no more child process?!?"); | ||
528 | return NULL; | ||
529 | } | ||
530 | bb_perror_msg("wait"); | ||
531 | continue; | ||
532 | } | ||
533 | prev = NULL; | ||
534 | inst = instance_list; | ||
535 | while (inst) { | ||
536 | if (inst->pid == pid) | ||
537 | break; | ||
538 | prev = inst; | ||
539 | inst = inst->next; | ||
540 | } | ||
541 | } while (!inst); | ||
542 | |||
543 | if (WIFEXITED(status)) | ||
544 | status = WEXITSTATUS(status); | ||
545 | else if (WIFSIGNALED(status)) { | ||
546 | sig = WTERMSIG(status); | ||
547 | status = EXIT_UNCORRECTED; | ||
548 | if (sig != SIGINT) { | ||
549 | printf("Warning... %s %s exited " | ||
550 | "with signal %d\n", | ||
551 | inst->prog, inst->device, sig); | ||
552 | status = EXIT_ERROR; | ||
553 | } | ||
554 | } else { | ||
555 | printf("%s %s: status is %x, should never happen\n", | ||
556 | inst->prog, inst->device, status); | ||
557 | status = EXIT_ERROR; | ||
558 | } | ||
559 | inst->exit_status = status; | ||
560 | |||
561 | #if DO_PROGRESS_INDICATOR | ||
562 | if (progress && (inst->flags & FLAG_PROGRESS) && !progress_active()) { | ||
563 | struct fsck_instance *inst2; | ||
564 | for (inst2 = instance_list; inst2; inst2 = inst2->next) { | ||
565 | if (inst2->flags & FLAG_DONE) | ||
566 | continue; | ||
567 | if (strcmp(inst2->type, "ext2") != 0 | ||
568 | && strcmp(inst2->type, "ext3") != 0 | ||
569 | ) { | ||
570 | continue; | ||
571 | } | ||
572 | /* ext[23], we will send USR1 | ||
573 | * (request to start displaying progress bar) | ||
574 | * | ||
575 | * If we've just started the fsck, wait a tiny | ||
576 | * bit before sending the kill, to give it | ||
577 | * time to set up the signal handler | ||
578 | */ | ||
579 | if (inst2->start_time >= time(NULL) - 1) | ||
580 | sleep(1); | ||
581 | kill(inst2->pid, SIGUSR1); | ||
582 | inst2->flags |= FLAG_PROGRESS; | ||
583 | break; | ||
584 | } | ||
585 | } | ||
586 | #endif | ||
587 | |||
588 | ret_inst: | ||
589 | if (prev) | ||
590 | prev->next = inst->next; | ||
591 | else | ||
592 | instance_list = inst->next; | ||
593 | if (verbose > 1) | ||
594 | printf("Finished with %s (exit status %d)\n", | ||
595 | inst->device, inst->exit_status); | ||
596 | num_running--; | ||
597 | return inst; | ||
598 | } | ||
599 | |||
600 | #define FLAG_WAIT_ALL 0 | ||
601 | #define FLAG_WAIT_ATLEAST_ONE 1 | ||
602 | /* | ||
603 | * Wait until all executing child processes have exited; return the | ||
604 | * logical OR of all of their exit code values. | ||
605 | */ | ||
606 | static int wait_many(int flags) | ||
607 | { | ||
608 | struct fsck_instance *inst; | ||
609 | int global_status = 0; | ||
610 | int wait_flags = 0; | ||
611 | |||
612 | while ((inst = wait_one(wait_flags))) { | ||
613 | global_status |= inst->exit_status; | ||
614 | free_instance(inst); | ||
615 | #ifdef RANDOM_DEBUG | ||
616 | if (noexecute && (flags & WNOHANG) && !(random() % 3)) | ||
617 | break; | ||
618 | #endif | ||
619 | if (flags & FLAG_WAIT_ATLEAST_ONE) | ||
620 | wait_flags = WNOHANG; | ||
621 | } | ||
622 | return global_status; | ||
623 | } | ||
624 | |||
625 | /* | ||
626 | * Execute a particular fsck program, and link it into the list of | ||
627 | * child processes we are waiting for. | ||
628 | */ | ||
629 | static void execute(const char *type, const char *device, const char *mntpt, | ||
630 | int interactive) | ||
631 | { | ||
632 | char *argv[num_args + 4]; /* see count below: */ | ||
633 | int argc; | ||
634 | int i; | ||
635 | struct fsck_instance *inst; | ||
636 | pid_t pid; | ||
637 | |||
638 | inst = xzalloc(sizeof(*inst)); | ||
639 | |||
640 | argv[0] = xasprintf("fsck.%s", type); /* 1 */ | ||
641 | for (i = 0; i < num_args; i++) | ||
642 | argv[i+1] = args[i]; /* num_args */ | ||
643 | argc = num_args + 1; | ||
644 | |||
645 | #if DO_PROGRESS_INDICATOR | ||
646 | if (progress && !progress_active()) { | ||
647 | if (strcmp(type, "ext2") == 0 | ||
648 | || strcmp(type, "ext3") == 0 | ||
649 | ) { | ||
650 | argv[argc++] = xasprintf("-C%d", progress_fd); /* 1 */ | ||
651 | inst->flags |= FLAG_PROGRESS; | ||
652 | } | ||
653 | } | ||
654 | #endif | ||
655 | |||
656 | argv[argc++] = xstrdup(device); /* 1 */ | ||
657 | argv[argc] = NULL; /* 1 */ | ||
658 | |||
659 | if (verbose || noexecute) { | ||
660 | printf("[%s (%d) -- %s]", argv[0], num_running, | ||
661 | mntpt ? mntpt : device); | ||
662 | for (i = 0; i < argc; i++) | ||
663 | printf(" %s", argv[i]); | ||
664 | puts(""); | ||
665 | } | ||
666 | |||
667 | /* Fork and execute the correct program. */ | ||
668 | pid = -1; | ||
669 | if (!noexecute) { | ||
670 | pid = fork(); /* TODO: NOMMU friendly way (vfork)? */ | ||
671 | if (pid < 0) | ||
672 | bb_perror_msg_and_die("fork"); | ||
673 | if (pid == 0) { | ||
674 | /* Child */ | ||
675 | if (!interactive) { | ||
676 | /* NB: e2fsck will complain because of this! | ||
677 | * Use "fsck -s" to avoid... */ | ||
678 | close(0); | ||
679 | } | ||
680 | execvp(argv[0], argv); | ||
681 | bb_perror_msg_and_die("%s", argv[0]); | ||
682 | } | ||
683 | } | ||
684 | |||
685 | for (i = num_args+1; i < argc; i++) | ||
686 | free(argv[i]); | ||
687 | |||
688 | inst->pid = pid; | ||
689 | inst->prog = argv[0]; | ||
690 | inst->type = xstrdup(type); | ||
691 | inst->device = xstrdup(device); | ||
692 | inst->base_device = base_device(device); | ||
693 | inst->start_time = time(NULL); | ||
694 | |||
695 | /* Add to the list of running fsck's. | ||
696 | * (was adding to the end, but adding to the front is simpler...) */ | ||
697 | inst->next = instance_list; | ||
698 | instance_list = inst; | ||
699 | } | ||
700 | |||
701 | /* | ||
702 | * Run the fsck program on a particular device | ||
703 | * | ||
704 | * If the type is specified using -t, and it isn't prefixed with "no" | ||
705 | * (as in "noext2") and only one filesystem type is specified, then | ||
706 | * use that type regardless of what is specified in /etc/fstab. | ||
707 | * | ||
708 | * If the type isn't specified by the user, then use either the type | ||
709 | * specified in /etc/fstab, or "auto". | ||
710 | */ | ||
711 | static void fsck_device(struct fs_info *fs, int interactive) | ||
712 | { | ||
713 | const char *type; | ||
714 | |||
715 | if (strcmp(fs->type, "auto") != 0) { | ||
716 | type = fs->type; | ||
717 | if (verbose > 2) | ||
718 | bb_info_msg("using filesystem type '%s' %s", | ||
719 | type, "from fstab"); | ||
720 | } else if (fstype | ||
721 | && (fstype[0] != 'n' || fstype[1] != 'o') /* != "no" */ | ||
722 | && strncmp(fstype, "opts=", 5) != 0 | ||
723 | && strncmp(fstype, "loop", 4) != 0 | ||
724 | && !strchr(fstype, ',') | ||
725 | ) { | ||
726 | type = fstype; | ||
727 | if (verbose > 2) | ||
728 | bb_info_msg("using filesystem type '%s' %s", | ||
729 | type, "from -t"); | ||
730 | } else { | ||
731 | type = "auto"; | ||
732 | if (verbose > 2) | ||
733 | bb_info_msg("using filesystem type '%s' %s", | ||
734 | type, "(default)"); | ||
735 | } | ||
736 | |||
737 | num_running++; | ||
738 | execute(type, fs->device, fs->mountpt, interactive); | ||
739 | } | ||
740 | |||
741 | /* | ||
742 | * Returns TRUE if a partition on the same disk is already being | ||
743 | * checked. | ||
744 | */ | ||
745 | static int device_already_active(char *device) | ||
746 | { | ||
747 | struct fsck_instance *inst; | ||
748 | char *base; | ||
749 | |||
750 | if (force_all_parallel) | ||
751 | return 0; | ||
752 | |||
753 | #ifdef BASE_MD | ||
754 | /* Don't check a soft raid disk with any other disk */ | ||
755 | if (instance_list | ||
756 | && (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) | ||
757 | || !strncmp(device, BASE_MD, sizeof(BASE_MD)-1)) | ||
758 | ) { | ||
759 | return 1; | ||
760 | } | ||
761 | #endif | ||
762 | |||
763 | base = base_device(device); | ||
764 | /* | ||
765 | * If we don't know the base device, assume that the device is | ||
766 | * already active if there are any fsck instances running. | ||
767 | */ | ||
768 | if (!base) | ||
769 | return (instance_list != NULL); | ||
770 | |||
771 | for (inst = instance_list; inst; inst = inst->next) { | ||
772 | if (!inst->base_device || !strcmp(base, inst->base_device)) { | ||
773 | free(base); | ||
774 | return 1; | ||
775 | } | ||
776 | } | ||
777 | |||
778 | free(base); | ||
779 | return 0; | ||
780 | } | ||
781 | |||
782 | /* | ||
783 | * This function returns true if a particular option appears in a | ||
784 | * comma-delimited options list | ||
785 | */ | ||
786 | static int opt_in_list(char *opt, char *optlist) | ||
787 | { | ||
788 | char *s; | ||
789 | int len; | ||
790 | |||
791 | if (!optlist) | ||
792 | return 0; | ||
793 | |||
794 | len = strlen(opt); | ||
795 | s = optlist - 1; | ||
796 | while (1) { | ||
797 | s = strstr(s + 1, opt); | ||
798 | if (!s) | ||
799 | return 0; | ||
800 | /* neither "opt.." nor "xxx,opt.."? */ | ||
801 | if (s != optlist && s[-1] != ',') | ||
802 | continue; | ||
803 | /* neither "..opt" nor "..opt,xxx"? */ | ||
804 | if (s[len] != '\0' && s[len] != ',') | ||
805 | continue; | ||
806 | return 1; | ||
807 | } | ||
808 | } | ||
809 | |||
810 | /* See if the filesystem matches the criteria given by the -t option */ | ||
811 | static int fs_match(struct fs_info *fs) | ||
812 | { | ||
813 | int n, ret, checked_type; | ||
814 | char *cp; | ||
815 | |||
816 | if (!fs_type_list) | ||
817 | return 1; | ||
818 | |||
819 | ret = 0; | ||
820 | checked_type = 0; | ||
821 | n = 0; | ||
822 | while (1) { | ||
823 | cp = fs_type_list[n]; | ||
824 | if (!cp) | ||
825 | break; | ||
826 | switch (fs_type_flag[n]) { | ||
827 | case FS_TYPE_FLAG_NORMAL: | ||
828 | checked_type++; | ||
829 | if (strcmp(cp, fs->type) == 0) | ||
830 | ret = 1; | ||
831 | break; | ||
832 | case FS_TYPE_FLAG_NEGOPT: | ||
833 | if (opt_in_list(cp, fs->opts)) | ||
834 | return 0; | ||
835 | break; | ||
836 | case FS_TYPE_FLAG_OPT: | ||
837 | if (!opt_in_list(cp, fs->opts)) | ||
838 | return 0; | ||
839 | break; | ||
840 | } | ||
841 | n++; | ||
842 | } | ||
843 | if (checked_type == 0) | ||
844 | return 1; | ||
845 | |||
846 | return (fs_type_negated ? !ret : ret); | ||
847 | } | ||
848 | |||
849 | /* Check if we should ignore this filesystem. */ | ||
850 | static int ignore(struct fs_info *fs) | ||
851 | { | ||
852 | /* | ||
853 | * If the pass number is 0, ignore it. | ||
854 | */ | ||
855 | if (fs->passno == 0) | ||
856 | return 1; | ||
857 | |||
858 | /* | ||
859 | * If a specific fstype is specified, and it doesn't match, | ||
860 | * ignore it. | ||
861 | */ | ||
862 | if (!fs_match(fs)) | ||
863 | return 1; | ||
864 | |||
865 | /* Are we ignoring this type? */ | ||
866 | if (index_in_str_array(ignored_types, fs->type) >= 0) | ||
867 | return 1; | ||
868 | |||
869 | /* We can and want to check this file system type. */ | ||
870 | return 0; | ||
871 | } | ||
872 | |||
873 | /* Check all file systems, using the /etc/fstab table. */ | ||
874 | static int check_all(void) | ||
875 | { | ||
876 | struct fs_info *fs; | ||
877 | int status = EXIT_OK; | ||
878 | smallint not_done_yet; | ||
879 | smallint pass_done; | ||
880 | int passno; | ||
881 | |||
882 | if (verbose) | ||
883 | puts("Checking all filesystems"); | ||
884 | |||
885 | /* | ||
886 | * Do an initial scan over the filesystem; mark filesystems | ||
887 | * which should be ignored as done, and resolve any "auto" | ||
888 | * filesystem types (done as a side-effect of calling ignore()). | ||
889 | */ | ||
890 | for (fs = filesys_info; fs; fs = fs->next) { | ||
891 | if (ignore(fs)) | ||
892 | fs->flags |= FLAG_DONE; | ||
893 | } | ||
894 | |||
895 | /* | ||
896 | * Find and check the root filesystem. | ||
897 | */ | ||
898 | if (!parallel_root) { | ||
899 | for (fs = filesys_info; fs; fs = fs->next) { | ||
900 | if (LONE_CHAR(fs->mountpt, '/')) | ||
901 | break; | ||
902 | } | ||
903 | if (fs) { | ||
904 | if (!skip_root && !ignore(fs)) { | ||
905 | fsck_device(fs, 1); | ||
906 | status |= wait_many(FLAG_WAIT_ALL); | ||
907 | if (status > EXIT_NONDESTRUCT) | ||
908 | return status; | ||
909 | } | ||
910 | fs->flags |= FLAG_DONE; | ||
911 | } | ||
912 | } | ||
913 | /* | ||
914 | * This is for the bone-headed user who enters the root | ||
915 | * filesystem twice. Skip root will skip all root entries. | ||
916 | */ | ||
917 | if (skip_root) | ||
918 | for (fs = filesys_info; fs; fs = fs->next) | ||
919 | if (LONE_CHAR(fs->mountpt, '/')) | ||
920 | fs->flags |= FLAG_DONE; | ||
921 | |||
922 | not_done_yet = 1; | ||
923 | passno = 1; | ||
924 | while (not_done_yet) { | ||
925 | not_done_yet = 0; | ||
926 | pass_done = 1; | ||
927 | |||
928 | for (fs = filesys_info; fs; fs = fs->next) { | ||
929 | if (cancel_requested) | ||
930 | break; | ||
931 | if (fs->flags & FLAG_DONE) | ||
932 | continue; | ||
933 | /* | ||
934 | * If the filesystem's pass number is higher | ||
935 | * than the current pass number, then we don't | ||
936 | * do it yet. | ||
937 | */ | ||
938 | if (fs->passno > passno) { | ||
939 | not_done_yet = 1; | ||
940 | continue; | ||
941 | } | ||
942 | /* | ||
943 | * If a filesystem on a particular device has | ||
944 | * already been spawned, then we need to defer | ||
945 | * this to another pass. | ||
946 | */ | ||
947 | if (device_already_active(fs->device)) { | ||
948 | pass_done = 0; | ||
949 | continue; | ||
950 | } | ||
951 | /* | ||
952 | * Spawn off the fsck process | ||
953 | */ | ||
954 | fsck_device(fs, serialize); | ||
955 | fs->flags |= FLAG_DONE; | ||
956 | |||
957 | /* | ||
958 | * Only do one filesystem at a time, or if we | ||
959 | * have a limit on the number of fsck's extant | ||
960 | * at one time, apply that limit. | ||
961 | */ | ||
962 | if (serialize | ||
963 | || (max_running && (num_running >= max_running)) | ||
964 | ) { | ||
965 | pass_done = 0; | ||
966 | break; | ||
967 | } | ||
968 | } | ||
969 | if (cancel_requested) | ||
970 | break; | ||
971 | if (verbose > 1) | ||
972 | printf("--waiting-- (pass %d)\n", passno); | ||
973 | status |= wait_many(pass_done ? FLAG_WAIT_ALL : | ||
974 | FLAG_WAIT_ATLEAST_ONE); | ||
975 | if (pass_done) { | ||
976 | if (verbose > 1) | ||
977 | puts("----------------------------------"); | ||
978 | passno++; | ||
979 | } else | ||
980 | not_done_yet = 1; | ||
981 | } | ||
982 | kill_all_if_cancel_requested(); | ||
983 | status |= wait_many(FLAG_WAIT_ATLEAST_ONE); | ||
984 | return status; | ||
985 | } | ||
986 | |||
987 | /* | ||
988 | * Deal with the fsck -t argument. | ||
989 | * Huh, for mount "-t novfat,nfs" means "neither vfat nor nfs"! | ||
990 | * Why here we require "-t novfat,nonfs" ?? | ||
991 | */ | ||
992 | static void compile_fs_type(char *fs_type) | ||
993 | { | ||
994 | char *s; | ||
995 | int num = 2; | ||
996 | smallint negate; | ||
997 | |||
998 | if (fs_type) { | ||
999 | s = fs_type; | ||
1000 | while ((s = strchr(s, ','))) { | ||
1001 | num++; | ||
1002 | s++; | ||
1003 | } | ||
1004 | } | ||
1005 | |||
1006 | fs_type_list = xzalloc(num * sizeof(fs_type_list[0])); | ||
1007 | fs_type_flag = xzalloc(num * sizeof(fs_type_flag[0])); | ||
1008 | fs_type_negated = -1; /* not yet known is it negated or not */ | ||
1009 | |||
1010 | if (!fs_type) | ||
1011 | return; | ||
1012 | |||
1013 | num = 0; | ||
1014 | s = fs_type; | ||
1015 | while (1) { | ||
1016 | char *comma; | ||
1017 | |||
1018 | negate = 0; | ||
1019 | if (s[0] == 'n' && s[1] == 'o') { /* "no.." */ | ||
1020 | s += 2; | ||
1021 | negate = 1; | ||
1022 | } else if (s[0] == '!') { | ||
1023 | s++; | ||
1024 | negate = 1; | ||
1025 | } | ||
1026 | |||
1027 | if (strcmp(s, "loop") == 0) | ||
1028 | /* loop is really short-hand for opts=loop */ | ||
1029 | goto loop_special_case; | ||
1030 | if (strncmp(s, "opts=", 5) == 0) { | ||
1031 | s += 5; | ||
1032 | loop_special_case: | ||
1033 | fs_type_flag[num] = negate ? FS_TYPE_FLAG_NEGOPT : FS_TYPE_FLAG_OPT; | ||
1034 | } else { | ||
1035 | if (fs_type_negated == -1) | ||
1036 | fs_type_negated = negate; | ||
1037 | if (fs_type_negated != negate) | ||
1038 | bb_error_msg_and_die( | ||
1039 | "either all or none of the filesystem types passed to -t must be prefixed " | ||
1040 | "with 'no' or '!'"); | ||
1041 | } | ||
1042 | comma = strchr(s, ','); | ||
1043 | fs_type_list[num++] = comma ? xstrndup(s, comma-s) : xstrdup(s); | ||
1044 | if (!comma) | ||
1045 | break; | ||
1046 | s = comma + 1; | ||
1047 | } | ||
1048 | } | ||
1049 | |||
1050 | static void parse_args(int argc, char *argv[]) | ||
1051 | { | ||
1052 | int i, j; | ||
1053 | char *arg, *tmp; | ||
1054 | char *options = NULL; | ||
1055 | int optpos = 0; | ||
1056 | int opts_for_fsck = 0; | ||
1057 | |||
1058 | /* in bss, so already zeroed | ||
1059 | num_devices = 0; | ||
1060 | num_args = 0; | ||
1061 | instance_list = NULL; | ||
1062 | */ | ||
1063 | |||
1064 | /* TODO: getopt32 */ | ||
1065 | for (i = 1; i < argc; i++) { | ||
1066 | arg = argv[i]; | ||
1067 | |||
1068 | /* "/dev/blk" or "/path" or "UUID=xxx" or "LABEL=xxx" */ | ||
1069 | if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) { | ||
1070 | // FIXME: must check that arg is a blkdev, or resolve | ||
1071 | // "/path", "UUID=xxx" or "LABEL=xxx" into block device name | ||
1072 | // ("UUID=xxx"/"LABEL=xxx" can probably shifted to fsck.auto duties) | ||
1073 | devices = xrealloc(devices, (num_devices+1) * sizeof(devices[0])); | ||
1074 | devices[num_devices++] = xstrdup(arg); | ||
1075 | continue; | ||
1076 | } | ||
1077 | |||
1078 | if (arg[0] != '-' || opts_for_fsck) { | ||
1079 | args = xrealloc(args, (num_args+1) * sizeof(args[0])); | ||
1080 | args[num_args++] = xstrdup(arg); | ||
1081 | continue; | ||
1082 | } | ||
1083 | |||
1084 | for (j = 1; arg[j]; j++) { | ||
1085 | if (opts_for_fsck) { | ||
1086 | optpos++; | ||
1087 | /* one extra for '\0' */ | ||
1088 | options = xrealloc(options, optpos + 2); | ||
1089 | options[optpos] = arg[j]; | ||
1090 | continue; | ||
1091 | } | ||
1092 | switch (arg[j]) { | ||
1093 | case 'A': | ||
1094 | doall = 1; | ||
1095 | break; | ||
1096 | #if DO_PROGRESS_INDICATOR | ||
1097 | case 'C': | ||
1098 | progress = 1; | ||
1099 | if (arg[++j]) { /* -Cn */ | ||
1100 | progress_fd = xatoi_u(&arg[j]); | ||
1101 | goto next_arg; | ||
1102 | } | ||
1103 | /* -C n */ | ||
1104 | progress_fd = xatoi_u(argv[++i]); | ||
1105 | goto next_arg; | ||
1106 | #endif | ||
1107 | case 'V': | ||
1108 | verbose++; | ||
1109 | break; | ||
1110 | case 'N': | ||
1111 | noexecute = 1; | ||
1112 | break; | ||
1113 | case 'R': | ||
1114 | skip_root = 1; | ||
1115 | break; | ||
1116 | case 'T': | ||
1117 | notitle = 1; | ||
1118 | break; | ||
1119 | /* case 'M': | ||
1120 | like_mount = 1; | ||
1121 | break; */ | ||
1122 | case 'P': | ||
1123 | parallel_root = 1; | ||
1124 | break; | ||
1125 | case 's': | ||
1126 | serialize = 1; | ||
1127 | break; | ||
1128 | case 't': | ||
1129 | if (fstype) | ||
1130 | bb_show_usage(); | ||
1131 | if (arg[++j]) | ||
1132 | tmp = &arg[j]; | ||
1133 | else if (++i < argc) | ||
1134 | tmp = argv[i]; | ||
1135 | else | ||
1136 | bb_show_usage(); | ||
1137 | fstype = xstrdup(tmp); | ||
1138 | compile_fs_type(fstype); | ||
1139 | goto next_arg; | ||
1140 | case '-': | ||
1141 | opts_for_fsck++; | ||
1142 | break; | ||
1143 | case '?': | ||
1144 | bb_show_usage(); | ||
1145 | break; | ||
1146 | default: | ||
1147 | optpos++; | ||
1148 | /* one extra for '\0' */ | ||
1149 | options = xrealloc(options, optpos + 2); | ||
1150 | options[optpos] = arg[j]; | ||
1151 | break; | ||
1152 | } | ||
1153 | } | ||
1154 | next_arg: | ||
1155 | if (optpos) { | ||
1156 | options[0] = '-'; | ||
1157 | options[optpos + 1] = '\0'; | ||
1158 | args = xrealloc(args, (num_args+1) * sizeof(args[0])); | ||
1159 | args[num_args++] = options; | ||
1160 | optpos = 0; | ||
1161 | options = NULL; | ||
1162 | } | ||
1163 | } | ||
1164 | if (getenv("FSCK_FORCE_ALL_PARALLEL")) | ||
1165 | force_all_parallel = 1; | ||
1166 | tmp = getenv("FSCK_MAX_INST"); | ||
1167 | if (tmp) | ||
1168 | max_running = xatoi(tmp); | ||
1169 | } | ||
1170 | |||
1171 | static void signal_cancel(int sig ATTRIBUTE_UNUSED) | ||
1172 | { | ||
1173 | cancel_requested = 1; | ||
1174 | } | ||
1175 | |||
1176 | int fsck_main(int argc, char *argv[]) | ||
1177 | { | ||
1178 | int i, status = 0; | ||
1179 | int interactive; | ||
1180 | const char *fstab; | ||
1181 | struct fs_info *fs; | ||
1182 | struct sigaction sa; | ||
1183 | |||
1184 | memset(&sa, 0, sizeof(sa)); | ||
1185 | sa.sa_handler = signal_cancel; | ||
1186 | sigaction(SIGINT, &sa, 0); | ||
1187 | sigaction(SIGTERM, &sa, 0); | ||
1188 | |||
1189 | setbuf(stdout, NULL); | ||
1190 | |||
1191 | parse_args(argc, argv); | ||
1192 | |||
1193 | if (!notitle) | ||
1194 | puts("fsck (busybox "BB_VER", "BB_BT")"); | ||
1195 | |||
1196 | /* Even plain "fsck /dev/hda1" needs fstab to get fs type, | ||
1197 | * so we are scanning it anyway */ | ||
1198 | fstab = getenv("FSTAB_FILE"); | ||
1199 | if (!fstab) | ||
1200 | fstab = "/etc/fstab"; | ||
1201 | load_fs_info(fstab); | ||
1202 | |||
1203 | interactive = (num_devices == 1) | serialize; | ||
1204 | |||
1205 | /* If -A was specified ("check all"), do that! */ | ||
1206 | if (doall) | ||
1207 | return check_all(); | ||
1208 | |||
1209 | if (num_devices == 0) { | ||
1210 | serialize = 1; | ||
1211 | interactive = 1; | ||
1212 | return check_all(); | ||
1213 | } | ||
1214 | |||
1215 | for (i = 0; i < num_devices; i++) { | ||
1216 | if (cancel_requested) { | ||
1217 | kill_all_if_cancel_requested(); | ||
1218 | break; | ||
1219 | } | ||
1220 | |||
1221 | fs = lookup(devices[i]); | ||
1222 | if (!fs) | ||
1223 | fs = create_fs_device(devices[i], 0, "auto", 0, -1, -1); | ||
1224 | fsck_device(fs, interactive); | ||
1225 | |||
1226 | if (serialize | ||
1227 | || (max_running && (num_running >= max_running)) | ||
1228 | ) { | ||
1229 | struct fsck_instance *inst; | ||
1230 | |||
1231 | inst = wait_one(0); | ||
1232 | if (inst) { | ||
1233 | status |= inst->exit_status; | ||
1234 | free_instance(inst); | ||
1235 | } | ||
1236 | if (verbose > 1) | ||
1237 | puts("----------------------------------"); | ||
1238 | } | ||
1239 | } | ||
1240 | status |= wait_many(FLAG_WAIT_ALL); | ||
1241 | return status; | ||
1242 | } |