386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Sat, 20 Apr 1991 21:39:06 +0000 (13:39 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Sat, 20 Apr 1991 21:39:06 +0000 (13:39 -0800)
Work on file usr/src/sbin/fsck/fsck.8
Work on file usr/src/sbin/fsck/fsck.h
Work on file usr/src/sbin/fsck/inode.c
Work on file usr/src/sbin/fsck/main.c
Work on file usr/src/sbin/fsck/pass1.c
Work on file usr/src/sbin/fsck/pass1b.c
Work on file usr/src/sbin/fsck/pass3.c
Work on file usr/src/sbin/fsck/pass4.c
Work on file usr/src/sbin/fsck/pass2.c
Work on file usr/src/sbin/fsck/pass5.c
Work on file usr/src/sbin/fsck/preen.c
Work on file usr/src/sbin/fsck/setup.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

12 files changed:
usr/src/sbin/fsck/fsck.8 [new file with mode: 0644]
usr/src/sbin/fsck/fsck.h [new file with mode: 0644]
usr/src/sbin/fsck/inode.c [new file with mode: 0644]
usr/src/sbin/fsck/main.c [new file with mode: 0644]
usr/src/sbin/fsck/pass1.c [new file with mode: 0644]
usr/src/sbin/fsck/pass1b.c [new file with mode: 0644]
usr/src/sbin/fsck/pass2.c [new file with mode: 0644]
usr/src/sbin/fsck/pass3.c [new file with mode: 0644]
usr/src/sbin/fsck/pass4.c [new file with mode: 0644]
usr/src/sbin/fsck/pass5.c [new file with mode: 0644]
usr/src/sbin/fsck/preen.c [new file with mode: 0644]
usr/src/sbin/fsck/setup.c [new file with mode: 0644]

diff --git a/usr/src/sbin/fsck/fsck.8 b/usr/src/sbin/fsck/fsck.8
new file mode 100644 (file)
index 0000000..a7dae66
--- /dev/null
@@ -0,0 +1,326 @@
+.\" Copyright (c) 1980, 1989 Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)fsck.8      6.9 (Berkeley) 4/20/91
+.\"
+.TH FSCK 8 "April 20, 1991"
+.UC 4
+.de us
+\\$1\l'|0\(ul'
+..
+.SH NAME
+fsck \- file system consistency check and interactive repair
+.SH SYNOPSIS
+.B fsck
+.B \-p
+[
+.B \-m
+mode
+]
+.br
+.B fsck
+[
+.B \-b
+block#
+] [
+.B \-c
+] [
+.B \-y
+] [
+.B \-n
+] [
+.B \-m
+mode
+] [
+filesystem 
+] ...
+.SH DESCRIPTION
+The first form of
+.I fsck
+preens a standard set of filesystems or the specified file systems.
+It is normally used in the script
+.B /etc/rc
+during automatic reboot.
+Here
+.I fsck
+reads the table
+.B /etc/fstab
+to determine which file systems to check.
+Only partitions in fstab that are mounted ``rw,'' ``rq'' or ``ro''
+and that have non-zero pass number are checked.
+Filesystems with pass number 1 (normally just the root filesystem)
+are checked one at a time.
+When pass 1 completes, all remaining filesystems are checked,
+running one process per disk drive.
+The disk drive containing each filesystem is inferred from the longest prefix
+of the device name that ends in a digit; the remaining characters are assumed
+to be the partition designator.
+.PP
+The system takes care that only a restricted class of innocuous
+inconsistencies can happen unless hardware or software failures intervene.
+These are limited to the following:
+.IP
+Unreferenced inodes
+.ns
+.IP
+Link counts in inodes too large
+.ns
+.IP
+Missing blocks in the free map
+.ns
+.IP
+Blocks in the free map also in files
+.ns
+.IP
+Counts in the super-block wrong
+.PP
+These are the only inconsistencies that
+.I fsck
+with the
+.B \-p
+option will correct; if it encounters other inconsistencies, it exits
+with an abnormal return status and an automatic reboot will then fail.
+For each corrected inconsistency one or more lines will be printed
+identifying the file system on which the correction will take place,
+and the nature of the correction.  After successfully correcting a file
+system,
+.I fsck
+will print the number of files on that file system,
+the number of used and free blocks,
+and the percentage of fragmentation.
+.PP
+If sent a QUIT signal,
+.I fsck
+will finish the file system checks, then exit with an abnormal
+return status that causes an automatic reboot to fail.
+This is useful when to finish the file system checks during an automatic reboot,
+but do not want the machine to come up multiuser after the checks complete.
+.PP
+Without the
+.B \-p
+option,
+.I fsck
+audits and interactively repairs inconsistent conditions for file systems. 
+If the file system is inconsistent the operator is prompted for concurrence
+before each correction is attempted.
+It should be noted that some of the corrective actions which are not
+correctable under the
+.B \-p
+option will result in some loss of data.
+The amount and severity of data lost may be determined from the diagnostic
+output.
+The default action for each consistency correction
+is to wait for the operator to respond \fByes\fP or \fBno\fP.
+If the operator does not have write permission on the file system
+.I fsck
+will default to a 
+.BR "\-n " action.
+.PP
+.I Fsck
+has more consistency checks than
+its predecessors
+.IR "check, dcheck, fcheck, " "and" " icheck"
+combined.
+.PP
+The following flags are interpreted by
+.I fsck.
+.TP 6
+.B \-b
+Use the block specified immediately after the flag as
+the super block for the file system.  Block 32 is usually
+an alternate super block.
+.TP 6
+.B \-l
+Limit the number of parallel checks to the number specified in the following
+argument.
+By default, the limit is the number of disks, running one process per disk.
+If a smaller limit is given, the disks are checked round-robin, one filesystem
+at a time.
+.TP 6
+.B \-m
+Use the mode specified in octal immediately after the flag as the
+permission bits to use when creating the lost+found directory
+rather than the default 1777.
+In particular, systems that do not wish to have lost files accessible
+by all users on the system should use a more restrictive
+set of permissions such as 700.
+.TP 6
+.B  \-y
+Assume a yes response to all questions asked by 
+.IR fsck ;
+this should be used with great caution as this is a free license
+to continue after essentially unlimited trouble has been encountered.
+.TP 6
+.B  \-n
+Assume a no response to all questions asked by 
+.I fsck
+except for ``CONTINUE?'', which is assumed to be affirmative;
+do not open the file system for writing.
+.TP 6
+.B  \-c
+If the file system is in the old (static table) format,
+convert it to the new (dynamic table) format.
+If the file system is in the new format,
+convert it to the old format provided the old format
+can support the filesystem configuration.
+In interactive mode,
+.I fsck
+will list the direction the conversion is to be made
+and ask whether the conversion should be done.
+If a negative answer is given,
+no further operations are done on the filesystem.
+In preen mode,
+the direction of the conversion is listed and done if
+possible without user interaction.
+Conversion in preen mode is best used when all the file systems
+are being converted at once.
+The format of a file system can be determined from the
+first line of output from 
+.IR dumpfs (8).
+.PP
+If no filesystems are given to 
+.I fsck
+then a default list of file systems is read from
+the file
+.BR /etc/fstab .
+.PP
+.ne 10
+Inconsistencies checked are as follows:
+.TP 6
+1.
+Blocks claimed by more than one inode or the free map.
+.br
+.br
+.ns
+.TP 6
+2.
+Blocks claimed by an inode outside the range of the file system.
+.br
+.br
+.ns
+.TP 6
+3.
+Incorrect link counts.
+.br
+.br
+.ns
+.TP 6
+4.
+Size checks:
+.br
+.ns
+.IP "" 12
+Directory size not of proper format.
+.br
+Partially truncated file.
+.br
+.br
+.ns
+.TP 6
+5.
+Bad inode format.
+.br
+.br
+.ns
+.TP 6
+6.
+Blocks not accounted for anywhere.
+.br
+.br
+.ns
+.TP 6
+7.
+Directory checks:
+.br
+.br
+.ns
+.IP "" 12
+File pointing to unallocated inode.
+.br
+Inode number out of range.
+.br
+Dot or dot-dot not the first two entries of a directory
+or having the wrong inode number.
+.br
+.br
+.ns
+.TP 6
+8.
+Super Block checks:
+.br
+.br
+.ns
+.IP "" 12
+More blocks for inodes than there are in the file system.
+.br
+.br
+.ns
+.TP 6
+9.
+Bad free block map format.
+.br
+.br
+.ns
+.TP 6
+10.
+Total free block and/or free inode count incorrect.
+.PP
+Orphaned files and directories (allocated but unreferenced) are,
+with the operator's concurrence, reconnected by
+placing them in the 
+.B lost+found
+directory.
+The name assigned is the inode number.
+If the
+.I lost+found
+directory does not exist, it is created.
+If there is insufficient space its size is increased.
+.PP
+Because of inconsistencies between the block device and the buffer cache,
+the raw device should always be used.
+.SH FILES
+.br
+.ns
+.TP 21
+/etc/fstab
+contains default list of file systems to check.
+.SH DIAGNOSTICS
+The diagnostics produced by 
+.I fsck
+are fully enumerated and explained in Appendix A of
+``Fsck \- The UNIX File System Check Program'' (SMM:5).
+.SH "SEE ALSO"
+fstab(5),
+fs(5),
+fsdb(8),
+newfs(8),
+mkfs(8),
+reboot(8)
diff --git a/usr/src/sbin/fsck/fsck.h b/usr/src/sbin/fsck/fsck.h
new file mode 100644 (file)
index 0000000..310bd21
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 1980, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)fsck.h      5.17 (Berkeley) 7/27/90
+ */
+
+#define        MAXDUP          10      /* limit on dup blks (per inode) */
+#define        MAXBAD          10      /* limit on bad blks (per inode) */
+#define        MAXBUFSPACE     40*1024 /* maximum space to allocate to buffers */
+#define        INOBUFSIZE      56*1024 /* size of buffer to read inodes in pass1 */
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+
+#define        USTATE  01              /* inode not allocated */
+#define        FSTATE  02              /* inode is file */
+#define        DSTATE  03              /* inode is directory */
+#define        DFOUND  04              /* directory found during descent */
+#define        DCLEAR  05              /* directory is to be cleared */
+#define        FCLEAR  06              /* file is to be cleared */
+
+/*
+ * buffer cache structure.
+ */
+struct bufarea {
+       struct bufarea  *b_next;                /* free list queue */
+       struct bufarea  *b_prev;                /* free list queue */
+       daddr_t b_bno;
+       int     b_size;
+       int     b_errs;
+       int     b_flags;
+       union {
+               char    *b_buf;                 /* buffer space */
+               daddr_t *b_indir;               /* indirect block */
+               struct  fs *b_fs;               /* super block */
+               struct  cg *b_cg;               /* cylinder group */
+               struct  dinode *b_dinode;       /* inode block */
+       } b_un;
+       char    b_dirty;
+};
+
+#define        B_INUSE 1
+
+#define        MINBUFS         5       /* minimum number of buffers required */
+struct bufarea bufhead;                /* head of list of other blks in filesys */
+struct bufarea sblk;           /* file system superblock */
+struct bufarea cgblk;          /* cylinder group blocks */
+struct bufarea *pdirbp;                /* current directory contents */
+struct bufarea *pbp;           /* current inode block */
+struct bufarea *getdatablk();
+
+#define        dirty(bp)       (bp)->b_dirty = 1
+#define        initbarea(bp) \
+       (bp)->b_dirty = 0; \
+       (bp)->b_bno = (daddr_t)-1; \
+       (bp)->b_flags = 0;
+
+#define        sbdirty()       sblk.b_dirty = 1
+#define        cgdirty()       cgblk.b_dirty = 1
+#define        sblock          (*sblk.b_un.b_fs)
+#define        cgrp            (*cgblk.b_un.b_cg)
+
+enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE};
+
+struct inodesc {
+       enum fixstate id_fix;   /* policy on fixing errors */
+       int (*id_func)();       /* function to be applied to blocks of inode */
+       ino_t id_number;        /* inode number described */
+       ino_t id_parent;        /* for DATA nodes, their parent */
+       daddr_t id_blkno;       /* current block number being examined */
+       int id_numfrags;        /* number of frags contained in block */
+       long id_filesize;       /* for DATA nodes, the size of the directory */
+       int id_loc;             /* for DATA nodes, current location in dir */
+       int id_entryno;         /* for DATA nodes, current entry number */
+       struct direct *id_dirp; /* for DATA nodes, ptr to current entry */
+       char *id_name;          /* for DATA nodes, name to find or enter */
+       char id_type;           /* type of descriptor, DATA or ADDR */
+};
+/* file types */
+#define        DATA    1
+#define        ADDR    2
+
+/*
+ * Linked list of duplicate blocks.
+ * 
+ * The list is composed of two parts. The first part of the
+ * list (from duplist through the node pointed to by muldup)
+ * contains a single copy of each duplicate block that has been 
+ * found. The second part of the list (from muldup to the end)
+ * contains duplicate blocks that have been found more than once.
+ * To check if a block has been found as a duplicate it is only
+ * necessary to search from duplist through muldup. To find the 
+ * total number of times that a block has been found as a duplicate
+ * the entire list must be searched for occurences of the block
+ * in question. The following diagram shows a sample list where
+ * w (found twice), x (found once), y (found three times), and z
+ * (found once) are duplicate block numbers:
+ *
+ *    w -> y -> x -> z -> y -> w -> y
+ *    ^                     ^
+ *    |                     |
+ * duplist       muldup
+ */
+struct dups {
+       struct dups *next;
+       daddr_t dup;
+};
+struct dups *duplist;          /* head of dup list */
+struct dups *muldup;           /* end of unique duplicate dup block numbers */
+
+/*
+ * Linked list of inodes with zero link counts.
+ */
+struct zlncnt {
+       struct zlncnt *next;
+       ino_t zlncnt;
+};
+struct zlncnt *zlnhead;                /* head of zero link count list */
+
+/*
+ * Inode cache data structures.
+ */
+struct inoinfo {
+       struct  inoinfo *i_nexthash;    /* next entry in hash chain */
+       ino_t   i_number;               /* inode number of this entry */
+       ino_t   i_parent;               /* inode number of parent */
+       ino_t   i_dotdot;               /* inode number of `..' */
+       size_t  i_isize;                /* size of inode */
+       u_int   i_numblks;              /* size of block array in bytes */
+       daddr_t i_blks[1];              /* actually longer */
+} **inphead, **inpsort;
+long numdirs, listmax, inplast;
+
+char   *devname;               /* name of device being checked */
+long   dev_bsize;              /* computed value of DEV_BSIZE */
+long   secsize;                /* actual disk sector size */
+char   nflag;                  /* assume a no response */
+char   yflag;                  /* assume a yes response */
+int    bflag;                  /* location of alternate super block */
+int    debug;                  /* output debugging info */
+int    cvtflag;                /* convert to old file system format */
+char   preen;                  /* just fix normal inconsistencies */
+char   hotroot;                /* checking root device */
+char   havesb;                 /* superblock has been read */
+int    fsmodified;             /* 1 => write done to file system */
+int    fsreadfd;               /* file descriptor for reading file system */
+int    fswritefd;              /* file descriptor for writing file system */
+
+daddr_t        maxfsblock;             /* number of blocks in the file system */
+char   *blockmap;              /* ptr to primary blk allocation map */
+ino_t  maxino;                 /* number of inodes in file system */
+ino_t  lastino;                /* last inode in use */
+char   *statemap;              /* ptr to inode state table */
+short  *lncntp;                /* ptr to link count table */
+
+ino_t  lfdir;                  /* lost & found directory inode number */
+char   *lfname;                /* lost & found directory name */
+int    lfmode;                 /* lost & found directory creation mode */
+
+daddr_t        n_blks;                 /* number of blocks in use */
+daddr_t        n_files;                /* number of files in use */
+
+#define        clearinode(dp)  (*(dp) = zino)
+struct dinode zino;
+
+#define        setbmap(blkno)  setbit(blockmap, blkno)
+#define        testbmap(blkno) isset(blockmap, blkno)
+#define        clrbmap(blkno)  clrbit(blockmap, blkno)
+
+#define        STOP    0x01
+#define        SKIP    0x02
+#define        KEEPON  0x04
+#define        ALTERED 0x08
+#define        FOUND   0x10
+
+time_t time();
+struct dinode *ginode();
+struct inoinfo *getinoinfo();
+void getblk();
+ino_t allocino();
+int findino();
diff --git a/usr/src/sbin/fsck/inode.c b/usr/src/sbin/fsck/inode.c
new file mode 100644 (file)
index 0000000..c87eb8f
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ * Copyright (c) 1980, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)inode.c    5.18 (Berkeley) 3/19/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <ufs/dir.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static ino_t startinum;
+
+ckinode(dp, idesc)
+       struct dinode *dp;
+       register struct inodesc *idesc;
+{
+       register daddr_t *ap;
+       long ret, n, ndb, offset;
+       struct dinode dino;
+
+       if (idesc->id_fix != IGNORE)
+               idesc->id_fix = DONTKNOW;
+       idesc->id_entryno = 0;
+       idesc->id_filesize = dp->di_size;
+       if ((dp->di_mode & IFMT) == IFBLK || (dp->di_mode & IFMT) == IFCHR)
+               return (KEEPON);
+       dino = *dp;
+       ndb = howmany(dino.di_size, sblock.fs_bsize);
+       for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) {
+               if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0)
+                       idesc->id_numfrags =
+                               numfrags(&sblock, fragroundup(&sblock, offset));
+               else
+                       idesc->id_numfrags = sblock.fs_frag;
+               if (*ap == 0)
+                       continue;
+               idesc->id_blkno = *ap;
+               if (idesc->id_type == ADDR)
+                       ret = (*idesc->id_func)(idesc);
+               else
+                       ret = dirscan(idesc);
+               if (ret & STOP)
+                       return (ret);
+       }
+       idesc->id_numfrags = sblock.fs_frag;
+       for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) {
+               if (*ap) {
+                       idesc->id_blkno = *ap;
+                       ret = iblock(idesc, n,
+                               dino.di_size - sblock.fs_bsize * NDADDR);
+                       if (ret & STOP)
+                               return (ret);
+               }
+       }
+       return (KEEPON);
+}
+
+iblock(idesc, ilevel, isize)
+       struct inodesc *idesc;
+       register long ilevel;
+       u_long isize;
+{
+       register daddr_t *ap;
+       register daddr_t *aplim;
+       int i, n, (*func)(), nif, sizepb;
+       register struct bufarea *bp;
+       char buf[BUFSIZ];
+       extern int dirscan(), pass1check();
+
+       if (idesc->id_type == ADDR) {
+               func = idesc->id_func;
+               if (((n = (*func)(idesc)) & KEEPON) == 0)
+                       return (n);
+       } else
+               func = dirscan;
+       if (chkrange(idesc->id_blkno, idesc->id_numfrags))
+               return (SKIP);
+       bp = getdatablk(idesc->id_blkno, sblock.fs_bsize);
+       ilevel--;
+       for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++)
+               sizepb *= NINDIR(&sblock);
+       nif = isize / sizepb + 1;
+       if (nif > NINDIR(&sblock))
+               nif = NINDIR(&sblock);
+       if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) {
+               aplim = &bp->b_un.b_indir[NINDIR(&sblock)];
+               for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) {
+                       if (*ap == 0)
+                               continue;
+                       (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu",
+                               idesc->id_number);
+                       if (dofix(idesc, buf)) {
+                               *ap = 0;
+                               dirty(bp);
+                       }
+               }
+               flush(fswritefd, bp);
+       }
+       aplim = &bp->b_un.b_indir[nif];
+       for (ap = bp->b_un.b_indir, i = 1; ap < aplim; ap++, i++) {
+               if (*ap) {
+                       idesc->id_blkno = *ap;
+                       if (ilevel > 0)
+                               n = iblock(idesc, ilevel, isize - i * sizepb);
+                       else
+                               n = (*func)(idesc);
+                       if (n & STOP) {
+                               bp->b_flags &= ~B_INUSE;
+                               return (n);
+                       }
+               }
+       }
+       bp->b_flags &= ~B_INUSE;
+       return (KEEPON);
+}
+
+/*
+ * Check that a block in a legal block number.
+ * Return 0 if in range, 1 if out of range.
+ */
+chkrange(blk, cnt)
+       daddr_t blk;
+       int cnt;
+{
+       register int c;
+
+       if ((unsigned)(blk + cnt) > maxfsblock)
+               return (1);
+       c = dtog(&sblock, blk);
+       if (blk < cgdmin(&sblock, c)) {
+               if ((blk + cnt) > cgsblock(&sblock, c)) {
+                       if (debug) {
+                               printf("blk %ld < cgdmin %ld;",
+                                   blk, cgdmin(&sblock, c));
+                               printf(" blk + cnt %ld > cgsbase %ld\n",
+                                   blk + cnt, cgsblock(&sblock, c));
+                       }
+                       return (1);
+               }
+       } else {
+               if ((blk + cnt) > cgbase(&sblock, c+1)) {
+                       if (debug)  {
+                               printf("blk %ld >= cgdmin %ld;",
+                                   blk, cgdmin(&sblock, c));
+                               printf(" blk + cnt %ld > sblock.fs_fpg %ld\n",
+                                   blk+cnt, sblock.fs_fpg);
+                       }
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+/*
+ * General purpose interface for reading inodes.
+ */
+struct dinode *
+ginode(inumber)
+       ino_t inumber;
+{
+       daddr_t iblk;
+
+       if (inumber < ROOTINO || inumber > maxino)
+               errexit("bad inode number %d to ginode\n", inumber);
+       if (startinum == 0 ||
+           inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
+               iblk = itod(&sblock, inumber);
+               if (pbp != 0)
+                       pbp->b_flags &= ~B_INUSE;
+               pbp = getdatablk(iblk, sblock.fs_bsize);
+               startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
+       }
+       return (&pbp->b_un.b_dinode[inumber % INOPB(&sblock)]);
+}
+
+/*
+ * Special purpose version of ginode used to optimize first pass
+ * over all the inodes in numerical order.
+ */
+ino_t nextino, lastinum;
+long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
+struct dinode *inodebuf;
+
+struct dinode *
+getnextinode(inumber)
+       ino_t inumber;
+{
+       long size;
+       daddr_t dblk;
+       static struct dinode *dp;
+
+       if (inumber != nextino++ || inumber > maxino)
+               errexit("bad inode number %d to nextinode\n", inumber);
+       if (inumber >= lastinum) {
+               readcnt++;
+               dblk = fsbtodb(&sblock, itod(&sblock, lastinum));
+               if (readcnt % readpercg == 0) {
+                       size = partialsize;
+                       lastinum += partialcnt;
+               } else {
+                       size = inobufsize;
+                       lastinum += fullcnt;
+               }
+               (void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */
+               dp = inodebuf;
+       }
+       return (dp++);
+}
+
+resetinodebuf()
+{
+
+       startinum = 0;
+       nextino = 0;
+       lastinum = 0;
+       readcnt = 0;
+       inobufsize = blkroundup(&sblock, INOBUFSIZE);
+       fullcnt = inobufsize / sizeof(struct dinode);
+       readpercg = sblock.fs_ipg / fullcnt;
+       partialcnt = sblock.fs_ipg % fullcnt;
+       partialsize = partialcnt * sizeof(struct dinode);
+       if (partialcnt != 0) {
+               readpercg++;
+       } else {
+               partialcnt = fullcnt;
+               partialsize = inobufsize;
+       }
+       if (inodebuf == NULL &&
+           (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL)
+               errexit("Cannot allocate space for inode buffer\n");
+       while (nextino < ROOTINO)
+               (void)getnextinode(nextino);
+}
+
+freeinodebuf()
+{
+
+       if (inodebuf != NULL)
+               free((char *)inodebuf);
+       inodebuf = NULL;
+}
+
+/*
+ * Routines to maintain information about directory inodes.
+ * This is built during the first pass and used during the
+ * second and third passes.
+ *
+ * Enter inodes into the cache.
+ */
+cacheino(dp, inumber)
+       register struct dinode *dp;
+       ino_t inumber;
+{
+       register struct inoinfo *inp;
+       struct inoinfo **inpp;
+       unsigned int blks;
+
+       blks = howmany(dp->di_size, sblock.fs_bsize);
+       if (blks > NDADDR)
+               blks = NDADDR + NIADDR;
+       inp = (struct inoinfo *)
+               malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t));
+       if (inp == NULL)
+               return;
+       inpp = &inphead[inumber % numdirs];
+       inp->i_nexthash = *inpp;
+       *inpp = inp;
+       inp->i_parent = (ino_t)0;
+       inp->i_dotdot = (ino_t)0;
+       inp->i_number = inumber;
+       inp->i_isize = dp->di_size;
+       inp->i_numblks = blks * sizeof(daddr_t);
+       bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0],
+           (size_t)inp->i_numblks);
+       if (inplast == listmax) {
+               listmax += 100;
+               inpsort = (struct inoinfo **)realloc((char *)inpsort,
+                   (unsigned)listmax * sizeof(struct inoinfo *));
+               if (inpsort == NULL)
+                       errexit("cannot increase directory list");
+       }
+       inpsort[inplast++] = inp;
+}
+
+/*
+ * Look up an inode cache structure.
+ */
+struct inoinfo *
+getinoinfo(inumber)
+       ino_t inumber;
+{
+       register struct inoinfo *inp;
+
+       for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
+               if (inp->i_number != inumber)
+                       continue;
+               return (inp);
+       }
+       errexit("cannot find inode %d\n", inumber);
+       return ((struct inoinfo *)0);
+}
+
+/*
+ * Clean up all the inode cache structure.
+ */
+inocleanup()
+{
+       register struct inoinfo **inpp;
+
+       if (inphead == NULL)
+               return;
+       for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
+               free((char *)(*inpp));
+       free((char *)inphead);
+       free((char *)inpsort);
+       inphead = inpsort = NULL;
+}
+       
+inodirty()
+{
+       
+       dirty(pbp);
+}
+
+clri(idesc, type, flag)
+       register struct inodesc *idesc;
+       char *type;
+       int flag;
+{
+       register struct dinode *dp;
+
+       dp = ginode(idesc->id_number);
+       if (flag == 1) {
+               pwarn("%s %s", type,
+                   (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE");
+               pinode(idesc->id_number);
+       }
+       if (preen || reply("CLEAR") == 1) {
+               if (preen)
+                       printf(" (CLEARED)\n");
+               n_files--;
+               (void)ckinode(dp, idesc);
+               clearinode(dp);
+               statemap[idesc->id_number] = USTATE;
+               inodirty();
+       }
+}
+
+findname(idesc)
+       struct inodesc *idesc;
+{
+       register struct direct *dirp = idesc->id_dirp;
+
+       if (dirp->d_ino != idesc->id_parent)
+               return (KEEPON);
+       bcopy(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1);
+       return (STOP|FOUND);
+}
+
+findino(idesc)
+       struct inodesc *idesc;
+{
+       register struct direct *dirp = idesc->id_dirp;
+
+       if (dirp->d_ino == 0)
+               return (KEEPON);
+       if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
+           dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) {
+               idesc->id_parent = dirp->d_ino;
+               return (STOP|FOUND);
+       }
+       return (KEEPON);
+}
+
+pinode(ino)
+       ino_t ino;
+{
+       register struct dinode *dp;
+       register char *p;
+       struct passwd *pw;
+       char *ctime();
+
+       printf(" I=%lu ", ino);
+       if (ino < ROOTINO || ino > maxino)
+               return;
+       dp = ginode(ino);
+       printf(" OWNER=");
+       if ((pw = getpwuid((int)dp->di_uid)) != 0)
+               printf("%s ", pw->pw_name);
+       else
+               printf("%u ", (unsigned)dp->di_uid);
+       printf("MODE=%o\n", dp->di_mode);
+       if (preen)
+               printf("%s: ", devname);
+       printf("SIZE=%lu ", dp->di_size);
+       p = ctime(&dp->di_mtime);
+       printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+}
+
+blkerror(ino, type, blk)
+       ino_t ino;
+       char *type;
+       daddr_t blk;
+{
+
+       pfatal("%ld %s I=%lu", blk, type, ino);
+       printf("\n");
+       switch (statemap[ino]) {
+
+       case FSTATE:
+               statemap[ino] = FCLEAR;
+               return;
+
+       case DSTATE:
+               statemap[ino] = DCLEAR;
+               return;
+
+       case FCLEAR:
+       case DCLEAR:
+               return;
+
+       default:
+               errexit("BAD STATE %d TO BLKERR", statemap[ino]);
+               /* NOTREACHED */
+       }
+}
+
+/*
+ * allocate an unused inode
+ */
+ino_t
+allocino(request, type)
+       ino_t request;
+       int type;
+{
+       register ino_t ino;
+       register struct dinode *dp;
+
+       if (request == 0)
+               request = ROOTINO;
+       else if (statemap[request] != USTATE)
+               return (0);
+       for (ino = request; ino < maxino; ino++)
+               if (statemap[ino] == USTATE)
+                       break;
+       if (ino == maxino)
+               return (0);
+       switch (type & IFMT) {
+       case IFDIR:
+               statemap[ino] = DSTATE;
+               break;
+       case IFREG:
+       case IFLNK:
+               statemap[ino] = FSTATE;
+               break;
+       default:
+               return (0);
+       }
+       dp = ginode(ino);
+       dp->di_db[0] = allocblk((long)1);
+       if (dp->di_db[0] == 0) {
+               statemap[ino] = USTATE;
+               return (0);
+       }
+       dp->di_mode = type;
+       (void)time(&dp->di_atime);
+       dp->di_mtime = dp->di_ctime = dp->di_atime;
+       dp->di_size = sblock.fs_fsize;
+       dp->di_blocks = btodb(sblock.fs_fsize);
+       n_files++;
+       inodirty();
+       return (ino);
+}
+
+/*
+ * deallocate an inode
+ */
+freeino(ino)
+       ino_t ino;
+{
+       struct inodesc idesc;
+       extern int pass4check();
+       struct dinode *dp;
+
+       bzero((char *)&idesc, sizeof(struct inodesc));
+       idesc.id_type = ADDR;
+       idesc.id_func = pass4check;
+       idesc.id_number = ino;
+       dp = ginode(ino);
+       (void)ckinode(dp, &idesc);
+       clearinode(dp);
+       inodirty();
+       statemap[ino] = USTATE;
+       n_files--;
+}
diff --git a/usr/src/sbin/fsck/main.c b/usr/src/sbin/fsck/main.c
new file mode 100644 (file)
index 0000000..8c2838f
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 1980, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980, 1986 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c     5.27 (Berkeley) 8/7/90";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <fstab.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include "fsck.h"
+
+void   catch(), catchquit(), voidquit();
+int    returntosingle;
+
+main(argc, argv)
+       int     argc;
+       char    *argv[];
+{
+       int ch;
+       int ret, maxrun = 0;
+       extern int docheck(), checkfilesys();
+       extern char *optarg;
+       extern int optind;
+
+       sync();
+       while ((ch = getopt(argc, argv, "cdpnNyYb:l:m:")) != EOF) {
+               switch (ch) {
+               case 'p':
+                       preen++;
+                       break;
+
+               case 'b':
+                       bflag = argtoi('b', "number", optarg, 10);
+                       printf("Alternate super block location: %d\n", bflag);
+                       break;
+
+               case 'c':
+                       cvtflag++;
+                       break;
+
+               case 'd':
+                       debug++;
+                       break;
+
+               case 'l':
+                       maxrun = argtoi('l', "number", optarg, 10);
+                       break;
+
+               case 'm':
+                       lfmode = argtoi('m', "mode", optarg, 8);
+                       if (lfmode &~ 07777)
+                               errexit("bad mode to -m: %o\n", lfmode);
+                       printf("** lost+found creation mode %o\n", lfmode);
+                       break;
+
+               case 'n':
+               case 'N':
+                       nflag++;
+                       yflag = 0;
+                       break;
+
+               case 'y':
+               case 'Y':
+                       yflag++;
+                       nflag = 0;
+                       break;
+
+               default:
+                       errexit("%c option?\n", ch);
+               }
+       }
+       argc -= optind;
+       argv += optind;
+       if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+               (void)signal(SIGINT, catch);
+       if (preen)
+               (void)signal(SIGQUIT, catchquit);
+       if (argc) {
+               while (argc-- > 0)
+                       (void)checkfilesys(*argv++, (char *)0, 0L, 0);
+               exit(0);
+       }
+       ret = checkfstab(preen, maxrun, docheck, checkfilesys);
+       if (returntosingle)
+               exit(2);
+       exit(ret);
+}
+
+argtoi(flag, req, str, base)
+       int flag;
+       char *req, *str;
+       int base;
+{
+       char *cp;
+       int ret;
+
+       ret = (int)strtol(str, &cp, base);
+       if (cp == str || *cp)
+               errexit("-%c flag requires a %s\n", flag, req);
+       return (ret);
+}
+
+/*
+ * Determine whether a filesystem should be checked.
+ */
+docheck(fsp)
+       register struct fstab *fsp;
+{
+
+       if (strcmp(fsp->fs_vfstype, "ufs") ||
+           (strcmp(fsp->fs_type, FSTAB_RW) &&
+            strcmp(fsp->fs_type, FSTAB_RO)) ||
+           fsp->fs_passno == 0)
+               return (0);
+       return (1);
+}
+
+/*
+ * Check the specified filesystem.
+ */
+/* ARGSUSED */
+checkfilesys(filesys, mntpt, auxdata, child)
+       char *filesys, *mntpt;
+       long auxdata;
+{
+       daddr_t n_ffree, n_bfree;
+       struct dups *dp;
+       struct zlncnt *zlnp;
+
+       if (preen && child)
+               (void)signal(SIGQUIT, voidquit);
+       devname = filesys;
+       if (debug && preen)
+               pwarn("starting\n");
+       if (setup(filesys) == 0) {
+               if (preen)
+                       pfatal("CAN'T CHECK FILE SYSTEM.");
+               return (0);
+       }
+       /*
+        * 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("%ld files, %ld used, %ld free ",
+           n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree);
+       printf("(%ld frags, %ld blocks, %.1f%% fragmentation)\n",
+           n_ffree, n_bfree, (float)(n_ffree * 100) / sblock.fs_dsize);
+       if (debug &&
+           (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree))
+               printf("%ld 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 -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree))
+                       printf("%ld blocks missing\n", n_blks);
+               if (duplist != NULL) {
+                       printf("The following duplicate blocks remain:");
+                       for (dp = duplist; dp; dp = dp->next)
+                               printf(" %ld,", dp->dup);
+                       printf("\n");
+               }
+               if (zlnhead != NULL) {
+                       printf("The following zero link count inodes remain:");
+                       for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
+                               printf(" %lu,", zlnp->zlncnt);
+                       printf("\n");
+               }
+       }
+       zlnhead = (struct zlncnt *)0;
+       duplist = (struct dups *)0;
+       inocleanup();
+       if (fsmodified) {
+               (void)time(&sblock.fs_time);
+               sbdirty();
+       }
+       ckfini();
+       free(blockmap);
+       free(statemap);
+       free((char *)lncntp);
+       if (!fsmodified)
+               return (0);
+       if (!preen) {
+               printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
+               if (hotroot)
+                       printf("\n***** REBOOT UNIX *****\n");
+       }
+       if (hotroot) {
+               sync();
+               return (4);
+       }
+       return (0);
+}
diff --git a/usr/src/sbin/fsck/pass1.c b/usr/src/sbin/fsck/pass1.c
new file mode 100644 (file)
index 0000000..ffea6cb
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 1980, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pass1.c    5.16 (Berkeley) 3/19/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static daddr_t badblk;
+static daddr_t dupblk;
+int pass1check();
+struct dinode *getnextinode();
+
+pass1()
+{
+       register int c, i, j;
+       register struct dinode *dp;
+       struct zlncnt *zlnp;
+       int ndb, cgd;
+       struct inodesc idesc;
+       ino_t inumber;
+
+       /*
+        * Set file system reserved blocks in used block map.
+        */
+       for (c = 0; c < sblock.fs_ncg; c++) {
+               cgd = cgdmin(&sblock, c);
+               if (c == 0) {
+                       i = cgbase(&sblock, c);
+                       cgd += howmany(sblock.fs_cssize, sblock.fs_fsize);
+               } else
+                       i = cgsblock(&sblock, c);
+               for (; i < cgd; i++)
+                       setbmap(i);
+       }
+       /*
+        * Find all allocated blocks.
+        */
+       bzero((char *)&idesc, sizeof(struct inodesc));
+       idesc.id_type = ADDR;
+       idesc.id_func = pass1check;
+       inumber = 0;
+       n_files = n_blks = 0;
+       resetinodebuf();
+       for (c = 0; c < sblock.fs_ncg; c++) {
+               for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
+                       if (inumber < ROOTINO)
+                               continue;
+                       dp = getnextinode(inumber);
+                       if ((dp->di_mode & IFMT) == 0) {
+                               if (bcmp((char *)dp->di_db, (char *)zino.di_db,
+                                       NDADDR * sizeof(daddr_t)) ||
+                                   bcmp((char *)dp->di_ib, (char *)zino.di_ib,
+                                       NIADDR * sizeof(daddr_t)) ||
+                                   dp->di_mode || dp->di_size) {
+                                       pfatal("PARTIALLY ALLOCATED INODE I=%lu",
+                                               inumber);
+                                       if (reply("CLEAR") == 1) {
+                                               dp = ginode(inumber);
+                                               clearinode(dp);
+                                               inodirty();
+                                       }
+                               }
+                               statemap[inumber] = USTATE;
+                               continue;
+                       }
+                       lastino = inumber;
+                       if (/* dp->di_size < 0 || */
+                           dp->di_size + sblock.fs_bsize - 1 < dp->di_size) {
+                               if (debug)
+                                       printf("bad size %lu:", dp->di_size);
+                               goto unknown;
+                       }
+                       if (!preen && (dp->di_mode & IFMT) == IFMT &&
+                           reply("HOLD BAD BLOCK") == 1) {
+                               dp = ginode(inumber);
+                               dp->di_size = sblock.fs_fsize;
+                               dp->di_mode = IFREG|0600;
+                               inodirty();
+                       }
+                       ndb = howmany(dp->di_size, sblock.fs_bsize);
+                       if (ndb < 0) {
+                               if (debug)
+                                       printf("bad size %lu ndb %d:",
+                                               dp->di_size, ndb);
+                               goto unknown;
+                       }
+                       if ((dp->di_mode & IFMT) == IFBLK ||
+                           (dp->di_mode & IFMT) == IFCHR)
+                               ndb++;
+                       for (j = ndb; j < NDADDR; j++)
+                               if (dp->di_db[j] != 0) {
+                                       if (debug)
+                                               printf("bad direct addr: %ld\n",
+                                                       dp->di_db[j]);
+                                       goto unknown;
+                               }
+                       for (j = 0, ndb -= NDADDR; ndb > 0; j++)
+                               ndb /= NINDIR(&sblock);
+                       for (; j < NIADDR; j++)
+                               if (dp->di_ib[j] != 0) {
+                                       if (debug)
+                                               printf("bad indirect addr: %ld\n",
+                                                       dp->di_ib[j]);
+                                       goto unknown;
+                               }
+                       if (ftypeok(dp) == 0)
+                               goto unknown;
+                       n_files++;
+                       lncntp[inumber] = dp->di_nlink;
+                       if (dp->di_nlink <= 0) {
+                               zlnp = (struct zlncnt *)malloc(sizeof *zlnp);
+                               if (zlnp == NULL) {
+                                       pfatal("LINK COUNT TABLE OVERFLOW");
+                                       if (reply("CONTINUE") == 0)
+                                               errexit("");
+                               } else {
+                                       zlnp->zlncnt = inumber;
+                                       zlnp->next = zlnhead;
+                                       zlnhead = zlnp;
+                               }
+                       }
+                       if ((dp->di_mode & IFMT) == IFDIR) {
+                               if (dp->di_size == 0)
+                                       statemap[inumber] = DCLEAR;
+                               else
+                                       statemap[inumber] = DSTATE;
+                               cacheino(dp, inumber);
+                       } else
+                               statemap[inumber] = FSTATE;
+                       badblk = dupblk = 0;
+                       idesc.id_number = inumber;
+                       (void)ckinode(dp, &idesc);
+                       idesc.id_entryno *= btodb(sblock.fs_fsize);
+                       if (dp->di_blocks != idesc.id_entryno) {
+                               pwarn("INCORRECT BLOCK COUNT I=%lu (%ld should be %ld)",
+                                   inumber, dp->di_blocks, idesc.id_entryno);
+                               if (preen)
+                                       printf(" (CORRECTED)\n");
+                               else if (reply("CORRECT") == 0)
+                                       continue;
+                               dp = ginode(inumber);
+                               dp->di_blocks = idesc.id_entryno;
+                               inodirty();
+                       }
+                       continue;
+       unknown:
+                       pfatal("UNKNOWN FILE TYPE I=%lu", inumber);
+                       statemap[inumber] = FCLEAR;
+                       if (reply("CLEAR") == 1) {
+                               statemap[inumber] = USTATE;
+                               dp = ginode(inumber);
+                               clearinode(dp);
+                               inodirty();
+                       }
+               }
+       }
+       freeinodebuf();
+}
+
+pass1check(idesc)
+       register struct inodesc *idesc;
+{
+       int res = KEEPON;
+       int anyout, nfrags;
+       daddr_t blkno = idesc->id_blkno;
+       register struct dups *dlp;
+       struct dups *new;
+
+       if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) {
+               blkerror(idesc->id_number, "BAD", blkno);
+               if (badblk++ >= MAXBAD) {
+                       pwarn("EXCESSIVE BAD BLKS I=%lu",
+                               idesc->id_number);
+                       if (preen)
+                               printf(" (SKIPPING)\n");
+                       else if (reply("CONTINUE") == 0)
+                               errexit("");
+                       return (STOP);
+               }
+       }
+       for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+               if (anyout && chkrange(blkno, 1)) {
+                       res = SKIP;
+               } else if (!testbmap(blkno)) {
+                       n_blks++;
+                       setbmap(blkno);
+               } else {
+                       blkerror(idesc->id_number, "DUP", blkno);
+                       if (dupblk++ >= MAXDUP) {
+                               pwarn("EXCESSIVE DUP BLKS I=%lu",
+                                       idesc->id_number);
+                               if (preen)
+                                       printf(" (SKIPPING)\n");
+                               else if (reply("CONTINUE") == 0)
+                                       errexit("");
+                               return (STOP);
+                       }
+                       new = (struct dups *)malloc(sizeof(struct dups));
+                       if (new == NULL) {
+                               pfatal("DUP TABLE OVERFLOW.");
+                               if (reply("CONTINUE") == 0)
+                                       errexit("");
+                               return (STOP);
+                       }
+                       new->dup = blkno;
+                       if (muldup == 0) {
+                               duplist = muldup = new;
+                               new->next = 0;
+                       } else {
+                               new->next = muldup->next;
+                               muldup->next = new;
+                       }
+                       for (dlp = duplist; dlp != muldup; dlp = dlp->next)
+                               if (dlp->dup == blkno)
+                                       break;
+                       if (dlp == muldup && dlp->dup != blkno)
+                               muldup = new;
+               }
+               /*
+                * count the number of blocks found in id_entryno
+                */
+               idesc->id_entryno++;
+       }
+       return (res);
+}
diff --git a/usr/src/sbin/fsck/pass1b.c b/usr/src/sbin/fsck/pass1b.c
new file mode 100644 (file)
index 0000000..8537162
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1980, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pass1b.c   5.8 (Berkeley) 7/20/90";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <string.h>
+#include "fsck.h"
+
+int    pass1bcheck();
+static  struct dups *duphead;
+
+pass1b()
+{
+       register int c, i;
+       register struct dinode *dp;
+       struct inodesc idesc;
+       ino_t inumber;
+
+       bzero((char *)&idesc, sizeof(struct inodesc));
+       idesc.id_type = ADDR;
+       idesc.id_func = pass1bcheck;
+       duphead = duplist;
+       inumber = 0;
+       for (c = 0; c < sblock.fs_ncg; c++) {
+               for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
+                       if (inumber < ROOTINO)
+                               continue;
+                       dp = ginode(inumber);
+                       if (dp == NULL)
+                               continue;
+                       idesc.id_number = inumber;
+                       if (statemap[inumber] != USTATE &&
+                           (ckinode(dp, &idesc) & STOP))
+                               return;
+               }
+       }
+}
+
+pass1bcheck(idesc)
+       register struct inodesc *idesc;
+{
+       register struct dups *dlp;
+       int nfrags, res = KEEPON;
+       daddr_t blkno = idesc->id_blkno;
+
+       for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+               if (chkrange(blkno, 1))
+                       res = SKIP;
+               for (dlp = duphead; dlp; dlp = dlp->next) {
+                       if (dlp->dup == blkno) {
+                               blkerror(idesc->id_number, "DUP", blkno);
+                               dlp->dup = duphead->dup;
+                               duphead->dup = blkno;
+                               duphead = duphead->next;
+                       }
+                       if (dlp == muldup)
+                               break;
+               }
+               if (muldup == 0 || duphead == muldup->next)
+                       return (STOP);
+       }
+       return (res);
+}
diff --git a/usr/src/sbin/fsck/pass2.c b/usr/src/sbin/fsck/pass2.c
new file mode 100644 (file)
index 0000000..70bc605
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 1980, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pass2.c    5.17 (Berkeley) 12/28/90";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#define KERNEL
+#include <ufs/dir.h>
+#undef KERNEL
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+#define MINDIRSIZE     (sizeof (struct dirtemplate))
+
+int    pass2check(), blksort();
+
+pass2()
+{
+       register struct dinode *dp;
+       register struct inoinfo **inpp, *inp;
+       struct inoinfo **inpend;
+       struct inodesc curino;
+       struct dinode dino;
+       char pathbuf[MAXPATHLEN + 1];
+
+       switch (statemap[ROOTINO]) {
+
+       case USTATE:
+               pfatal("ROOT INODE UNALLOCATED");
+               if (reply("ALLOCATE") == 0)
+                       errexit("");
+               if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+                       errexit("CANNOT ALLOCATE ROOT INODE\n");
+               break;
+
+       case DCLEAR:
+               pfatal("DUPS/BAD IN ROOT INODE");
+               if (reply("REALLOCATE")) {
+                       freeino(ROOTINO);
+                       if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+                               errexit("CANNOT ALLOCATE ROOT INODE\n");
+                       break;
+               }
+               if (reply("CONTINUE") == 0)
+                       errexit("");
+               break;
+
+       case FSTATE:
+       case FCLEAR:
+               pfatal("ROOT INODE NOT DIRECTORY");
+               if (reply("REALLOCATE")) {
+                       freeino(ROOTINO);
+                       if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+                               errexit("CANNOT ALLOCATE ROOT INODE\n");
+                       break;
+               }
+               if (reply("FIX") == 0)
+                       errexit("");
+               dp = ginode(ROOTINO);
+               dp->di_mode &= ~IFMT;
+               dp->di_mode |= IFDIR;
+               inodirty();
+               break;
+
+       case DSTATE:
+               break;
+
+       default:
+               errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
+       }
+       statemap[ROOTINO] = DFOUND;
+       /*
+        * Sort the directory list into disk block order.
+        */
+       qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
+       /*
+        * Check the integrity of each directory.
+        */
+       bzero((char *)&curino, sizeof(struct inodesc));
+       curino.id_type = DATA;
+       curino.id_func = pass2check;
+       dino.di_mode = IFDIR;
+       dp = &dino;
+       inpend = &inpsort[inplast];
+       for (inpp = inpsort; inpp < inpend; inpp++) {
+               inp = *inpp;
+               if (inp->i_isize == 0)
+                       continue;
+               if (inp->i_isize < MINDIRSIZE) {
+                       direrror(inp->i_number, "DIRECTORY TOO SHORT");
+                       inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
+                       if (reply("FIX") == 1) {
+                               dp = ginode(inp->i_number);
+                               dp->di_size = inp->i_isize;
+                               inodirty();
+                               dp = &dino;
+                       }
+               } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
+                       getpathname(pathbuf, inp->i_number, inp->i_number);
+                       pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
+                               pathbuf, inp->i_isize, DIRBLKSIZ);
+                       if (preen)
+                               printf(" (ADJUSTED)\n");
+                       inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
+                       if (preen || reply("ADJUST") == 1) {
+                               dp = ginode(inp->i_number);
+                               dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
+                               inodirty();
+                               dp = &dino;
+                       }
+               }
+               dp->di_size = inp->i_isize;
+               bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0],
+                       (size_t)inp->i_numblks);
+               curino.id_number = inp->i_number;
+               curino.id_parent = inp->i_parent;
+               (void)ckinode(dp, &curino);
+       }
+       /*
+        * Now that the parents of all directories have been found,
+        * make another pass to verify the value of `..'
+        */
+       for (inpp = inpsort; inpp < inpend; inpp++) {
+               inp = *inpp;
+               if (inp->i_parent == 0 || inp->i_isize == 0)
+                       continue;
+               if (statemap[inp->i_parent] == DFOUND &&
+                   statemap[inp->i_number] == DSTATE)
+                       statemap[inp->i_number] = DFOUND;
+               if (inp->i_dotdot == inp->i_parent ||
+                   inp->i_dotdot == (ino_t)-1)
+                       continue;
+               if (inp->i_dotdot == 0) {
+                       inp->i_dotdot = inp->i_parent;
+                       fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
+                       if (reply("FIX") == 0)
+                               continue;
+                       (void)makeentry(inp->i_number, inp->i_parent, "..");
+                       lncntp[inp->i_parent]--;
+                       continue;
+               }
+               fileerror(inp->i_parent, inp->i_number,
+                       "BAD INODE NUMBER FOR '..'");
+               if (reply("FIX") == 0)
+                       continue;
+               lncntp[inp->i_dotdot]++;
+               lncntp[inp->i_parent]--;
+               inp->i_dotdot = inp->i_parent;
+               (void)changeino(inp->i_number, "..", inp->i_parent);
+       }
+       /*
+        * Mark all the directories that can be found from the root.
+        */
+       propagate();
+}
+
+pass2check(idesc)
+       struct inodesc *idesc;
+{
+       register struct direct *dirp = idesc->id_dirp;
+       register struct inoinfo *inp;
+       int n, entrysize, ret = 0;
+       struct dinode *dp;
+       char *errmsg;
+       struct direct proto;
+       char namebuf[MAXPATHLEN + 1];
+       char pathbuf[MAXPATHLEN + 1];
+
+       /* 
+        * check for "."
+        */
+       if (idesc->id_entryno != 0)
+               goto chk1;
+       if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
+               if (dirp->d_ino != idesc->id_number) {
+                       direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
+                       dirp->d_ino = idesc->id_number;
+                       if (reply("FIX") == 1)
+                               ret |= ALTERED;
+               }
+               goto chk1;
+       }
+       direrror(idesc->id_number, "MISSING '.'");
+       proto.d_ino = idesc->id_number;
+       proto.d_namlen = 1;
+       (void)strcpy(proto.d_name, ".");
+       entrysize = DIRSIZ(&proto);
+       if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
+               pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
+                       dirp->d_name);
+       } else if (dirp->d_reclen < entrysize) {
+               pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
+       } else if (dirp->d_reclen < 2 * entrysize) {
+               proto.d_reclen = dirp->d_reclen;
+               bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
+               if (reply("FIX") == 1)
+                       ret |= ALTERED;
+       } else {
+               n = dirp->d_reclen - entrysize;
+               proto.d_reclen = entrysize;
+               bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
+               idesc->id_entryno++;
+               lncntp[dirp->d_ino]--;
+               dirp = (struct direct *)((char *)(dirp) + entrysize);
+               bzero((char *)dirp, (size_t)n);
+               dirp->d_reclen = n;
+               if (reply("FIX") == 1)
+                       ret |= ALTERED;
+       }
+chk1:
+       if (idesc->id_entryno > 1)
+               goto chk2;
+       inp = getinoinfo(idesc->id_number);
+       proto.d_ino = inp->i_parent;
+       proto.d_namlen = 2;
+       (void)strcpy(proto.d_name, "..");
+       entrysize = DIRSIZ(&proto);
+       if (idesc->id_entryno == 0) {
+               n = DIRSIZ(dirp);
+               if (dirp->d_reclen < n + entrysize)
+                       goto chk2;
+               proto.d_reclen = dirp->d_reclen - n;
+               dirp->d_reclen = n;
+               idesc->id_entryno++;
+               lncntp[dirp->d_ino]--;
+               dirp = (struct direct *)((char *)(dirp) + n);
+               bzero((char *)dirp, (size_t)proto.d_reclen);
+               dirp->d_reclen = proto.d_reclen;
+       }
+       if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
+               inp->i_dotdot = dirp->d_ino;
+               goto chk2;
+       }
+       if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
+               fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+               pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
+                       dirp->d_name);
+               inp->i_dotdot = (ino_t)-1;
+       } else if (dirp->d_reclen < entrysize) {
+               fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+               pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
+               inp->i_dotdot = (ino_t)-1;
+       } else if (inp->i_parent != 0) {
+               /*
+                * We know the parent, so fix now.
+                */
+               inp->i_dotdot = inp->i_parent;
+               fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+               proto.d_reclen = dirp->d_reclen;
+               bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
+               if (reply("FIX") == 1)
+                       ret |= ALTERED;
+       }
+       idesc->id_entryno++;
+       if (dirp->d_ino != 0)
+               lncntp[dirp->d_ino]--;
+       return (ret|KEEPON);
+chk2:
+       if (dirp->d_ino == 0)
+               return (ret|KEEPON);
+       if (dirp->d_namlen <= 2 &&
+           dirp->d_name[0] == '.' &&
+           idesc->id_entryno >= 2) {
+               if (dirp->d_namlen == 1) {
+                       direrror(idesc->id_number, "EXTRA '.' ENTRY");
+                       dirp->d_ino = 0;
+                       if (reply("FIX") == 1)
+                               ret |= ALTERED;
+                       return (KEEPON | ret);
+               }
+               if (dirp->d_name[1] == '.') {
+                       direrror(idesc->id_number, "EXTRA '..' ENTRY");
+                       dirp->d_ino = 0;
+                       if (reply("FIX") == 1)
+                               ret |= ALTERED;
+                       return (KEEPON | ret);
+               }
+       }
+       idesc->id_entryno++;
+       n = 0;
+       if (dirp->d_ino > maxino) {
+               fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
+               n = reply("REMOVE");
+       } else {
+again:
+               switch (statemap[dirp->d_ino]) {
+               case USTATE:
+                       if (idesc->id_entryno <= 2)
+                               break;
+                       fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
+                       n = reply("REMOVE");
+                       break;
+
+               case DCLEAR:
+               case FCLEAR:
+                       if (idesc->id_entryno <= 2)
+                               break;
+                       if (statemap[dirp->d_ino] == DCLEAR)
+                               errmsg = "ZERO LENGTH DIRECTORY";
+                       else
+                               errmsg = "DUP/BAD";
+                       fileerror(idesc->id_number, dirp->d_ino, errmsg);
+                       if ((n = reply("REMOVE")) == 1)
+                               break;
+                       dp = ginode(dirp->d_ino);
+                       statemap[dirp->d_ino] =
+                           (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
+                       lncntp[dirp->d_ino] = dp->di_nlink;
+                       goto again;
+
+               case DSTATE:
+                       if (statemap[idesc->id_number] == DFOUND)
+                               statemap[dirp->d_ino] = DFOUND;
+                       /* fall through */
+
+               case DFOUND:
+                       inp = getinoinfo(dirp->d_ino);
+                       if (inp->i_parent != 0 && idesc->id_entryno > 2) {
+                               getpathname(pathbuf, idesc->id_number,
+                                   idesc->id_number);
+                               getpathname(namebuf, dirp->d_ino, dirp->d_ino);
+                               pwarn("%s %s %s\n", pathbuf,
+                                   "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
+                                   namebuf);
+                               if (preen)
+                                       printf(" (IGNORED)\n");
+                               else if ((n = reply("REMOVE")) == 1)
+                                       break;
+                       }
+                       if (idesc->id_entryno > 2)
+                               inp->i_parent = idesc->id_number;
+                       /* fall through */
+
+               case FSTATE:
+                       lncntp[dirp->d_ino]--;
+                       break;
+
+               default:
+                       errexit("BAD STATE %d FOR INODE I=%d",
+                           statemap[dirp->d_ino], dirp->d_ino);
+               }
+       }
+       if (n == 0)
+               return (ret|KEEPON);
+       dirp->d_ino = 0;
+       return (ret|KEEPON|ALTERED);
+}
+
+/*
+ * Routine to sort disk blocks.
+ */
+blksort(inpp1, inpp2)
+       struct inoinfo **inpp1, **inpp2;
+{
+
+       return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
+}
diff --git a/usr/src/sbin/fsck/pass3.c b/usr/src/sbin/fsck/pass3.c
new file mode 100644 (file)
index 0000000..0d372ae
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1980, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pass3.c    5.10 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include "fsck.h"
+
+pass3()
+{
+       register struct inoinfo **inpp, *inp;
+       ino_t orphan;
+       int loopcnt;
+
+       for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) {
+               inp = *inpp;
+               if (inp->i_number == ROOTINO ||
+                   !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE))
+                       continue;
+               if (statemap[inp->i_number] == DCLEAR)
+                       continue;
+               for (loopcnt = 0; ; loopcnt++) {
+                       orphan = inp->i_number;
+                       if (inp->i_parent == 0 ||
+                           statemap[inp->i_parent] != DSTATE ||
+                           loopcnt > numdirs)
+                               break;
+                       inp = getinoinfo(inp->i_parent);
+               }
+               (void)linkup(orphan, inp->i_dotdot);
+               inp->i_parent = inp->i_dotdot = lfdir;
+               lncntp[lfdir]--;
+               statemap[orphan] = DFOUND;
+               propagate();
+       }
+}
diff --git a/usr/src/sbin/fsck/pass4.c b/usr/src/sbin/fsck/pass4.c
new file mode 100644 (file)
index 0000000..4a01583
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 1980, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pass4.c    5.10 (Berkeley) 7/20/90";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+int    pass4check();
+
+pass4()
+{
+       register ino_t inumber;
+       register struct zlncnt *zlnp;
+       struct dinode *dp;
+       struct inodesc idesc;
+       int n;
+
+       bzero((char *)&idesc, sizeof(struct inodesc));
+       idesc.id_type = ADDR;
+       idesc.id_func = pass4check;
+       for (inumber = ROOTINO; inumber <= lastino; inumber++) {
+               idesc.id_number = inumber;
+               switch (statemap[inumber]) {
+
+               case FSTATE:
+               case DFOUND:
+                       n = lncntp[inumber];
+                       if (n)
+                               adjust(&idesc, (short)n);
+                       else {
+                               for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
+                                       if (zlnp->zlncnt == inumber) {
+                                               zlnp->zlncnt = zlnhead->zlncnt;
+                                               zlnp = zlnhead;
+                                               zlnhead = zlnhead->next;
+                                               free((char *)zlnp);
+                                               clri(&idesc, "UNREF", 1);
+                                               break;
+                                       }
+                       }
+                       break;
+
+               case DSTATE:
+                       clri(&idesc, "UNREF", 1);
+                       break;
+
+               case DCLEAR:
+                       dp = ginode(inumber);
+                       if (dp->di_size == 0) {
+                               clri(&idesc, "ZERO LENGTH", 1);
+                               break;
+                       }
+                       /* fall through */
+               case FCLEAR:
+                       clri(&idesc, "BAD/DUP", 1);
+                       break;
+
+               case USTATE:
+                       break;
+
+               default:
+                       errexit("BAD STATE %d FOR INODE I=%d",
+                           statemap[inumber], inumber);
+               }
+       }
+}
+
+pass4check(idesc)
+       register struct inodesc *idesc;
+{
+       register struct dups *dlp;
+       int nfrags, res = KEEPON;
+       daddr_t blkno = idesc->id_blkno;
+
+       for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+               if (chkrange(blkno, 1)) {
+                       res = SKIP;
+               } else if (testbmap(blkno)) {
+                       for (dlp = duplist; dlp; dlp = dlp->next) {
+                               if (dlp->dup != blkno)
+                                       continue;
+                               dlp->dup = duplist->dup;
+                               dlp = duplist;
+                               duplist = duplist->next;
+                               free((char *)dlp);
+                               break;
+                       }
+                       if (dlp == 0) {
+                               clrbmap(blkno);
+                               n_blks--;
+                       }
+               }
+       }
+       return (res);
+}
diff --git a/usr/src/sbin/fsck/pass5.c b/usr/src/sbin/fsck/pass5.c
new file mode 100644 (file)
index 0000000..8c4d2aa
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 1980, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pass5.c    5.13 (Berkeley) 7/20/90";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <string.h>
+#include "fsck.h"
+
+pass5()
+{
+       int c, blk, frags, basesize, sumsize, mapsize, savednrpos;
+       register struct fs *fs = &sblock;
+       register struct cg *cg = &cgrp;
+       daddr_t dbase, dmax;
+       register daddr_t d;
+       register long i, j;
+       struct csum *cs;
+       time_t now;
+       struct csum cstotal;
+       struct inodesc idesc[3];
+       char buf[MAXBSIZE];
+       register struct cg *newcg = (struct cg *)buf;
+       struct ocg *ocg = (struct ocg *)buf;
+
+       bzero((char *)newcg, (size_t)fs->fs_cgsize);
+       newcg->cg_niblk = fs->fs_ipg;
+       switch ((int)fs->fs_postblformat) {
+
+       case FS_42POSTBLFMT:
+               basesize = (char *)(&ocg->cg_btot[0]) - (char *)(&ocg->cg_link);
+               sumsize = &ocg->cg_iused[0] - (char *)(&ocg->cg_btot[0]);
+               mapsize = &ocg->cg_free[howmany(fs->fs_fpg, NBBY)] -
+                       (u_char *)&ocg->cg_iused[0];
+               ocg->cg_magic = CG_MAGIC;
+               savednrpos = fs->fs_nrpos;
+               fs->fs_nrpos = 8;
+               break;
+
+       case FS_DYNAMICPOSTBLFMT:
+               newcg->cg_btotoff =
+                       &newcg->cg_space[0] - (u_char *)(&newcg->cg_link);
+               newcg->cg_boff =
+                       newcg->cg_btotoff + fs->fs_cpg * sizeof(long);
+               newcg->cg_iusedoff = newcg->cg_boff + 
+                       fs->fs_cpg * fs->fs_nrpos * sizeof(short);
+               newcg->cg_freeoff =
+                       newcg->cg_iusedoff + howmany(fs->fs_ipg, NBBY);
+               newcg->cg_nextfreeoff = newcg->cg_freeoff +
+                       howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs),
+                               NBBY);
+               newcg->cg_magic = CG_MAGIC;
+               basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_link);
+               sumsize = newcg->cg_iusedoff - newcg->cg_btotoff;
+               mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff;
+               break;
+
+       default:
+               errexit("UNKNOWN ROTATIONAL TABLE FORMAT %d\n",
+                       fs->fs_postblformat);
+       }
+       bzero((char *)&idesc[0], sizeof idesc);
+       for (i = 0; i < 3; i++)
+               idesc[i].id_type = ADDR;
+       bzero((char *)&cstotal, sizeof(struct csum));
+       (void)time(&now);
+       j = blknum(fs, fs->fs_size + fs->fs_frag - 1);
+       for (i = fs->fs_size; i < j; i++)
+               setbmap(i);
+       for (c = 0; c < fs->fs_ncg; c++) {
+               getblk(&cgblk, cgtod(fs, c), fs->fs_cgsize);
+               if (!cg_chkmagic(cg))
+                       pfatal("CG %d: BAD MAGIC NUMBER\n", c);
+               dbase = cgbase(fs, c);
+               dmax = dbase + fs->fs_fpg;
+               if (dmax > fs->fs_size)
+                       dmax = fs->fs_size;
+               if (now > cg->cg_time)
+                       newcg->cg_time = cg->cg_time;
+               else
+                       newcg->cg_time = now;
+               newcg->cg_cgx = c;
+               if (c == fs->fs_ncg - 1)
+                       newcg->cg_ncyl = fs->fs_ncyl % fs->fs_cpg;
+               else
+                       newcg->cg_ncyl = fs->fs_cpg;
+               newcg->cg_ndblk = dmax - dbase;
+               newcg->cg_cs.cs_ndir = 0;
+               newcg->cg_cs.cs_nffree = 0;
+               newcg->cg_cs.cs_nbfree = 0;
+               newcg->cg_cs.cs_nifree = fs->fs_ipg;
+               if (cg->cg_rotor < newcg->cg_ndblk)
+                       newcg->cg_rotor = cg->cg_rotor;
+               else
+                       newcg->cg_rotor = 0;
+               if (cg->cg_frotor < newcg->cg_ndblk)
+                       newcg->cg_frotor = cg->cg_frotor;
+               else
+                       newcg->cg_frotor = 0;
+               if (cg->cg_irotor < newcg->cg_niblk)
+                       newcg->cg_irotor = cg->cg_irotor;
+               else
+                       newcg->cg_irotor = 0;
+               bzero((char *)&newcg->cg_frsum[0], sizeof newcg->cg_frsum);
+               bzero((char *)&cg_blktot(newcg)[0],
+                     (size_t)(sumsize + mapsize));
+               if (fs->fs_postblformat == FS_42POSTBLFMT)
+                       ocg->cg_magic = CG_MAGIC;
+               j = fs->fs_ipg * c;
+               for (i = 0; i < fs->fs_ipg; j++, i++) {
+                       switch (statemap[j]) {
+
+                       case USTATE:
+                               break;
+
+                       case DSTATE:
+                       case DCLEAR:
+                       case DFOUND:
+                               newcg->cg_cs.cs_ndir++;
+                               /* fall through */
+
+                       case FSTATE:
+                       case FCLEAR:
+                               newcg->cg_cs.cs_nifree--;
+                               setbit(cg_inosused(newcg), i);
+                               break;
+
+                       default:
+                               if (j < ROOTINO)
+                                       break;
+                               errexit("BAD STATE %d FOR INODE I=%d",
+                                   statemap[j], j);
+                       }
+               }
+               if (c == 0)
+                       for (i = 0; i < ROOTINO; i++) {
+                               setbit(cg_inosused(newcg), i);
+                               newcg->cg_cs.cs_nifree--;
+                       }
+               for (i = 0, d = dbase;
+                    d < dmax;
+                    d += fs->fs_frag, i += fs->fs_frag) {
+                       frags = 0;
+                       for (j = 0; j < fs->fs_frag; j++) {
+                               if (testbmap(d + j))
+                                       continue;
+                               setbit(cg_blksfree(newcg), i + j);
+                               frags++;
+                       }
+                       if (frags == fs->fs_frag) {
+                               newcg->cg_cs.cs_nbfree++;
+                               j = cbtocylno(fs, i);
+                               cg_blktot(newcg)[j]++;
+                               cg_blks(fs, newcg, j)[cbtorpos(fs, i)]++;
+                       } else if (frags > 0) {
+                               newcg->cg_cs.cs_nffree += frags;
+                               blk = blkmap(fs, cg_blksfree(newcg), i);
+                               fragacct(fs, blk, newcg->cg_frsum, 1);
+                       }
+               }
+               cstotal.cs_nffree += newcg->cg_cs.cs_nffree;
+               cstotal.cs_nbfree += newcg->cg_cs.cs_nbfree;
+               cstotal.cs_nifree += newcg->cg_cs.cs_nifree;
+               cstotal.cs_ndir += newcg->cg_cs.cs_ndir;
+               cs = &fs->fs_cs(fs, c);
+               if (bcmp((char *)&newcg->cg_cs, (char *)cs, sizeof *cs) != 0 &&
+                   dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+                       bcopy((char *)&newcg->cg_cs, (char *)cs, sizeof *cs);
+                       sbdirty();
+               }
+               if (cvtflag) {
+                       bcopy((char *)newcg, (char *)cg, (size_t)fs->fs_cgsize);
+                       cgdirty();
+                       continue;
+               }
+               if (bcmp(cg_inosused(newcg),
+                        cg_inosused(cg), mapsize) != 0 &&
+                   dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) {
+                       bcopy(cg_inosused(newcg), cg_inosused(cg),
+                             (size_t)mapsize);
+                       cgdirty();
+               }
+               if ((bcmp((char *)newcg, (char *)cg, basesize) != 0 ||
+                    bcmp((char *)&cg_blktot(newcg)[0],
+                         (char *)&cg_blktot(cg)[0], sumsize) != 0) &&
+                   dofix(&idesc[2], "SUMMARY INFORMATION BAD")) {
+                       bcopy((char *)newcg, (char *)cg, (size_t)basesize);
+                       bcopy((char *)&cg_blktot(newcg)[0],
+                             (char *)&cg_blktot(cg)[0], (size_t)sumsize);
+                       cgdirty();
+               }
+       }
+       if (fs->fs_postblformat == FS_42POSTBLFMT)
+               fs->fs_nrpos = savednrpos;
+       if (bcmp((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs) != 0
+           && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+               bcopy((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs);
+               fs->fs_ronly = 0;
+               fs->fs_fmod = 0;
+               sbdirty();
+       }
+}
diff --git a/usr/src/sbin/fsck/preen.c b/usr/src/sbin/fsck/preen.c
new file mode 100644 (file)
index 0000000..29f2d28
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)preen.c    5.7 (Berkeley) 3/19/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fstab.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+char   *rawname(), *unrawname(), *blockcheck();
+
+struct part {
+       struct  part *next;             /* forward link of partitions on disk */
+       char    *name;                  /* device name */
+       char    *fsname;                /* mounted filesystem name */
+       long    auxdata;                /* auxillary data for application */
+} *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;
+char   hotroot;
+
+checkfstab(preen, maxrun, docheck, chkit)
+       int preen, maxrun;
+       int (*docheck)(), (*chkit)();
+{
+       register struct fstab *fsp;
+       register struct disk *dk, *nextdisk;
+       register struct part *pt;
+       int ret, pid, retcode, passno, sumstatus, status;
+       long auxdata;
+       char *name;
+
+       sumstatus = 0;
+       for (passno = 1; passno <= 2; passno++) {
+               if (setfsent() == 0) {
+                       fprintf(stderr, "Can't open checklist file: %s\n",
+                           _PATH_FSTAB);
+                       return (8);
+               }
+               while ((fsp = getfsent()) != 0) {
+                       if ((auxdata = (*docheck)(fsp)) == 0)
+                               continue;
+                       if (preen == 0 || passno == 1 && fsp->fs_passno == 1) {
+                               if (name = blockcheck(fsp->fs_spec)) {
+                                       if (sumstatus = (*chkit)(name,
+                                           fsp->fs_file, auxdata, 0))
+                                               return (sumstatus);
+                               } else if (preen)
+                                       return (8);
+                       } else if (passno == 2 && fsp->fs_passno > 1) {
+                               if ((name = blockcheck(fsp->fs_spec)) == NULL) {
+                                       fprintf(stderr, "BAD DISK NAME %s\n",
+                                               fsp->fs_spec);
+                                       sumstatus |= 8;
+                                       continue;
+                               }
+                               addpart(name, fsp->fs_file, auxdata);
+                       }
+               }
+               if (preen == 0)
+                       return (0);
+       }
+       if (preen) {
+               if (maxrun == 0)
+                       maxrun = ndisks;
+               if (maxrun > ndisks)
+                       maxrun = ndisks;
+               nextdisk = disks;
+               for (passno = 0; passno < maxrun; ++passno) {
+                       while (ret = startdisk(nextdisk, chkit) && nrun > 0)
+                               sleep(10);
+                       if (ret)
+                               return (ret);
+                       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 (WIFEXITED(status))
+                               retcode = WEXITSTATUS(status);
+                       else
+                               retcode = 0;
+                       if (WIFSIGNALED(status)) {
+                               printf("%s (%s): EXITED WITH SIGNAL %d\n",
+                                       dk->part->name, dk->part->fsname,
+                                       WTERMSIG(status));
+                               retcode = 8;
+                       }
+                       if (retcode != 0) {
+                               sumstatus |= 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) {
+                                       while (ret = startdisk(dk, chkit) &&
+                                           nrun > 0)
+                                               sleep(10);
+                                       if (ret)
+                                               return (ret);
+                               }
+                       } else if (nrun < maxrun && nrun < ndisks) {
+                               for ( ;; ) {
+                                       if ((nextdisk = nextdisk->next) == NULL)
+                                               nextdisk = disks;
+                                       if (nextdisk->part != NULL &&
+                                           nextdisk->pid == 0)
+                                               break;
+                               }
+                               while (ret = startdisk(nextdisk, chkit) &&
+                                   nrun > 0)
+                                       sleep(10);
+                               if (ret)
+                                       return (ret);
+                       }
+               }
+       }
+       if (sumstatus) {
+               if (badlist == 0)
+                       return (sumstatus);
+               fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+                       badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
+               for (pt = badlist; pt; pt = pt->next)
+                       fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname,
+                           pt->next ? ", " : "\n");
+               return (sumstatus);
+       }
+       (void)endfsent();
+       return (0);
+}
+
+struct disk *
+finddisk(name)
+       char *name;
+{
+       register struct disk *dk, **dkp;
+       register char *p;
+       size_t 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) {
+               fprintf(stderr, "out of memory");
+               exit (8);
+       }
+       dk = *dkp;
+       if ((dk->name = malloc(len + 1)) == NULL) {
+               fprintf(stderr, "out of memory");
+               exit (8);
+       }
+       (void)strncpy(dk->name, name, len);
+       dk->name[len] = '\0';
+       dk->part = NULL;
+       dk->next = NULL;
+       dk->pid = 0;
+       ndisks++;
+       return (dk);
+}
+
+addpart(name, fsname, auxdata)
+       char *name, *fsname;
+       long auxdata;
+{
+       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) {
+               fprintf(stderr, "out of memory");
+               exit (8);
+       }
+       pt = *ppt;
+       if ((pt->name = malloc(strlen(name) + 1)) == NULL) {
+               fprintf(stderr, "out of memory");
+               exit (8);
+       }
+       (void)strcpy(pt->name, name);
+       if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) {
+               fprintf(stderr, "out of memory");
+               exit (8);
+       }
+       (void)strcpy(pt->fsname, fsname);
+       pt->next = NULL;
+       pt->auxdata = auxdata;
+}
+
+startdisk(dk, checkit)
+       register struct disk *dk;
+       int (*checkit)();
+{
+       register struct part *pt = dk->part;
+
+       dk->pid = fork();
+       if (dk->pid < 0) {
+               perror("fork");
+               return (8);
+       }
+       if (dk->pid == 0)
+               exit((*checkit)(pt->name, pt->fsname, pt->auxdata, 1));
+       nrun++;
+       return (0);
+}
+
+char *
+blockcheck(name)
+       char *name;
+{
+       struct stat stslash, stblock, stchar;
+       char *raw;
+       int retried = 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++;
+               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 && !retried) {
+               name = unrawname(name);
+               retried++;
+               goto retry;
+       }
+       printf("Can't make sense out of name %s\n", name);
+       return (0);
+}
+
+char *
+unrawname(name)
+       char *name;
+{
+       char *dp;
+       struct stat stb;
+
+       if ((dp = rindex(name, '/')) == 0)
+               return (name);
+       if (stat(name, &stb) < 0)
+               return (name);
+       if ((stb.st_mode & S_IFMT) != S_IFCHR)
+               return (name);
+       if (dp[1] != 'r')
+               return (name);
+       (void)strcpy(&dp[1], &dp[2]);
+       return (name);
+}
+
+char *
+rawname(name)
+       char *name;
+{
+       static char rawbuf[32];
+       char *dp;
+
+       if ((dp = rindex(name, '/')) == 0)
+               return (0);
+       *dp = 0;
+       (void)strcpy(rawbuf, name);
+       *dp = '/';
+       (void)strcat(rawbuf, "/r");
+       (void)strcat(rawbuf, &dp[1]);
+       return (rawbuf);
+}
diff --git a/usr/src/sbin/fsck/setup.c b/usr/src/sbin/fsck/setup.c
new file mode 100644 (file)
index 0000000..e29d8e5
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 1980, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)setup.c    5.33 (Berkeley) 2/22/91";
+#endif /* not lint */
+
+#define DKTYPENAMES
+#include <sys/param.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "fsck.h"
+
+struct bufarea asblk;
+#define altsblock (*asblk.b_un.b_fs)
+#define POWEROF2(num)  (((num) & ((num) - 1)) == 0)
+
+/*
+ * The size of a cylinder group is calculated by CGSIZE. The maximum size
+ * is limited by the fact that cylinder groups are at most one block.
+ * Its size is derived from the size of the maps maintained in the 
+ * cylinder group and the (struct cg) size.
+ */
+#define CGSIZE(fs) \
+    /* base cg */      (sizeof(struct cg) + \
+    /* blktot size */  (fs)->fs_cpg * sizeof(long) + \
+    /* blks size */    (fs)->fs_cpg * (fs)->fs_nrpos * sizeof(short) + \
+    /* inode map */    howmany((fs)->fs_ipg, NBBY) + \
+    /* block map */    howmany((fs)->fs_cpg * (fs)->fs_spc / NSPF(fs), NBBY))
+
+char   *index();
+struct disklabel *getdisklabel();
+
+setup(dev)
+       char *dev;
+{
+       long cg, size, asked, i, j;
+       long bmapsize;
+       struct disklabel *lp;
+       struct stat statb;
+       struct fs proto;
+
+       havesb = 0;
+       if (stat(dev, &statb) < 0) {
+               printf("Can't stat %s: %s\n", dev, strerror(errno));
+               return (0);
+       }
+       if ((statb.st_mode & S_IFMT) != S_IFCHR) {
+               pfatal("%s is not a character device", dev);
+               if (reply("CONTINUE") == 0)
+                       return (0);
+       }
+       if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
+               printf("Can't open %s: %s\n", dev, strerror(errno));
+               return (0);
+       }
+       if (preen == 0)
+               printf("** %s", dev);
+       if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) {
+               fswritefd = -1;
+               if (preen)
+                       pfatal("NO WRITE ACCESS");
+               printf(" (NO WRITE)");
+       }
+       if (preen == 0)
+               printf("\n");
+       fsmodified = 0;
+       lfdir = 0;
+       initbarea(&sblk);
+       initbarea(&asblk);
+       sblk.b_un.b_buf = malloc(SBSIZE);
+       asblk.b_un.b_buf = malloc(SBSIZE);
+       if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL)
+               errexit("cannot allocate space for superblock\n");
+       if (lp = getdisklabel((char *)NULL, fsreadfd))
+               dev_bsize = secsize = lp->d_secsize;
+       else
+               dev_bsize = secsize = DEV_BSIZE;
+#ifdef tahoe
+       /*
+        * On the tahoe, the disk label and the disk driver disagree.
+        * The label knows that sectors are 512 bytes, but the disk
+        * drivers will only transfer in 1024 sized pieces.
+        */
+       secsize = 1024;
+#endif
+       /*
+        * Read in the superblock, looking for alternates if necessary
+        */
+       if (readsb(1) == 0) {
+               if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
+                       return(0);
+               if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0)
+                       return (0);
+               for (cg = 0; cg < proto.fs_ncg; cg++) {
+                       bflag = fsbtodb(&proto, cgsblock(&proto, cg));
+                       if (readsb(0) != 0)
+                               break;
+               }
+               if (cg >= proto.fs_ncg) {
+                       printf("%s %s\n%s %s\n%s %s\n",
+                               "SEARCH FOR ALTERNATE SUPER-BLOCK",
+                               "FAILED. YOU MUST USE THE",
+                               "-b OPTION TO FSCK TO SPECIFY THE",
+                               "LOCATION OF AN ALTERNATE",
+                               "SUPER-BLOCK TO SUPPLY NEEDED",
+                               "INFORMATION; SEE fsck(8).");
+                       return(0);
+               }
+               pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
+       }
+       maxfsblock = sblock.fs_size;
+       maxino = sblock.fs_ncg * sblock.fs_ipg;
+       /*
+        * Check and potentially fix certain fields in the super block.
+        */
+       if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) {
+               pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK");
+               if (reply("SET TO DEFAULT") == 1) {
+                       sblock.fs_optim = FS_OPTTIME;
+                       sbdirty();
+               }
+       }
+       if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) {
+               pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK",
+                       sblock.fs_minfree);
+               if (reply("SET TO DEFAULT") == 1) {
+                       sblock.fs_minfree = 10;
+                       sbdirty();
+               }
+       }
+       if (sblock.fs_interleave < 1 || 
+           sblock.fs_interleave > sblock.fs_nsect) {
+               pwarn("IMPOSSIBLE INTERLEAVE=%d IN SUPERBLOCK",
+                       sblock.fs_interleave);
+               sblock.fs_interleave = 1;
+               if (preen)
+                       printf(" (FIXED)\n");
+               if (preen || reply("SET TO DEFAULT") == 1) {
+                       sbdirty();
+                       dirty(&asblk);
+               }
+       }
+       if (sblock.fs_npsect < sblock.fs_nsect || 
+           sblock.fs_npsect > sblock.fs_nsect*2) {
+               pwarn("IMPOSSIBLE NPSECT=%d IN SUPERBLOCK",
+                       sblock.fs_npsect);
+               sblock.fs_npsect = sblock.fs_nsect;
+               if (preen)
+                       printf(" (FIXED)\n");
+               if (preen || reply("SET TO DEFAULT") == 1) {
+                       sbdirty();
+                       dirty(&asblk);
+               }
+       }
+       if (cvtflag) {
+               if (sblock.fs_postblformat == FS_42POSTBLFMT) {
+                       /*
+                        * Requested to convert from old format to new format
+                        */
+                       if (preen)
+                               pwarn("CONVERTING TO NEW FILE SYSTEM FORMAT\n");
+                       else if (!reply("CONVERT TO NEW FILE SYSTEM FORMAT"))
+                               return(0);
+                       sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT;
+                       sblock.fs_nrpos = 8;
+                       sblock.fs_postbloff =
+                           (char *)(&sblock.fs_opostbl[0][0]) -
+                           (char *)(&sblock.fs_link);
+                       sblock.fs_rotbloff = &sblock.fs_space[0] -
+                           (u_char *)(&sblock.fs_link);
+                       sblock.fs_cgsize =
+                               fragroundup(&sblock, CGSIZE(&sblock));
+                       /*
+                        * Planning now for future expansion.
+                        */
+#                      if (BYTE_ORDER == BIG_ENDIAN)
+                               sblock.fs_qbmask.val[0] = 0;
+                               sblock.fs_qbmask.val[1] = ~sblock.fs_bmask;
+                               sblock.fs_qfmask.val[0] = 0;
+                               sblock.fs_qfmask.val[1] = ~sblock.fs_fmask;
+#                      endif /* BIG_ENDIAN */
+#                      if (BYTE_ORDER == LITTLE_ENDIAN)
+                               sblock.fs_qbmask.val[0] = ~sblock.fs_bmask;
+                               sblock.fs_qbmask.val[1] = 0;
+                               sblock.fs_qfmask.val[0] = ~sblock.fs_fmask;
+                               sblock.fs_qfmask.val[1] = 0;
+#                      endif /* LITTLE_ENDIAN */
+                       sbdirty();
+                       dirty(&asblk);
+               } else if (sblock.fs_postblformat == FS_DYNAMICPOSTBLFMT) {
+                       /*
+                        * Requested to convert from new format to old format
+                        */
+                       if (sblock.fs_nrpos != 8 || sblock.fs_ipg > 2048 ||
+                           sblock.fs_cpg > 32 || sblock.fs_cpc > 16) {
+                               printf(
+                               "PARAMETERS OF CURRENT FILE SYSTEM DO NOT\n\t");
+                               errexit(
+                               "ALLOW CONVERSION TO OLD FILE SYSTEM FORMAT\n");
+                       }
+                       if (preen)
+                               pwarn("CONVERTING TO OLD FILE SYSTEM FORMAT\n");
+                       else if (!reply("CONVERT TO OLD FILE SYSTEM FORMAT"))
+                               return(0);
+                       sblock.fs_postblformat = FS_42POSTBLFMT;
+                       sblock.fs_cgsize = fragroundup(&sblock,
+                           sizeof(struct ocg) + howmany(sblock.fs_fpg, NBBY));
+                       sbdirty();
+                       dirty(&asblk);
+               } else {
+                       errexit("UNKNOWN FILE SYSTEM FORMAT\n");
+               }
+       }
+       if (asblk.b_dirty) {
+               bcopy((char *)&sblock, (char *)&altsblock,
+                       (size_t)sblock.fs_sbsize);
+               flush(fswritefd, &asblk);
+       }
+       /*
+        * read in the summary info.
+        */
+       asked = 0;
+       for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
+               size = sblock.fs_cssize - i < sblock.fs_bsize ?
+                   sblock.fs_cssize - i : sblock.fs_bsize;
+               sblock.fs_csp[j] = (struct csum *)calloc(1, (unsigned)size);
+               if (bread(fsreadfd, (char *)sblock.fs_csp[j],
+                   fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
+                   size) != 0 && !asked) {
+                       pfatal("BAD SUMMARY INFORMATION");
+                       if (reply("CONTINUE") == 0)
+                               errexit("");
+                       asked++;
+               }
+       }
+       /*
+        * allocate and initialize the necessary maps
+        */
+       bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(short));
+       blockmap = calloc((unsigned)bmapsize, sizeof (char));
+       if (blockmap == NULL) {
+               printf("cannot alloc %u bytes for blockmap\n",
+                   (unsigned)bmapsize);
+               goto badsb;
+       }
+       statemap = calloc((unsigned)(maxino + 1), sizeof(char));
+       if (statemap == NULL) {
+               printf("cannot alloc %u bytes for statemap\n",
+                   (unsigned)(maxino + 1));
+               goto badsb;
+       }
+       lncntp = (short *)calloc((unsigned)(maxino + 1), sizeof(short));
+       if (lncntp == NULL) {
+               printf("cannot alloc %u bytes for lncntp\n", 
+                   (unsigned)(maxino + 1) * sizeof(short));
+               goto badsb;
+       }
+       numdirs = sblock.fs_cstotal.cs_ndir;
+       inplast = 0;
+       listmax = numdirs + 10;
+       inpsort = (struct inoinfo **)calloc((unsigned)listmax,
+           sizeof(struct inoinfo *));
+       inphead = (struct inoinfo **)calloc((unsigned)numdirs,
+           sizeof(struct inoinfo *));
+       if (inpsort == NULL || inphead == NULL) {
+               printf("cannot alloc %u bytes for inphead\n", 
+                   (unsigned)numdirs * sizeof(struct inoinfo *));
+               goto badsb;
+       }
+       bufinit();
+       return (1);
+
+badsb:
+       ckfini();
+       return (0);
+}
+
+/*
+ * Read in the super block and its summary info.
+ */
+readsb(listerr)
+       int listerr;
+{
+       daddr_t super = bflag ? bflag : SBOFF / dev_bsize;
+
+       if (bread(fsreadfd, (char *)&sblock, super, (long)SBSIZE) != 0)
+               return (0);
+       sblk.b_bno = super;
+       sblk.b_size = SBSIZE;
+       /*
+        * run a few consistency checks of the super block
+        */
+       if (sblock.fs_magic != FS_MAGIC)
+               { badsb(listerr, "MAGIC NUMBER WRONG"); return (0); }
+       if (sblock.fs_ncg < 1)
+               { badsb(listerr, "NCG OUT OF RANGE"); return (0); }
+       if (sblock.fs_cpg < 1)
+               { badsb(listerr, "CPG OUT OF RANGE"); return (0); }
+       if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl ||
+           (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl)
+               { badsb(listerr, "NCYL LESS THAN NCG*CPG"); return (0); }
+       if (sblock.fs_sbsize > SBSIZE)
+               { badsb(listerr, "SIZE PREPOSTEROUSLY LARGE"); return (0); }
+       /*
+        * Compute block size that the filesystem is based on,
+        * according to fsbtodb, and adjust superblock block number
+        * so we can tell if this is an alternate later.
+        */
+       super *= dev_bsize;
+       dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
+       sblk.b_bno = super / dev_bsize;
+       /*
+        * Set all possible fields that could differ, then do check
+        * of whole super block against an alternate super block.
+        * When an alternate super-block is specified this check is skipped.
+        */
+       getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize);
+       if (asblk.b_errs)
+               return (0);
+       if (bflag) {
+               havesb = 1;
+               return (1);
+       }
+       altsblock.fs_link = sblock.fs_link;
+       altsblock.fs_rlink = sblock.fs_rlink;
+       altsblock.fs_time = sblock.fs_time;
+       altsblock.fs_cstotal = sblock.fs_cstotal;
+       altsblock.fs_cgrotor = sblock.fs_cgrotor;
+       altsblock.fs_fmod = sblock.fs_fmod;
+       altsblock.fs_clean = sblock.fs_clean;
+       altsblock.fs_ronly = sblock.fs_ronly;
+       altsblock.fs_flags = sblock.fs_flags;
+       altsblock.fs_maxcontig = sblock.fs_maxcontig;
+       altsblock.fs_minfree = sblock.fs_minfree;
+       altsblock.fs_optim = sblock.fs_optim;
+       altsblock.fs_rotdelay = sblock.fs_rotdelay;
+       altsblock.fs_maxbpg = sblock.fs_maxbpg;
+       bcopy((char *)sblock.fs_csp, (char *)altsblock.fs_csp,
+               sizeof sblock.fs_csp);
+       bcopy((char *)sblock.fs_fsmnt, (char *)altsblock.fs_fsmnt,
+               sizeof sblock.fs_fsmnt);
+       bcopy((char *)sblock.fs_sparecon, (char *)altsblock.fs_sparecon,
+               sizeof sblock.fs_sparecon);
+       /*
+        * The following should not have to be copied.
+        */
+       altsblock.fs_fsbtodb = sblock.fs_fsbtodb;
+       altsblock.fs_interleave = sblock.fs_interleave;
+       altsblock.fs_npsect = sblock.fs_npsect;
+       altsblock.fs_nrpos = sblock.fs_nrpos;
+       if (bcmp((char *)&sblock, (char *)&altsblock, (int)sblock.fs_sbsize)) {
+               badsb(listerr,
+               "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE");
+               return (0);
+       }
+       havesb = 1;
+       return (1);
+}
+
+badsb(listerr, s)
+       int listerr;
+       char *s;
+{
+
+       if (!listerr)
+               return;
+       if (preen)
+               printf("%s: ", devname);
+       pfatal("BAD SUPER BLOCK: %s\n", s);
+}
+
+/*
+ * Calculate a prototype superblock based on information in the disk label.
+ * When done the cgsblock macro can be calculated and the fs_ncg field
+ * can be used. Do NOT attempt to use other macros without verifying that
+ * their needed information is available!
+ */
+calcsb(dev, devfd, fs)
+       char *dev;
+       int devfd;
+       register struct fs *fs;
+{
+       register struct disklabel *lp;
+       register struct partition *pp;
+       register char *cp;
+       int i;
+
+       cp = index(dev, '\0') - 1;
+       if (cp == (char *)-1 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp)) {
+               pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev);
+               return (0);
+       }
+       lp = getdisklabel(dev, devfd);
+       if (isdigit(*cp))
+               pp = &lp->d_partitions[0];
+       else
+               pp = &lp->d_partitions[*cp - 'a'];
+       if (pp->p_fstype != FS_BSDFFS) {
+               pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n",
+                       dev, pp->p_fstype < FSMAXTYPES ?
+                       fstypenames[pp->p_fstype] : "unknown");
+               return (0);
+       }
+       bzero((char *)fs, sizeof(struct fs));
+       fs->fs_fsize = pp->p_fsize;
+       fs->fs_frag = pp->p_frag;
+       fs->fs_cpg = pp->p_cpg;
+       fs->fs_size = pp->p_size;
+       fs->fs_ntrak = lp->d_ntracks;
+       fs->fs_nsect = lp->d_nsectors;
+       fs->fs_spc = lp->d_secpercyl;
+       fs->fs_nspf = fs->fs_fsize / lp->d_secsize;
+       fs->fs_sblkno = roundup(
+               howmany(lp->d_bbsize + lp->d_sbsize, fs->fs_fsize),
+               fs->fs_frag);
+       fs->fs_cgmask = 0xffffffff;
+       for (i = fs->fs_ntrak; i > 1; i >>= 1)
+               fs->fs_cgmask <<= 1;
+       if (!POWEROF2(fs->fs_ntrak))
+               fs->fs_cgmask <<= 1;
+       fs->fs_cgoffset = roundup(
+               howmany(fs->fs_nsect, NSPF(fs)), fs->fs_frag);
+       fs->fs_fpg = (fs->fs_cpg * fs->fs_spc) / NSPF(fs);
+       fs->fs_ncg = howmany(fs->fs_size / fs->fs_spc, fs->fs_cpg);
+       for (fs->fs_fsbtodb = 0, i = NSPF(fs); i > 1; i >>= 1)
+               fs->fs_fsbtodb++;
+       dev_bsize = lp->d_secsize;
+       return (1);
+}
+
+struct disklabel *
+getdisklabel(s, fd)
+       char *s;
+       int     fd;
+{
+       static struct disklabel lab;
+
+       if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
+               if (s == NULL)
+                       return ((struct disklabel *)NULL);
+               pwarn("ioctl (GCINFO): %s\n", strerror(errno));
+               errexit("%s: can't read disk label\n", s);
+       }
+       return (&lab);
+}