/* * Copyright (c) 1980, 1989 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #ifndef lint char copyright[] = "@(#) Copyright (c) 1980, 1989 Regents of the University of California.\n\ All rights reserved.\n"; #endif not lint #ifndef lint static char sccsid[] = "@(#)main.c 5.13 (Berkeley) 07/04/89"; #endif not lint #include #include #include #include #include #include #include #include #include #include #include "fsck.h" char *rawname(), *unrawname(), *blockcheck(), *malloc(); int catch(), catchquit(), voidquit(); int returntosingle; int (*signal())(); struct part { char *name; /* device name */ char *fsname; /* mounted filesystem name */ struct part *next; /* forward link of partitions on disk */ } *badlist, **badnext = &badlist; struct disk { char *name; /* disk base name */ struct disk *next; /* forward link for list of disks */ struct part *part; /* head of list of partitions on disk */ int pid; /* If != 0, pid of proc working on */ } *disks; int nrun, ndisks, maxrun; main(argc, argv) int argc; char *argv[]; { struct fstab *fsp; int pid, passno, sumstatus; char *name; register struct disk *dk, *nextdisk; register struct part *pt; sync(); while (--argc > 0 && **++argv == '-') { switch (*++*argv) { case 'p': preen++; break; case 'b': if (argv[0][1] != '\0') { bflag = atoi(argv[0]+1); } else { bflag = atoi(*++argv); argc--; } printf("Alternate super block location: %d\n", bflag); break; case 'c': cvtflag++; break; case 'd': debug++; break; case 'l': if (!isdigit(argv[1][0])) errexit("-l flag requires a number\n"); maxrun = atoi(*++argv); argc--; break; case 'm': if (!isdigit(argv[1][0])) errexit("-m flag requires a mode\n"); sscanf(*++argv, "%o", &lfmode); if (lfmode &~ 07777) errexit("bad mode to -m: %o\n", lfmode); argc--; printf("** lost+found creation mode %o\n", lfmode); break; case 'n': /* default no answer flag */ case 'N': nflag++; yflag = 0; break; case 'y': /* default yes answer flag */ case 'Y': yflag++; nflag = 0; break; default: errexit("%c option?\n", **argv); } } if (signal(SIGINT, SIG_IGN) != SIG_IGN) (void)signal(SIGINT, catch); if (preen) (void)signal(SIGQUIT, catchquit); if (argc) { while (argc-- > 0) { hotroot = 0; checkfilesys(*argv++); } exit(0); } sumstatus = 0; for (passno = 1; passno <= 2; passno++) { if (setfsent() == 0) errexit("Can't open checklist file: %s\n", _PATH_FSTAB); while ((fsp = getfsent()) != 0) { if (strcmp(fsp->fs_type, FSTAB_RW) && strcmp(fsp->fs_type, FSTAB_RO) && strcmp(fsp->fs_type, FSTAB_RQ)) continue; if (preen == 0 || passno == 1 && fsp->fs_passno == 1) { name = blockcheck(fsp->fs_spec); if (name != NULL) checkfilesys(name); else if (preen) exit(8); } else if (passno == 2 && fsp->fs_passno > 1) { name = blockcheck(fsp->fs_spec); if (name == NULL) { pwarn("BAD DISK NAME %s\n", fsp->fs_spec); sumstatus |= 8; continue; } addpart(name, fsp->fs_file); } } } if (preen) { union wait status; if (maxrun == 0) maxrun = ndisks; if (maxrun > ndisks) maxrun = ndisks; nextdisk = disks; for (passno = 0; passno < maxrun; ++passno) { startdisk(nextdisk); nextdisk = nextdisk->next; } while ((pid = wait(&status)) != -1) { for (dk = disks; dk; dk = dk->next) if (dk->pid == pid) break; if (dk == 0) { printf("Unknown pid %d\n", pid); continue; } if (status.w_termsig) { printf("%s (%s): EXITED WITH SIGNAL %d\n", dk->part->name, dk->part->fsname, status.w_termsig); status.w_retcode = 8; } if (status.w_retcode != 0) { sumstatus |= status.w_retcode; *badnext = dk->part; badnext = &dk->part->next; dk->part = dk->part->next; *badnext = NULL; } else dk->part = dk->part->next; dk->pid = 0; nrun--; if (dk->part == NULL) ndisks--; if (nextdisk == NULL) { if (dk->part) startdisk(dk); } else if (nrun < maxrun && nrun < ndisks) { for ( ;; ) { if ((nextdisk = nextdisk->next) == NULL) nextdisk = disks; if (nextdisk->part != NULL && nextdisk->pid == 0) break; } startdisk(nextdisk); } } } if (sumstatus) { if (badlist == 0) exit(8); printf("THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t", badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:"); for (pt = badlist; pt; pt = pt->next) printf("%s (%s)%s", pt->name, pt->fsname, pt->next ? ", " : "\n"); exit(8); } (void)endfsent(); if (returntosingle) exit(2); exit(0); } struct disk * finddisk(name) char *name; { register struct disk *dk, **dkp; register char *p; int len; for (p = name + strlen(name) - 1; p >= name; --p) if (isdigit(*p)) { len = p - name + 1; break; } if (p < name) len = strlen(name); for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) { if (strncmp(dk->name, name, len) == 0 && dk->name[len] == 0) return (dk); } if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) errexit("out of memory"); dk = *dkp; if ((dk->name = malloc(len + 1)) == NULL) errexit("out of memory"); strncpy(dk->name, name, len); dk->name[len] = '\0'; dk->part = NULL; dk->next = NULL; dk->pid = 0; ndisks++; return (dk); } addpart(name, fsname) char *name, *fsname; { struct disk *dk = finddisk(name); register struct part *pt, **ppt = &dk->part; for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next) if (strcmp(pt->name, name) == 0) { printf("%s in fstab more than once!\n", name); return; } if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) errexit("out of memory"); pt = *ppt; if ((pt->name = malloc(strlen(name) + 1)) == NULL) errexit("out of memory"); strcpy(pt->name, name); if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) errexit("out of memory"); strcpy(pt->fsname, fsname); pt->next = NULL; } startdisk(dk) register struct disk *dk; { nrun++; dk->pid = fork(); if (dk->pid < 0) { perror("fork"); exit(8); } if (dk->pid == 0) { (void)signal(SIGQUIT, voidquit); checkfilesys(dk->part->name); exit(0); } } checkfilesys(filesys) char *filesys; { daddr_t n_ffree, n_bfree; struct dups *dp; struct zlncnt *zlnp; devname = filesys; if (debug && preen) pwarn("starting\n"); if (setup(filesys) == 0) { if (preen) pfatal("CAN'T CHECK FILE SYSTEM."); return; } /* * 1: scan inodes tallying blocks used */ if (preen == 0) { printf("** Last Mounted on %s\n", sblock.fs_fsmnt); if (hotroot) printf("** Root file system\n"); printf("** Phase 1 - Check Blocks and Sizes\n"); } pass1(); /* * 1b: locate first references to duplicates, if any */ if (duplist) { if (preen) pfatal("INTERNAL ERROR: dups with -p"); printf("** Phase 1b - Rescan For More DUPS\n"); pass1b(); } /* * 2: traverse directories from root to mark all connected directories */ if (preen == 0) printf("** Phase 2 - Check Pathnames\n"); pass2(); /* * 3: scan inodes looking for disconnected directories */ if (preen == 0) printf("** Phase 3 - Check Connectivity\n"); pass3(); /* * 4: scan inodes looking for disconnected files; check reference counts */ if (preen == 0) printf("** Phase 4 - Check Reference Counts\n"); pass4(); /* * 5: check and repair resource counts in cylinder groups */ if (preen == 0) printf("** Phase 5 - Check Cyl groups\n"); pass5(); /* * print out summary statistics */ n_ffree = sblock.fs_cstotal.cs_nffree; n_bfree = sblock.fs_cstotal.cs_nbfree; pwarn("%d files, %d used, %d free ", n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree); printf("(%d frags, %d blocks, %.1f%% fragmentation)\n", n_ffree, n_bfree, (float)(n_ffree * 100) / sblock.fs_dsize); if (debug && (n_files -= imax - ROOTINO - sblock.fs_cstotal.cs_nifree)) printf("%d files missing\n", n_files); if (debug) { n_blks += sblock.fs_ncg * (cgdmin(&sblock, 0) - cgsblock(&sblock, 0)); n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0); n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize); if (n_blks -= fmax - (n_ffree + sblock.fs_frag * n_bfree)) printf("%d blocks missing\n", n_blks); if (duplist != NULL) { printf("The following duplicate blocks remain:"); for (dp = duplist; dp; dp = dp->next) printf(" %d,", dp->dup); printf("\n"); } if (zlnhead != NULL) { printf("The following zero link count inodes remain:"); for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) printf(" %d,", zlnp->zlncnt); printf("\n"); } } zlnhead = (struct zlncnt *)0; duplist = (struct dups *)0; if (dfile.mod) { (void)time(&sblock.fs_time); sbdirty(); } ckfini(); free(blockmap); free(statemap); free((char *)lncntp); if (!dfile.mod) return; if (!preen) { printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); if (hotroot) printf("\n***** REBOOT UNIX *****\n"); } if (hotroot) { sync(); exit(4); } } char * blockcheck(name) char *name; { struct stat stslash, stblock, stchar; char *raw; int looped = 0; hotroot = 0; if (stat("/", &stslash) < 0){ perror("/"); printf("Can't stat root\n"); return (0); } retry: if (stat(name, &stblock) < 0){ perror(name); printf("Can't stat %s\n", name); return (0); } if ((stblock.st_mode & S_IFMT) == S_IFBLK) { if (stslash.st_dev == stblock.st_rdev) { hotroot++; return (name); } raw = rawname(name); if (stat(raw, &stchar) < 0){ perror(raw); printf("Can't stat %s\n", raw); return (name); } if ((stchar.st_mode & S_IFMT) == S_IFCHR) return (raw); else { printf("%s is not a character device\n", raw); return (name); } } else if ((stblock.st_mode & S_IFMT) == S_IFCHR) { if (looped) { printf("Can't make sense out of name %s\n", name); return (0); } name = unrawname(name); looped++; goto retry; } printf("Can't make sense out of name %s\n", name); return (0); } char * unrawname(cp) char *cp; { char *dp = rindex(cp, '/'); struct stat stb; if (dp == 0) return (cp); if (stat(cp, &stb) < 0) return (cp); if ((stb.st_mode&S_IFMT) != S_IFCHR) return (cp); if (*(dp+1) != 'r') return (cp); (void)strcpy(dp+1, dp+2); return (cp); } char * rawname(cp) char *cp; { static char rawbuf[32]; char *dp = rindex(cp, '/'); if (dp == 0) return (0); *dp = 0; (void)strcpy(rawbuf, cp); *dp = '/'; (void)strcat(rawbuf, "/r"); (void)strcat(rawbuf, dp+1); return (rawbuf); }