summaryrefslogtreecommitdiff
path: root/ufs-fsck
diff options
context:
space:
mode:
Diffstat (limited to 'ufs-fsck')
-rw-r--r--ufs-fsck/ChangeLog300
-rw-r--r--ufs-fsck/Makefile35
-rw-r--r--ufs-fsck/dir.c567
-rw-r--r--ufs-fsck/fsck.h183
-rw-r--r--ufs-fsck/inode.c203
-rw-r--r--ufs-fsck/main.c170
-rw-r--r--ufs-fsck/pass1.c439
-rw-r--r--ufs-fsck/pass1b.c92
-rw-r--r--ufs-fsck/pass2.c401
-rw-r--r--ufs-fsck/pass3.c71
-rw-r--r--ufs-fsck/pass4.c94
-rw-r--r--ufs-fsck/pass5.c450
-rw-r--r--ufs-fsck/setup.c191
-rw-r--r--ufs-fsck/utilities.c454
14 files changed, 3650 insertions, 0 deletions
diff --git a/ufs-fsck/ChangeLog b/ufs-fsck/ChangeLog
new file mode 100644
index 00000000..0fede606
--- /dev/null
+++ b/ufs-fsck/ChangeLog
@@ -0,0 +1,300 @@
+Thu May 6 10:25:27 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * utilities.c (pextend): Free MORE before returning.
+ * dir.c (linkup): Don't free tempname until after we're done using
+ it in the call to warning.
+ Reported by Katsuya Tanaka (tanaka@boarderz.com).
+
+1999-03-25 Roland McGrath <roland@baalperazim.frob.com>
+
+ * setup.c (setup): Don't complain if the device is a block device.
+
+Wed Feb 19 23:10:39 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (argp_program_version): Make const.
+
+Thu Sep 12 16:40:10 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * Makefile (HURDLIBS): New variable.
+ ($(target)): Delete special depedency.
+
+ * Makefile (vpath tables.c): Put after Makeconf inclusion to catch
+ setting of $(top_srcdir).
+
+Fri Sep 6 16:44:07 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * main.c (argp_program_version): Fix typo.
+
+Thu Sep 5 11:42:21 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * main.c: Include <version.h>.
+ (argp_program_version): Define with STANDARD_HURD_VERSION.
+
+Fri Aug 16 10:25:37 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * dir.c (record_directory): Maximum number of block pointers to
+ record is NDADDR + NIADDR, not NDADDR * NIADDR.
+ * pass2.c: Include <assert.h>.
+ (pass2): Before copying block addresses to DINO in basic
+ integrity check, assert that DNP->i_numblks isn't too big.
+
+Mon Aug 12 11:39:12 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * Makefile (dir): Now ufs-fsck.
+
+Tue Jul 23 19:32:09 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * inode.c (allocino): `struct timespec' now uses a field prefix
+ of `tv_'.
+ * utilities.c (pinode): Likewise.
+
+Thu Jul 18 14:55:14 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * pass2.c (pass2): If an entire directory block is null, allow
+ preen to patch it into a normal empty directory entry.
+
+Sat Jul 6 19:59:27 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (argp_program_version): New variable.
+ <hurd.h>: New include.
+
+Mon Jul 1 12:55:48 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * pass2.c (pass2): Don't skip empty directories in `.' and `..'
+ correctness check; we don't clear them the way BSD does, so we
+ want `.' and `..' to get created for us. Also handle `.' before
+ `..' so that they get created in the usual order for empty
+ directories.
+
+ * dir.c (makeentry): After successful directory expansion, write
+ out modified directory inode.
+
+ * pass4.c (pass4): If a reconnect fails while we are preening,
+ give up.
+
+Mon Jun 24 10:19:39 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * utilities.c (errexit, punt): Exit with status 8 for catastrophic
+ failures.
+
+Thu May 23 14:12:21 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * pass2.c (pass2): Don't clear all node types in directories, just
+ clear those that are wrong.
+
+Tue May 14 16:49:46 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * pass2.c (pass2): Fix up test in preen case.
+
+Tue May 14 15:29:36 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * pass2.c (pass2): Handle directory entry type fields better for
+ Hurd.
+
+Sat May 11 01:07:49 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (parse_opt): Use ARGP_ERR_UNKNOWN instead of EINVAL.
+
+Thu May 9 20:12:51 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * pass1b.c (pass1b): Bother to initialize NUMBER.
+
+Fri May 3 00:48:39 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (nice_size, show_stats): New functions.
+ (main): Use show_stats.
+
+Wed May 1 13:59:06 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (main): Shorten summary message so that it fits on one line.
+ * utilities.c (no_preen): New function.
+ (problem, warning, pinode): Use it.
+ (warning): Don't flush all pending problems, just our own.
+ * dir.c (linkup): Consistently put quotes around filenames.
+
+ * main.c (preen, num_files): New variables.
+ (main): Implement clean-bit checking in preen mode, and print
+ summary statistics.
+ (main, options): Add --force & --silent options.
+ * pass1.c (pass1): Increment NUM_FILES.
+ When clearing inode due to bad blocks, continue.
+ * inode.c (allocino, freeino): Frob NUM_FILES.
+ * fsck.h (force): New declaration.
+ * pass5.c (pass5): Vary clean msg depending on whether FSMODIFIED.
+ * setup.c (setup): Use error to print error msgs.
+ <error.h>, <errno.h>: New includes.
+
+ * utilities.c (problem, warning, pextend, pfail): New functions.
+ (pinode, pfix, reply): Use new problem recording stuff.
+ (push_problem, resolve_problem, flush_problems): New functions.
+ (struct problem): New type.
+ (problems, free_problems): New variables.
+ (retch, punt): New functions.
+ * fsck.h (problem, warning, pextend, pfail): New declarations.
+ (pinode): Update declaration.
+ * dir.c (validdir, makeentry, linkup): Use new printing functions.
+ * pass1.c (pass1): Likewise.
+ * pass1b.c (pass1b): Likewise.
+ * pass2.c (pass2): Likewise.
+ * pass3.c (pass3): Likewise.
+ * pass4.c (pass4): Likewise.
+ * pass5.c (pass5): Likewise.
+ * setup.c (setup): Likewise.
+
+Tue Apr 30 19:06:42 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * pass5.c (pass5): Be sure to call pwarn before pfix.
+ * main.c (main): Don't print large obnoxious banner if PREEN.
+
+Fri Apr 26 16:20:37 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * inode.c (allocino): Parenthesize test correctly.
+
+ * fsck.h (swab_disk): Define as constant zero.
+
+ * pass5.c (pass5): If not marked clean, but now it is, then offer
+ to mark it clean.
+ * utilities.c (reply): Set fix_denied anytime we return 0.
+ * fsck.h (fix_denied): New variable.
+
+Wed Apr 24 13:32:39 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * pass1.c (pass1): Don't print block numbers as we go anymore.
+
+Tue Apr 23 10:11:49 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * pass5.c (pass5): Correctly track contig summaries even though
+ they aren't used by the filesystem; we still need to preserve the
+ format.
+
+Mon Apr 15 12:51:41 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * Makefile (vpath tables.c): Find ufs directory in $(top_srcdir).
+
+Tue Apr 2 09:00:53 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * pass1.c (pass1): Print mode correctly in unknown file type case.
+ Recognize inode type IFSOCK too.
+
+Mon Mar 18 19:48:39 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (main): Pass new arg to argp_parse. Use argp_usage correctly.
+
+Thu Oct 19 17:45:12 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (main): Exit with a non-zero status if we fixed anything.
+ Use argp to parse options.
+ (options): Converted to argp format.
+ (args_doc): New variable.
+ (USAGE, usage, SHORT_OPTIONS): Removed.
+ Include <argp.h> instead of <getopt.h>.
+ * Makefile ($(target)): Depend on libshouldbeinlibc.a.
+
+Fri Sep 22 16:55:03 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * utilities.c (pfix): New function.
+ (pfatal, pwarn, errexit): Print DEVICE_NAME too if in preen mode.
+ * fsck.h: Declare DEVICE_NAME.
+ * setup.c (setup): Set DEVICE_NAME.
+ * pass1.c, pass2.c, pass3.c, pass4.c, pass5.c (pass1, pass2,
+ pass3, pass4, pass5): Call pfix instead of printf.
+ * pass1.c (pass1): Only print progress report if not in preen mode.
+ * main.c (main): Only print section headers if not in preen mode.
+
+Wed Sep 20 09:11:59 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * utilities.c (pinode): Take a message & args to print as well.
+ * fsck.h: Change declaration of pinode.
+ * pass2.c (pass2): Use changed pinode.
+ * pass3.c (pass3): Use changed pinode.
+ * pass4.c (pass4): Use changed pinode.
+
+Tue Sep 19 15:37:02 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * pass1.c (pass1): Change the extent of DBWARN & IBWARN so that
+ they actually work.
+ * pass2.c (pass2): Adjust our record of link counts when we
+ add/change dir entries; also print error messages when we can't.
+ * pass4.c (pass4): If an unlinked file can't be reconnected, offer
+ to clear it. Once a reconnect attempt fails, don't try again.
+ * dir.c (linkup): Print the value of LFNAME rather than `lost+found'.
+ (searchdir, changeino): Fix backward compare.
+ (linkup): Don't fail when makeentry succeeds.
+ (searchdir): Make searchdir return zero if there's an error
+ during the search.
+ (linkup): Print appropiate error messages if searchdir fails.
+ (validdir): Get rid of extra newlines in error messages --
+ everyone who calls this routine prints extra information if it
+ fails, which should immediately follow.
+ * main.c (main): Use getopt to parse command line options.
+ (usage): New function.
+ (options): New variable.
+ (lfname, lfmode): Variables moved here from setup.c.
+ (lfname): Made into a char* so that we can change it.
+ (lfmode): Get rid of IFDIR; it's added when necessary.
+ * fsck.h: Change LFNAME to char*.
+ * setup.c (lfname, lfmode): Variables moved to main.c.
+
+Sat Sep 9 12:12:59 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * Makefile (target): Changed to `fsck.ufs'.
+ (installationdir): New variable, install into $(sbindir).
+
+Thu Jul 6 15:33:46 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu>
+
+ * fsck.h (lookup_directory): New decl.
+
+ * pass1.c (pass1): Remove assignment from if test.
+ * utilities.c (pinode): Likewise.
+
+ * Makefile (tables.o): Delete rule.
+ (vpath tables.c): Tell where to find tables.c.
+
+ * Makefile: Removed dependencies that are now automatically
+ generated.
+
+Thu Nov 3 17:19:03 1994 Michael I Bushnell <mib@geech.gnu.ai.mit.edu>
+
+ * Makefile (dir): Changed to fsck.
+ (target): Changed to fsck.
+
+Wed Nov 2 14:39:13 1994 Michael I Bushnell <mib@geech.gnu.ai.mit.edu>
+
+ * pass2.c (pass2): Use DIRECT_NAMLEN instead of d_namlen
+ throughout.
+ * dir.c (searchdir): Likewise.
+ (changeino): Likewise.
+ (makeentry): Likewise.
+
+Mon Oct 17 16:07:56 1994 Michael I Bushnell <mib@geech.gnu.ai.mit.edu>
+
+ * inode.c (inode_iterate): FN takes new third arg.
+ Keep track of new var `offset' and pass it to FN.
+ * pass2.c (pass2/checkdirblock): New third arg.
+ Only scan DIRBLKSIZ chunks to the total size of the file.
+ * dir.c (searchdir/checkdirblock): Likewise.
+ (changeino/checkdirblock): Likewise.
+ (makeentry/checkdirblock): Likewise.
+ * pass1.c (pass1/checkblock): New third arg (ignored).
+ * pass1b.c (pass1b/checkblock): Likewise.
+
+ * inode.c (inode_iterate): Compute MAXB correctly.
+
+ * utilities.c (getinode): Multiple ino_to_fsbo by
+ sizeof (struct dinode).
+ (write_inode): Likewise.
+ (getinode): Inode buffer needs to be a full block, not a
+ fragment.
+
+Fri Oct 14 21:07:09 1994 Michael I Bushnell <mib@churchy.gnu.ai.mit.edu>
+
+ * utilities.c (lastifrag): New variable.
+ (getinode): Use lastifrag instead of buf; Only I/O new block
+ if lastifrag isn't what we want.
+ (write_inode): Likewise.
+
+Fri Oct 14 17:44:59 1994 Michael I Bushnell <mib@geech.gnu.ai.mit.edu>
+
+ * setup.c (setup): Test ISCHR, not ISDIR.
+ Fix NCYL against NCG * CPG test.
+ Bother to set MAXFSBLOCK, MAXINO, and DIRECT_SYMLINK_EXTENSION.
diff --git a/ufs-fsck/Makefile b/ufs-fsck/Makefile
new file mode 100644
index 00000000..a484428e
--- /dev/null
+++ b/ufs-fsck/Makefile
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 1994, 1995, 1996 Free Software Foundation
+# Written by Michael I. Bushnell.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := ufs-fsck
+makemode := utility
+
+SRCS = dir.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \
+ pass5.c setup.c utilities.c inode.c
+OBJS = $(subst .c,.o,$(SRCS)) tables.o
+LCLHDRS = fsck.h
+target = fsck.ufs
+installationdir = $(sbindir)
+HURDLIBS=shouldbeinlibc
+
+include ../Makeconf
+
+vpath tables.c $(top_srcdir)/ufs
+
diff --git a/ufs-fsck/dir.c b/ufs-fsck/dir.c
new file mode 100644
index 00000000..04541c4f
--- /dev/null
+++ b/ufs-fsck/dir.c
@@ -0,0 +1,567 @@
+/* Directory management subroutines
+ Copyright (C) 1994, 1996, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+
+/* This routine is used in pass 1 to initialize DIRARRAY and DIRSORTED.
+ Copy information from DP (for number NUMBER) into a newly allocated
+ dirinfo structure and add it to the arrays. */
+void
+record_directory (struct dinode *dp, ino_t number)
+{
+ u_int blks;
+ struct dirinfo *dnp;
+
+ blks = howmany (dp->di_size, sblock->fs_bsize);
+ if (blks > NDADDR)
+ blks = NDADDR + NIADDR;
+ blks *= sizeof (daddr_t);
+ dnp = malloc (sizeof (struct dirinfo) + blks);
+
+ dnp->i_number = number;
+ dnp->i_parent = dnp->i_dotdot = 0;
+ dnp->i_isize = dp->di_size;
+ dnp->i_numblks = blks;
+ bcopy (dp->di_db, dnp->i_blks, blks);
+
+ if (dirarrayused == dirarraysize)
+ {
+ if (dirarraysize == 0)
+ {
+ dirarraysize = 100;
+ dirarray = malloc (dirarraysize * sizeof (struct dirinfo *));
+ dirsorted = malloc (dirarraysize * sizeof (struct dirinfo *));
+ }
+ else
+ {
+ dirarraysize *= 2;
+ dirarray = realloc (dirarray,
+ dirarraysize * sizeof (struct dirinfo *));
+ dirsorted = realloc (dirsorted,
+ dirarraysize * sizeof (struct dirinfo *));
+ }
+ }
+ dirarray[dirarrayused] = dnp;
+ dirsorted[dirarrayused] = dnp;
+ dirarrayused++;
+}
+
+/* Return the cached dirinfo structure for directory INO. */
+struct dirinfo *
+lookup_directory (ino_t ino)
+{
+ int i;
+
+ for (i = 0; i < dirarrayused; i++)
+ if (dirarray[i]->i_number == ino)
+ return dirarray[i];
+
+ errexit ("Cannot find chached directory I=%d\n", ino);
+}
+
+/* Check to see if DIR is really a readable directory; if it
+ isn't, then bail with an appropriate message and return 0;
+ else return 1. MSG identifies the action contemplated */
+static int
+validdir (ino_t dir, char *action)
+{
+ switch (inodestate[dir])
+ {
+ case DIRECTORY:
+ case DIRECTORY|DIR_REF:
+ return 1;
+
+ case UNALLOC:
+ warning (1, "CANNOT %s I=%d; NOT ALLOCATED", action, dir);
+ return 0;
+
+ case BADDIR:
+ warning (1, "CANNOT %s I=%d; BAD BLOCKS", action, dir);
+ return 0;
+
+ case REG:
+ warning (1, "CANNOT %s I=%d; NOT DIRECTORY", action, dir);
+ return 0;
+
+ default:
+ errexit ("ILLEGAL STATE");
+ }
+}
+
+/* Search directory DIR for name NAME. If NAME is found, then
+ set *INO to the inode of the entry; otherwise clear INO. Returns 1 if all
+ was normal, or 0 if there was some error doing the search. */
+int
+searchdir (ino_t dir, char *name, ino_t *ino)
+{
+ struct dinode dino;
+ int len;
+
+ /* Scan through one directory block and see if it
+ contains NAME. */
+ void
+ check1block (void *buf)
+ {
+ struct directory_entry *dp;
+
+ for (dp = buf; (void *)dp - buf < DIRBLKSIZ;
+ dp = (struct directory_entry *) ((void *)dp + dp->d_reclen))
+ {
+ if (dp->d_reclen == 0
+ || dp->d_reclen + (void *)dp - buf > DIRBLKSIZ)
+ return;
+
+ if (dp->d_ino == 0 || dp->d_ino > maxino)
+ continue;
+
+ if (DIRECT_NAMLEN (dp) == len && strcmp (dp->d_name, name) == 0)
+ {
+ *ino = dp->d_ino;
+ return;
+ }
+ }
+ }
+
+ /* Read part of a directory and look to see if it contains
+ NAME. Return 1 if we should keep looking at more
+ blocks. */
+ int
+ checkdirblock (daddr_t bno, int nfrags, off_t offset)
+ {
+ void *buf = alloca (nfrags * sblock->fs_fsize);
+ void *bufp;
+
+ readblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize);
+ for (bufp = buf;
+ bufp - buf < nfrags * sblock->fs_fsize
+ && offset + (bufp - buf) + DIRBLKSIZ <= dino.di_size;
+ bufp += DIRBLKSIZ)
+ {
+ check1block (bufp);
+ if (*ino)
+ return 0;
+ }
+ return 1;
+ }
+
+ *ino = 0;
+
+ if (!validdir (dir, "READ"))
+ return 0;
+
+ getinode (dir, &dino);
+
+ len = strlen (name);
+ datablocks_iterate (&dino, checkdirblock);
+
+ return 1;
+}
+
+/* Change the existing entry in DIR for name NAME to be
+ inode INO. Return 1 if the entry was found and
+ replaced, else return 0. */
+int
+changeino (ino_t dir, char *name, ino_t ino)
+{
+ struct dinode dino;
+ int len;
+ int madechange;
+
+ /* Scan through a directory block looking for NAME;
+ if we find it then change the inode pointer to point
+ at INO and return 1; if we don't find it then return 0. */
+ int
+ check1block (void *buf)
+ {
+ struct directory_entry *dp;
+
+ for (dp = buf; (void *)dp - buf < DIRBLKSIZ;
+ dp = (struct directory_entry *) ((void *)dp + dp->d_reclen))
+ {
+ if (dp->d_reclen == 0
+ || dp->d_reclen + (void *)dp - buf > DIRBLKSIZ)
+ return 0;
+
+ if (dp->d_ino == 0 || dp->d_ino > maxino)
+ continue;
+
+ if (DIRECT_NAMLEN (dp) == len && strcmp (dp->d_name, name) == 0)
+ {
+ dp->d_ino = ino;
+ madechange = 1;
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ /* Read part of a directory and look to see if it
+ contains NAME. Return 1 if we should keep looking
+ at more blocks. */
+ int
+ checkdirblock (daddr_t bno, int nfrags, off_t offset)
+ {
+ void *buf = alloca (nfrags * sblock->fs_fsize);
+ void *bufp;
+
+ readblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize);
+ for (bufp = buf;
+ bufp - buf < nfrags * sblock->fs_fsize
+ && offset + (bufp - buf) + DIRBLKSIZ <= dino.di_size;
+ bufp += DIRBLKSIZ)
+ {
+ if (check1block (bufp))
+ {
+ writeblock (fsbtodb (sblock, bno), buf,
+ nfrags * sblock->fs_fsize);
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ if (!validdir (dir, "REWRITE"))
+ return 0;
+
+ getinode (dir, &dino);
+ len = strlen (name);
+ madechange = 0;
+ datablocks_iterate (&dino, checkdirblock);
+ return madechange;
+}
+
+/* Attempt to expand the size of a directory. Return
+ 1 if we succeeded. */
+static int
+expanddir (struct dinode *dp)
+{
+ daddr_t lastbn, newblk;
+ char *cp, buf[sblock->fs_bsize];
+
+ lastbn = lblkno (sblock, dp->di_size);
+ if (blkoff (sblock, dp->di_size) && lastbn >= NDADDR - 1)
+ return 0;
+ else if (!blkoff (sblock, dp->di_size) && lastbn >= NDADDR)
+ return 0;
+ else if (blkoff (sblock, dp->di_size) && !dp->di_db[lastbn])
+ return 0;
+ else if (!blkoff (sblock, dp->di_size) && dp->di_db[lastbn])
+ return 0;
+
+ newblk = allocblk (sblock->fs_frag);
+ if (!newblk)
+ return 0;
+
+ if (blkoff (sblock, dp->di_size))
+ dp->di_db[lastbn + 1] = dp->di_db[lastbn];
+ dp->di_db[lastbn] = newblk;
+ dp->di_size += sblock->fs_bsize;
+ dp->di_blocks += sblock->fs_bsize / DEV_BSIZE;
+
+ for (cp = buf; cp < buf + sblock->fs_bsize; cp += DIRBLKSIZ)
+ {
+ struct directory_entry *dir = (struct directory_entry *) cp;
+ dir->d_ino = 0;
+ dir->d_reclen = DIRBLKSIZ;
+ }
+
+ writeblock (fsbtodb (sblock, newblk), buf, sblock->fs_bsize);
+ return 1;
+}
+
+/* Add a new link into directory DIR with name NAME and target
+ INO. Return 1 if we succeeded and 0 if we failed. It is
+ an error to call this routine if NAME is already present
+ in DIR. */
+int
+makeentry (ino_t dir, ino_t ino, char *name)
+{
+ int len;
+ struct dinode dino;
+ int needed;
+ int madeentry;
+
+ /* Read a directory block and see if it contains room for the
+ new entry. If so, add it and return 1; otherwise return 0. */
+ int
+ check1block (void *buf)
+ {
+ struct directory_entry *dp;
+
+ for (dp = buf; (void *)dp - buf < DIRBLKSIZ;
+ dp = (struct directory_entry *) ((void *)dp + dp->d_reclen))
+ {
+ if (dp->d_reclen == 0
+ || dp->d_reclen + (void *)dp - buf > DIRBLKSIZ)
+ return 0;
+ if (dp->d_ino
+ && dp->d_reclen - DIRSIZ (DIRECT_NAMLEN (dp)) >= needed)
+ {
+ struct directory_entry *newdp;
+ newdp = (struct directory_entry *)
+ ((void *)dp + DIRSIZ (DIRECT_NAMLEN (dp)));
+
+ newdp->d_reclen = dp->d_reclen - DIRSIZ (DIRECT_NAMLEN (dp));
+ DIRECT_NAMLEN (newdp) = len;
+ newdp->d_ino = ino;
+ if (direct_symlink_extension)
+ newdp->d_type = typemap[ino];
+ bcopy (name, newdp->d_name, len + 1);
+
+ dp->d_reclen -= newdp->d_reclen;
+ madeentry = 1;
+ return 1;
+ }
+ else if (!dp->d_ino && dp->d_reclen >= needed)
+ {
+ DIRECT_NAMLEN (dp) = len;
+ dp->d_ino = ino;
+ if (direct_symlink_extension)
+ dp->d_type = typemap[ino];
+ bcopy (name, dp->d_name, len + 1);
+ madeentry = 1;
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ /* Read part of a directory and look to see if it
+ contains NAME. Return 1 if we should keep looking
+ at more blocks. */
+ int
+ checkdirblock (daddr_t bno, int nfrags, off_t offset)
+ {
+ void *buf = alloca (nfrags * sblock->fs_fsize);
+ void *bufp;
+
+ readblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize);
+ for (bufp = buf;
+ bufp - buf < nfrags * sblock->fs_fsize
+ && offset + (bufp - buf) + DIRBLKSIZ <= dino.di_size;
+ bufp += DIRBLKSIZ)
+ {
+ if (check1block (bufp))
+ {
+ writeblock (fsbtodb (sblock, bno), buf,
+ nfrags * sblock->fs_fsize);
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ if (!validdir (dir, "MODIFY"))
+ return 0;
+
+ getinode (dir, &dino);
+ len = strlen (name);
+ needed = DIRSIZ (len);
+ madeentry = 0;
+ datablocks_iterate (&dino, checkdirblock);
+ if (!madeentry)
+ {
+ /* Attempt to expand the directory. */
+ problem (0, "NO SPACE LEFT IN DIR INO=%d", dir);
+ if (preen || reply ("EXPAND"))
+ {
+ if (expanddir (&dino))
+ {
+ write_inode (ino, &dino);
+ datablocks_iterate (&dino, checkdirblock);
+ pfix ("EXPANDED");
+ }
+ else
+ {
+ pfail (0);
+ warning (1, "CANNOT EXPAND DIRECTORY");
+ }
+ }
+ }
+ return madeentry;
+}
+
+/* Create a directory node whose parent is to be PARENT, whose inode
+ is REQUEST, and whose mode is to be MODE. If REQUEST is zero, then
+ allocate any inode. Initialze the contents of the
+ directory. Return the inode of the new directory. */
+ino_t
+allocdir (ino_t parent, ino_t request, mode_t mode)
+{
+ ino_t ino;
+
+ mode |= IFDIR;
+
+ ino = allocino (request, mode);
+ if (!ino)
+ return 0;
+ if (!makeentry (ino, ino, "."))
+ goto bad;
+ if (!makeentry (ino, parent, ".."))
+ goto bad;
+
+ linkfound[ino]++;
+ linkfound[parent]++;
+ return ino;
+
+ bad:
+ freeino (ino);
+ return 0;
+}
+
+/* Link node INO into lost+found. If PARENT is positive then INO is
+ a directory, and PARENT is the number of `..' as found in INO.
+ If PARENT is zero then INO is a directory without any .. entry.
+ If the node could be linked, return 1; else return 0. */
+int
+linkup (ino_t ino, ino_t parent)
+{
+ int search_failed;
+ struct dinode lfdino;
+ char *tempname;
+ ino_t foo;
+
+ if (lfdir == 0)
+ {
+ if (!searchdir (ROOTINO, lfname, &lfdir))
+ {
+ warning (1, "FAILURE SEARCHING FOR `%s'", lfname);
+ return 0;
+ }
+ if (lfdir == 0)
+ {
+ problem (0, "NO `%s' DIRECTORY", lfname);
+ if (preen || reply ("CREATE"))
+ {
+ lfdir = allocdir (ROOTINO, 0, lfmode);
+ if (lfdir != 0)
+ {
+ if (! makeentry (ROOTINO, lfdir, lfname))
+ {
+ freeino (lfdir);
+ linkfound[ROOTINO]--;
+ lfdir = 0;
+ }
+ }
+ }
+ if (lfdir)
+ pfix ("CREATED");
+ else
+ {
+ pfail (0);
+ warning (1, "SORRY, CANNOT CREATE `%s' DIRECTORY", lfname);
+ return 0;
+ }
+ }
+ }
+
+ getinode (lfdir, &lfdino);
+ if ((lfdino.di_model & IFMT) != IFDIR)
+ {
+ ino_t oldlfdir;
+
+ problem (1, "`%s' IS NOT A DIRECTORY", lfname);
+ if (! reply ("REALLOCATE"))
+ return 0;
+
+ oldlfdir = lfdir;
+
+ lfdir = allocdir (ROOTINO, 0, lfmode);
+ if (!lfdir)
+ {
+ warning (1, "SORRY, CANNOT CREATE `%s' DIRECTORY", lfname);
+ return 0;
+ }
+ if (!changeino (ROOTINO, lfname, lfdir))
+ {
+ warning (1, "SORRY, CANNOT CREATE `%s' DIRECTORY", lfname);
+ return 0;
+ }
+
+ /* One less link to the old one */
+ linkfound[oldlfdir]--;
+
+ getinode (lfdir, &lfdino);
+ }
+
+ if (inodestate[lfdir] != DIRECTORY && inodestate[lfdir] != (DIRECTORY|DIR_REF))
+ {
+ warning (1, "SORRY. `%s' DIRECTORY NOT ALLOCATED", lfname);
+ return 0;
+ }
+
+ asprintf (&tempname, "#%d", ino);
+ search_failed = !searchdir (lfdir, tempname, &foo);
+ while (foo)
+ {
+ char *newname;
+ asprintf (&newname, "%sa", tempname);
+ free (tempname);
+ tempname = newname;
+ search_failed = !searchdir (lfdir, tempname, &foo);
+ }
+ if (search_failed)
+ {
+ warning (1, "FAILURE SEARCHING FOR `%s' IN `%s'", tempname, lfname);
+ free (tempname);
+ return 0;
+ }
+ if (!makeentry (lfdir, ino, tempname))
+ {
+ free (tempname);
+ warning (1, "SORRY, NO SPACE IN `%s' DIRECTORY", lfname);
+ return 0;
+ }
+ free (tempname);
+ linkfound[ino]++;
+
+ if (parent != -1)
+ {
+ /* Reset `..' in ino */
+ if (parent)
+ {
+ if (!changeino (ino, "..", lfdir))
+ {
+ warning (1, "CANNOT ADJUST `..' LINK I=%u", ino);
+ return 0;
+ }
+ /* Forget about link to old parent */
+ linkfound[parent]--;
+ }
+ else if (!makeentry (ino, lfdir, ".."))
+ {
+ warning (1, "CANNOT CREAT `..' LINK I=%u", ino);
+ return 0;
+ }
+
+ /* Account for link to lost+found; update inode directly
+ here to avoid confusing warning later. */
+ linkfound[lfdir]++;
+ linkcount[lfdir]++;
+ lfdino.di_nlink++;
+ write_inode (lfdir, &lfdino);
+
+ if (parent)
+ warning (0, "DIR I=%u CONNECTED; PARENT WAS I=%u", ino, parent);
+ else
+ warning (0, "DIR I=%u CONNECTED", ino);
+ }
+ return 1;
+}
diff --git a/ufs-fsck/fsck.h b/ufs-fsck/fsck.h
new file mode 100644
index 00000000..7d55bea6
--- /dev/null
+++ b/ufs-fsck/fsck.h
@@ -0,0 +1,183 @@
+/*
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <dirent.h>
+
+#define swab_disk 0
+
+#include "../ufs/fs.h"
+#include "../ufs/dinode.h"
+#include "../ufs/dir.h"
+
+/* Type of an inode */
+#define UNALLOC 0
+#define REG 1
+#define DIRECTORY 2
+#define BADDIR 3
+
+/* Added to directories in pass 2 */
+#define DIR_REF 4 /* dir has been found in connectivity search */
+
+/* State of each inode (set by pass 1) */
+char *inodestate;
+
+/* Number of links claimed by each inode (set by pass 1) */
+nlink_t *linkcount;
+
+/* Number of links found to each inode (set by pass 2) */
+nlink_t *linkfound;
+
+/* DT_foo type of each inode (set by pass 1) */
+char *typemap;
+
+/* Map of blocks allocated */
+char *blockmap;
+
+/* A string identifying what we're trying to check. */
+extern char *device_name;
+
+
+/* Command line flags */
+int nowrite; /* all questions fail */
+int noquery; /* all questions succeed */
+
+
+enum contret
+{
+ RET_STOP,
+ RET_GOOD,
+ RET_BAD,
+};
+
+
+/* One of these structures is set up for each directory by
+ pass 1 and used by passes 2 and 3. */
+struct dirinfo
+{
+ struct inoinfo *i_nexthash; /* next entry in hash chain */
+ ino_t i_number; /* inode entry of this dir */
+ ino_t i_parent; /* inode entry of parent */
+ ino_t i_dotdot; /* inode number of `..' */
+ ino_t i_dot; /* inode number of `.' */
+ ino_t i_isize; /* size of inode */
+ u_int i_numblks; /* size of block array in bytes */
+ daddr_t i_blks[0]; /* array of inode block addresses */
+};
+
+/* Array of all the dirinfo structures in inode number order */
+struct dirinfo **dirarray;
+
+/* Array of all thi dirinfo structures sorted by their first
+ block address */
+struct dirinfo **dirsorted;
+
+int dirarrayused; /* number of directories */
+int dirarraysize; /* alloced size of dirarray/dirsorted */
+
+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 */
+
+
+extern struct fs *sblock;
+
+extern daddr_t maxfsblock;
+extern int maxino;
+extern int direct_symlink_extension;
+
+extern int newinofmt;
+
+/* Terse automatic mode for noninteractive use; punts on severe problems. */
+extern int preen;
+
+extern int readfd, writefd;
+
+extern int fix_denied;
+
+extern int fsmodified;
+
+extern int lfdir;
+
+/* Total number of files found on the partition. */
+extern daddr_t num_files;
+
+extern mode_t lfmode;
+extern char *lfname;
+
+#define NBBY 8
+#define howmany(x,y) (((x)+((y)-1))/(y))
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+#define isclr(a, i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+#define isset(a, i) ((a)[(i)/NBBY] & (1<<((i)%NBBY)))
+#define setbit(a,i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY))
+#define clrbit(a,i) ((a)[(i)/NBBY] &= ~(1<<(i)%NBBY))
+#define DEV_BSIZE 512
+
+#define setbmap(blkno) setbit (blockmap, blkno)
+#define testbmap(blkno) isset (blockmap, blkno)
+#define clrbmap(blkno) clrbit (blockmap, blkno)
+
+#define DI_MODE(dp) (((dp)->di_modeh << 16) | (dp)->di_model)
+
+
+
+int setup (char *);
+void pass1 (), pass1b (), pass2 (), pass3 (), pass4 (), pass5 ();
+
+void readblock (daddr_t, void *, size_t);
+void writeblock (daddr_t, void *, size_t);
+
+void getinode (ino_t, struct dinode *);
+void write_inode (ino_t, struct dinode *);
+void clear_inode (ino_t, struct dinode *);
+
+daddr_t allocblk (int);
+int check_range (daddr_t, int);
+
+ino_t allocino (ino_t, mode_t);
+void freeino (ino_t);
+ino_t allocdir (ino_t, ino_t, mode_t);
+
+int makeentry (ino_t, ino_t, char *);
+int changeino (ino_t, char *, ino_t);
+
+int linkup (ino_t, ino_t);
+
+void datablocks_iterate (struct dinode *, int (*)(daddr_t, int, off_t));
+void allblock_iterate (struct dinode *, int (*)(daddr_t, int, off_t));
+
+void record_directory (struct dinode *, ino_t);
+struct dirinfo *lookup_directory (ino_t);
+
+void errexit (char *, ...) __attribute__ ((format (printf, 1, 2), noreturn));
+void warning (int, char *, ...) __attribute__ ((format (printf, 2, 3)));
+void problem (int, char *, ...) __attribute__ ((format (printf, 2, 3)));
+void pinode (int, ino_t, char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
+void pextend (char *, ...) __attribute__ ((format (printf, 1, 2)));
+void pfix (char *fix), pfail (char *reason);
+int reply (char *);
diff --git a/ufs-fsck/inode.c b/ufs-fsck/inode.c
new file mode 100644
index 00000000..df4b880e
--- /dev/null
+++ b/ufs-fsck/inode.c
@@ -0,0 +1,203 @@
+/* Inode allocation, deallocation, etc.
+ Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+
+static void
+inode_iterate (struct dinode *dp,
+ int (*fn) (daddr_t, int, off_t),
+ int doaddrblocks)
+{
+ mode_t mode = dp->di_model & IFMT;
+ int nb, maxb;
+ off_t totaloffset = 0;
+
+ /* Call FN for iblock IBLOCK of level LEVEL and recurse down
+ the indirect block pointers. */
+ int
+ scaniblock (daddr_t iblock, int level)
+ {
+ int cont;
+ daddr_t ptrs[NINDIR(sblock)];
+ int i;
+
+ if (doaddrblocks)
+ {
+ cont = (*fn)(iblock, sblock->fs_frag, totaloffset);
+ if (cont == RET_STOP)
+ return RET_STOP;
+ else if (cont == RET_BAD)
+ return RET_GOOD;
+ }
+
+ readblock (fsbtodb (sblock, iblock), ptrs, sblock->fs_bsize);
+ for (i = 0; i < NINDIR (sblock); i++)
+ {
+ if (!ptrs[i])
+ continue;
+
+ if (level == 0)
+ {
+ cont = (*fn)(ptrs[i], sblock->fs_frag, totaloffset);
+ totaloffset += sblock->fs_bsize;
+ }
+ else
+ cont = scaniblock (ptrs[i], level - 1);
+ if (cont == RET_STOP)
+ return RET_STOP;
+ }
+ return RET_GOOD;
+ }
+
+ if (mode == IFBLK || mode == IFCHR
+ || (mode == IFLNK && sblock->fs_maxsymlinklen != -1
+ && (dp->di_size < sblock->fs_maxsymlinklen
+ || (sblock->fs_maxsymlinklen == 0 && dp->di_blocks == 0))))
+ return;
+
+ maxb = lblkno (sblock, dp->di_size - 1);
+ totaloffset = 0;
+ for (nb = 0; nb < NDADDR; nb++)
+ {
+ int offset;
+ int nfrags;
+
+ if (nb == maxb && (offset = blkoff (sblock, dp->di_size)))
+ nfrags = numfrags (sblock, fragroundup (sblock, offset));
+ else
+ nfrags = sblock->fs_frag;
+
+ if (dp->di_db[nb]
+ && (*fn)(dp->di_db[nb], nfrags, totaloffset) != RET_GOOD)
+ return;
+ totaloffset += nfrags * sizeof (sblock->fs_fsize);
+ }
+
+ for (nb = 0; nb < NIADDR; nb++)
+ if (dp->di_ib[nb] && scaniblock (dp->di_ib[nb], nb) != RET_GOOD)
+ return;
+
+ if (doaddrblocks && dp->di_trans)
+ (*fn)(dp->di_trans, sblock->fs_frag, totaloffset);
+}
+
+void
+datablocks_iterate (struct dinode *dp,
+ int (*fn) (daddr_t, int, off_t))
+{
+ inode_iterate (dp, fn, 0);
+}
+
+void
+allblock_iterate (struct dinode *dp,
+ int (*fn) (daddr_t, int, off_t))
+{
+ inode_iterate (dp, fn, 1);
+}
+
+/* Allocate an inode. If INUM is nonzero, then allocate that
+ node specifically, otherwise allocate any available inode.
+ MODE is the mode of the new file. Return the allocated
+ inode number (or 0 if the allocation failed). */
+ino_t
+allocino (ino_t request, mode_t mode)
+{
+ ino_t ino;
+ struct dinode dino;
+ struct timeval tv;
+
+ if (request)
+ {
+ if (inodestate[request] != UNALLOC)
+ return 0;
+ ino = request;
+ }
+ else
+ {
+ for (ino = ROOTINO; ino < maxino; ino++)
+ if (inodestate[ino] == UNALLOC)
+ break;
+ if (ino == maxino)
+ return 0;
+ }
+
+ if ((mode & IFMT) == IFDIR)
+ inodestate[ino] = DIRECTORY | DIR_REF;
+ else
+ inodestate[ino] = REG;
+
+ getinode (ino, &dino);
+ dino.di_modeh = (mode & 0xffff0000) >> 16;
+ dino.di_model = (mode & 0x0000ffff);
+ gettimeofday (&tv, 0);
+ dino.di_atime.tv_sec = tv.tv_sec;
+ dino.di_atime.tv_nsec = tv.tv_usec * 1000;
+ dino.di_mtime = dino.di_ctime = dino.di_atime;
+ dino.di_size = 0;
+ dino.di_blocks = 0;
+ num_files++;
+ write_inode (ino, &dino);
+ typemap[ino] = IFTODT (mode);
+ return ino;
+}
+
+/* Deallocate inode INUM. */
+void
+freeino (ino_t inum)
+{
+ struct dinode dino;
+
+ int
+ clearblock (daddr_t bno, int nfrags, off_t offset)
+ {
+ int i;
+
+ for (i = 0; i < nfrags; i++)
+ {
+ if (check_range (bno + i, 1))
+ return RET_BAD;
+ if (testbmap (bno + i))
+ {
+ struct dups *dlp;
+ for (dlp = duplist; dlp; dlp = dlp->next)
+ {
+ if (dlp->dup != bno + i)
+ continue;
+ dlp->dup = duplist->dup;
+ dlp = duplist;
+ duplist = duplist->next;
+ free (dlp);
+ break;
+ }
+ if (dlp == 0)
+ clrbmap (bno + i);
+ }
+ }
+ return RET_GOOD;
+ }
+
+ getinode (inum, &dino);
+ allblock_iterate (&dino, clearblock);
+
+ clear_inode (inum, &dino);
+ inodestate[inum] = UNALLOC;
+
+ num_files--;
+}
diff --git a/ufs-fsck/main.c b/ufs-fsck/main.c
new file mode 100644
index 00000000..34c32a67
--- /dev/null
+++ b/ufs-fsck/main.c
@@ -0,0 +1,170 @@
+/* Main program for GNU fsck
+ Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <argp.h>
+#include <hurd.h>
+#include <version.h>
+
+#include "fsck.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (fsck.ufs);
+
+char *lfname = "lost+found";
+mode_t lfmode = 0755;
+
+/* Terse automatic mode for noninteractive use; punts on severe problems. */
+int preen = 0;
+
+/* Total number of files found on the partition. */
+long num_files = 0;
+
+static struct argp_option options[] =
+{
+ {"preen", 'p', 0, 0, "Terse automatic mode", 1},
+ {"yes", 'y', 0, 0, "Automatically answer yes to all questions"},
+ {"no", 'n', 0, 0, "Automatically answer no to all questions"},
+ {"lost+found", 'l', "NAME", 0, "The name of the lost+found directory in /"},
+ {"lf-mode", 'm', "MODE", 0, "The mode of the lost+found directory in /"},
+ {0, 0, 0, 0, "In --preen mode, the following also apply:", 2},
+ {"force", 'f', 0, 0, "Check even if clean"},
+ {"silent", 's', 0, 0, "Only print diagostic messages"},
+ {0, 0}
+};
+char *args_doc = "DEVICE";
+
+/* Returns a malloced buffer containing a nice printable size for FRAGS. */
+static char *
+nice_size (long frags)
+{
+ char *rep;
+ char *units = "KMGT", *u = units;
+ float num = ((float)frags * sblock->fs_fsize) / 1024;
+
+ while (num > 1024)
+ {
+ num /= 1024;
+ u++;
+ }
+
+ asprintf (&rep, num >= 1000 ? "%.0f%c" : "%.3g%c", num, *u);
+
+ return rep;
+}
+
+/* Print summary statistics. */
+static void
+show_stats ()
+{
+ long num_ffree = sblock->fs_cstotal.cs_nffree;
+ long num_bfree = sblock->fs_cstotal.cs_nbfree;
+ long tot_ffree = num_ffree + sblock->fs_frag * num_bfree;
+ char *urep = nice_size (sblock->fs_dsize - tot_ffree);
+ char *frep = nice_size (tot_ffree);
+ warning (0, "%ld files, %s used, %s free (%ld.%ld%% fragmentation)",
+ num_files, urep, frep,
+ (num_ffree * 100) / sblock->fs_dsize,
+ (((num_ffree * 1000 + sblock->fs_dsize / 2) / sblock->fs_dsize)
+ % 10));
+ free (urep);
+ free (frep);
+}
+
+int
+main (int argc, char **argv)
+{
+ int silent = 0, force = 0;
+ char *device = 0;
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'p': preen = 1; break;
+ case 'y': noquery = 1; break;
+ case 'n': nowrite = 1; break;
+ case 'l': lfname = arg; break;
+ case 'm': lfmode = strtol (arg, 0, 8); break;
+ case 'f': force = 1; break;
+ case 's': silent = 1; break;
+ case ARGP_KEY_ARG:
+ if (!device)
+ {
+ device = arg;
+ break;
+ }
+ /* Fall through */
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc};
+
+ preen = nowrite = noquery = 0;
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ if (!setup (device))
+ exit (1);
+
+ if (preen && sblock->fs_clean && !force)
+ {
+ if (! silent)
+ warning (0, "FILESYSTEM CLEAN");
+ }
+ else
+ {
+ if (!preen)
+ printf ("** Phase 1 -- Check Blocks and Sizes\n");
+ pass1 ();
+
+ if (duplist)
+ {
+ if (!preen)
+ printf ("** Phase 1b -- Rescan for More Duplicates\n");
+ pass1b ();
+ }
+
+ if (!preen)
+ printf ("** Phase 2 -- Check Pathnames\n");
+ pass2 ();
+
+ if (!preen)
+ printf ("** Phase 3 -- Check Connectivity\n");
+ pass3 ();
+
+ if (!preen)
+ printf ("** Phase 4 -- Check Reference Counts\n");
+ pass4 ();
+
+ if (!preen)
+ printf ("** Phase 5 -- Check Cyl Groups\n");
+ pass5 ();
+
+ if (! silent)
+ show_stats (sblock);
+ }
+
+ if (fsmodified && !preen)
+ printf ("\n***** FILE SYSTEM WAS MODIFIED *****\n");
+
+ exit (fsmodified ? 2 : 0);
+}
diff --git a/ufs-fsck/pass1.c b/ufs-fsck/pass1.c
new file mode 100644
index 00000000..066f4649
--- /dev/null
+++ b/ufs-fsck/pass1.c
@@ -0,0 +1,439 @@
+/* Pass one of GNU fsck -- count blocks and verify inodes
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+
+#include "fsck.h"
+
+static struct dinode zino;
+
+/* Find all the blocks in use by files and filesystem reserved blocks.
+ Set them in the global block map. For each file, if a block is found
+ allocated twice, then record the block and inode in DUPLIST.
+ Initialize INODESTATE, LINKCOUNT, and TYPEMAP. */
+void
+pass1 ()
+{
+ ino_t number;
+ ino_t i;
+ int cg;
+ struct dinode dino;
+ struct dinode *dp = &dino;
+ mode_t mode, type;
+ int ndb;
+ int holdallblocks;
+ int lbn;
+ int nblocks;
+ int blkerror;
+ int nblkrngerrors;
+ int nblkduperrors;
+
+ /* This function is called for each block of DP. Check to see
+ if the block number is valid. If so, set the entry in the
+ block map. If the block map entry is already set, then keep
+ track of this block and see if the user wants to clear the
+ node. Increment NBLOCKS by the number of data blocks held.
+ Set BLKERROR if this block is invalid.
+ Return RET_GOOD, RET_BAD, RET_STOP if the block is good,
+ bad, or if we should entirely stop checking blocks in this
+ inode. */
+ int
+ checkblock (daddr_t bno, int nfrags, off_t offset)
+ {
+#define MAXBAD 10
+ int outofrange;
+ struct dups *dlp, *new;
+ int wasbad = 0;
+
+ /* Check to see if this block is in range */
+ outofrange = check_range (bno, nfrags);
+ if (outofrange)
+ {
+ blkerror = 1;
+ wasbad = 1;
+ if (nblkrngerrors == 0)
+ warning (0, "I=%d HAS BAD BLOCKS", number);
+ if (nblkrngerrors++ > MAXBAD)
+ {
+ problem (0, "EXCESSIVE BAD BLKS I=%d", number);
+ if (preen || reply ("SKIP"))
+ {
+ pfail ("SKIPPING");
+ return RET_STOP;
+ }
+ }
+ }
+
+ for (; nfrags > 0; bno++, nfrags--)
+ {
+ if (outofrange && check_range (bno, 1))
+ warning (0, "BAD BLOCK %lu", bno);
+ else
+ {
+ if (!testbmap (bno))
+ setbmap (bno);
+ else
+ {
+ blkerror = 1;
+ if (nblkduperrors == 0)
+ warning (0, "I=%d HAS DUPLICATE BLOCKS", number);
+ warning (0, "DUPLICATE BLOCK %ld", bno);
+ wasbad = 1;
+ if (nblkduperrors++ > MAXBAD)
+ {
+ problem (0, "EXCESSIVE DUP BLKS I=%d", number);
+ if (preen || reply ("SKIP"))
+ {
+ pfail ("SKIPPING");
+ return RET_STOP;
+ }
+ }
+ new = malloc (sizeof (struct dups));
+ new->dup = bno;
+ 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 == bno)
+ break;
+ if (dlp == muldup && dlp->dup != bno)
+ muldup = new;
+ }
+ }
+ nblocks += sblock->fs_fsize / DEV_BSIZE;
+ }
+ return wasbad ? RET_BAD : RET_GOOD;
+ }
+
+
+ /* Account for blocks used by meta data */
+ for (cg = 0; cg < sblock->fs_ncg; cg++)
+ {
+ daddr_t firstdata, firstcgblock, bno;
+
+ /* Each cylinder group past the first reserves data
+ from its cylinder group copy to (but not including)
+ the first datablock.
+
+ The first, however, reserves from the very front of the
+ cylinder group (thus including the boot block), and it also
+ reserves the data blocks holding the csum information. */
+ firstdata = cgdmin (sblock, cg);
+ if (cg == 0)
+ {
+ firstcgblock = cgbase (sblock, cg);
+ firstdata += howmany (sblock->fs_cssize, sblock->fs_fsize);
+ }
+ else
+ firstcgblock = cgsblock (sblock, cg);
+
+ /* Mark the blocks set */
+ for (bno = firstcgblock; bno < firstdata; bno++)
+ setbmap (bno);
+ }
+
+ /* Loop through each inode, doing initial checks */
+ for (number = 0, cg = 0; cg < sblock->fs_ncg; cg++)
+ for (i = 0; i < sblock->fs_ipg; i++, number++)
+ {
+ /* These record whether we've already complained about extra
+ direct/indirect blocks. */
+ int dbwarn = 0, ibwarn = 0;
+
+/* if (!preen && !(number % 10000))
+ printf ("I=%d\n", number); */
+
+ if (number < ROOTINO)
+ continue;
+
+ getinode (number, dp);
+ mode = DI_MODE (dp);
+ type = mode & IFMT;
+
+ /* If the node is not allocated, then make sure it's
+ properly clear */
+ if (type == 0)
+ {
+ if (bcmp (dp->di_db, zino.di_db, NDADDR * sizeof (daddr_t))
+ || bcmp (dp->di_ib, zino.di_ib, NIADDR * sizeof (daddr_t))
+ || dp->di_trans
+ || DI_MODE (dp)
+ || dp->di_size)
+ {
+ problem (0, "PARTIALLY ALLOCATED INODE I=%d", number);
+ if (preen || reply ("CLEAR"))
+ {
+ clear_inode (number, dp);
+ pfix ("CLEARED");
+ }
+ }
+ inodestate[number] = UNALLOC;
+ }
+ else
+ {
+ /* Node is allocated. */
+
+ /* Check to see if we think the node should be cleared */
+
+ /* Verify size for basic validity */
+ holdallblocks = 0;
+
+ if (dp->di_size + sblock->fs_bsize - 1 < dp->di_size)
+ {
+ problem (1, "OVERFLOW IN FILE SIZE I=%d (SIZE == %lld)", number,
+ dp->di_size);
+ if (reply ("CLEAR"))
+ {
+ clear_inode (number, dp);
+ inodestate[number] = UNALLOC;
+ continue;
+ }
+ inodestate[number] = UNALLOC;
+ warning (0, "WILL TREAT ANY BLOCKS HELD BY I=%d AS ALLOCATED",
+ number);
+ holdallblocks = 1;
+ }
+
+ /* Decode type and set NDB
+ also set inodestate correctly. */
+ inodestate[number] = REG;
+ switch (type)
+ {
+ case IFBLK:
+ case IFCHR:
+ ndb = 1;
+ break;
+
+ case IFIFO:
+ case IFSOCK:
+ ndb = 0;
+ break;
+
+ case IFLNK:
+ if (sblock->fs_maxsymlinklen != -1)
+ {
+ /* Check to see if this is a fastlink. The
+ old fast link format has fs_maxsymlinklen
+ of zero and di_blocks zero; the new format has
+ fs_maxsymlinklen set and we ignore di_blocks.
+ So check for either. */
+ if ((sblock->fs_maxsymlinklen
+ && dp->di_size < sblock->fs_maxsymlinklen)
+ || (!sblock->fs_maxsymlinklen && !dp->di_blocks))
+ {
+ /* Fake NDB value so that we will check
+ all the block pointers past the symlink */
+ ndb = howmany (dp->di_size, sizeof (daddr_t));
+ if (ndb > NDADDR)
+ {
+ int j = ndb - NDADDR;
+ for (ndb = 1; j > 1; i--)
+ ndb *= NINDIR (sblock);
+ ndb += NDADDR;
+ }
+ }
+ else
+ ndb = howmany (dp->di_size, sblock->fs_bsize);
+ }
+ else
+ ndb = howmany (dp->di_size, sblock->fs_bsize);
+ break;
+
+ case IFDIR:
+ inodestate[number] = DIRECTORY;
+ /* Fall through */
+ case IFREG:
+ ndb = howmany (dp->di_size, sblock->fs_bsize);
+ break;
+
+ default:
+ problem (1, "UNKNOWN FILE TYPE I=%d (MODE=%ol)", number, mode);
+ if (reply ("CLEAR"))
+ {
+ clear_inode (number, dp);
+ inodestate[number] = UNALLOC;
+ continue;
+ }
+ inodestate[number] = UNALLOC;
+ holdallblocks = 1;
+ warning (0, "WILL TREAT ANY BLOCKS HELD BY I=%d "
+ "AS ALLOCATED", number);
+ ndb = 0;
+ }
+
+ if (ndb < 0)
+ {
+ problem (1, "BAD FILE SIZE I= %d (SIZE == %lld)", number,
+ dp->di_size);
+ if (reply ("CLEAR"))
+ {
+ clear_inode (number, dp);
+ inodestate[number] = UNALLOC;
+ continue;
+ }
+ inodestate[number] = UNALLOC;
+ warning (0, "WILL TREAT ANY BLOCKS HELD BY I=%d AS ALLOCATED",
+ number);
+ holdallblocks = 1;
+ }
+
+ /* Make sure that direct and indirect block pointers
+ past the file size are zero. If the size is bogus, then
+ don't bother (they should all be zero, but the user has
+ requested that they be treated as allocated). */
+ if (!holdallblocks)
+ {
+ if (dp->di_size
+ && (type == IFBLK || type == IFCHR
+ || type == IFSOCK || type == IFIFO))
+ {
+ problem (1, "SPECIAL NODE I=%d (MODE=%ol) HAS SIZE %lld",
+ number, mode, dp->di_size);
+ if (reply ("TRUNCATE"))
+ {
+ dp->di_size = 0;
+ write_inode (number, dp);
+ }
+ }
+
+ /* If we haven't set NDB speciall above, then it is set from
+ the file size correctly by the size check. */
+
+ /* Check all the direct and indirect blocks that are past the
+ amount necessary to be zero. */
+ for (lbn = ndb; lbn < NDADDR; lbn++)
+ {
+ if (dp->di_db[lbn])
+ {
+ if (!dbwarn)
+ {
+ dbwarn = 1;
+ problem (0, "INODE I=%d HAS EXTRA DIRECT BLOCKS",
+ number);
+ if (preen || reply ("DEALLOCATE"))
+ {
+ dp->di_db[lbn] = 0;
+ dbwarn = 2;
+ pfix ("DEALLOCATED");
+ }
+ }
+ else if (dbwarn == 2)
+ dp->di_db[lbn] = 0;
+ }
+ if (dbwarn == 2)
+ write_inode (number, dp);
+ }
+
+ for (lbn = 0, ndb -= NDADDR; ndb > 0; lbn++)
+ ndb /= NINDIR (sblock);
+ for (; lbn < NIADDR; lbn++)
+ {
+ if (dp->di_ib[lbn])
+ {
+ if (ibwarn)
+ {
+ ibwarn = 1;
+ problem (0, "INODE I=%d HAS EXTRA INDIRECT BLOCKS",
+ number);
+ if (preen || reply ("DEALLOCATE"))
+ {
+ dp->di_ib[lbn] = 0;
+ ibwarn = 2;
+ pfix ("DEALLOCATED");
+ }
+ }
+ else if (ibwarn == 2)
+ dp->di_ib[lbn] = 0;
+ }
+ if (ibwarn == 2)
+ write_inode (number, dp);
+ }
+ }
+
+ /* If this node is really allocated (as opposed to something
+ that we should clear but the user won't) then set LINKCOUNT
+ and TYPEMAP entries. */
+ if (inodestate[number] != UNALLOC)
+ {
+ linkcount[number] = dp->di_nlink;
+ typemap[number] = IFTODT (mode);
+ }
+
+ /* Iterate over the blocks of the file,
+ calling CHECKBLOCK for each file. */
+ nblocks = 0;
+ blkerror = 0;
+ nblkduperrors = 0;
+ nblkrngerrors = 0;
+ allblock_iterate (dp, checkblock);
+
+ if (blkerror)
+ {
+ if (preen)
+ warning (1, "DUPLICATE or BAD BLOCKS");
+ else
+ {
+ problem (0, "I=%d has ", number);
+ if (nblkduperrors)
+ {
+ pextend ("%d DUPLICATE BLOCKS", nblkduperrors);
+ if (nblkrngerrors)
+ pextend (" and ");
+ }
+ if (nblkrngerrors)
+ pextend ("%d BAD BLOCKS", nblkrngerrors);
+ if (reply ("CLEAR"))
+ {
+ clear_inode (number, dp);
+ inodestate[number] = UNALLOC;
+ continue;
+ }
+ else if (inodestate[number] == DIRECTORY)
+ inodestate[number] = BADDIR;
+ }
+ }
+ else if (dp->di_blocks != nblocks)
+ {
+ problem (0, "INCORRECT BLOCK COUNT I=%d (%ld should be %d)",
+ number, dp->di_blocks, nblocks);
+ if (preen || reply ("CORRECT"))
+ {
+ dp->di_blocks = nblocks;
+ write_inode (number, dp);
+ pfix ("CORRECTED");
+ }
+ }
+
+ num_files++;
+
+ if (type == IFDIR)
+ record_directory (dp, number);
+ }
+ }
+}
+
+
diff --git a/ufs-fsck/pass1b.c b/ufs-fsck/pass1b.c
new file mode 100644
index 00000000..0cf50a17
--- /dev/null
+++ b/ufs-fsck/pass1b.c
@@ -0,0 +1,92 @@
+/* Pass 1b of fsck -- scan inodes for references to duplicate blocks
+ Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+
+void
+pass1b ()
+{
+ struct dinode dino;
+ struct dinode *dp = &dino;
+ int cg, i;
+ ino_t number = 0;
+ int dupblk;
+ struct dups *duphead = duplist;
+
+ /* Check each block of file DP; if the block is in the dup block
+ list then add it to the dup block list under this file.
+ Return RET_GOOD or RET_BAD if the block is
+ good or bad, respectively. */
+ int
+ checkblock (daddr_t bno, int nfrags, off_t offset)
+ {
+ struct dups *dlp;
+ int hadbad = 0;
+
+ for (; nfrags > 0; bno++, nfrags--)
+ {
+ if (check_range (bno, 1))
+ return RET_BAD;
+ for (dlp = duphead; dlp; dlp = dlp->next)
+ {
+ if (dlp->dup == bno)
+ {
+ dupblk++;
+ warning (0, "DUPLICATE BLOCK %ld\n", bno);
+ dlp->dup = duphead->dup;
+ duphead->dup = bno;
+ duphead = duphead->next;
+ hadbad = 1;
+ }
+ if (dlp == muldup)
+ break;
+ }
+ }
+ return hadbad ? RET_BAD : RET_GOOD;
+ }
+
+ /* Call CHECKBLOCK for each block of each node, to see if it holds
+ a block already found to be a duplicate. */
+ for (cg = 0; cg < sblock->fs_ncg; cg++)
+ for (i = 0; i < sblock->fs_ipg; i++, number++)
+ {
+ if (number < ROOTINO)
+ continue;
+ if (inodestate[number] != UNALLOC)
+ {
+ getinode (number, dp);
+ dupblk = 0;
+ allblock_iterate (dp, checkblock);
+ if (dupblk)
+ {
+ problem (1, "I=%d HAS %d DUPLICATE BLOCKS", number, dupblk);
+ if (reply ("CLEAR"))
+ {
+ clear_inode (number, dp);
+ inodestate[number] = UNALLOC;
+ }
+ else if (inodestate[number] == DIRECTORY)
+ inodestate[number] = BADDIR;
+ }
+ }
+ }
+}
+
+
diff --git a/ufs-fsck/pass2.c b/ufs-fsck/pass2.c
new file mode 100644
index 00000000..a2d5996c
--- /dev/null
+++ b/ufs-fsck/pass2.c
@@ -0,0 +1,401 @@
+/* Pass 2 of GNU fsck -- examine all directories for validity
+ Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+#include <assert.h>
+
+/* Verify root inode's allocation and check all directories for
+ viability. Set DIRSORTED array fully and check to make sure
+ each directory has a correct . and .. in it. */
+void
+pass2 ()
+{
+ int nd;
+ struct dirinfo *dnp;
+ struct dinode dino;
+
+ /* Return negative, zero, or positive according to the
+ ordering of the first data block of **DNP1 and **DNP2. */
+ int
+ sortfunc (const void *ptr1, const void *ptr2)
+ {
+ struct dirinfo * const *dnp1 = ptr1;
+ struct dirinfo * const *dnp2 = ptr2;
+ return ((*dnp1)->i_blks[0] - (*dnp2)->i_blks[0]);
+ }
+
+ /* Called for each DIRBLKSIZ chunk of the directory.
+ BUF is the data of the directory block. Return
+ 1 if this block has been modified and should be written
+ to disk; otherwise return 0. */
+ int
+ check1block (void *buf)
+ {
+ struct directory_entry *dp;
+ int mod = 0;
+ u_char namlen;
+ char type;
+ int i;
+
+ for (dp = buf; (void *)dp - buf < DIRBLKSIZ;
+ dp = (struct directory_entry *) ((void *)dp + dp->d_reclen))
+ {
+ /* Check RECLEN for basic validity */
+ if (dp->d_reclen == 0
+ || dp->d_reclen + (void *)dp - buf > DIRBLKSIZ)
+ {
+ /* Perhaps the entire dir block is zero. UFS does that
+ when extending directories. So allow preening
+ to safely patch up all-null dir blocks. */
+ if (dp == buf)
+ {
+ char *bp;
+ for (bp = (char *)buf; bp < (char *)buf + DIRBLKSIZ; bp++)
+ if (*bp)
+ goto reclen_problem;
+
+ problem (0, "NULL BLOCK IN DIRECTORY");
+ if (preen || reply ("PATCH"))
+ {
+ /* Mark this entry free, and return. */
+ dp->d_ino = 0;
+ dp->d_reclen = DIRBLKSIZ;
+ pfix ("PATCHED");
+ return 1;
+ }
+ else
+ return mod;
+ }
+
+ reclen_problem:
+ problem (1, "BAD RECLEN IN DIRECTORY");
+ if (reply ("SALVAGE"))
+ {
+ /* Skip over everything else in this dirblock;
+ mark this entry free. */
+ dp->d_ino = 0;
+ dp->d_reclen = DIRBLKSIZ - ((void *)dp - buf);
+ return 1;
+ }
+ else
+ /* But give up regardless */
+ return mod;
+ }
+
+ /* Check INO */
+ if (dp->d_ino > maxino)
+ {
+ problem (1, "BAD INODE NUMBER IN DIRECTORY");
+ if (reply ("SALVAGE"))
+ {
+ /* Mark this entry clear */
+ dp->d_ino = 0;
+ mod = 1;
+ }
+ }
+
+ if (!dp->d_ino)
+ continue;
+
+ /* Check INO */
+ if (inodestate[dp->d_ino] == UNALLOC)
+ {
+ pinode (0, dnp->i_number, "REF TO UNALLOCATED NODE IN");
+ if (preen || reply ("REMOVE"))
+ {
+ dp->d_ino = 0;
+ mod = 1;
+ pfix ("REMOVED");
+ continue;
+ }
+ }
+
+ /* Check NAMLEN */
+ namlen = DIRECT_NAMLEN (dp);
+ if (namlen > MAXNAMLEN)
+ {
+ problem (1, "BAD NAMLEN IN DIRECTORY");
+ if (reply ("SALVAGE"))
+ {
+ /* Mark this entry clear */
+ dp->d_ino = 0;
+ mod = 1;
+ }
+ }
+ else
+ {
+ /* Check for illegal characters */
+ for (i = 0; i < DIRECT_NAMLEN (dp); i++)
+ if (dp->d_name[i] == '\0' || dp->d_name[i] == '/')
+ {
+ problem (1, "ILLEGAL CHARACTER IN FILE NAME");
+ if (reply ("SALVAGE"))
+ {
+ /* Mark this entry clear */
+ dp->d_ino = 0;
+ mod = 1;
+ break;
+ }
+ }
+ if (dp->d_name[DIRECT_NAMLEN (dp)])
+ {
+ problem (1, "DIRECTORY NAME NOT TERMINATED");
+ if (reply ("SALVAGE"))
+ {
+ /* Mark this entry clear */
+ dp->d_ino = 0;
+ mod = 1;
+ }
+ }
+ }
+
+ if (!dp->d_ino)
+ continue;
+
+ /* Check TYPE */
+ type = DIRECT_TYPE (dp);
+ if (type != DT_UNKNOWN && type != typemap[dp->d_ino])
+ {
+ problem (0, "INCORRECT NODE TYPE IN DIRECTORY");
+ if (preen || reply ("CLEAR"))
+ {
+ pfix ("CLEARED");
+ dp->d_type = 0;
+ mod = 1;
+ }
+ }
+
+ /* Here we should check for duplicate directory entries;
+ that's too much trouble right now. */
+
+ /* Account for the inode in the linkfound map */
+ if (inodestate[dp->d_ino] != UNALLOC)
+ linkfound[dp->d_ino]++;
+
+ if (inodestate[dp->d_ino] == DIRECTORY
+ || inodestate[dp->d_ino] == BADDIR)
+ {
+ if (DIRECT_NAMLEN (dp) == 1 && dp->d_name[0] == '.')
+ dnp->i_dot = dp->d_ino;
+ else if (DIRECT_NAMLEN (dp) == 2
+ && dp->d_name[0] == '.' && dp->d_name[1] == '.')
+ dnp->i_dotdot = dp->d_ino;
+ else
+ {
+ struct dirinfo *targetdir;
+ targetdir = lookup_directory (dp->d_ino);
+ if (targetdir->i_parent)
+ {
+ problem (0, "EXTRANEOUS LINK `%s' TO DIR I=%ld",
+ dp->d_name, dp->d_ino);
+ pextend (" FOUND IN DIR I=%d", dnp->i_number);
+ if (preen || reply ("REMOVE"))
+ {
+ dp->d_ino = 0;
+ mod = 1;
+ pfix ("REMOVED");
+ }
+ }
+ else
+ targetdir->i_parent = dnp->i_number;
+ }
+ }
+ }
+ return mod;
+ }
+
+ /* Called for each filesystem block of the directory. Load BNO
+ into core and then call CHECK1BLOCK for each DIRBLKSIZ chunk.
+ OFFSET is the offset this block occupies ithe file.
+ Always return 1. */
+ int
+ checkdirblock (daddr_t bno, int nfrags, off_t offset)
+ {
+ void *buf = alloca (nfrags * sblock->fs_fsize);
+ void *bufp;
+ int rewrite;
+
+ readblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize);
+ rewrite = 0;
+ for (bufp = buf;
+ bufp - buf < nfrags * sblock->fs_fsize
+ && offset + (bufp - buf) + DIRBLKSIZ <= dnp->i_isize;
+ bufp += DIRBLKSIZ)
+ {
+ if (check1block (bufp))
+ rewrite = 1;
+ }
+ if (rewrite)
+ writeblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize);
+ return 1;
+ }
+
+ switch (inodestate [ROOTINO])
+ {
+ default:
+ errexit ("BAD STATE %d FOR ROOT INODE", (int) (inodestate[ROOTINO]));
+
+ case DIRECTORY:
+ break;
+
+ case UNALLOC:
+ problem (1, "ROOT INODE UNALLOCATED");
+ if (!reply ("ALLOCATE"))
+ errexit ("ABORTING");
+ if (allocdir (ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit ("CANNOT ALLOCATE ROOT INODE");
+ break;
+
+ case REG:
+ problem (1, "ROOT INODE NOT DIRECTORY");
+ if (reply ("REALLOCATE"))
+ freeino (ROOTINO);
+ if (allocdir (ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit ("CANNOT ALLOCATE ROOT INODE");
+ break;
+
+ case BADDIR:
+ problem (1, "DUPLICATE or BAD BLOCKS IN ROOT INODE");
+ if (reply ("REALLOCATE"))
+ {
+ freeino (ROOTINO);
+ if (allocdir (ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit ("CANNOT ALLOCATE ROOT INODE");
+ }
+ if (reply ("CONTINUE") == 0)
+ errexit ("ABORTING");
+ break;
+ }
+
+ /* Sort inpsort */
+ qsort (dirsorted, dirarrayused, sizeof (struct dirinfo *), sortfunc);
+
+ /* Check basic integrity of each directory */
+ for (nd = 0; nd < dirarrayused; nd++)
+ {
+ dnp = dirsorted[nd];
+
+ if (dnp->i_isize == 0)
+ continue;
+ if (dnp->i_isize % DIRBLKSIZ)
+ {
+ problem (0, "DIRECTORY INO=%d: LENGTH %d NOT MULTIPLE OF %d",
+ dnp->i_number, dnp->i_isize, DIRBLKSIZ);
+ if (preen || reply ("ADJUST"))
+ {
+ getinode (dnp->i_number, &dino);
+ dino.di_size = roundup (dnp->i_isize, DIRBLKSIZ);
+ write_inode (dnp->i_number, &dino);
+ pfix ("ADJUSTED");
+ }
+ }
+ bzero (&dino, sizeof (struct dinode));
+ dino.di_size = dnp->i_isize;
+ assert (dnp->i_numblks <= (NDADDR + NIADDR) * sizeof (daddr_t));
+ bcopy (dnp->i_blks, dino.di_db, dnp->i_numblks);
+
+ datablocks_iterate (&dino, checkdirblock);
+ }
+
+
+ /* At this point for each directory:
+ If this directory is an entry in another directory, then i_parent is
+ set to that node's number.
+ If this directory has a `..' entry, then i_dotdot is set to that link.
+ Check to see that `..' is set correctly. */
+ for (nd = 0; nd < dirarrayused; nd++)
+ {
+ dnp = dirsorted[nd];
+
+ /* Root is considered to be its own parent even though it isn't
+ listed. */
+ if (dnp->i_number == ROOTINO && !dnp->i_parent)
+ dnp->i_parent = ROOTINO;
+
+ /* Check `.' to make sure it exists and is correct */
+ if (dnp->i_dot == 0)
+ {
+ dnp->i_dot = dnp->i_number;
+ pinode (0, dnp->i_number, "MISSING `.' IN");
+ if ((preen || reply ("FIX"))
+ && makeentry (dnp->i_number, dnp->i_number, "."))
+ {
+ linkfound[dnp->i_number]++;
+ pfix ("FIXED");
+ }
+ else
+ pfail (0);
+ }
+ else if (dnp->i_dot != dnp->i_number)
+ {
+ pinode (0, dnp->i_number, "BAD INODE NUMBER FOR `.' IN");
+ if (preen || reply ("FIX"))
+ {
+ ino_t old_dot = dnp->i_dot;
+ dnp->i_dot = dnp->i_number;
+ if (changeino (dnp->i_number, ".", dnp->i_number))
+ {
+ linkfound[dnp->i_number]++;
+ if (inodestate[old_dot] != UNALLOC)
+ linkfound[old_dot]--;
+ pfix ("FIXED");
+ }
+ else
+ pfail (0);
+ }
+ }
+
+ /* Check `..' to make sure it exists and is correct */
+ if (dnp->i_parent && dnp->i_dotdot == 0)
+ {
+ dnp->i_dotdot = dnp->i_parent;
+ pinode (0, dnp->i_number, "MISSING `..' IN");
+ if ((preen || reply ("FIX"))
+ && makeentry (dnp->i_number, dnp->i_parent, ".."))
+ {
+ linkfound[dnp->i_parent]++;
+ pfix ("FIXED");
+ }
+ else
+ pfail (0);
+ }
+ else if (dnp->i_parent && dnp->i_dotdot != dnp->i_parent)
+ {
+ pinode (0, dnp->i_number, "BAD INODE NUMBER FOR `..' IN");
+ if (preen || reply ("FIX"))
+ {
+ ino_t parent = dnp->i_parent, old_dotdot = dnp->i_dotdot;
+ dnp->i_dotdot = parent;
+ if (changeino (dnp->i_number, "..", parent))
+ /* Adjust what the parent's link count should be; the actual
+ count will be corrected in an later pass. */
+ {
+ linkfound[parent]++;
+ if (inodestate[old_dotdot] != UNALLOC)
+ linkfound[old_dotdot]--;
+ pfix ("FIXED");
+ }
+ else
+ pfail (0);
+ }
+ }
+ }
+}
+
diff --git a/ufs-fsck/pass3.c b/ufs-fsck/pass3.c
new file mode 100644
index 00000000..fd5ad1b0
--- /dev/null
+++ b/ufs-fsck/pass3.c
@@ -0,0 +1,71 @@
+/* Pass 3 of GNU fsck -- Look for disconnected directories
+ Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+
+void
+pass3 ()
+{
+ struct dirinfo *dnp;
+ int nd;
+ int change;
+
+ /* Mark all the directories that can be found from the root. */
+
+ inodestate[ROOTINO] |= DIR_REF;
+
+ do
+ {
+ change = 0;
+ for (nd = 0; nd < dirarrayused; nd++)
+ {
+ dnp = dirsorted[nd];
+ if (dnp->i_parent
+ && inodestate[dnp->i_parent] == (DIRECTORY | DIR_REF)
+ && inodestate[dnp->i_number] == DIRECTORY)
+ {
+ inodestate[dnp->i_number] |= DIR_REF;
+ change = 1;
+ }
+ }
+ }
+ while (change);
+
+ /* Check for orphaned directories */
+ for (nd = 0; nd < dirarrayused; nd++)
+ {
+ dnp = dirsorted[nd];
+
+ if (dnp->i_parent == 0)
+ {
+ if (inodestate[dnp->i_number] & DIR_REF)
+ errexit ("ORPHANED DIR MARKED WITH CONNECT");
+ pinode (0, dnp->i_number, "UNREF");
+ if ((preen || reply ("RECONNECT"))
+ && linkup (dnp->i_number, dnp->i_dotdot))
+ {
+ dnp->i_parent = dnp->i_dotdot = lfdir;
+ pfix ("RECONNECTED");
+ }
+ else
+ pfail (0);
+ }
+ }
+}
diff --git a/ufs-fsck/pass4.c b/ufs-fsck/pass4.c
new file mode 100644
index 00000000..f8fe9814
--- /dev/null
+++ b/ufs-fsck/pass4.c
@@ -0,0 +1,94 @@
+/* Pass 4 of GNU fsck -- Check reference counts
+ Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+
+void
+pass4()
+{
+ ino_t number;
+ /* True if any reconnect attempt failed, in which case we don't try again. */
+ int reconn_failed = 0;
+
+ for (number = ROOTINO; number < maxino; number++)
+ {
+ if (linkfound[number] && inodestate[number] != UNALLOC)
+ {
+ if (linkcount[number] != linkfound[number])
+ {
+ pinode (0, number,
+ "LINK COUNT %d SHOULD BE %d IN",
+ linkcount[number], linkfound[number]);
+ if (preen || reply ("ADJUST"))
+ {
+ struct dinode dino;
+ getinode (number, &dino);
+ dino.di_nlink = linkfound[number];
+ write_inode (number, &dino);
+ pfix ("ADJUSTED");
+ }
+ }
+ }
+ else if (linkfound[number] && inodestate[number] == UNALLOC)
+ {
+ /* This can't happen because we never count links to unallocated
+ nodes. */
+ errexit ("LINK RECORDED FOR UNALLOCATED NODE");
+ }
+ else if (!linkfound[number] && inodestate[number] != UNALLOC)
+ {
+ /* No links to allocated node. If the size is zero, then
+ we want to clear it; if the size is positive, then we
+ want to reattach in. */
+ struct dinode dino;
+
+ pinode (0, number, "UNREF");
+
+ getinode (number, &dino);
+ if (dino.di_size && !reconn_failed)
+ {
+ /* This can't happen for dirctories because pass 3 should
+ already have reset them up. */
+ if ((DI_MODE (&dino) & IFMT) == IFDIR)
+ errexit ("NO LINKS TO NONZERO DIRECTORY");
+
+ if (preen || reply ("RECONNECT"))
+ reconn_failed = !linkup (number, -1);
+ if (! reconn_failed)
+ pfix ("RECONNECTED");
+ if (preen && reconn_failed)
+ pfail ("RECONNECT FAILED");
+ }
+ if (dino.di_size == 0 || reconn_failed)
+ {
+ if (reconn_failed && !preen)
+ /* If preening, the previous call to problem is still active
+ (more likely the failure was too severe, and exited). */
+ problem (0, "RECONNECT FAILED");
+ if (preen || reply ("CLEAR"))
+ {
+ inodestate[number] = UNALLOC;
+ clear_inode (number, &dino);
+ pfix ("CLEARED");
+ }
+ }
+ }
+ }
+}
diff --git a/ufs-fsck/pass5.c b/ufs-fsck/pass5.c
new file mode 100644
index 00000000..7ea7aeed
--- /dev/null
+++ b/ufs-fsck/pass5.c
@@ -0,0 +1,450 @@
+/* Pass 5 of GNU fsck -- check allocation maps and summaries
+ Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+
+/* From ../ufs/subr.c: */
+
+/*
+ * Update the frsum fields to reflect addition or deletion
+ * of some frags.
+ */
+static void
+ffs_fragacct(fs, fragmap, fraglist, cnt)
+ struct fs *fs;
+ int fragmap;
+ long fraglist[];
+ int cnt;
+{
+ int inblk;
+ register int field, subfield;
+ register int siz, pos;
+
+ inblk = (int)(fragtbl[fs->fs_frag][fragmap]) << 1;
+ fragmap <<= 1;
+ for (siz = 1; siz < fs->fs_frag; siz++) {
+ if ((inblk & (1 << (siz + (fs->fs_frag % NBBY)))) == 0)
+ continue;
+ field = around[siz];
+ subfield = inside[siz];
+ for (pos = siz; pos <= fs->fs_frag; pos++) {
+ if ((fragmap & field) == subfield) {
+ fraglist[siz] += cnt;
+ pos += siz;
+ field <<= siz;
+ subfield <<= siz;
+ }
+ field <<= 1;
+ subfield <<= 1;
+ }
+ }
+}
+
+void
+pass5 ()
+{
+ struct cg *newcg, *cg;
+ struct ocg *newocg;
+ int savednrpos = 0;
+ struct csum cstotal;
+ int i, j;
+ int c;
+ daddr_t d;
+ struct csum *sbcsums;
+
+ int basesize; /* size of cg not counting flexibly sized */
+ int sumsize; /* size of block totals and pos tbl */
+ int mapsize; /* size of inode map + block map */
+
+ int writesb;
+ int writecg;
+ int writecsum;
+
+ writesb = 0;
+ writecsum = 0;
+
+ cg = alloca (sblock->fs_cgsize);
+
+ newcg = alloca (sblock->fs_cgsize);
+ newocg = (struct ocg *)newcg;
+
+ sbcsums = alloca (fragroundup (sblock, sblock->fs_cssize));
+
+ readblock (fsbtodb (sblock, sblock->fs_csaddr), sbcsums,
+ fragroundup (sblock, sblock->fs_cssize));
+
+ /* Construct a CG structure; initialize everything that's the same
+ in each cylinder group. */
+ bzero (newcg, sblock->fs_cgsize);
+ newcg->cg_niblk = sblock->fs_ipg;
+ switch (sblock->fs_postblformat)
+ {
+ case FS_42POSTBLFMT:
+ /* Initialize size information */
+ basesize = (char *)(&newocg->cg_btot[0]) - (char *)(&newocg->cg_link);
+ sumsize = &newocg->cg_iused[0] - (char *)(&newocg->cg_btot[0]);
+ mapsize = (&newocg->cg_free[howmany(sblock->fs_fpg, NBBY)]
+ - (u_char *)&newocg->cg_iused[0]);
+ savednrpos = sblock->fs_nrpos;
+ sblock->fs_nrpos = 8;
+ break;
+
+ case FS_DYNAMICPOSTBLFMT:
+ /* Set fields unique to new cg structure */
+ newcg->cg_btotoff = &newcg->cg_space[0] - (u_char *)(&newcg->cg_link);
+ newcg->cg_boff = newcg->cg_btotoff + sblock->fs_cpg * sizeof (long);
+ newcg->cg_iusedoff = newcg->cg_boff + (sblock->fs_cpg
+ * sblock->fs_nrpos
+ * sizeof (short));
+ newcg->cg_freeoff = newcg->cg_iusedoff + howmany (sblock->fs_ipg, NBBY);
+
+ if (sblock->fs_contigsumsize <= 0)
+ {
+ newcg->cg_nextfreeoff =
+ (newcg->cg_freeoff
+ + howmany (sblock->fs_cpg * sblock->fs_spc / NSPF (sblock),
+ NBBY));
+ }
+ else
+ {
+ newcg->cg_clustersumoff =
+ (newcg->cg_freeoff
+ + howmany (sblock->fs_cpg * sblock->fs_spc / NSPF (sblock), NBBY)
+ - sizeof (long));
+ newcg->cg_clustersumoff =
+ roundup (newcg->cg_clustersumoff, sizeof (long));
+ newcg->cg_clusteroff =
+ (newcg->cg_clustersumoff
+ + (sblock->fs_contigsumsize + 1) * sizeof (long));
+ newcg->cg_nextfreeoff =
+ (newcg->cg_clusteroff
+ + howmany (sblock->fs_cpg * sblock->fs_spc / NSPB (sblock),
+ NBBY));
+ }
+
+ newcg->cg_magic = CG_MAGIC;
+
+ /* Set map sizes */
+ 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 POSTBL FORMAT");
+ }
+
+ bzero (&cstotal, sizeof (struct csum));
+
+ /* Mark fragments past the end of the filesystem as used. */
+ j = blknum (sblock, sblock->fs_size + sblock->fs_frag - 1);
+ for (i = sblock->fs_size; i < j; i++)
+ setbmap (i);
+
+ /* Now walk through the cylinder groups, checking each one. */
+ for (c = 0; c < sblock->fs_ncg; c++)
+ {
+ int dbase, dmax;
+
+ /* Read the cylinder group structure */
+ readblock (fsbtodb (sblock, cgtod (sblock, c)), cg, sblock->fs_cgsize);
+ writecg = 0;
+
+ if (!cg_chkmagic (cg))
+ warning (1, "CG %d: BAD MAGIC NUMBER", c);
+
+ /* Compute first and last data block addresses in this group */
+ dbase = cgbase (sblock, c);
+ dmax = dbase + sblock->fs_fpg;
+ if (dmax > sblock->fs_size)
+ dmax = sblock->fs_size;
+
+ /* Initialize newcg fully; values from cg for those
+ we can't check. */
+ newcg->cg_time = cg->cg_time;
+ newcg->cg_cgx = c;
+ if (c == sblock->fs_ncg - 1)
+ newcg->cg_ncyl = sblock->fs_ncyl % sblock->fs_cpg;
+ else
+ newcg->cg_ncyl = sblock->fs_cpg;
+ newcg->cg_ndblk = dmax - dbase;
+ if (sblock->fs_contigsumsize > 0)
+ newcg->cg_nclusterblks = newcg->cg_ndblk / sblock->fs_frag;
+ newcg->cg_cs.cs_ndir = 0;
+ newcg->cg_cs.cs_nffree = 0;
+ newcg->cg_cs.cs_nbfree = 0;
+ newcg->cg_cs.cs_nifree = sblock->fs_ipg;
+
+ /* Check these for basic viability; if they are wrong
+ then clear them. */
+ newcg->cg_rotor = cg->cg_rotor;
+ newcg->cg_frotor = cg->cg_frotor;
+ newcg->cg_irotor = cg->cg_irotor;
+ if (newcg->cg_rotor > newcg->cg_ndblk)
+ {
+ problem (0, "ILLEGAL ROTOR VALUE IN CG %d", c);
+ if (preen || reply ("FIX"))
+ {
+ newcg->cg_rotor = 0;
+ cg->cg_rotor = 0;
+ writecg = 1;
+ pfix ("FIXED");
+ }
+ }
+ if (newcg->cg_frotor > newcg->cg_ndblk)
+ {
+ problem (0, "ILLEGAL FROTOR VALUE IN CG %d", c);
+ if (preen || reply ("FIX"))
+ {
+ newcg->cg_frotor = 0;
+ cg->cg_frotor = 0;
+ writecg = 1;
+ pfix ("FIXED");
+ }
+ }
+ if (newcg->cg_irotor > newcg->cg_niblk)
+ {
+ problem (0, "ILLEGAL IROTOR VALUE IN CG %d", c);
+ if (preen || reply ("FIX"))
+ {
+ newcg->cg_irotor = 0;
+ cg->cg_irotor = 0;
+ writecg = 1;
+ pfix ("FIXED");
+ }
+ }
+
+ /* Zero the block maps and summary areas */
+ bzero (&newcg->cg_frsum[0], sizeof newcg->cg_frsum);
+ bzero (&cg_blktot (newcg)[0], sumsize + mapsize);
+ if (sblock->fs_postblformat == FS_42POSTBLFMT)
+ newocg->cg_magic = CG_MAGIC;
+
+ /* Walk through each inode, accounting for it in
+ the inode map and in newcg->cg_cs. */
+ /* In this loop, J is the inode number, and I is the
+ inode number relative to this CG. */
+ j = sblock->fs_ipg * c;
+ for (i = 0; i < sblock->fs_ipg; j++, i++)
+ switch (inodestate[j])
+ {
+ case DIRECTORY:
+ case DIRECTORY | DIR_REF:
+ case BADDIR:
+ newcg->cg_cs.cs_ndir++;
+ /* Fall through... */
+ case REG:
+ newcg->cg_cs.cs_nifree--;
+ setbit (cg_inosused (newcg), i);
+ /* Fall through... */
+ case UNALLOC:
+ break;
+
+ default:
+ errexit ("UNKNOWN STATE I=%d", j);
+ }
+ /* Account for inodes 0 and 1 */
+ if (c == 0)
+ for (i = 0; i < ROOTINO; i++)
+ {
+ setbit (cg_inosused (newcg), i);
+ newcg->cg_cs.cs_nifree--;
+ }
+
+ /* Walk through each data block, accounting for it in
+ the block map and in newcg->cg_cs. */
+ /* In this look, D is the block number and I is the
+ block number relative to this CG. */
+ for (i = 0, d = dbase;
+ d < dmax;
+ d += sblock->fs_frag, i += sblock->fs_frag)
+ {
+ int frags = 0;
+
+ /* Set each free frag of this block in the block map;
+ count how many frags were free. */
+ for (j = 0; j < sblock->fs_frag; j++)
+ {
+ if (testbmap (d + j))
+ continue;
+ setbit (cg_blksfree (newcg), i + j);
+ frags++;
+ }
+
+ /* If all the frags were free, then count this as
+ a free block too. */
+ if (frags == sblock->fs_frag)
+ {
+ newcg->cg_cs.cs_nbfree++;
+ j = cbtocylno (sblock, i);
+ cg_blktot(newcg)[j]++;
+ cg_blks(sblock, newcg, j)[cbtorpos(sblock, i)]++;
+ if (sblock->fs_contigsumsize > 0)
+ setbit (cg_clustersfree (newcg), i / sblock->fs_frag);
+ }
+ else if (frags)
+ {
+ /* Partial; account for the frags. */
+ int blk;
+ newcg->cg_cs.cs_nffree += frags;
+ blk = blkmap (sblock, cg_blksfree (newcg), i);
+ ffs_fragacct (sblock, blk, newcg->cg_frsum, 1);
+ }
+ }
+
+ if (sblock->fs_contigsumsize > 0)
+ {
+ long *sump = cg_clustersum (newcg);
+ u_char *mapp = cg_clustersfree (newcg);
+ int map = *mapp++;
+ int bit = 1;
+ int run = 0;
+
+ for (i = 0; i < newcg->cg_nclusterblks; i++)
+ {
+ if ((map & bit) != 0)
+ run++;
+ else if (run)
+ {
+ if (run > sblock->fs_contigsumsize)
+ run = sblock->fs_contigsumsize;
+ sump[run]++;
+ run = 0;
+ }
+
+ if ((i & (NBBY - 1)) != (NBBY - 1))
+ bit <<= 1;
+ else
+ {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (run != 0)
+ {
+ if (run > sblock->fs_contigsumsize)
+ run = sblock->fs_contigsumsize;
+ sump[run]++;
+ }
+ }
+
+ /* Add this cylinder group's totals into the superblock's
+ totals. */
+ 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;
+
+ /* Check counts in superblock */
+ if (bcmp (&newcg->cg_cs, &sbcsums[c], sizeof (struct csum)))
+ {
+ problem (0, "FREE BLK COUNTS FOR CG %d WRONG IN SUPERBLOCK", c);
+ if (preen || reply ("FIX"))
+ {
+ bcopy (&newcg->cg_cs, &sbcsums[c], sizeof (struct csum));
+ writecsum = 1;
+ pfix ("FIXED");
+ }
+ }
+
+ /* Check inode and block maps */
+ if (bcmp (cg_inosused (newcg), cg_inosused (cg), mapsize))
+ {
+ problem (0, "BLKS OR INOS MISSING IN CG %d BIT MAPS", c);
+ if (preen || reply ("FIX"))
+ {
+ bcopy (cg_inosused (newcg), cg_inosused (cg), mapsize);
+ writecg = 1;
+ pfix ("FIXED");
+ }
+ }
+
+ if (bcmp (&cg_blktot(newcg)[0], &cg_blktot(cg)[0], sumsize))
+ {
+ problem (0, "SUMMARY INFORMATION FOR CG %d BAD", c);
+ if (preen || reply ("FIX"))
+ {
+ bcopy (&cg_blktot(newcg)[0], &cg_blktot(cg)[0], sumsize);
+ writecg = 1;
+ pfix ("FIXED");
+ }
+ }
+
+ if (bcmp (newcg, cg, basesize))
+ {
+ problem (0, "CYLINDER GROUP %d BAD", c);
+ if (preen || reply ("FIX"))
+ {
+ bcopy (newcg, cg, basesize);
+ writecg = 1;
+ pfix ("FIXED");
+ }
+ }
+
+ if (writecg)
+ writeblock (fsbtodb (sblock, cgtod (sblock, c)),
+ cg, sblock->fs_cgsize);
+ }
+
+ /* Restore nrpos */
+ if (sblock->fs_postblformat == FS_42POSTBLFMT)
+ sblock->fs_nrpos = savednrpos;
+
+ if (bcmp (&cstotal, &sblock->fs_cstotal, sizeof (struct csum)))
+ {
+ problem (0, "TOTAL FREE BLK COUNTS WRONG IN SUPERBLOCK");
+ if (preen || reply ("FIX"))
+ {
+ bcopy (&cstotal, &sblock->fs_cstotal, sizeof (struct csum));
+ sblock->fs_ronly = 0;
+ sblock->fs_fmod = 0;
+ writesb = 1;
+ pfix ("FIXED");
+ }
+ }
+
+ if (sblock->fs_clean == 0 && !fix_denied)
+ {
+ problem (0, fsmodified ? "FILESYSTEM MODIFIED" : "FILESYSTEM UNCLEAN");
+ if (preen || reply ("MARK CLEAN"))
+ {
+ sblock->fs_clean = 1;
+ writesb = 1;
+ pfix ("MARKED CLEAN");
+ }
+ }
+
+ if (writesb)
+ writeblock (SBLOCK, sblock, SBSIZE);
+
+ if (writecsum)
+ {
+ struct csum *test;
+
+ writeblock (fsbtodb (sblock, sblock->fs_csaddr), sbcsums,
+ fragroundup (sblock, sblock->fs_cssize));
+
+ test = alloca (fragroundup (sblock, sblock->fs_cssize));
+ readblock (fsbtodb (sblock, sblock->fs_csaddr), test,
+ fragroundup (sblock, sblock->fs_cssize));
+ if (bcmp (test, sbcsums, fragroundup (sblock, sblock->fs_cssize)))
+ warning (0, "CSUM WRITE INCONSISTENT");
+ }
+}
diff --git a/ufs-fsck/setup.c b/ufs-fsck/setup.c
new file mode 100644
index 00000000..6309a901
--- /dev/null
+++ b/ufs-fsck/setup.c
@@ -0,0 +1,191 @@
+/*
+ Copyright (C) 1994, 1996, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <error.h>
+
+static char sblockbuf[SBSIZE];
+struct fs *sblock = (struct fs *)sblockbuf;
+
+/* A string identifying what we're trying to check. */
+char *device_name = 0;
+
+daddr_t maxfsblock;
+int maxino;
+int direct_symlink_extension;
+
+int newinofmt;
+
+int readfd, writefd;
+
+int fix_denied = 0;
+
+int fsmodified = 0;
+
+int lfdir;
+
+/* Get ready to run on device with pathname DEV. */
+int
+setup (char *dev)
+{
+ struct stat st;
+ int changedsb;
+ size_t bmapsize;
+
+ device_name = dev;
+
+ if (stat (dev, &st) == -1)
+ {
+ error (0, errno, "%s", dev);
+ return 0;
+ }
+ if (!S_ISCHR (st.st_mode) && !S_ISBLK (st.st_mode))
+ {
+ problem (1, "%s is not a character or block device", dev);
+ if (! reply ("CONTINUE"))
+ return 0;
+ }
+ if (preen == 0)
+ printf ("** %s", dev);
+ if (!nowrite)
+ readfd = open (dev, O_RDWR);
+ if (nowrite || readfd == -1)
+ {
+ readfd = open (dev, O_RDONLY);
+ if (readfd == -1)
+ {
+ error (0, errno, "%s", dev);
+ return 0;
+ }
+ writefd = -1;
+ nowrite = 1;
+ if (preen)
+ warning (1, "NO WRITE ACCESS");
+ printf (" (NO WRITE)");
+ }
+ else
+ writefd = readfd;
+
+ if (preen == 0)
+ printf ("\n");
+
+ lfdir = 0;
+
+ /* We don't do the alternate superblock stuff here (yet). */
+ readblock (SBLOCK, sblock, SBSIZE);
+ changedsb = 0;
+
+ if (sblock->fs_magic != FS_MAGIC)
+ {
+ warning (1, "BAD MAGIC NUMBER");
+ return 0;
+ }
+ if (sblock->fs_ncg < 1)
+ {
+ warning (1, "NCG OUT OF RANGE");
+ return 0;
+ }
+ if (sblock->fs_cpg < 1)
+ {
+ warning (1, "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)
+ {
+ warning (1, "NCYL INCONSISTENT WITH NCG AND CPG");
+ return 0;
+ }
+ if (sblock->fs_sbsize > SBSIZE)
+ {
+ warning (1, "SBLOCK SIZE PREPONTEROUSLY LARGE");
+ return 0;
+ }
+ if (sblock->fs_optim != FS_OPTTIME && sblock->fs_optim != FS_OPTSPACE)
+ {
+ problem (1, "UNDEFINED OPTIMIZATION IN SUPERBLOCK");
+ if (reply ("SET TO DEFAULT"))
+ {
+ sblock->fs_optim = FS_OPTTIME;
+ changedsb = 1;
+ }
+ }
+ if (sblock->fs_minfree < 0 || sblock->fs_minfree > 99)
+ {
+ problem (0, "IMPOSSIBLE MINFREE=%ld IN SUPERBLOCK", sblock->fs_minfree);
+ if (preen || reply ("SET TO DEFAULT"))
+ {
+ sblock->fs_minfree = 10;
+ changedsb = 1;
+ pfix ("SET TO DEFAULT");
+ }
+ }
+ if (sblock->fs_interleave < 1
+ || sblock->fs_interleave > sblock->fs_nsect)
+ {
+ problem (0, "IMPOSSIBLE INTERLEAVE=%ld IN SUPERBLOCK",
+ sblock->fs_interleave);
+ if (preen || reply ("SET TO DEFAULT"))
+ {
+ sblock->fs_interleave = 1;
+ changedsb = 1;
+ pfix ("SET TO DEFAULT");
+ }
+ }
+ if (sblock->fs_npsect < sblock->fs_nsect
+ || sblock->fs_npsect > sblock->fs_nsect * 2)
+ {
+ problem (0, "IMPOSSIBLE NPSECT=%ld IN SUPERBLOCK", sblock->fs_npsect);
+ if (preen || reply ("SET TO DEFAULT"))
+ {
+ sblock->fs_npsect = sblock->fs_nsect;
+ changedsb = 1;
+ pfix ("SET TO DEFAULT");
+ }
+ }
+ if (sblock->fs_inodefmt >= FS_44INODEFMT)
+ newinofmt = 1;
+ else
+ {
+ sblock->fs_qbmask = ~sblock->fs_bmask;
+ sblock->fs_qfmask = ~sblock->fs_fmask;
+ newinofmt = 0;
+ }
+
+ if (changedsb)
+ writeblock (SBLOCK, sblock, SBSIZE);
+
+ /* Constants */
+ maxfsblock = sblock->fs_size;
+ maxino = sblock->fs_ncg * sblock->fs_ipg;
+ direct_symlink_extension = sblock->fs_maxsymlinklen > 0;
+
+ /* Allocate and initialize maps */
+ bmapsize = roundup (howmany (maxfsblock, NBBY), sizeof (short));
+ blockmap = calloc (bmapsize, sizeof (char));
+ inodestate = calloc (maxino + 1, sizeof (char));
+ typemap = calloc (maxino + 1, sizeof (char));
+ linkcount = calloc (maxino + 1, sizeof (nlink_t));
+ linkfound = calloc (maxino + 1, sizeof (nlink_t));
+ return 1;
+}
diff --git a/ufs-fsck/utilities.c b/ufs-fsck/utilities.c
new file mode 100644
index 00000000..2ccade06
--- /dev/null
+++ b/ufs-fsck/utilities.c
@@ -0,0 +1,454 @@
+/* Miscellaneous functions for fsck
+ Copyright (C) 1994, 1995, 1996, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+#include <fcntl.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <pwd.h>
+#include <error.h>
+
+static void retch (char *reason);
+
+/* Read disk block ADDR into BUF of SIZE bytes. */
+void
+readblock (daddr_t addr, void *buf, size_t size)
+{
+ if (lseek (readfd, addr * DEV_BSIZE, L_SET) == -1)
+ errexit ("CANNOT SEEK TO BLOCK %ld", addr);
+ if (read (readfd, buf, size) != size)
+ errexit ("CANNOT READ BLOCK %ld", addr);
+}
+
+/* Write disk block BLKNO from BUF of SIZE bytes. */
+void
+writeblock (daddr_t addr, void *buf, size_t size)
+{
+ if (lseek (writefd, addr * DEV_BSIZE, L_SET) == -1)
+ errexit ("CANNOT SEEK TO BLOCK %ld", addr);
+ if (write (writefd, buf, size) != size)
+ errexit ("CANNOT WRITE BLOCK %ld", addr);
+ fsmodified = 1;
+}
+
+/* Last filesystem fragment that we read an inode from */
+static char *lastifrag;
+static daddr_t lastifragaddr;
+
+/* Read inode number INO into DINODE. */
+void
+getinode (ino_t ino, struct dinode *di)
+{
+ daddr_t iblk;
+
+ if (!lastifrag)
+ lastifrag = malloc (sblock->fs_bsize);
+
+ iblk = ino_to_fsba (sblock, ino);
+ if (iblk != lastifragaddr)
+ readblock (fsbtodb (sblock, iblk), lastifrag, sblock->fs_bsize);
+ lastifragaddr = iblk;
+ bcopy (lastifrag + ino_to_fsbo (sblock, ino) * sizeof (struct dinode),
+ di, sizeof (struct dinode));
+}
+
+/* Write inode number INO from DINODE. */
+void
+write_inode (ino_t ino, struct dinode *di)
+{
+ daddr_t iblk;
+
+ iblk = ino_to_fsba (sblock, ino);
+ if (iblk != lastifragaddr)
+ readblock (fsbtodb (sblock, iblk), lastifrag, sblock->fs_bsize);
+ lastifragaddr = iblk;
+ bcopy (di, lastifrag + ino_to_fsbo (sblock, ino) * sizeof (struct dinode),
+ sizeof (struct dinode));
+ writeblock (fsbtodb (sblock, iblk), lastifrag, sblock->fs_bsize);
+}
+
+/* Clear inode number INO and zero DI. */
+void
+clear_inode (ino_t ino, struct dinode *di)
+{
+ bzero (di, sizeof (struct dinode));
+ write_inode (ino, di);
+}
+
+/* Allocate and return a block and account for it in all the block
+ maps locally. Don't trust or change the disk block maps.
+ The block should be NFRAGS fragments long. */
+daddr_t
+allocblk (int nfrags)
+{
+ daddr_t i;
+ int j, k;
+
+ if (nfrags <= 0 || nfrags > sblock->fs_frag)
+ return 0;
+
+ /* Examine each block of the filesystem. */
+ for (i = 0; i < maxfsblock - sblock->fs_frag; i += sblock->fs_frag)
+ {
+ /* For each piece of the block big enough to hold this frag... */
+ for (j = 0; j <= sblock->fs_frag - nfrags; j++)
+ {
+ /* For each frag of this piece... */
+ for (k = 0; k < nfrags; k++)
+ if (testbmap (i + j + k))
+ break;
+
+ /* If one of the frags was allocated... */
+ if (k < nfrags)
+ {
+ /* Skip at least that far (short cut) */
+ j += k;
+ continue;
+ }
+
+ /* It's free (at address i + j) */
+
+ /* Mark the frags allocated in our map */
+ for (k = 0; k < nfrags; k++)
+ setbmap (i + j + k);
+
+ return (i + j);
+ }
+ }
+ return 0;
+}
+
+/* Check if a block starting at BLK and extending for CNT
+ fragments is out of range; if it is, then return 1; otherwise return 0. */
+int
+check_range (daddr_t blk, int cnt)
+{
+ int c;
+
+ if ((unsigned)(blk + cnt) > maxfsblock)
+ return 1;
+
+ c = dtog (sblock, blk);
+ if (blk < cgdmin (sblock, c))
+ {
+ if (blk + cnt > cgsblock (sblock, c))
+ return 1;
+ }
+ else
+ {
+ if (blk + cnt > cgbase (sblock, c + 1))
+ return 1;
+ }
+
+ return 0;
+}
+
+struct problem {
+ char *desc;
+ struct problem *prev;
+};
+
+/* A queue of problems found by fsck that are waiting resolution. The front
+ of the list is the most recent problem found (and presumably since
+ previous problems haven't been resolved yet, they depend on this one being
+ solved for their resolution). */
+static struct problem *problems = 0;
+
+static struct problem *free_problems = 0;
+
+static void
+push_problem (char *fmt, va_list args)
+{
+ struct problem *prob = free_problems;
+
+ if (! prob)
+ prob = malloc (sizeof (struct problem));
+ else
+ problems = prob->prev;
+ if (! prob)
+ retch ("malloc failed");
+
+ if (vasprintf (&prob->desc, fmt, args) < 0)
+ retch ("vasprintf failed");
+
+ prob->prev = problems;
+ problems = prob;
+}
+
+/* Print the most recent problem, and perhaps how it was resolved. */
+static void
+resolve_problem (char *fix)
+{
+ struct problem *prob = problems;
+
+ if (! prob)
+ retch ("no more problems");
+
+ problems = prob->prev;
+ prob->prev = free_problems;
+
+ if (preen && device_name)
+ printf ("%s: %s", device_name, prob->desc);
+ else
+ printf ("%s", prob->desc);
+ if (fix)
+ printf (" (%s)\n", fix);
+ else
+ putchar ('\n');
+ free (prob->desc);
+}
+
+/* Retire all problems as if they failed. We print them in chronological
+ order rather than lifo order, as this is a bit clearer, and we can do it
+ when we know they're all going to fail. */
+static void
+flush_problems ()
+{
+ struct problem *fail (struct problem *prob)
+ {
+ struct problem *last = prob->prev ? fail (prob->prev) : prob;
+ if (preen && device_name)
+ printf ("%s: %s\n", device_name, prob->desc);
+ else
+ puts (prob->desc);
+ free (prob->desc);
+ return last;
+ }
+ if (problems)
+ {
+ fail (problems)->prev = free_problems;
+ free_problems = problems;
+ }
+}
+
+/* Like printf, but exit after printing. */
+void
+errexit (char *fmt, ...)
+{
+ va_list args;
+
+ flush_problems ();
+
+ if (preen && device_name)
+ printf ("%s: ", device_name);
+
+ va_start (args, fmt);
+ vprintf (fmt, args);
+ va_end (args);
+ putchar ('\n');
+
+ exit (8);
+}
+
+static void
+retch (char *reason)
+{
+ flush_problems ();
+ error (99, 0, "(internal error) %s!", reason);
+}
+
+/* Prints all unresolved problems and exits, printing MSG as well. */
+static void
+punt (char *msg)
+{
+ problem (0, msg);
+ flush_problems ();
+ exit (8);
+}
+
+/* If SEVERE is true, and we're in preen mode, then things are too hair to
+ fix automatically, so tell the user to do it himself and punt. */
+static void
+no_preen (int severe)
+{
+ if (severe && preen)
+ punt ("PLEASE RUN fsck MANUALLY");
+}
+
+/* Store away the given message about a problem found. A call to problem must
+ be matched later with a call to pfix, pfail, or reply; to print more
+ in the same message, intervening calls to pextend can be used. If SEVERE is
+ true, and we're in preen mode, then the program is terminated. */
+void
+problem (int severe, char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ push_problem (fmt, args);
+ va_end (args);
+
+ no_preen (severe);
+}
+
+/* Following a call to problem (with perhaps intervening calls to
+ pmore), appends the given message to that message. */
+void
+pextend (char *fmt, ...)
+{
+ va_list args;
+ char *more, *concat;
+ struct problem *prob = problems;
+
+ if (! prob)
+ retch ("No pending problem to add to");
+
+ va_start (args, fmt);
+ if (vasprintf (&more, fmt, args) < 0)
+ retch ("vasprintf failed");
+ va_end (args);
+
+ concat = realloc (prob->desc, strlen (prob->desc) + 1 + strlen (more) + 1);
+ if (! concat)
+ retch ("realloc failed");
+
+ strcpy (concat + strlen (concat), more);
+ prob->desc = concat;
+ free (more);
+}
+
+/* Like problem, but as if immediately followed by pfail. */
+void
+warning (int severe, char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ push_problem (fmt, args);
+ va_end (args);
+
+ no_preen (severe);
+
+ resolve_problem (0);
+}
+
+/* Like problem, but appends a helpful description of the given inode number to
+ the message. */
+void
+pinode (int severe, ino_t ino, char *fmt, ...)
+{
+ if (fmt)
+ {
+ va_list args;
+ va_start (args, fmt);
+ push_problem (fmt, args);
+ va_end (args);
+ }
+
+ if (ino < ROOTINO || ino > maxino)
+ pextend (" (BOGUS INODE) I=%d", ino);
+ else
+ {
+ char *p;
+ struct dinode dino;
+ struct passwd *pw;
+
+ getinode (ino, &dino);
+
+ pextend (" %s I=%d", (DI_MODE (&dino) & IFMT) == IFDIR ? "DIR" : "FILE",
+ ino);
+
+ pw = getpwuid (dino.di_uid);
+ if (pw)
+ pextend (" O=%s", pw->pw_name);
+ else
+ pextend (" O=%lu", dino.di_uid);
+
+ pextend (" M=0%o", DI_MODE (&dino));
+ pextend (" SZ=%llu", dino.di_size);
+ p = ctime (&dino.di_mtime.tv_sec);
+ pextend (" MT=%12.12s %4.4s", &p[4], &p[20]);
+ }
+
+ no_preen (severe);
+}
+
+/* Print a successful resolution to a pending problem. Must follow a call to
+ problem or pextend. */
+void
+pfix (char *fix)
+{
+ if (preen)
+ resolve_problem (fix ?: "FIXED");
+}
+
+/* Print an unsuccessful resolution to a pending problem. Must follow a call
+ to problem or pextend. */
+void
+pfail (char *failure)
+{
+ if (preen)
+ resolve_problem (failure);
+}
+
+/* Ask the user a question; return 1 if the user says yes, and 0
+ if the user says no. This call must follow a call to problem or pextend,
+ which it completes. */
+int
+reply (char *question)
+{
+ int persevere;
+ char c;
+
+ if (preen)
+ retch ("Got to reply() in preen mode");
+
+ /* Emit the problem to which the question pertains. */
+ resolve_problem (0);
+
+ persevere = !strcmp (question, "CONTINUE");
+
+ if (!persevere && (nowrite || writefd < 0))
+ {
+ fix_denied = 1;
+ printf ("%s? no\n\n", question);
+ return 0;
+ }
+ else if (noquery || (persevere && nowrite))
+ {
+ printf ("%s? yes\n\n", question);
+ return 1;
+ }
+ else
+ {
+ do
+ {
+ printf ("%s? [yn] ", question);
+ fflush (stdout);
+ c = getchar ();
+ while (c != '\n' && getchar () != '\n')
+ if (feof (stdin))
+ {
+ fix_denied = 1;
+ return 0;
+ }
+ }
+ while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
+ putchar ('\n');
+ if (c == 'y' || c == 'Y')
+ return 1;
+ else
+ {
+ fix_denied = 1;
+ return 0;
+ }
+ }
+}