diff -urN e2fsprogs-1.35/misc/base_device.c e2fsprogs-1.35-mike/misc/base_device.c --- e2fsprogs-1.35/misc/base_device.c 2003-12-08 01:11:38.000000000 +0800 +++ e2fsprogs-1.35-mike/misc/base_device.c 2005-01-04 12:17:35.000000000 +0800 @@ -28,6 +28,11 @@ #include #include +#include +#include +#include +#include + #include "fsck.h" /* @@ -38,6 +43,462 @@ "host", "bus", "target", "lun", 0 }; + +#ifndef DM_MAJOR +#define DM_MAJOR 253 +#endif + +#define MAX_MINOR 0xFF +#define NO_DEV 0xFFFF + +#define MAX_DEPENDENT_DEVS 50 + +static int get_md_deps(const dev_t devid, dev_t *deps, int depsleft); +static int get_mapper_deps(const dev_t devid, dev_t *deps, int depsleft); +char *get_dev_name(dev_t devid); + +/*************************************************************************/ + +static dev_t get_dev_id(const char *device) +{ + struct stat st; + if (stat(device, &st)) return MKDEV(NO_DEV,MAX_MINOR); + + // regular file - depends on it's device. + if (S_ISREG(st.st_mode)) return st.st_dev; + + // everything else (though perhaps we should not allow some devices) + return st.st_rdev; +} + +// round the minor number down to that of the device containing the partition +// but only if it's a hd* or sd* device. +static dev_t container_device(dev_t devid) +{ + char *dn; + int major=MAJOR(devid); + + // first, check against some well known majors to speed things up.... + switch (major) { + case MD_MAJOR: + case DM_MAJOR: + case SCSI_GENERIC_MAJOR: + return devid; + } + + /* do we consider anything on the same controller as the same? */ + /* or just the same disk like scsi devs? */ + if (IDE_DISK_MAJOR(major)) +#ifdef IDE_PER_CONTROLLER + return MKDEV(MAJOR(devid), MINOR(devid)); +#else + return MKDEV(MAJOR(devid), MINOR(devid) & ~0x0F); +#endif + + if (SCSI_DISK_MAJOR(major)) + return MKDEV(MAJOR(devid), MINOR(devid) & ~0x0F); + + // not a well-known major - look at the device name + // it is highly unlikely we will ever run this code on most systems... + dn=get_dev_name(devid); + if (dn && dn[0] && dn[1]=='d' && isdigit(dn[3])) { // ?d?x + if (dn[0]=='h') + return MKDEV(MAJOR(devid), 0); + if (dn[0]=='s') + return MKDEV(MAJOR(devid), MINOR(devid) & ~0x0F); + } + return devid; +} + +static char *string_copy(char *s) +{ char *new=malloc(strlen(s)+1); + if (new) strcpy(new,s); + return new; +} + +static char *skip_word(char *s) +{ + while (*s && *s!=' ') s++; + while (*s==' ') s++; + return s; +} + +static char *chomp(char *s) +{ char *sp=s; + while (*sp) sp++; + sp--; + while (sp>=s && (*sp=='\r' || *sp=='\n')) *sp--=0; + return s; +} + + +/************************************************************************* + * Convert deviceid to device name * + *************************************************************************/ + +struct partition { + struct partition *next; + dev_t devid; + char *name; +}; + +static struct partition *partitions=NULL; + +static struct partition *load_dev_names() +{ char buf[80], *lp; + int major, minor; + struct partition *part; + + FILE *fp=fopen("/proc/partitions", "r"); + if (fp) { + fgets(buf,79,fp); // column headers + fgets(buf,79,fp); // blank line + while (fgets(buf, 79, fp)) { + lp=buf; + while (*lp==' ') lp++; + major=atoi(lp); + lp=skip_word(lp); + minor=atoi(lp); + lp=skip_word(lp); + lp=skip_word(lp); + part=malloc(sizeof(*part)); + if (part) { + part->devid=MKDEV(major,minor); + part->name=string_copy(chomp(lp)); + part->next=partitions; + partitions=part; + } + } + fclose(fp); + } +} + +char *get_dev_name(dev_t devid) +{ struct partition *part; + if (!partitions) load_dev_names(); + part=partitions; + while (part && part->devid!=devid) part=part->next; + if (part) return part->name; + return NULL; +} + +/************************************************************************* + * Device Mapper Support * + *************************************************************************/ + +struct mapper_dep { + struct mapper_dep *next; + dev_t devid; + char *name; + dev_t *deps; +}; + +static struct mapper_dep *mapper_deps=NULL; + + +static int read_devid(char *s, dev_t *devid) +{ int major,minor; + char *sp=s; + + major=atoi(sp); + while (*sp && *sp!=' ') sp++; + while (*sp==' ') sp++; + if (major && isdigit(*sp)) { + minor=atoi(sp); + while (isdigit(*sp)) sp++; + *devid=MKDEV(major, minor); + } + else *devid=MKDEV(NO_DEV, MAX_MINOR); + return sp-s; +} + +static dev_t *read_deplist(char * s) +{ int count=0; + dev_t deps[MAX_DEPENDENT_DEVS], *dp=deps; + + while (*s) { + while (*s && *s!='(') s++; // next '(' + if (*s) { + s++; + s+=read_devid(s, dp++); + s++; + while (*s == ' ') s++; + count++; + } + } + *dp++=0; + count++; + dp=malloc(count * sizeof(*dp)); + if (dp) memcpy(dp, deps, count * sizeof(*dp)); + return dp; +} + + +// ask dmsetup for a list of dependencies by running "dmsetup -v deps" +static struct mapper_dep *load_mapper_deps() +{ + pid_t pid; + int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2]; + struct mapper_dep *md=NULL; + + if (mapper_deps) mapper_deps; // already got it? good... + + if (pipe(stdin_pipe)==0 && pipe(stdout_pipe)==0 && pipe(stderr_pipe)==0) + { + if ((pid=fork()) < 0) { + return NULL; // fork failed + } + else if (pid) { // parent - read output of dmsetup + char *lp, *le, buf[200]; + int data_read=0; + + close(stdin_pipe[0]); + close(stdin_pipe[1]); + close(stderr_pipe[1]); + close(stderr_pipe[0]); + close(stdout_pipe[1]); + + while (1) { + // get one line into buf + le=buf; + while ((le-buf)<200 && (data_read=read(stdout_pipe[0], le, 1)) && *le!='\n') le++; + *le=0; + if (!data_read) break; + + lp=buf; + // split into name : value + while (*lp && *lp!=':') lp++; + if (*lp) { + *lp++=0; + while (*lp && *lp==' ') lp++; + } + // buf=name, lp=value + if (!strncasecmp(buf,"Name",4)) { + if (!md) md=malloc(sizeof(*md)); + if (md) { + md->name=string_copy(lp); + md->devid=0; + md->deps=NULL; + md->next=NULL; + } + } + else if (!strncasecmp(buf, "Major, minor", 12)) { + if (md) read_devid(lp, &md->devid); + } + else if (!strncasecmp(buf+2, "dependencies", 12)) { + if (md) { + + md->deps=read_deplist(lp); +/* + { dev_t *dep; + printf("stored dev: %s (%lld,%lld) :", + md->name, MAJOR(md->devid), MINOR(md->devid)); + dep=md->deps; + while (dep && *dep) { + printf(" (%lld,%lld)", MAJOR(*dep), MINOR(*dep)); + dep++; + } + printf("\n"); + } +*/ + md->next=mapper_deps; + mapper_deps=md; + md=NULL; + } + } + } + close(stdout_pipe[0]); + } + else { // child - exec dmsetup + char prog[80], *argv[10]; + strcpy(prog,"/sbin/dmsetup"); + argv[0]=prog; + argv[1]="-v"; + argv[2]="deps"; + argv[3]=0; + +#define X(pipe,h1,h2) close(h1); dup(pipe[h2]); close(pipe[0]); close(pipe[1]) + X(stdin_pipe, 0, 0); + X(stdout_pipe, 1, 1); + X(stderr_pipe, 2, 1); +#undef X + execv(prog,argv); + exit(1); + } + return mapper_deps; + } + else return NULL; +} + +static int get_mapper_deps(const dev_t devid, dev_t *deps, int depsleft) +{ + int depsused=0, fake_it=0; + if (deps && depsleft) { + if (geteuid()) { // not root? can't ask so treat all as one. + fake_it=1; + } + else { + struct mapper_dep *deplist=load_mapper_deps(); + if (deplist) { + while (deplist) { + // if this is the devid we want + if ((deplist->devid == devid) && (deplist->deps)) { + dev_t *devdeps=deplist->deps; + int copypos=0; + + // copy dependencies, expanding any DM and MD devices + while (devdeps[copypos] && depsusednext; + } + } + else fake_it=1; + } + } + if (fake_it) { + deps[depsused++]=MKDEV(DM_MAJOR,MAX_MINOR); + depsleft--; + } + return depsused; +} + + +/************************************************************************* + * MD (Raid) Support * + *************************************************************************/ + + +static int get_md_deps(const dev_t devid, dev_t *deps, int depsleft) +{ + int depsused=0; + if (deps && depsleft) { + + FILE *fd=fopen("/proc/mdstat", "r"); + if (fd) { + char buf[200], wanted_dev[20], dev_path[40], *lp, *le; + int cmplen=sprintf(wanted_dev, "md%d", MINOR(devid)); + + while (fgets(buf, 200, fd)) { + le=buf; + while (*le) le++; + if (le>buf) le--; + while ((le > buf) && (*le=='\n' || *le=='\r')) *le--=0; + lp=buf; + while (*lp && *lp!=':') lp++; + if (*lp) { + if (*(lp-1)==' ') *(lp-1)=0; + if (!strncmp(wanted_dev, buf, cmplen+1)) { + lp++; + while (*lp==' ') lp++; + lp=skip_word(lp); // state + lp=skip_word(lp); // personality + while (*lp) { + le=lp; + while (*le && *le!='[' && *le!=' ') le++; + if (*le) *le++=0; + sprintf(dev_path, "/dev/%s", lp); + deps[depsused++]=container_device(get_dev_id(dev_path)); + if (*le) le=skip_word(le); + lp=le; + } + } + } + } + fclose(fd); + } + if (!depsused) { + // we cheat - all md devices are treated as one because we don't know + // how to ask them for dependencies. + //deps[depsused++]=MKDEV(MAJOR(devid), MAX_MINOR); + deps[depsused++]=MKDEV(NO_DEV, MAX_MINOR); + } + } + return depsused; +} + + +/************************************************************************* + * "public interface" functions * + *************************************************************************/ + +/* get_dev_deps + returns an array of dev_t listing physical device dependencies, zero terminated + if the device depends only on itself, only one entry is returned. + + We find out the actual major/minor devices behind the device name + so we don't care where it is or what name it got - eliminating all + those nasty path and name checks. + + Loopback files are also handled - we report the physical device(s) + they are stored on. (a file on a LVM volume will report the physical + devices that make up the volume) + + NOTE: this is recursive (ie we handle DM devices made of DM devices, + and even device-mapper devices made of RAID devices. + RAID devices can't be made of anything other than real disks. + The limit is the maximum number of physical devices we return - defined + by MAX_DEPENDENT_DEVS, default 50. + +*/ +dev_t *get_dev_deps(const char *device) +{ + int devcount=0, depsused=0, depsmax=MAX_DEPENDENT_DEVS; + dev_t *deps=malloc(sizeof(*deps) * (depsmax+1)); + + if (deps) { + dev_t devid=get_dev_id(device); + if (MAJOR(devid) == MD_MAJOR) depsused+=get_md_deps(devid, deps, depsmax); + else if (MAJOR(devid) == DM_MAJOR) depsused+=get_mapper_deps(devid, deps, depsmax); + else deps[depsused++]=container_device(devid); + deps[depsused]=0; + } + return deps; +} + +/* common_dev + returns non-zero if deps1 contains a deviceid that is in deps2 + (should return the devid of the first common device) +*/ +int common_dev(dev_t *deps1, dev_t *deps2) +{ + if (!deps1 || !deps2) return -1; // we can't tell - play it safe + if (!*deps1 || !*deps2) return -1; + + while (*deps1) { + dev_t *dp=deps2; + while (*dp) { + if (*deps1 == *dp) return *dp; + dp++; + } + deps1++; + } + return 0; +} + +int unknown_dev(dev_t *deps) +{ + if (!deps) return 1; + if (!*deps) return 1; + while (*deps) { + if (MAJOR(*deps)==NO_DEV) return 1; + deps++; + } + return 0; +} + +/*********************************************************** + * Original base_device function * + ***********************************************************/ + char *base_device(const char *device) { char *str, *cp; @@ -141,6 +602,13 @@ return str; } + /* device mapper - treat like md devices */ + if (strncmp(cp, "mapper/", 7) == 0) { + cp+=7; + *cp = 0; + return str; + } + errout: free(str); return NULL; @@ -151,6 +619,7 @@ { const char *base; char buf[256], *cp; + dev_t *deps, *d; while (1) { if (fgets(buf, sizeof(buf), stdin) == NULL) @@ -163,6 +632,20 @@ *cp = 0; base = base_device(buf); printf("%s\t%s\n", buf, base ? base : "NONE"); + + deps=get_dev_deps(buf); + d=deps; + printf("%s\t", buf); + while (*d) { + if (MAJOR(*d)==NO_DEV) + printf ("NO_DEVICE"); + else + printf("%s (%3lld,%3lld)", + get_dev_name(*d), MAJOR(*d), MINOR(*d)); + d++; + } + free(deps); + printf("\n"); } exit(0); } diff -urN e2fsprogs-1.35/misc/base_device.tst2 e2fsprogs-1.35-mike/misc/base_device.tst2 --- e2fsprogs-1.35/misc/base_device.tst2 1970-01-01 08:00:00.000000000 +0800 +++ e2fsprogs-1.35-mike/misc/base_device.tst2 2005-01-03 16:18:01.000000000 +0800 @@ -0,0 +1,3 @@ +/dev/sda +/dev/sdb +/dev/mapper/sde-crypt diff -urN e2fsprogs-1.35/misc/ChangeLog e2fsprogs-1.35-mike/misc/ChangeLog --- e2fsprogs-1.35/misc/ChangeLog 2004-02-28 23:52:34.000000000 +0800 +++ e2fsprogs-1.35-mike/misc/ChangeLog 2005-01-04 12:38:37.000000000 +0800 @@ -1,3 +1,19 @@ +2005-01-03 Mike Nix + + * support for RAID and Device-Mapper devices (get_dev_deps) + allowing fsck to check these in parrallel. + + * -S selects "slow" checks (fstab entries with negative passno) + Actually having a negative passno in fstab requires a + patched mount. + + * -m mount when check completes successfully. I reccomend only using + this during a "slow checks" run, but it will work everywhere. + + The combination of -S -m is intended to allow very large / slow + fscks to be done in the background after the system boots, and + automatically mount the filesystems as they are sucessfully checked. + 2004-02-28 Theodore Ts'o * Release of E2fsprogs 1.35 diff -urN e2fsprogs-1.35/misc/fsck.c e2fsprogs-1.35-mike/misc/fsck.c --- e2fsprogs-1.35/misc/fsck.c 2004-02-22 10:02:15.000000000 +0800 +++ e2fsprogs-1.35-mike/misc/fsck.c 2005-01-04 12:28:10.000000000 +0800 @@ -107,6 +107,8 @@ int force_all_parallel = 0; int num_running = 0; int max_running = 0; +int slow_filesystems = 0; +int mount_when_done = 0; volatile int cancel_requested = 0; int kill_sent = 0; char *progname; @@ -220,6 +222,8 @@ free(i->device); if (i->base_device) free(i->base_device); + if (i->mountpt) + free(i->mountpt); free(i); return; } @@ -344,10 +348,13 @@ } if (!fs) continue; - if (fs->passno < 0) + if (fs->passno == -1) fs->passno = 0; - else + else { + if (slow_filesystems) fs->passno=0-fs->passno; + if (fs->passno < 0) fs->passno=0; old_fstab = 0; + } } fclose(f); @@ -402,6 +409,23 @@ return(s ? prog : NULL); } +static char *find_mount() +{ + char *s; + static char prog[256]; + char *p = string_copy(fsck_path); + struct stat st; + + for(s = strtok(p, ":"); s; s = strtok(NULL, ":")) { + sprintf(prog, "%s/%s", s, "mount"); + if (stat(prog, &st) == 0) break; + } + free(p); + return(s ? prog : NULL); +} + + + static int progress_active(NOARGS) { struct fsck_instance *inst; @@ -462,6 +486,18 @@ for (i=0; i < argc; i++) printf("%s ", argv[i]); printf("\n"); + if (verbose) { + dev_t *dp, *deps=get_dev_deps(device); + printf("Devices:"); + dp=deps; + while (*dp) { + printf(" %s (%lld,%lld)", + get_dev_name(*dp), *dp>>8, *dp & 0xFF); + dp++; + } + printf("\n"); + free(deps); + } } /* Fork and execute the correct program. */ @@ -485,7 +521,8 @@ inst->prog = string_copy(prog); inst->type = string_copy(type); inst->device = string_copy(device); - inst->base_device = base_device(device); + inst->base_device = get_dev_deps(device); + inst->mountpt = string_copy(mntpt); inst->start_time = time(0); inst->next = NULL; @@ -597,6 +634,25 @@ inst->prog, inst->device, status); status = EXIT_ERROR; } + if ((status<=1) && mount_when_done) { + pid_t pid; + if ((pid = fork()) < 0) { + perror("fork for mount"); + } else if (pid == 0) { + char *s,*argv[5]; + int argc; + printf(_("Check Successful, Mounting %s\n"), inst->mountpt); + close(0); + s=find_mount(); + argc=0; + argv[argc++]=s; + argv[argc++]=inst->mountpt; + argv[argc]=0; + (void) execv(s, argv); + perror(argv[0]); + exit(EXIT_ERROR); + } + } inst->exit_status = status; if (progress && (inst->flags & FLAG_PROGRESS) && !progress_active()) { @@ -885,11 +941,15 @@ static int device_already_active(char *device) { struct fsck_instance *inst; - char *base; + dev_t *base; if (force_all_parallel) return 0; + +#if 0 + // we can now check raid in parallel because we know how to + // find out what the devices are. #ifdef BASE_MD /* Don't check a soft raid disk with any other disk */ if (instance_list && @@ -897,16 +957,16 @@ !strncmp(device, BASE_MD, sizeof(BASE_MD)-1))) return 1; #endif - - base = base_device(device); +#endif + base = get_dev_deps(device); /* * If we don't know the base device, assume that the device is * already active if there are any fsck instances running. */ - if (!base) + if (unknown_dev(base)) return (instance_list != 0); for (inst = instance_list; inst; inst = inst->next) { - if (!inst->base_device || !strcmp(base, inst->base_device)) { + if (common_dev(inst->base_device, base)) { free(base); return 1; } @@ -1031,7 +1091,7 @@ static void usage(NOARGS) { - fputs(_("Usage: fsck [-ACNPRTV] [-t fstype] [fs-options] [filesys ...]\n"), stderr); + fputs(_("Usage: fsck [-ACNPRTVSm] [-t fstype] [fs-options] [filesys ...]\n"), stderr); exit(EXIT_USAGE); } @@ -1158,6 +1218,12 @@ fstype = string_copy(tmp); compile_fs_type(fstype, &fs_type_compiled); goto next_arg; + case 'S': + slow_filesystems++; + break; + case 'm': + mount_when_done++; + break; case '-': opts_for_fsck++; break; diff -urN e2fsprogs-1.35/misc/fsck.h e2fsprogs-1.35-mike/misc/fsck.h --- e2fsprogs-1.35/misc/fsck.h 2003-12-08 01:11:38.000000000 +0800 +++ e2fsprogs-1.35-mike/misc/fsck.h 2005-01-04 10:16:05.000000000 +0800 @@ -62,9 +62,15 @@ char * prog; char * type; char * device; - char * base_device; + dev_t * base_device; + char * mountpt; struct fsck_instance *next; }; +extern dev_t *get_dev_deps(const char *device); +extern int common_dev(dev_t *deps1, dev_t *deps2); +extern int unknown_dev(dev_t *deps); +extern char *get_dev_name(dev_t devid); + extern char *base_device(const char *device); extern const char *identify_fs(const char *fs_name, const char *fs_types);