| /* |
| * pfsck --- A generic, parallelizing front-end for the fsck program. |
| * It will automatically try to run fsck programs in parallel if the |
| * devices are on separate spindles. It is based on the same ideas as |
| * the generic front end for fsck by David Engel and Fred van Kempen, |
| * but it has been completely rewritten from scratch to support |
| * parallel execution. |
| * |
| * Written by Theodore Ts'o, <tytso@mit.edu> |
| * |
| * Usage: fsck [-ACVRNTM] [-s] [-t fstype] [fs-options] device |
| * |
| * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994: |
| * o Changed -t fstype to behave like with mount when -A (all file |
| * systems) or -M (like mount) is specified. |
| * o fsck looks if it can find the fsck.type program to decide |
| * if it should ignore the fs type. This way more fsck programs |
| * can be added without changing this front-end. |
| * o -R flag skip root file system. |
| * |
| * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 Theodore Ts'o. |
| * |
| * %Begin-Header% |
| * This file may be redistributed under the terms of the GNU Public |
| * License. |
| * %End-Header% |
| */ |
| |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <sys/signal.h> |
| #include <sys/stat.h> |
| #include <limits.h> |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <string.h> |
| #include <time.h> |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| #if HAVE_PATHS_H |
| #include <paths.h> |
| #endif |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #if HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| #include <malloc.h> |
| |
| #include "../version.h" |
| #include "nls-enable.h" |
| #include "fsck.h" |
| #include "get_device_by_label.h" |
| |
| #ifndef _PATH_MNTTAB |
| #define _PATH_MNTTAB "/etc/fstab" |
| #endif |
| |
| static const char *ignored_types[] = { |
| "ignore", |
| "iso9660", |
| "nfs", |
| "proc", |
| "sw", |
| "swap", |
| NULL |
| }; |
| |
| static const char *really_wanted[] = { |
| "minix", |
| "ext2", |
| "ext3", |
| "xiafs", |
| NULL |
| }; |
| |
| #ifdef DEV_DSK_DEVICES |
| static const char *base_devices[] = { |
| "/dev/dsk/hda", |
| "/dev/dsk/hdb", |
| "/dev/dsk/hdc", |
| "/dev/dsk/hdd", |
| "/dev/dsk/hde", |
| "/dev/dsk/hdf", |
| "/dev/dsk/hdg", |
| "/dev/dsk/hdh", |
| "/dev/dsk/hd1a", |
| "/dev/dsk/hd1b", |
| "/dev/dsk/hd1c", |
| "/dev/dsk/hd1d", |
| "/dev/dsk/sda", |
| "/dev/dsk/sdb", |
| "/dev/dsk/sdc", |
| "/dev/dsk/sdd", |
| "/dev/dsk/sde", |
| "/dev/dsk/sdf", |
| "/dev/dsk/sdg", |
| NULL |
| }; |
| #else |
| #define BASE_MD "/dev/md" |
| static const char *base_devices[] = { |
| "/dev/hda", |
| "/dev/hdb", |
| "/dev/hdc", |
| "/dev/hdd", |
| "/dev/hde", |
| "/dev/hdf", |
| "/dev/hdg", |
| "/dev/hdh", |
| "/dev/hd1a", |
| "/dev/hd1b", |
| "/dev/hd1c", |
| "/dev/hd1d", |
| "/dev/sda", |
| "/dev/sdb", |
| "/dev/sdc", |
| "/dev/sdd", |
| "/dev/sde", |
| "/dev/sdf", |
| "/dev/sdg", |
| BASE_MD, |
| NULL |
| }; |
| #endif |
| |
| /* |
| * Global variables for options |
| */ |
| char *devices[MAX_DEVICES]; |
| char *args[MAX_ARGS]; |
| int num_devices, num_args; |
| |
| int verbose = 0; |
| int doall = 0; |
| int noexecute = 0; |
| int serialize = 0; |
| int skip_root = 0; |
| int like_mount = 0; |
| int notitle = 0; |
| int parallel_root = 0; |
| int progress = 0; |
| int force_all_parallel = 0; |
| char *progname; |
| char *fstype = NULL; |
| struct fs_info *filesys_info; |
| struct fsck_instance *instance_list; |
| const char *fsck_prefix_path = "/sbin:/sbin/fs.d:/sbin/fs:/etc/fs:/etc"; |
| char *fsck_path = 0; |
| static int ignore(struct fs_info *); |
| |
| static char *string_copy(const char *s) |
| { |
| char *ret; |
| |
| ret = malloc(strlen(s)+1); |
| if (ret) |
| strcpy(ret, s); |
| return ret; |
| } |
| |
| static char *skip_over_blank(char *cp) |
| { |
| while (*cp && isspace(*cp)) |
| cp++; |
| return cp; |
| } |
| |
| static char *skip_over_word(char *cp) |
| { |
| while (*cp && !isspace(*cp)) |
| cp++; |
| return cp; |
| } |
| |
| static void strip_line(char *line) |
| { |
| char *p; |
| |
| while (*line) { |
| p = line + strlen(line) - 1; |
| if ((*p == '\n') || (*p == '\r')) |
| *p = 0; |
| else |
| break; |
| } |
| } |
| |
| static char *parse_word(char **buf) |
| { |
| char *word, *next; |
| |
| word = *buf; |
| if (*word == 0) |
| return 0; |
| |
| word = skip_over_blank(word); |
| next = skip_over_word(word); |
| if (*next) |
| *next++ = 0; |
| *buf = next; |
| return word; |
| } |
| |
| static void free_instance(struct fsck_instance *i) |
| { |
| if (i->prog) |
| free(i->prog); |
| if (i->device) |
| free(i->device); |
| free(i); |
| return; |
| } |
| |
| static int parse_fstab_line(char *line, struct fs_info **ret_fs) |
| { |
| char *device, *mntpnt, *type, *opts, *freq, *passno, *cp; |
| struct fs_info *fs; |
| |
| *ret_fs = 0; |
| strip_line(line); |
| if ((cp = strchr(line, '#'))) |
| *cp = 0; /* Ignore everything after the comment char */ |
| cp = line; |
| |
| device = parse_word(&cp); |
| mntpnt = parse_word(&cp); |
| type = parse_word(&cp); |
| opts = parse_word(&cp); |
| freq = parse_word(&cp); |
| passno = parse_word(&cp); |
| |
| if (!device) |
| return 0; /* Allow blank lines */ |
| |
| if (!mntpnt || !type) |
| return -1; |
| |
| if (!(fs = malloc(sizeof(struct fs_info)))) |
| return -1; |
| |
| fs->device = string_copy(device); |
| fs->mountpt = string_copy(mntpnt); |
| fs->type = string_copy(type); |
| fs->opts = string_copy(opts ? opts : ""); |
| fs->freq = freq ? atoi(freq) : -1; |
| fs->passno = passno ? atoi(passno) : -1; |
| fs->flags = 0; |
| fs->next = NULL; |
| |
| *ret_fs = fs; |
| |
| return 0; |
| } |
| |
| /* |
| * Interpret the device name if necessary |
| */ |
| static char *interpret_device(char *spec) |
| { |
| char *dev = NULL; |
| |
| if (!strncmp(spec, "UUID=", 5)) |
| dev = get_spec_by_uuid(spec+5); |
| else if (!strncmp(spec, "LABEL=", 6)) |
| dev = get_spec_by_volume_label(spec+6); |
| else |
| return spec; |
| if (dev) { |
| free(spec); |
| return (dev); |
| } |
| /* |
| * Check to see if this was because /proc/partitions isn't |
| * found. |
| */ |
| if (access("/proc/partitions", R_OK) < 0) { |
| fprintf(stderr, "Couldn't open /proc/partitions: %s\n", |
| strerror(errno)); |
| fprintf(stderr, "Is /proc mounted?\n"); |
| exit(1); |
| } |
| /* |
| * Check to see if this is because we're not running as root |
| */ |
| if (geteuid()) |
| fprintf(stderr, "Must be root to scan for matching " |
| "filesystems: %s\n", spec); |
| else |
| fprintf(stderr, "Couldn't find matching filesystem: %s\n", |
| spec); |
| exit(1); |
| } |
| |
| /* |
| * Load the filesystem database from /etc/fstab |
| */ |
| static void load_fs_info(const char *filename) |
| { |
| FILE *f; |
| char buf[1024]; |
| int lineno = 0; |
| int old_fstab = 1; |
| struct fs_info *fs, *fs_last = NULL; |
| |
| filesys_info = NULL; |
| if ((f = fopen(filename, "r")) == NULL) { |
| fprintf(stderr, _("WARNING: couldn't open %s: %s\n"), |
| filename, strerror(errno)); |
| return; |
| } |
| while (!feof(f)) { |
| lineno++; |
| if (!fgets(buf, sizeof(buf), f)) |
| break; |
| buf[sizeof(buf)-1] = 0; |
| if (parse_fstab_line(buf, &fs) < 0) { |
| fprintf(stderr, _("WARNING: bad format " |
| "on line %d of %s\n"), lineno, filename); |
| continue; |
| } |
| if (!fs) |
| continue; |
| if (!filesys_info) |
| filesys_info = fs; |
| else |
| fs_last->next = fs; |
| fs_last = fs; |
| if (fs->passno < 0) |
| fs->passno = 0; |
| else |
| old_fstab = 0; |
| } |
| |
| fclose(f); |
| |
| if (old_fstab) { |
| fprintf(stderr, _("\007\007\007" |
| "WARNING: Your /etc/fstab does not contain the fsck passno\n" |
| " field. I will kludge around things for you, but you\n" |
| " should fix your /etc/fstab file as soon as you can.\n\n")); |
| |
| for (fs = filesys_info; fs; fs = fs->next) { |
| fs->passno = 1; |
| } |
| } |
| } |
| |
| /* Lookup filesys in /etc/fstab and return the corresponding entry. */ |
| static struct fs_info *lookup(char *filesys) |
| { |
| struct fs_info *fs; |
| int try_again = 0; |
| |
| /* No filesys name given. */ |
| if (filesys == NULL) |
| return NULL; |
| |
| for (fs = filesys_info; fs; fs = fs->next) { |
| if (strchr(fs->device, '=')) |
| try_again++; |
| if (!strcmp(filesys, fs->device) || |
| !strcmp(filesys, fs->mountpt)) |
| break; |
| } |
| if (fs && strchr(fs->device, '=')) |
| fs->device = interpret_device(fs->device); |
| |
| if (fs || !try_again) |
| return fs; |
| |
| for (fs = filesys_info; fs; fs = fs->next) { |
| fs->device = interpret_device(fs->device); |
| if (!strcmp(filesys, fs->device) || |
| !strcmp(filesys, fs->mountpt)) |
| break; |
| } |
| |
| return fs; |
| } |
| |
| /* Find fsck program for a given fs type. */ |
| static char *find_fsck(char *type) |
| { |
| char *s; |
| const char *tpl; |
| static char prog[256]; |
| char *p = string_copy(fsck_path); |
| struct stat st; |
| |
| /* Are we looking for a program or just a type? */ |
| tpl = (strncmp(type, "fsck.", 5) ? "%s/fsck.%s" : "%s/%s"); |
| |
| for(s = strtok(p, ":"); s; s = strtok(NULL, ":")) { |
| sprintf(prog, tpl, s, type); |
| if (stat(prog, &st) == 0) break; |
| } |
| free(p); |
| return(s ? prog : NULL); |
| } |
| |
| static int progress_active(NOARGS) |
| { |
| struct fsck_instance *inst; |
| |
| for (inst = instance_list; inst; inst = inst->next) { |
| if (inst->flags & FLAG_DONE) |
| continue; |
| if (inst->flags & FLAG_PROGRESS) |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* |
| * Execute a particular fsck program, and link it into the list of |
| * child processes we are waiting for. |
| */ |
| static int execute(const char *type, char *device, char *mntpt, |
| int interactive) |
| { |
| char *s, *argv[80], prog[80]; |
| int argc, i; |
| struct fsck_instance *inst, *p; |
| pid_t pid; |
| |
| inst = malloc(sizeof(struct fsck_instance)); |
| if (!inst) |
| return ENOMEM; |
| memset(inst, 0, sizeof(struct fsck_instance)); |
| |
| sprintf(prog, "fsck.%s", type); |
| argv[0] = string_copy(prog); |
| argc = 1; |
| |
| for (i=0; i <num_args; i++) |
| argv[argc++] = string_copy(args[i]); |
| |
| if (progress & !progress_active()) { |
| if ((strcmp(type, "ext2") == 0) || |
| (strcmp(type, "ext3") == 0)) { |
| argv[argc++] = string_copy("-C0"); |
| inst->flags |= FLAG_PROGRESS; |
| } |
| } |
| |
| argv[argc++] = string_copy(device); |
| argv[argc] = 0; |
| |
| s = find_fsck(prog); |
| if (s == NULL) { |
| fprintf(stderr, _("fsck: %s: not found\n"), prog); |
| return ENOENT; |
| } |
| |
| if (verbose || noexecute) { |
| printf("[%s -- %s] ", s, mntpt ? mntpt : device); |
| for (i=0; i < argc; i++) |
| printf("%s ", argv[i]); |
| printf("\n"); |
| } |
| |
| /* Fork and execute the correct program. */ |
| if (noexecute) |
| pid = -1; |
| else if ((pid = fork()) < 0) { |
| perror("fork"); |
| return errno; |
| } else if (pid == 0) { |
| if (!interactive) |
| close(0); |
| (void) execv(s, argv); |
| perror(argv[0]); |
| exit(EXIT_ERROR); |
| } |
| |
| for (i=0; i < argc; i++) |
| free(argv[i]); |
| |
| inst->pid = pid; |
| inst->prog = string_copy(prog); |
| inst->type = string_copy(type); |
| inst->device = string_copy(device); |
| inst->start_time = time(0); |
| inst->next = NULL; |
| |
| /* |
| * Find the end of the list, so we add the instance on at the end. |
| */ |
| for (p = instance_list; p && p->next; p = p->next); |
| |
| if (p) |
| p->next = inst; |
| else |
| instance_list = inst; |
| |
| return 0; |
| } |
| |
| /* |
| * Wait for one child process to exit; when it does, unlink it from |
| * the list of executing child processes, and return it. |
| */ |
| static struct fsck_instance *wait_one(NOARGS) |
| { |
| int status; |
| int sig; |
| struct fsck_instance *inst, *inst2, *prev; |
| pid_t pid; |
| |
| if (!instance_list) |
| return NULL; |
| |
| if (noexecute) { |
| inst = instance_list; |
| instance_list = inst->next; |
| inst->exit_status = 0; |
| return(inst); |
| } |
| |
| /* |
| * gcc -Wall fails saving throw against stupidity |
| * (inst and prev are thought to be uninitialized variables) |
| */ |
| inst = prev = NULL; |
| |
| do { |
| pid = wait(&status); |
| if (pid < 0) { |
| if ((errno == EINTR) || (errno == EAGAIN)) |
| continue; |
| if (errno == ECHILD) { |
| fprintf(stderr, |
| _("%s: wait: No more child process?!?\n"), |
| progname); |
| return NULL; |
| } |
| perror("wait"); |
| continue; |
| } |
| for (prev = 0, inst = instance_list; |
| inst; |
| prev = inst, inst = inst->next) { |
| if (inst->pid == pid) |
| break; |
| } |
| } while (!inst); |
| |
| if (WIFEXITED(status)) |
| status = WEXITSTATUS(status); |
| else if (WIFSIGNALED(status)) { |
| sig = WTERMSIG(status); |
| if (sig == SIGINT) { |
| status = EXIT_UNCORRECTED; |
| } else { |
| printf(_("Warning... %s for device %s exited " |
| "with signal %d.\n"), |
| inst->prog, inst->device, sig); |
| status = EXIT_ERROR; |
| } |
| } else { |
| printf(_("%s %s: status is %x, should never happen.\n"), |
| inst->prog, inst->device, status); |
| status = EXIT_ERROR; |
| } |
| inst->exit_status = status; |
| if (prev) |
| prev->next = inst->next; |
| else |
| instance_list = inst->next; |
| if (progress && (inst->flags & FLAG_PROGRESS) && |
| !progress_active()) { |
| for (inst2 = instance_list; inst2; inst2 = inst2->next) { |
| if (inst2->flags & FLAG_DONE) |
| continue; |
| if (strcmp(inst2->type, "ext2") && |
| strcmp(inst2->type, "ext3")) |
| continue; |
| /* |
| * If we've just started the fsck, wait a tiny |
| * bit before sending the kill, to give it |
| * time to set up the signal handler |
| */ |
| if (inst2->start_time < time(0)+2) { |
| if (fork() == 0) { |
| sleep(1); |
| kill(inst2->pid, SIGUSR1); |
| exit(0); |
| } |
| } else |
| kill(inst2->pid, SIGUSR1); |
| break; |
| } |
| } |
| return inst; |
| } |
| |
| /* |
| * Wait until all executing child processes have exited; return the |
| * logical OR of all of their exit code values. |
| */ |
| static int wait_all(NOARGS) |
| { |
| struct fsck_instance *inst; |
| int global_status = 0; |
| |
| while (instance_list) { |
| inst = wait_one(); |
| if (!inst) |
| break; |
| global_status |= inst->exit_status; |
| free_instance(inst); |
| } |
| return global_status; |
| } |
| |
| /* |
| * Run the fsck program on a particular device |
| * |
| * If the type is specified using -t, and it isn't prefixed with "no" |
| * (as in "noext2") and only one filesystem type is specified, then |
| * use that type regardless of what is specified in /etc/fstab. |
| * |
| * If the type isn't specified by the user, then use either the type |
| * specified in /etc/fstab, or DEFAULT_FSTYPE. |
| */ |
| static void fsck_device(char *device, int interactive) |
| { |
| const char *type = 0; |
| struct fs_info *fsent; |
| int retval; |
| |
| if (fstype && strncmp(fstype, "no", 2) && !strchr(fstype, ',')) |
| type = fstype; |
| |
| if ((fsent = lookup(device))) { |
| device = fsent->device; |
| if (!type) |
| type = fsent->type; |
| } |
| if (!type) |
| type = DEFAULT_FSTYPE; |
| |
| retval = execute(type, device, fsent ? fsent->mountpt : 0, |
| interactive); |
| if (retval) { |
| fprintf(stderr, _("%s: Error %d while executing fsck.%s " |
| "for %s\n"), progname, retval, type, device); |
| } |
| } |
| |
| /* See if filesystem type matches the list. */ |
| static int fs_match(char *type, char *fs_type) |
| { |
| int ret = 0, negate = 0; |
| char list[128]; |
| char *s; |
| |
| if (!fs_type) return(1); |
| |
| if (strncmp(fs_type, "no", 2) == 0) { |
| fs_type += 2; |
| negate = 1; |
| } |
| strcpy(list, fs_type); |
| s = strtok(list, ","); |
| while(s) { |
| if (strcmp(s, type) == 0) { |
| ret = 1; |
| break; |
| } |
| s = strtok(NULL, ","); |
| } |
| return(negate ? !ret : ret); |
| } |
| |
| |
| /* Check if we should ignore this filesystem. */ |
| static int ignore(struct fs_info *fs) |
| { |
| const char **ip; |
| int wanted = 0; |
| |
| /* |
| * If the pass number is 0, ignore it. |
| */ |
| if (fs->passno == 0) |
| return 1; |
| |
| /* |
| * If a specific fstype is specified, and it doesn't match, |
| * ignore it. |
| */ |
| if (!fs_match(fs->type, fstype)) return 1; |
| |
| /* Are we ignoring this type? */ |
| for(ip = ignored_types; *ip; ip++) |
| if (strcmp(fs->type, *ip) == 0) return(1); |
| |
| /* Do we really really want to check this fs? */ |
| for(ip = really_wanted; *ip; ip++) |
| if (strcmp(fs->type, *ip) == 0) { |
| wanted = 1; |
| break; |
| } |
| |
| /* See if the <fsck.fs> program is available. */ |
| if (find_fsck(fs->type) == NULL) { |
| if (wanted) |
| fprintf(stderr, _("fsck: cannot check %s: fsck.%s not found\n"), |
| fs->device, fs->type); |
| return(1); |
| } |
| |
| /* We can and want to check this file system type. */ |
| return 0; |
| } |
| |
| /* |
| * Return the "base device" given a particular device; this is used to |
| * assure that we only fsck one partition on a particular drive at any |
| * one time. Otherwise, the disk heads will be seeking all over the |
| * place. |
| */ |
| static const char *base_device(char *device) |
| { |
| const char **base; |
| |
| for (base = base_devices; *base; base++) { |
| if (!strncmp(*base, device, strlen(*base))) |
| return *base; |
| } |
| return device; |
| } |
| |
| /* |
| * Returns TRUE if a partition on the same disk is already being |
| * checked. |
| */ |
| static int device_already_active(char *device) |
| { |
| struct fsck_instance *inst; |
| const char *base = base_device(device); |
| |
| if (force_all_parallel) |
| return 0; |
| |
| #ifdef BASE_MD |
| /* Don't check a soft raid disk with any other disk */ |
| if (instance_list && |
| (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) || |
| !strncmp(device, BASE_MD, sizeof(BASE_MD)-1))) |
| return 1; |
| #endif |
| |
| for (inst = instance_list; inst; inst = inst->next) { |
| if (!strcmp(base, base_device(inst->device))) |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* Check all file systems, using the /etc/fstab table. */ |
| static int check_all(NOARGS) |
| { |
| struct fs_info *fs = NULL; |
| struct fsck_instance *inst; |
| int status = EXIT_OK; |
| int not_done_yet = 1; |
| int passno = 1; |
| int pass_done; |
| |
| if (verbose) |
| printf(_("Checking all file systems.\n")); |
| |
| /* |
| * Find and check the root filesystem first. |
| */ |
| if (!parallel_root) { |
| for (fs = filesys_info; fs; fs = fs->next) { |
| if (!strcmp(fs->mountpt, "/")) |
| break; |
| } |
| if (fs && !skip_root && !ignore(fs)) { |
| fs->device = interpret_device(fs->device); |
| fsck_device(fs->device, 1); |
| fs->flags |= FLAG_DONE; |
| status |= wait_all(); |
| if (status > EXIT_NONDESTRUCT) |
| return status; |
| } |
| } |
| if (fs) fs->flags |= FLAG_DONE; |
| |
| /* |
| * Mark filesystems that should be ignored as done. |
| */ |
| for (fs = filesys_info; fs; fs = fs->next) { |
| if (ignore(fs)) |
| fs->flags |= FLAG_DONE; |
| } |
| |
| while (not_done_yet) { |
| not_done_yet = 0; |
| pass_done = 1; |
| |
| for (fs = filesys_info; fs; fs = fs->next) { |
| if (fs->flags & FLAG_DONE) |
| continue; |
| /* |
| * If the filesystem's pass number is higher |
| * than the current pass number, then we don't |
| * do it yet. |
| */ |
| if (fs->passno > passno) { |
| not_done_yet++; |
| continue; |
| } |
| /* |
| * If a filesystem on a particular device has |
| * already been spawned, then we need to defer |
| * this to another pass. |
| */ |
| if (device_already_active(fs->device)) { |
| pass_done = 0; |
| continue; |
| } |
| /* |
| * Spawn off the fsck process |
| */ |
| fs->device = interpret_device(fs->device); |
| fsck_device(fs->device, serialize); |
| fs->flags |= FLAG_DONE; |
| |
| if (serialize) { |
| pass_done = 0; |
| break; /* Only do one filesystem at a time */ |
| } |
| } |
| if (verbose > 1) |
| printf(_("--waiting-- (pass %d)\n"), passno); |
| inst = wait_one(); |
| if (inst) { |
| status |= inst->exit_status; |
| free_instance(inst); |
| } |
| if (pass_done) { |
| status |= wait_all(); |
| if (verbose > 1) |
| printf("----------------------------------\n"); |
| passno++; |
| } else |
| not_done_yet++; |
| } |
| status |= wait_all(); |
| return status; |
| } |
| |
| static void usage(NOARGS) |
| { |
| fprintf(stderr, |
| _("Usage: fsck [-ACNPRTV] [-t fstype] [fs-options] filesys\n")); |
| exit(EXIT_USAGE); |
| } |
| |
| static void PRS(int argc, char *argv[]) |
| { |
| int i, j; |
| char *arg; |
| char options[128]; |
| int opt = 0; |
| int opts_for_fsck = 0; |
| |
| num_devices = 0; |
| num_args = 0; |
| instance_list = 0; |
| |
| progname = argv[0]; |
| |
| for (i=1; i < argc; i++) { |
| arg = argv[i]; |
| if (!arg) |
| continue; |
| if ((arg[0] == '/' && !opts_for_fsck) || |
| (strncmp(arg, "LABEL=", 6) == 0) || |
| (strncmp(arg, "UUID=", 5) == 0)) { |
| if (num_devices >= MAX_DEVICES) { |
| fprintf(stderr, _("%s: too many devices\n"), |
| progname); |
| exit(1); |
| } |
| devices[num_devices++] = |
| interpret_device(string_copy(arg)); |
| continue; |
| } |
| if (arg[0] != '-' || opts_for_fsck) { |
| if (num_args >= MAX_ARGS) { |
| fprintf(stderr, _("%s: too many arguments\n"), |
| progname); |
| exit(1); |
| } |
| args[num_args++] = string_copy(arg); |
| continue; |
| } |
| for (j=1; arg[j]; j++) { |
| if (opts_for_fsck) { |
| options[++opt] = arg[j]; |
| continue; |
| } |
| switch (arg[j]) { |
| case 'A': |
| doall++; |
| break; |
| case 'C': |
| progress++; |
| break; |
| case 'V': |
| verbose++; |
| break; |
| case 'N': |
| noexecute++; |
| break; |
| case 'R': |
| skip_root++; |
| break; |
| case 'T': |
| notitle++; |
| break; |
| case 'M': |
| like_mount++; |
| break; |
| case 'P': |
| parallel_root++; |
| break; |
| case 's': |
| serialize++; |
| break; |
| case 't': |
| if (arg[j+1]) { |
| fstype = string_copy(arg+j+1); |
| goto next_arg; |
| } |
| if ((i+1) < argc) { |
| i++; |
| fstype = string_copy(argv[i]); |
| goto next_arg; |
| } |
| usage(); |
| break; |
| case '-': |
| opts_for_fsck++; |
| break; |
| default: |
| options[++opt] = arg[j]; |
| break; |
| } |
| } |
| next_arg: |
| if (opt) { |
| options[0] = '-'; |
| options[++opt] = '\0'; |
| if (num_args >= MAX_ARGS) { |
| fprintf(stderr, |
| _("%s: too many arguments\n"), |
| progname); |
| exit(1); |
| } |
| args[num_args++] = string_copy(options); |
| opt = 0; |
| } |
| } |
| if (getenv("FSCK_FORCE_ALL_PARALLEL")) |
| force_all_parallel++; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int i; |
| int status = 0; |
| int interactive = 0; |
| char *oldpath = getenv("PATH"); |
| const char *fstab; |
| |
| #ifdef ENABLE_NLS |
| setlocale(LC_MESSAGES, ""); |
| bindtextdomain(NLS_CAT_NAME, LOCALEDIR); |
| textdomain(NLS_CAT_NAME); |
| #endif |
| PRS(argc, argv); |
| |
| if (!notitle) |
| printf(_("Parallelizing fsck version %s (%s)\n"), |
| E2FSPROGS_VERSION, E2FSPROGS_DATE); |
| |
| fstab = getenv("FSTAB_FILE"); |
| if (!fstab) |
| fstab = _PATH_MNTTAB; |
| load_fs_info(fstab); |
| |
| /* Update our search path to include uncommon directories. */ |
| if (oldpath) { |
| fsck_path = malloc (strlen (fsck_prefix_path) + 1 + |
| strlen (oldpath) + 1); |
| strcpy (fsck_path, fsck_prefix_path); |
| strcat (fsck_path, ":"); |
| strcat (fsck_path, oldpath); |
| } else { |
| fsck_path = string_copy(fsck_prefix_path); |
| } |
| |
| if ((num_devices == 1) || (serialize)) |
| interactive = 1; |
| |
| /* If -A was specified ("check all"), do that! */ |
| if (doall) |
| return check_all(); |
| |
| for (i = 0 ; i < num_devices; i++) { |
| fsck_device(devices[i], interactive); |
| if (serialize) { |
| struct fsck_instance *inst; |
| |
| inst = wait_one(); |
| if (inst) { |
| status |= inst->exit_status; |
| free_instance(inst); |
| } |
| } |
| } |
| status |= wait_all(); |
| free(fsck_path); |
| return status; |
| } |