diff options
Diffstat (limited to 'ufs-fsck')
-rw-r--r-- | ufs-fsck/Makefile | 35 | ||||
-rw-r--r-- | ufs-fsck/dir.c | 567 | ||||
-rw-r--r-- | ufs-fsck/fsck.h | 183 | ||||
-rw-r--r-- | ufs-fsck/inode.c | 203 | ||||
-rw-r--r-- | ufs-fsck/main.c | 170 | ||||
-rw-r--r-- | ufs-fsck/pass1.c | 437 | ||||
-rw-r--r-- | ufs-fsck/pass1b.c | 90 | ||||
-rw-r--r-- | ufs-fsck/pass2.c | 400 | ||||
-rw-r--r-- | ufs-fsck/pass3.c | 71 | ||||
-rw-r--r-- | ufs-fsck/pass4.c | 94 | ||||
-rw-r--r-- | ufs-fsck/pass5.c | 450 | ||||
-rw-r--r-- | ufs-fsck/setup.c | 191 | ||||
-rw-r--r-- | ufs-fsck/utilities.c | 455 |
13 files changed, 3346 insertions, 0 deletions
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..85757b16 --- /dev/null +++ b/ufs-fsck/dir.c @@ -0,0 +1,567 @@ +/* Directory management subroutines + Copyright (C) 1994,96,99,2002 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 cached directory I=%Ld\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=%Ld; NOT ALLOCATED", action, dir); + return 0; + + case BADDIR: + warning (1, "CANNOT %s I=%Ld; BAD BLOCKS", action, dir); + return 0; + + case REG: + warning (1, "CANNOT %s I=%Ld; 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=%Ld", 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, "#%Ld", 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=%Ld", ino); + return 0; + } + /* Forget about link to old parent */ + linkfound[parent]--; + } + else if (!makeentry (ino, lfdir, "..")) + { + warning (1, "CANNOT CREAT `..' LINK I=%Ld", 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=%Ld CONNECTED; PARENT WAS I=%Ld", ino, parent); + else + warning (0, "DIR I=%Ld CONNECTED", ino); + } + return 1; +} diff --git a/ufs-fsck/fsck.h b/ufs-fsck/fsck.h new file mode 100644 index 00000000..4a5dabf5 --- /dev/null +++ b/ufs-fsck/fsck.h @@ -0,0 +1,183 @@ +/* + Copyright (C) 1994,95,96,2002 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 `.' */ + u_int 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 ino_t lfdir; + +/* Total number of files found on the partition. */ +extern long 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..bd41cc62 --- /dev/null +++ b/ufs-fsck/pass1.c @@ -0,0 +1,437 @@ +/* Pass one of GNU fsck -- count blocks and verify inodes + Copyright (C) 1994,95,96,2002 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=%Ld HAS BAD BLOCKS", number); + if (nblkrngerrors++ > MAXBAD) + { + problem (0, "EXCESSIVE BAD BLKS I=%Ld", 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=%Ld HAS DUPLICATE BLOCKS", number); + warning (0, "DUPLICATE BLOCK %ld", bno); + wasbad = 1; + if (nblkduperrors++ > MAXBAD) + { + problem (0, "EXCESSIVE DUP BLKS I=%Ld", 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=%Ld\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=%Ld", 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=%Ld (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=%Ld 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=%Ld (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=%Ld " + "AS ALLOCATED", number); + ndb = 0; + } + + if (ndb < 0) + { + problem (1, "BAD FILE SIZE I= %Ld (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=%Ld 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=%Ld (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=%Ld 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=%Ld 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=%Ld 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=%Ld (%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..4da86974 --- /dev/null +++ b/ufs-fsck/pass1b.c @@ -0,0 +1,90 @@ +/* Pass 1b of fsck -- scan inodes for references to duplicate blocks + Copyright (C) 1994,96,2002 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=%Ld 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..d95929ef --- /dev/null +++ b/ufs-fsck/pass2.c @@ -0,0 +1,400 @@ +/* Pass 2 of GNU fsck -- examine all directories for validity + Copyright (C) 1994,96,2002 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=%Ld", 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=%Ld: 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..cb426a7d --- /dev/null +++ b/ufs-fsck/pass5.c @@ -0,0 +1,450 @@ +/* Pass 5 of GNU fsck -- check allocation maps and summaries + Copyright (C) 1994,96,2001 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) + { + const int cssize = fragroundup (sblock, sblock->fs_cssize); + struct csum *test; + + writeblock (fsbtodb (sblock, sblock->fs_csaddr), sbcsums, + fragroundup (sblock, sblock->fs_cssize)); + + test = alloca (cssize); + readblock (fsbtodb (sblock, sblock->fs_csaddr), test, cssize); + if (bcmp (test, sbcsums, cssize)) + warning (0, "CSUM WRITE INCONSISTENT"); + } +} diff --git a/ufs-fsck/setup.c b/ufs-fsck/setup.c new file mode 100644 index 00000000..9433bd68 --- /dev/null +++ b/ufs-fsck/setup.c @@ -0,0 +1,191 @@ +/* + Copyright (C) 1994,96,99,2002 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; + +ino_t 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..14705f84 --- /dev/null +++ b/ufs-fsck/utilities.c @@ -0,0 +1,455 @@ +/* Miscellaneous functions for fsck + Copyright (C) 1994,95,96,99,2001,02 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> +#include <time.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, "%s", 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=%Ld", ino); + else + { + char *p; + struct dinode dino; + struct passwd *pw; + + getinode (ino, &dino); + + pextend (" %s I=%Ld", (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; + } + } +} |