source: branches/stable/mindi-busybox/e2fsprogs/fsck.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 13 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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