aboutsummaryrefslogtreecommitdiff
path: root/sbin/fsck_ffs
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/fsck_ffs')
-rw-r--r--sbin/fsck_ffs/Makefile4
-rw-r--r--sbin/fsck_ffs/Makefile.depend2
-rw-r--r--sbin/fsck_ffs/dir.c195
-rw-r--r--sbin/fsck_ffs/ea.c12
-rw-r--r--sbin/fsck_ffs/fsck.h89
-rw-r--r--sbin/fsck_ffs/fsck_ffs.86
-rw-r--r--sbin/fsck_ffs/fsutil.c427
-rw-r--r--sbin/fsck_ffs/gjournal.c21
-rw-r--r--sbin/fsck_ffs/globs.c46
-rw-r--r--sbin/fsck_ffs/inode.c578
-rw-r--r--sbin/fsck_ffs/main.c178
-rw-r--r--sbin/fsck_ffs/pass1.c129
-rw-r--r--sbin/fsck_ffs/pass1b.c10
-rw-r--r--sbin/fsck_ffs/pass2.c169
-rw-r--r--sbin/fsck_ffs/pass3.c16
-rw-r--r--sbin/fsck_ffs/pass4.c8
-rw-r--r--sbin/fsck_ffs/pass5.c63
-rw-r--r--sbin/fsck_ffs/setup.c329
-rw-r--r--sbin/fsck_ffs/suj.c210
-rw-r--r--sbin/fsck_ffs/utilities.c8
20 files changed, 1752 insertions, 748 deletions
diff --git a/sbin/fsck_ffs/Makefile b/sbin/fsck_ffs/Makefile
index ba8a39a87e1f..59805dbbf3d6 100644
--- a/sbin/fsck_ffs/Makefile
+++ b/sbin/fsck_ffs/Makefile
@@ -1,7 +1,5 @@
-# $FreeBSD$
-# @(#)Makefile 8.2 (Berkeley) 4/27/95
-PACKAGE=runtime
+PACKAGE=ufs
PROG= fsck_ffs
LINKS+= ${BINDIR}/fsck_ffs ${BINDIR}/fsck_ufs
LINKS+= ${BINDIR}/fsck_ffs ${BINDIR}/fsck_4.2bsd
diff --git a/sbin/fsck_ffs/Makefile.depend b/sbin/fsck_ffs/Makefile.depend
index 735bf03b75b6..910688a678fa 100644
--- a/sbin/fsck_ffs/Makefile.depend
+++ b/sbin/fsck_ffs/Makefile.depend
@@ -1,8 +1,6 @@
-# $FreeBSD$
# Autogenerated - do NOT edit!
DIRDEPS = \
- gnu/lib/csu \
include \
include/xlocale \
lib/${CSU_DIR} \
diff --git a/sbin/fsck_ffs/dir.c b/sbin/fsck_ffs/dir.c
index 42ecf4112253..3eb0b63c0988 100644
--- a/sbin/fsck_ffs/dir.c
+++ b/sbin/fsck_ffs/dir.c
@@ -29,14 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char sccsid[] = "@(#)dir.c 8.8 (Berkeley) 4/28/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/time.h>
#include <sys/types.h>
@@ -63,7 +55,6 @@ static struct dirtemplate dirhead = {
static int chgino(struct inodesc *);
static int dircheck(struct inodesc *, struct bufarea *, struct direct *);
static int expanddir(struct inode *ip, char *name);
-static void freedir(ino_t ino, ino_t parent);
static struct direct *fsck_readdir(struct inodesc *);
static struct bufarea *getdirblk(ufs2_daddr_t blkno, long size);
static int lftempname(char *bufp, ino_t ino);
@@ -89,6 +80,7 @@ propagate(void)
if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
INO_IS_DUNFOUND(inp->i_number)) {
inoinfo(inp->i_number)->ino_state = DFOUND;
+ check_dirdepth(inp);
change++;
}
}
@@ -96,6 +88,105 @@ propagate(void)
}
/*
+ * Check that the recorded depth of the directory is correct.
+ */
+void
+check_dirdepth(struct inoinfo *inp)
+{
+ struct inoinfo *parentinp;
+ struct inode ip;
+ union dinode *dp;
+ int saveresolved;
+ size_t size;
+ static int updateasked, dirdepthupdate;
+
+ if ((parentinp = getinoinfo(inp->i_parent)) == NULL) {
+ pfatal("check_dirdepth: UNKNOWN PARENT DIR");
+ return;
+ }
+ /*
+ * If depth is correct, nothing to do.
+ */
+ if (parentinp->i_depth + 1 == inp->i_depth)
+ return;
+ /*
+ * Only the root inode should have depth of 0, so if any other
+ * directory has a depth of 0 then this is an old filesystem
+ * that has not been tracking directory depth. Ask just once
+ * whether it should start tracking directory depth.
+ */
+ if (inp->i_depth == 0 && updateasked == 0) {
+ updateasked = 1;
+ if (preen) {
+ pwarn("UPDATING FILESYSTEM TO TRACK DIRECTORY DEPTH\n");
+ dirdepthupdate = 1;
+ } else {
+ /*
+ * The file system can be marked clean even if
+ * a directory does not have the right depth.
+ * Hence, resolved should not be cleared when
+ * the filesystem does not update directory depths.
+ */
+ saveresolved = resolved;
+ dirdepthupdate =
+ reply("UPDATE FILESYSTEM TO TRACK DIRECTORY DEPTH");
+ resolved = saveresolved;
+ }
+ }
+ /*
+ * If we are not converting or we are running in no-write mode
+ * there is nothing more to do.
+ */
+ if ((inp->i_depth == 0 && dirdepthupdate == 0) ||
+ (fswritefd < 0 && bkgrdflag == 0))
+ return;
+ /*
+ * Individual directory at wrong depth. Report it and correct if
+ * in preen mode or ask if in interactive mode. Note that if a
+ * directory is renamed to a new location that is at a different
+ * level in the tree, its depth will be recalculated, but none of
+ * the directories that it contains will be updated. Thus it is
+ * not unexpected to find directories with incorrect depths. No
+ * operational harm will come from this though new directory
+ * placement in the subtree may not be as optimal until the depths
+ * of the affected directories are corrected.
+ *
+ * To avoid much spurious output on otherwise clean filesystems
+ * we only generate detailed output when the debug flag is given.
+ */
+ ginode(inp->i_number, &ip);
+ dp = ip.i_dp;
+ if (inp->i_depth != 0 && debug) {
+ pwarn("DIRECTORY");
+ prtinode(&ip);
+ printf(" DEPTH %d SHOULD BE %d", inp->i_depth,
+ parentinp->i_depth + 1);
+ if (preen == 0 && reply("ADJUST") == 0) {
+ irelse(&ip);
+ return;
+ }
+ if (preen)
+ printf(" (ADJUSTED)\n");
+ }
+ inp->i_depth = parentinp->i_depth + 1;
+ if (bkgrdflag == 0) {
+ DIP_SET(dp, di_dirdepth, inp->i_depth);
+ inodirty(&ip);
+ } else {
+ cmd.value = inp->i_number;
+ cmd.size = (int64_t)inp->i_depth - DIP(dp, di_dirdepth);
+ if (debug)
+ printf("adjdepth ino %ld amt %jd\n", (long)cmd.value,
+ (intmax_t)cmd.size);
+ size = MIBSIZE;
+ if (sysctlnametomib("vfs.ffs.adjdepth", adjdepth, &size) < 0 ||
+ sysctl(adjdepth, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1)
+ rwerror("ADJUST INODE DEPTH", cmd.value);
+ }
+ irelse(&ip);
+}
+
+/*
* Scan each entry in a directory block.
*/
int
@@ -161,7 +252,7 @@ fsck_readdir(struct inodesc *idesc)
dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
/*
* Only need to check current entry if it is the first in the
- * the block, as later entries will have been checked in the
+ * block, as later entries will have been checked in the
* previous call to this function.
*/
if (idesc->id_loc % DIRBLKSIZ != 0 || dircheck(idesc, bp, dp) != 0) {
@@ -338,7 +429,7 @@ fileerror(ino_t cwd, ino_t ino, const char *errmesg)
char pathbuf[MAXPATHLEN + 1];
pwarn("%s ", errmesg);
- if (ino < UFS_ROOTINO || ino > maxino) {
+ if (ino < UFS_ROOTINO || ino >= maxino) {
pfatal("out-of-range inode number %ju", (uintmax_t)ino);
return;
}
@@ -422,7 +513,8 @@ adjust(struct inodesc *idesc, int lcnt)
(long long)cmd.size);
if (sysctl(adjrefcnt, MIBSIZE, 0, 0,
&cmd, sizeof cmd) == -1)
- rwerror("ADJUST INODE", cmd.value);
+ rwerror("ADJUST INODE LINK COUNT",
+ cmd.value);
}
}
}
@@ -472,8 +564,9 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
{
struct inode ip;
union dinode *dp;
- int lostdir;
+ int lostdir, depth;
ino_t oldlfdir;
+ struct inoinfo *inp;
struct inodesc idesc;
char tempname[BUFSIZ];
@@ -516,7 +609,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
if (preen)
printf(" (CREATED)\n");
} else {
- freedir(lfdir, UFS_ROOTINO);
+ freedirino(lfdir, UFS_ROOTINO);
lfdir = 0;
if (preen)
printf("\n");
@@ -525,6 +618,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
}
}
irelse(&ip);
+ free(idesc.id_name);
if (lfdir == 0) {
pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
printf("\n\n");
@@ -545,7 +639,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
irelse(&ip);
return (0);
}
- if ((changeino(UFS_ROOTINO, lfname, lfdir) & ALTERED) == 0) {
+ if ((changeino(UFS_ROOTINO, lfname, lfdir, 1) & ALTERED) == 0) {
pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
irelse(&ip);
return (0);
@@ -574,23 +668,27 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
}
inoinfo(orphan)->ino_linkcnt--;
if (lostdir) {
- if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
+ depth = DIP(dp, di_dirdepth) + 1;
+ if ((changeino(orphan, "..", lfdir, depth) & ALTERED) == 0 &&
parentdir != (ino_t)-1)
(void)makeentry(orphan, lfdir, "..");
DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
inodirty(&ip);
inoinfo(lfdir)->ino_linkcnt++;
pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan);
- if (parentdir != (ino_t)-1) {
+ inp = getinoinfo(parentdir);
+ if (parentdir != (ino_t)-1 && inp != NULL) {
printf("PARENT WAS I=%lu\n", (u_long)parentdir);
/*
- * The parent directory, because of the ordering
+ * If the parent directory did not have to
+ * be replaced then because of the ordering
* guarantees, has had the link count incremented
* for the child, but no entry was made. This
* fixes the parent link count so that fsck does
* not need to be rerun.
*/
- inoinfo(parentdir)->ino_linkcnt++;
+ if ((inp->i_flags & INFO_NEW) != 0)
+ inoinfo(parentdir)->ino_linkcnt++;
}
if (preen == 0)
printf("\n");
@@ -603,7 +701,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
* fix an entry in a directory.
*/
int
-changeino(ino_t dir, const char *name, ino_t newnum)
+changeino(ino_t dir, const char *name, ino_t newnum, int depth)
{
struct inodesc idesc;
struct inode ip;
@@ -617,7 +715,12 @@ changeino(ino_t dir, const char *name, ino_t newnum)
idesc.id_name = strdup(name);
idesc.id_parent = newnum; /* new value for name */
ginode(dir, &ip);
- error = ckinode(ip.i_dp, &idesc);
+ if (((error = ckinode(ip.i_dp, &idesc)) & ALTERED) && newnum != 0) {
+ DIP_SET(ip.i_dp, di_dirdepth, depth);
+ inodirty(&ip);
+ getinoinfo(dir)->i_depth = depth;
+ }
+ free(idesc.id_name);
irelse(&ip);
return (error);
}
@@ -652,15 +755,18 @@ makeentry(ino_t parent, ino_t ino, const char *name)
}
if ((ckinode(dp, &idesc) & ALTERED) != 0) {
irelse(&ip);
+ free(idesc.id_name);
return (1);
}
getpathname(pathbuf, parent, parent);
if (expanddir(&ip, pathbuf) == 0) {
irelse(&ip);
+ free(idesc.id_name);
return (0);
}
retval = ckinode(dp, &idesc) & ALTERED;
irelse(&ip);
+ free(idesc.id_name);
return (retval);
}
@@ -675,14 +781,17 @@ expanddir(struct inode *ip, char *name)
struct bufarea *bp, *nbp;
struct inodesc idesc;
union dinode *dp;
- int indiralloced;
+ long cg, indiralloced;
char *cp;
nbp = NULL;
indiralloced = newblk = indirblk = 0;
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
pwarn("NO SPACE LEFT IN %s", name);
if (!preen && reply("EXPAND") == 0)
return (0);
+ cg = ino_to_cg(&sblock, ip->i_number);
dp = ip->i_dp;
filesize = DIP(dp, di_size);
lastlbn = lblkno(&sblock, filesize);
@@ -701,7 +810,8 @@ expanddir(struct inode *ip, char *name)
bp = getdirblk(oldblk, lastlbnsize);
if (bp->b_errs)
goto bad;
- if ((newblk = allocblk(sblock.fs_frag)) == 0)
+ newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail);
+ if (newblk == 0)
goto bad;
nbp = getdatablk(newblk, sblock.fs_bsize, BT_DIRDATA);
if (nbp->b_errs)
@@ -720,6 +830,7 @@ expanddir(struct inode *ip, char *name)
memmove(cp, &emptydir, sizeof emptydir);
dirty(nbp);
brelse(nbp);
+ binval(bp);
idesc.id_blkno = oldblk;
idesc.id_numfrags = numfrags(&sblock, lastlbnsize);
(void)freeblock(&idesc);
@@ -727,7 +838,7 @@ expanddir(struct inode *ip, char *name)
printf(" (EXPANDED)\n");
return (1);
}
- if ((newblk = allocblk(sblock.fs_frag)) == 0)
+ if ((newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail)) == 0)
goto bad;
bp = getdirblk(newblk, sblock.fs_bsize);
if (bp->b_errs)
@@ -745,8 +856,12 @@ expanddir(struct inode *ip, char *name)
* Allocate indirect block if needed.
*/
if ((indirblk = DIP(dp, di_ib[0])) == 0) {
- if ((indirblk = allocblk(sblock.fs_frag)) == 0)
+ indirblk = allocblk(cg, sblock.fs_frag,
+ std_checkblkavail);
+ if (indirblk == 0) {
+ binval(bp);
goto bad;
+ }
indiralloced = 1;
}
nbp = getdatablk(indirblk, sblock.fs_bsize, BT_LEVEL1);
@@ -757,6 +872,7 @@ expanddir(struct inode *ip, char *name)
DIP_SET(dp, di_ib[0], indirblk);
DIP_SET(dp, di_blocks,
DIP(dp, di_blocks) + btodb(sblock.fs_bsize));
+ inodirty(ip);
}
IBLK_SET(nbp, lastlbn - UFS_NDADDR, newblk);
dirty(nbp);
@@ -770,8 +886,10 @@ expanddir(struct inode *ip, char *name)
return (1);
bad:
pfatal(" (EXPANSION FAILED)\n");
- if (nbp != NULL)
+ if (nbp != NULL) {
+ binval(bp);
brelse(nbp);
+ }
if (newblk != 0) {
idesc.id_blkno = newblk;
idesc.id_numfrags = sblock.fs_frag;
@@ -796,8 +914,8 @@ allocdir(ino_t parent, ino_t request, int mode)
struct inode ip;
union dinode *dp;
struct bufarea *bp;
- struct inoinfo *inp;
struct dirtemplate *dirp;
+ struct inoinfo *inp, *parentinp;
ino = allocino(request, IFDIR|mode);
if (ino == 0)
@@ -822,8 +940,12 @@ allocdir(ino_t parent, ino_t request, int mode)
DIP_SET(dp, di_nlink, 2);
inodirty(&ip);
if (ino == UFS_ROOTINO) {
+ inp = cacheino(dp, ino);
+ inp->i_parent = parent;
+ inp->i_dotdot = parent;
+ inp->i_flags |= INFO_NEW;
+ inoinfo(ino)->ino_type = DT_DIR;
inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
- cacheino(dp, ino);
irelse(&ip);
return(ino);
}
@@ -832,10 +954,18 @@ allocdir(ino_t parent, ino_t request, int mode)
irelse(&ip);
return (0);
}
- cacheino(dp, ino);
- inp = getinoinfo(ino);
+ inp = cacheino(dp, ino);
inp->i_parent = parent;
inp->i_dotdot = parent;
+ inp->i_flags |= INFO_NEW;
+ if ((parentinp = getinoinfo(inp->i_parent)) == NULL) {
+ pfatal("allocdir: UNKNOWN PARENT DIR");
+ } else {
+ inp->i_depth = parentinp->i_depth + 1;
+ DIP_SET(dp, di_dirdepth, inp->i_depth);
+ inodirty(&ip);
+ }
+ inoinfo(ino)->ino_type = DT_DIR;
inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
if (inoinfo(ino)->ino_state == DSTATE) {
inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
@@ -853,8 +983,8 @@ allocdir(ino_t parent, ino_t request, int mode)
/*
* free a directory inode
*/
-static void
-freedir(ino_t ino, ino_t parent)
+void
+freedirino(ino_t ino, ino_t parent)
{
struct inode ip;
union dinode *dp;
@@ -866,6 +996,7 @@ freedir(ino_t ino, ino_t parent)
inodirty(&ip);
irelse(&ip);
}
+ removecachedino(ino);
freeino(ino);
}
diff --git a/sbin/fsck_ffs/ea.c b/sbin/fsck_ffs/ea.c
index 54353005bdc0..f6ebc5c072fc 100644
--- a/sbin/fsck_ffs/ea.c
+++ b/sbin/fsck_ffs/ea.c
@@ -1,5 +1,5 @@
/*-
- * SPDX-License-Identifier: BSD-3-Clause-FreeBSD
+ * SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2002 Poul-Henning Kamp
* Copyright (c) 2002 Networks Associates Technology, Inc.
@@ -35,9 +35,6 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/time.h>
#include <sys/stdint.h>
@@ -61,10 +58,9 @@ eascan(struct inodesc *idesc, struct ufs2_dinode *dp)
return (0);
#else
struct bufarea *bp;
- u_int dsize, n;
+ u_int n;
u_char *cp;
long blksiz;
- char dbuf[DIRBLKSIZ];
printf("Inode %ju extsize %ju\n",
(intmax_t)idesc->id_number, (uintmax_t)dp->di_extsize);
@@ -74,8 +70,10 @@ eascan(struct inodesc *idesc, struct ufs2_dinode *dp)
blksiz = sblock.fs_fsize;
else
blksiz = sblock.fs_bsize;
- printf("blksiz = %ju\n", (intmax_t)blksiz);
bp = getdatablk(dp->di_extb[0], blksiz, BT_EXTATTR);
+ if (bp->b_errs)
+ return (STOP);
+ printf("blksiz = %ju\n", (intmax_t)blksiz);
cp = (u_char *)bp->b_un.b_buf;
for (n = 0; n < blksiz; n++) {
printf("%02x", cp[n]);
diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h
index 1fb0df0c5124..312142eab9a7 100644
--- a/sbin/fsck_ffs/fsck.h
+++ b/sbin/fsck_ffs/fsck.h
@@ -1,5 +1,5 @@
/*-
- * SPDX-License-Identifier: BSD-3-Clause and BSD-2-Clause-FreeBSD
+ * SPDX-License-Identifier: BSD-3-Clause and BSD-2-Clause
*
* Copyright (c) 2002 Networks Associates Technology, Inc.
* All rights reserved.
@@ -57,9 +57,6 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
- * @(#)fsck.h 8.4 (Berkeley) 5/9/95
- * $FreeBSD$
*/
#ifndef _FSCK_H_
@@ -68,6 +65,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
+#include <libufs.h>
#include <sys/queue.h>
@@ -112,7 +110,7 @@ struct inostat {
#define FSTATE 0x2 /* inode is file */
#define FZLINK 0x3 /* inode is file with a link count of zero */
#define DSTATE 0x4 /* inode is directory */
-#define DZLINK 0x5 /* inode is directory with a zero link count */
+#define DZLINK 0x5 /* inode is directory with a zero link count */
#define DFOUND 0x6 /* directory found during descent */
/* 0x7 UNUSED - see S_IS_DVALID() definition */
#define DCLEAR 0x8 /* directory is to be cleared */
@@ -200,8 +198,7 @@ struct bufarea {
#define BT_INODES 7 /* Buffer holds inodes */
#define BT_DIRDATA 8 /* Buffer holds directory data */
#define BT_DATA 9 /* Buffer holds user data */
-#define BT_EMPTY 10 /* Buffer allocated but not filled */
-#define BT_NUMBUFTYPES 11
+#define BT_NUMBUFTYPES 10
#define BT_NAMES { \
"unknown", \
"Superblock", \
@@ -212,8 +209,7 @@ struct bufarea {
"External Attribute", \
"Inode Block", \
"Directory Contents", \
- "User Data", \
- "Allocated but not filled" }
+ "User Data" }
extern char *buftype[];
#define BT_BUFTYPE(type) \
type < BT_NUMBUFTYPES ? buftype[type] : buftype[BT_UNKNOWN]
@@ -234,7 +230,7 @@ extern struct bufarea *pdirbp; /* current directory contents */
(bp)->b_flags |= B_DIRTY; \
} while (0)
#define initbarea(bp, type) do { \
- (bp)->b_bno = (ufs2_daddr_t)-1; \
+ (bp)->b_bno = (ufs2_daddr_t)-4; \
(bp)->b_size = 0; \
(bp)->b_errs = 0; \
(bp)->b_flags = 0; \
@@ -299,21 +295,30 @@ struct dups {
struct dups *next;
ufs2_daddr_t dup;
};
-extern struct dups *duplist; /* head of dup list */
-extern struct dups *muldup; /* end of unique duplicate dup block numbers */
+extern struct dups *duplist; /* head of dup list */
+extern struct dups *muldup; /* end of unique duplicate dup block numbers */
/*
* Inode cache data structures.
*/
-extern struct inoinfo {
- struct inoinfo *i_nexthash; /* next entry in hash chain */
+struct inoinfo {
+ SLIST_ENTRY(inoinfo) i_hash; /* hash list */
ino_t i_number; /* inode number of this entry */
ino_t i_parent; /* inode number of parent */
ino_t i_dotdot; /* inode number of `..' */
size_t i_isize; /* size of inode */
+ u_int i_depth; /* depth of directory from root */
+ u_int i_flags; /* flags, see below */
u_int i_numblks; /* size of block array in bytes */
ufs2_daddr_t i_blks[1]; /* actually longer */
-} **inphead, **inpsort;
+};
+extern SLIST_HEAD(inohash, inoinfo) *inphash;
+extern struct inoinfo **inpsort;
+/*
+ * flags for struct inoinfo
+ */
+#define INFO_NEW 0x0000001 /* replaced broken directory */
+
extern long dirhash, inplast;
extern unsigned long numdirs, listmax;
extern long countdirs; /* number of directories we actually found */
@@ -326,6 +331,7 @@ extern int adjnbfree[MIBSIZE]; /* MIB cmd to adjust number of free blocks */
extern int adjnifree[MIBSIZE]; /* MIB cmd to adjust number of free inodes */
extern int adjnffree[MIBSIZE]; /* MIB cmd to adjust number of free frags */
extern int adjnumclusters[MIBSIZE]; /* MIB cmd adjust number of free clusters */
+extern int adjdepth[MIBSIZE]; /* MIB cmd to adjust directory depth count */
extern int freefiles[MIBSIZE]; /* MIB cmd to free a set of files */
extern int freedirs[MIBSIZE]; /* MIB cmd to free a set of directories */
extern int freeblks[MIBSIZE]; /* MIB cmd to free a set of data blocks */
@@ -339,8 +345,10 @@ extern off_t bflag; /* location of alternate super block */
extern int bkgrdflag; /* use a snapshot to run on an active system */
extern char *blockmap; /* ptr to primary blk allocation map */
extern char *cdevname; /* name of device being checked */
+extern int cgheader_corrupt; /* one or more CG headers are corrupt */
extern char ckclean; /* only do work if not cleanly unmounted */
extern int ckhashadd; /* check hashes to be added */
+extern char *copybuf; /* buffer to copy snapshot blocks */
extern int cvtlevel; /* convert to newer file system format */
extern long dev_bsize; /* computed value of DEV_BSIZE */
extern u_int real_dev_bsize; /* actual disk sector size, not overridden */
@@ -363,10 +371,10 @@ extern char preen; /* just fix normal inconsistencies */
extern char rerun; /* rerun fsck. Only used in non-preen mode */
extern char resolved; /* cleared if unresolved changes => not clean */
extern int returntosingle; /* 1 => return to single user mode on exit */
-extern int sbhashfailed; /* when reading superblock check hash failed */
extern long secsize; /* actual disk sector size */
extern char skipclean; /* skip clean file systems if preening */
-extern char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */
+extern int snapcnt; /* number of active snapshots */
+extern struct inode snaplist[FSMAXSNAP + 1]; /* list of active snapshots */
extern int sujrecovery; /* 1 => doing check using the journal */
extern int surrender; /* Give up if reads fail */
extern char usedsoftdep; /* just fix soft dependency inconsistencies */
@@ -415,6 +423,20 @@ Malloc(size_t size)
break;
return (retval);
}
+/*
+ * Allocate a block of memory to be used as an I/O buffer.
+ * Ensure that the buffer is aligned to the I/O subsystem requirements.
+ */
+static inline void*
+Balloc(size_t size)
+{
+ void *retval;
+
+ while ((retval = aligned_alloc(LIBUFS_BUFALIGN, size)) == NULL)
+ if (flushentry() == 0)
+ break;
+ return (retval);
+}
/*
* Wrapper for calloc() that flushes the cylinder group cache to try
@@ -436,9 +458,11 @@ struct fstab;
void adjust(struct inodesc *, int lcnt);
void alarmhandler(int sig);
-ufs2_daddr_t allocblk(long frags);
+ufs2_daddr_t allocblk(long cg, long frags, ufs2_daddr_t (*checkblkavail)
+ (ufs2_daddr_t blkno, long frags));
ino_t allocdir(ino_t parent, ino_t request, int mode);
ino_t allocino(ino_t request, int type);
+void binval(struct bufarea *);
void blkerror(ino_t ino, const char *type, ufs2_daddr_t blk);
char *blockcheck(char *name);
int blread(int fd, char *buf, ufs2_daddr_t blk, long size);
@@ -447,18 +471,24 @@ void blwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size);
void blerase(int fd, ufs2_daddr_t blk, long size);
void blzero(int fd, ufs2_daddr_t blk, long size);
void brelse(struct bufarea *);
-void cacheino(union dinode *dp, ino_t inumber);
+struct inoinfo *cacheino(union dinode *dp, ino_t inumber);
void catch(int);
void catchquit(int);
void cgdirty(struct bufarea *);
struct bufarea *cglookup(int cg);
-int changeino(ino_t dir, const char *name, ino_t newnum);
-int check_cgmagic(int cg, struct bufarea *cgbp, int requestrebuild);
+int changeino(ino_t dir, const char *name, ino_t newnum, int depth);
+void check_blkcnt(struct inode *ip);
+int check_cgmagic(int cg, struct bufarea *cgbp);
+void rebuild_cg(int cg, struct bufarea *cgbp);
+void check_dirdepth(struct inoinfo *inp);
+int chkfilesize(mode_t mode, u_int64_t filesize);
int chkrange(ufs2_daddr_t blk, int cnt);
void ckfini(int markclean);
int ckinode(union dinode *dp, struct inodesc *);
void clri(struct inodesc *, const char *type, int flag);
int clearentry(struct inodesc *);
+void copyonwrite(struct fs *, struct bufarea *,
+ ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t, long));
void direrror(ino_t ino, const char *errmesg);
int dirscan(struct inodesc *);
int dofix(struct inodesc *, const char *msg);
@@ -469,16 +499,19 @@ int findino(struct inodesc *);
int findname(struct inodesc *);
void flush(int fd, struct bufarea *bp);
int freeblock(struct inodesc *);
+void freedirino(ino_t ino, ino_t parent);
void freeino(ino_t ino);
void freeinodebuf(void);
+void fsckinit(void);
void fsutilinit(void);
int ftypeok(union dinode *dp);
void getblk(struct bufarea *bp, ufs2_daddr_t blk, long size);
struct bufarea *getdatablk(ufs2_daddr_t blkno, long size, int type);
struct inoinfo *getinoinfo(ino_t inumber);
-union dinode *getnextinode(ino_t inumber, int rebuildcg);
+union dinode *getnextinode(ino_t inumber, int rebuiltcg);
void getpathname(char *namebuf, ino_t curdir, ino_t ino);
void ginode(ino_t, struct inode *);
+void gjournal_check(const char *filesys);
void infohandler(int sig);
void irelse(struct inode *);
ufs2_daddr_t ino_blkatoff(union dinode *, ino_t, ufs_lbn_t, int *,
@@ -500,17 +533,23 @@ void pass4(void);
void pass5(void);
void pfatal(const char *fmt, ...) __printflike(1, 2);
void propagate(void);
+void prtbuf(struct bufarea *, const char *, ...) __printflike(2, 3);
void prtinode(struct inode *);
void pwarn(const char *fmt, ...) __printflike(1, 2);
-int readsb(int listerr);
+int readsb(void);
+int removecachedino(ino_t);
int reply(const char *question);
void rwerror(const char *mesg, ufs2_daddr_t blk);
void sblock_init(void);
void setinodebuf(int, ino_t);
int setup(char *dev);
-void gjournal_check(const char *filesys);
+int snapblkfree(struct fs *, ufs2_daddr_t, long, ino_t,
+ ufs2_daddr_t (*)(ufs2_daddr_t, long));
+void snapremove(ino_t);
+void snapflush(ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t, long));
+ufs2_daddr_t std_checkblkavail(ufs2_daddr_t blkno, long frags);
+ufs2_daddr_t suj_checkblkavail(ufs2_daddr_t, long);
int suj_check(const char *filesys);
void update_maps(struct cg *, struct cg*, int);
-void fsckinit(void);
#endif /* !_FSCK_H_ */
diff --git a/sbin/fsck_ffs/fsck_ffs.8 b/sbin/fsck_ffs/fsck_ffs.8
index 35f3e112007b..1eb9ebeb3b9b 100644
--- a/sbin/fsck_ffs/fsck_ffs.8
+++ b/sbin/fsck_ffs/fsck_ffs.8
@@ -26,10 +26,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" @(#)fsck.8 8.4 (Berkeley) 5/9/95
-.\" $FreeBSD$
-.\"
-.Dd May 3, 2019
+.Dd November 17, 2023
.Dt FSCK_FFS 8
.Os
.Sh NAME
@@ -426,6 +423,7 @@ are fully enumerated and explained in Appendix A of
.Sh SEE ALSO
.Xr fs 5 ,
.Xr fstab 5 ,
+.Xr ffs 7 ,
.Xr fsck 8 ,
.Xr fsdb 8 ,
.Xr newfs 8 ,
diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c
index 424f8d9794f6..2acf825b159f 100644
--- a/sbin/fsck_ffs/fsutil.c
+++ b/sbin/fsck_ffs/fsutil.c
@@ -29,14 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/time.h>
#include <sys/types.h>
@@ -60,7 +52,6 @@ __FBSDID("$FreeBSD$");
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
-#include <libufs.h>
#include "fsck.h"
@@ -71,7 +62,6 @@ static void cg_write(struct bufarea *);
static void slowio_start(void);
static void slowio_end(void);
static void printIOstats(void);
-static void prtbuf(const char *, struct bufarea *);
static long diskreads, totaldiskreads, totalreads; /* Disk cache statistics */
static struct timespec startpass, finishpass;
@@ -79,11 +69,13 @@ struct timeval slowio_starttime;
int slowio_delay_usec = 10000; /* Initial IO delay for background fsck */
int slowio_pollcnt;
static struct bufarea cgblk; /* backup buffer for cylinder group blocks */
+static struct bufarea failedbuf; /* returned by failed getdatablk() */
static TAILQ_HEAD(bufqueue, bufarea) bufqueuehd; /* head of buffer cache LRU */
static LIST_HEAD(bufhash, bufarea) bufhashhd[HASHSIZE]; /* buffer hash list */
-static int numbufs; /* size of buffer cache */
-static int cachelookups; /* number of cache lookups */
-static int cachereads; /* number of cache reads */
+static struct bufhash freebufs; /* unused buffers */
+static int numbufs; /* size of buffer cache */
+static int cachelookups; /* number of cache lookups */
+static int cachereads; /* number of cache reads */
static int flushtries; /* number of tries to reclaim memory */
char *buftype[BT_NUMBUFTYPES] = BT_NAMES;
@@ -165,11 +157,11 @@ reply(const char *question)
struct inostat *
inoinfo(ino_t inum)
{
- static struct inostat unallocated = { USTATE, 0, 0 };
+ static struct inostat unallocated = { USTATE, 0, 0, 0 };
struct inostatlist *ilp;
int iloff;
- if (inum > maxino)
+ if (inum >= maxino)
errx(EEXIT, "inoinfo: inumber %ju out of range",
(uintmax_t)inum);
ilp = &inostathead[inum / sblock.fs_ipg];
@@ -187,11 +179,15 @@ bufinit(void)
{
int i;
- if ((cgblk.b_un.b_buf = Malloc((unsigned int)sblock.fs_bsize)) == NULL)
+ initbarea(&failedbuf, BT_UNKNOWN);
+ failedbuf.b_errs = -1;
+ failedbuf.b_un.b_buf = NULL;
+ if ((cgblk.b_un.b_buf = Balloc((unsigned int)sblock.fs_bsize)) == NULL)
errx(EEXIT, "Initial malloc(%d) failed", sblock.fs_bsize);
initbarea(&cgblk, BT_CYLGRP);
numbufs = cachelookups = cachereads = 0;
TAILQ_INIT(&bufqueuehd);
+ LIST_INIT(&freebufs);
for (i = 0; i < HASHSIZE; i++)
LIST_INIT(&bufhashhd[i]);
for (i = 0; i < BT_NUMBUFTYPES; i++) {
@@ -208,7 +204,7 @@ allocbuf(const char *failreason)
char *bufp;
bp = (struct bufarea *)Malloc(sizeof(struct bufarea));
- bufp = Malloc((unsigned int)sblock.fs_bsize);
+ bufp = Balloc((unsigned int)sblock.fs_bsize);
if (bp == NULL || bufp == NULL) {
errx(EEXIT, "%s", failreason);
/* NOTREACHED */
@@ -238,7 +234,7 @@ cglookup(int cg)
if ((unsigned) cg >= sblock.fs_ncg)
errx(EEXIT, "cglookup: out of range cylinder group %d", cg);
if (cgbufs == NULL) {
- cgbufs = calloc(sblock.fs_ncg, sizeof(struct bufarea));
+ cgbufs = Calloc(sblock.fs_ncg, sizeof(struct bufarea));
if (cgbufs == NULL)
errx(EEXIT, "Cannot allocate cylinder group buffers");
}
@@ -247,7 +243,7 @@ cglookup(int cg)
return (cgbp);
cgp = NULL;
if (flushtries == 0)
- cgp = Malloc((unsigned int)sblock.fs_cgsize);
+ cgp = Balloc((unsigned int)sblock.fs_cgsize);
if (cgp == NULL) {
if (sujrecovery)
errx(EEXIT,"Ran out of memory during journal recovery");
@@ -300,7 +296,7 @@ flushentry(void)
}
/*
- * Manage a cache of directory blocks.
+ * Manage a cache of filesystem disk blocks.
*/
struct bufarea *
getdatablk(ufs2_daddr_t blkno, long size, int type)
@@ -309,19 +305,25 @@ getdatablk(ufs2_daddr_t blkno, long size, int type)
struct bufhash *bhdp;
cachelookups++;
- /* If out of range, return empty buffer with b_err == -1 */
+ /*
+ * If out of range, return empty buffer with b_err == -1
+ *
+ * Skip check for inodes because chkrange() considers
+ * metadata areas invalid to write data.
+ */
if (type != BT_INODES && chkrange(blkno, size / sblock.fs_fsize)) {
- blkno = -1;
- type = BT_EMPTY;
+ failedbuf.b_refcnt++;
+ return (&failedbuf);
}
bhdp = &bufhashhd[HASH(blkno)];
LIST_FOREACH(bp, bhdp, b_hash)
if (bp->b_bno == fsbtodb(&sblock, blkno)) {
if (debug && bp->b_size != size) {
- prtbuf("getdatablk: size mismatch", bp);
+ prtbuf(bp, "getdatablk: size mismatch");
pfatal("getdatablk: b_size %d != size %ld\n",
bp->b_size, size);
}
+ TAILQ_REMOVE(&bufqueuehd, bp, b_list);
goto foundit;
}
/*
@@ -340,7 +342,9 @@ getdatablk(ufs2_daddr_t blkno, long size, int type)
if (size > sblock.fs_bsize)
errx(EEXIT, "Excessive buffer size %ld > %d\n", size,
sblock.fs_bsize);
- if (numbufs < MINBUFS) {
+ if ((bp = LIST_FIRST(&freebufs)) != NULL) {
+ LIST_REMOVE(bp, b_hash);
+ } else if (numbufs < MINBUFS) {
bp = allocbuf("cannot create minimal buffer pool");
} else if (sujrecovery) {
/*
@@ -368,6 +372,7 @@ getdatablk(ufs2_daddr_t blkno, long size, int type)
else
LIST_REMOVE(bp, b_hash);
}
+ TAILQ_REMOVE(&bufqueuehd, bp, b_list);
flush(fswritefd, bp);
bp->b_type = type;
LIST_INSERT_HEAD(bhdp, bp, b_hash);
@@ -375,13 +380,12 @@ getdatablk(ufs2_daddr_t blkno, long size, int type)
cachereads++;
/* fall through */
foundit:
+ TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list);
if (debug && bp->b_type != type) {
printf("getdatablk: buffer type changed to %s",
BT_BUFTYPE(type));
- prtbuf("", bp);
+ prtbuf(bp, "");
}
- TAILQ_REMOVE(&bufqueuehd, bp, b_list);
- TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list);
if (bp->b_errs == 0)
bp->b_refcnt++;
return (bp);
@@ -401,11 +405,7 @@ getblk(struct bufarea *bp, ufs2_daddr_t blk, long size)
readcnt[bp->b_type]++;
clock_gettime(CLOCK_REALTIME_PRECISE, &start);
}
- if (bp->b_type != BT_EMPTY)
- bp->b_errs =
- blread(fsreadfd, bp->b_un.b_buf, dblk, size);
- else
- bp->b_errs = -1;
+ bp->b_errs = blread(fsreadfd, bp->b_un.b_buf, dblk, size);
if (debug) {
clock_gettime(CLOCK_REALTIME_PRECISE, &finish);
timespecsub(&finish, &start, &finish);
@@ -422,11 +422,20 @@ brelse(struct bufarea *bp)
{
if (bp->b_refcnt <= 0)
- prtbuf("brelse: buffer with negative reference count", bp);
+ prtbuf(bp, "brelse: buffer with negative reference count");
bp->b_refcnt--;
}
void
+binval(struct bufarea *bp)
+{
+
+ bp->b_flags &= ~B_DIRTY;
+ LIST_REMOVE(bp, b_hash);
+ LIST_INSERT_HEAD(&freebufs, bp, b_hash);
+}
+
+void
flush(int fd, struct bufarea *bp)
{
struct inode ip;
@@ -451,10 +460,18 @@ flush(int fd, struct bufarea *bp)
if (bp != &sblk)
pfatal("BUFFER %p DOES NOT MATCH SBLK %p\n",
bp, &sblk);
+ /*
+ * Superblocks are always pre-copied so we do not need
+ * to check them for copy-on-write.
+ */
if (sbput(fd, bp->b_un.b_fs, 0) == 0)
fsmodified = 1;
break;
case BT_CYLGRP:
+ /*
+ * Cylinder groups are always pre-copied so we do not
+ * need to check them for copy-on-write.
+ */
if (sujrecovery)
cg_write(bp);
if (cgput(fswritefd, &sblock, bp->b_un.b_cg) == 0)
@@ -465,13 +482,13 @@ flush(int fd, struct bufarea *bp)
struct ufs2_dinode *dp = bp->b_un.b_dinode2;
int i;
- for (i = 0; i < INOPB(&sblock); dp++, i++) {
+ for (i = 0; i < bp->b_size; dp++, i += sizeof(*dp)) {
if (ffs_verify_dinode_ckhash(&sblock, dp) == 0)
continue;
pwarn("flush: INODE CHECK-HASH FAILED");
ip.i_bp = bp;
ip.i_dp = (union dinode *)dp;
- ip.i_number = bp->b_index + i;
+ ip.i_number = bp->b_index + (i / sizeof(*dp));
prtinode(&ip);
if (preen || reply("FIX") != 0) {
if (preen)
@@ -483,12 +500,39 @@ flush(int fd, struct bufarea *bp)
}
/* FALLTHROUGH */
default:
+ copyonwrite(&sblock, bp, std_checkblkavail);
blwrite(fd, bp->b_un.b_buf, bp->b_bno, bp->b_size);
break;
}
}
/*
+ * If there are any snapshots, ensure that all the blocks that they
+ * care about have been copied, then release the snapshot inodes.
+ * These operations need to be done before we rebuild the cylinder
+ * groups so that any block allocations are properly recorded.
+ * Since all the cylinder group maps have already been copied in
+ * the snapshots, no further snapshot copies will need to be done.
+ */
+void
+snapflush(ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t, long))
+{
+ struct bufarea *bp;
+ int cnt;
+
+ if (snapcnt > 0) {
+ if (debug)
+ printf("Check for snapshot copies\n");
+ TAILQ_FOREACH_REVERSE(bp, &bufqueuehd, bufqueue, b_list)
+ if ((bp->b_flags & B_DIRTY) != 0)
+ copyonwrite(&sblock, bp, checkblkavail);
+ for (cnt = 0; cnt < snapcnt; cnt++)
+ irelse(&snaplist[cnt]);
+ snapcnt = 0;
+ }
+}
+
+/*
* Journaled soft updates does not maintain cylinder group summary
* information during cleanup, so this routine recalculates the summary
* information and updates the superblock summary in preparation for
@@ -499,6 +543,7 @@ cg_write(struct bufarea *bp)
{
ufs1_daddr_t fragno, cgbno, maxbno;
u_int8_t *blksfree;
+ struct csum *csp;
struct cg *cgp;
int blk;
int i;
@@ -536,6 +581,11 @@ cg_write(struct bufarea *bp)
* Update the superblock cg summary from our now correct values
* before writing the block.
*/
+ csp = &sblock.fs_cs(&sblock, cgp->cg_cgx);
+ sblock.fs_cstotal.cs_ndir += cgp->cg_cs.cs_ndir - csp->cs_ndir;
+ sblock.fs_cstotal.cs_nbfree += cgp->cg_cs.cs_nbfree - csp->cs_nbfree;
+ sblock.fs_cstotal.cs_nifree += cgp->cg_cs.cs_nifree - csp->cs_nifree;
+ sblock.fs_cstotal.cs_nffree += cgp->cg_cs.cs_nffree - csp->cs_nffree;
sblock.fs_cs(&sblock, cgp->cg_cgx) = cgp->cg_cs;
}
@@ -556,11 +606,9 @@ void
ckfini(int markclean)
{
struct bufarea *bp, *nbp;
- struct inoinfo *inp, *ninp;
- int ofsmodified, cnt, cg, i;
+ int ofsmodified, cnt, cg;
if (bkgrdflag) {
- unlink(snapname);
if ((!(sblock.fs_flags & FS_UNCLEAN)) != markclean) {
cmd.value = FS_UNCLEAN;
cmd.size = markclean ? -1 : 1;
@@ -587,6 +635,7 @@ ckfini(int markclean)
(void)close(fsreadfd);
return;
}
+
/*
* To remain idempotent with partial truncations the buffers
* must be flushed in this order:
@@ -629,14 +678,9 @@ ckfini(int markclean)
case BT_SUPERBLK:
case BT_CYLGRP:
default:
- prtbuf("ckfini: improper buffer type on cache list",bp);
+ prtbuf(bp,"ckfini: improper buffer type on cache list");
continue;
/* These are the ones to flush in this step */
- case BT_EMPTY:
- if (bp->b_bno >= 0)
- pfatal("Unused BT_EMPTY buffer for block %jd\n",
- (intmax_t)bp->b_bno);
- /* FALLTHROUGH */
case BT_LEVEL1:
case BT_LEVEL2:
case BT_LEVEL3:
@@ -648,11 +692,10 @@ ckfini(int markclean)
case BT_INODES:
continue;
}
- if (debug && bp->b_refcnt != 0) {
- prtbuf("ckfini: clearing in-use buffer", bp);
- pfatal("ckfini: clearing in-use buffer\n");
- }
+ if (debug && bp->b_refcnt != 0)
+ prtbuf(bp, "ckfini: clearing in-use buffer");
TAILQ_REMOVE(&bufqueuehd, bp, b_list);
+ LIST_REMOVE(bp, b_hash);
cnt++;
flush(fswritefd, bp);
free(bp->b_un.b_buf);
@@ -666,11 +709,10 @@ ckfini(int markclean)
icachebp = NULL;
}
TAILQ_FOREACH_REVERSE_SAFE(bp, &bufqueuehd, bufqueue, b_list, nbp) {
- if (debug && bp->b_refcnt != 0) {
- prtbuf("ckfini: clearing in-use buffer", bp);
- pfatal("ckfini: clearing in-use buffer\n");
- }
+ if (debug && bp->b_refcnt != 0)
+ prtbuf(bp, "ckfini: clearing in-use buffer");
TAILQ_REMOVE(&bufqueuehd, bp, b_list);
+ LIST_REMOVE(bp, b_hash);
cnt++;
flush(fswritefd, bp);
free(bp->b_un.b_buf);
@@ -682,14 +724,17 @@ ckfini(int markclean)
if (debug)
printf("Flush the superblock\n");
flush(fswritefd, &sblk);
- if (havesb && cursnapshot == 0 && sblock.fs_magic == FS_UFS2_MAGIC &&
- sblk.b_bno != sblock.fs_sblockloc / dev_bsize &&
- !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
- /* Change the write destination to standard superblock */
- sblock.fs_sblockactualloc = sblock.fs_sblockloc;
- sblk.b_bno = sblock.fs_sblockloc / dev_bsize;
- sbdirty();
- flush(fswritefd, &sblk);
+ if (havesb && cursnapshot == 0 &&
+ sblk.b_bno != sblock.fs_sblockloc / dev_bsize) {
+ if (preen || reply("UPDATE STANDARD SUPERBLOCK")) {
+ /* Change write destination to standard superblock */
+ sblock.fs_sblockactualloc = sblock.fs_sblockloc;
+ sblk.b_bno = sblock.fs_sblockloc / dev_bsize;
+ sbdirty();
+ flush(fswritefd, &sblk);
+ } else {
+ markclean = 0;
+ }
}
if (cursnapshot == 0 && sblock.fs_clean != markclean) {
if ((sblock.fs_clean = markclean) != 0) {
@@ -728,19 +773,7 @@ ckfini(int markclean)
free(inostathead);
}
inostathead = NULL;
- if (inpsort != NULL)
- free(inpsort);
- inpsort = NULL;
- if (inphead != NULL) {
- for (i = 0; i < dirhash; i++) {
- for (inp = inphead[i]; inp != NULL; inp = ninp) {
- ninp = inp->i_nexthash;
- free(inp);
- }
- }
- free(inphead);
- }
- inphead = NULL;
+ inocleanup();
finalIOstats();
(void)close(fsreadfd);
(void)close(fswritefd);
@@ -926,7 +959,7 @@ blzero(int fd, ufs2_daddr_t blk, long size)
if (fd < 0)
return;
if (zero == NULL) {
- zero = calloc(ZEROBUFSIZE, 1);
+ zero = Balloc(ZEROBUFSIZE);
if (zero == NULL)
errx(EEXIT, "cannot allocate buffer pool");
}
@@ -945,13 +978,26 @@ blzero(int fd, ufs2_daddr_t blk, long size)
/*
* Verify cylinder group's magic number and other parameters. If the
* test fails, offer an option to rebuild the whole cylinder group.
+ *
+ * Return 1 if the cylinder group is good or return 0 if it is bad.
*/
+#undef CHK
+#define CHK(lhs, op, rhs, fmt) \
+ if (lhs op rhs) { \
+ pwarn("UFS%d cylinder group %d failed: " \
+ "%s (" #fmt ") %s %s (" #fmt ")\n", \
+ sblock.fs_magic == FS_UFS1_MAGIC ? 1 : 2, cg, \
+ #lhs, (intmax_t)lhs, #op, #rhs, (intmax_t)rhs); \
+ error = 1; \
+ }
int
-check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild)
+check_cgmagic(int cg, struct bufarea *cgbp)
{
struct cg *cgp = cgbp->b_un.b_cg;
uint32_t cghash, calchash;
static int prevfailcg = -1;
+ long start;
+ int error;
/*
* Extended cylinder group checks.
@@ -964,32 +1010,69 @@ check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild)
calchash = calculate_crc32c(~0L, (void *)cgp, sblock.fs_cgsize);
cgp->cg_ckhash = cghash;
}
- if (cgp->cg_ckhash == calchash &&
- cg_chkmagic(cgp) &&
- cgp->cg_cgx == cg &&
- ((sblock.fs_magic == FS_UFS1_MAGIC &&
- cgp->cg_old_niblk == sblock.fs_ipg &&
- cgp->cg_ndblk <= sblock.fs_fpg &&
- cgp->cg_old_ncyl <= sblock.fs_old_cpg) ||
- (sblock.fs_magic == FS_UFS2_MAGIC &&
- cgp->cg_niblk == sblock.fs_ipg &&
- cgp->cg_ndblk <= sblock.fs_fpg &&
- cgp->cg_initediblk <= sblock.fs_ipg))) {
- return (1);
+ error = 0;
+ CHK(cgp->cg_ckhash, !=, calchash, "%jd");
+ CHK(cg_chkmagic(cgp), ==, 0, "%jd");
+ CHK(cgp->cg_cgx, !=, cg, "%jd");
+ CHK(cgp->cg_ndblk, >, sblock.fs_fpg, "%jd");
+ if (sblock.fs_magic == FS_UFS1_MAGIC) {
+ CHK(cgp->cg_old_niblk, !=, sblock.fs_ipg, "%jd");
+ } else if (sblock.fs_magic == FS_UFS2_MAGIC) {
+ CHK(cgp->cg_niblk, !=, sblock.fs_ipg, "%jd");
+ CHK(cgp->cg_initediblk, >, sblock.fs_ipg, "%jd");
+ }
+ if (cgbase(&sblock, cg) + sblock.fs_fpg < sblock.fs_size) {
+ CHK(cgp->cg_ndblk, !=, sblock.fs_fpg, "%jd");
+ } else {
+ CHK(cgp->cg_ndblk, !=, sblock.fs_size - cgbase(&sblock, cg),
+ "%jd");
+ }
+ start = sizeof(*cgp);
+ if (sblock.fs_magic == FS_UFS2_MAGIC) {
+ CHK(cgp->cg_iusedoff, !=, start, "%jd");
+ } else if (sblock.fs_magic == FS_UFS1_MAGIC) {
+ CHK(cgp->cg_niblk, !=, 0, "%jd");
+ CHK(cgp->cg_initediblk, !=, 0, "%jd");
+ CHK(cgp->cg_old_niblk, !=, sblock.fs_ipg, "%jd");
+ CHK(cgp->cg_old_btotoff, !=, start, "%jd");
+ CHK(cgp->cg_old_boff, !=, cgp->cg_old_btotoff +
+ sblock.fs_old_cpg * sizeof(int32_t), "%jd");
+ CHK(cgp->cg_iusedoff, !=, cgp->cg_old_boff +
+ sblock.fs_old_cpg * sizeof(u_int16_t), "%jd");
}
+ CHK(cgp->cg_freeoff, !=,
+ cgp->cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT), "%jd");
+ if (sblock.fs_contigsumsize == 0) {
+ CHK(cgp->cg_nextfreeoff, !=,
+ cgp->cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT), "%jd");
+ } else {
+ CHK(cgp->cg_nclusterblks, !=, cgp->cg_ndblk / sblock.fs_frag,
+ "%jd");
+ CHK(cgp->cg_clustersumoff, !=,
+ roundup(cgp->cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT),
+ sizeof(u_int32_t)) - sizeof(u_int32_t), "%jd");
+ CHK(cgp->cg_clusteroff, !=, cgp->cg_clustersumoff +
+ (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t), "%jd");
+ CHK(cgp->cg_nextfreeoff, !=, cgp->cg_clusteroff +
+ howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT),
+ "%jd");
+ }
+ if (error == 0)
+ return (1);
if (prevfailcg == cg)
return (0);
prevfailcg = cg;
pfatal("CYLINDER GROUP %d: INTEGRITY CHECK FAILED", cg);
- if (!request_rebuild) {
- printf("\n");
- return (0);
- }
- if (!reply("REBUILD CYLINDER GROUP")) {
- printf("YOU WILL NEED TO RERUN FSCK.\n");
- rerun = 1;
- return (1);
- }
+ printf("\n");
+ return (0);
+}
+
+void
+rebuild_cg(int cg, struct bufarea *cgbp)
+{
+ struct cg *cgp = cgbp->b_un.b_cg;
+ long start;
+
/*
* Zero out the cylinder group and then initialize critical fields.
* Bit maps and summaries will be recalculated by later passes.
@@ -1003,13 +1086,15 @@ check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild)
cgp->cg_ndblk = sblock.fs_fpg;
else
cgp->cg_ndblk = sblock.fs_size - cgbase(&sblock, cg);
- cgp->cg_iusedoff = &cgp->cg_space[0] - (u_char *)(&cgp->cg_firstfield);
- if (sblock.fs_magic == FS_UFS1_MAGIC) {
+ start = sizeof(*cgp);
+ if (sblock.fs_magic == FS_UFS2_MAGIC) {
+ cgp->cg_iusedoff = start;
+ } else if (sblock.fs_magic == FS_UFS1_MAGIC) {
cgp->cg_niblk = 0;
cgp->cg_initediblk = 0;
cgp->cg_old_ncyl = sblock.fs_old_cpg;
cgp->cg_old_niblk = sblock.fs_ipg;
- cgp->cg_old_btotoff = cgp->cg_iusedoff;
+ cgp->cg_old_btotoff = start;
cgp->cg_old_boff = cgp->cg_old_btotoff +
sblock.fs_old_cpg * sizeof(int32_t);
cgp->cg_iusedoff = cgp->cg_old_boff +
@@ -1027,58 +1112,115 @@ check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild)
cgp->cg_nextfreeoff = cgp->cg_clusteroff +
howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT);
}
+ cgp->cg_ckhash = calculate_crc32c(~0L, (void *)cgp, sblock.fs_cgsize);
cgdirty(cgbp);
- return (0);
}
/*
* allocate a data block with the specified number of fragments
*/
ufs2_daddr_t
-allocblk(long frags)
+allocblk(long startcg, long frags,
+ ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t blkno, long frags))
+{
+ ufs2_daddr_t blkno, newblk;
+
+ if (sujrecovery && checkblkavail == std_checkblkavail) {
+ pfatal("allocblk: std_checkblkavail used for SUJ recovery\n");
+ return (0);
+ }
+ if (frags <= 0 || frags > sblock.fs_frag)
+ return (0);
+ for (blkno = MAX(cgdata(&sblock, startcg), 0);
+ blkno < maxfsblock - sblock.fs_frag;
+ blkno += sblock.fs_frag) {
+ if ((newblk = (*checkblkavail)(blkno, frags)) == 0)
+ continue;
+ if (newblk > 0)
+ return (newblk);
+ if (newblk < 0)
+ blkno = -newblk;
+ }
+ for (blkno = MAX(cgdata(&sblock, 0), 0);
+ blkno < cgbase(&sblock, startcg) - sblock.fs_frag;
+ blkno += sblock.fs_frag) {
+ if ((newblk = (*checkblkavail)(blkno, frags)) == 0)
+ continue;
+ if (newblk > 0)
+ return (newblk);
+ if (newblk < 0)
+ blkno = -newblk;
+ }
+ return (0);
+}
+
+ufs2_daddr_t
+std_checkblkavail(ufs2_daddr_t blkno, long frags)
{
- int i, j, k, cg, baseblk;
struct bufarea *cgbp;
struct cg *cgp;
+ ufs2_daddr_t j, k, baseblk;
+ long cg;
- if (frags <= 0 || frags > sblock.fs_frag)
+ if ((u_int64_t)blkno > sblock.fs_size)
return (0);
- for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
- for (j = 0; j <= sblock.fs_frag - frags; j++) {
- if (testbmap(i + j))
- continue;
- for (k = 1; k < frags; k++)
- if (testbmap(i + j + k))
- break;
- if (k < frags) {
- j += k;
- continue;
- }
- cg = dtog(&sblock, i + j);
- cgbp = cglookup(cg);
- cgp = cgbp->b_un.b_cg;
- if (!check_cgmagic(cg, cgbp, 0)) {
- i = (cg + 1) * sblock.fs_fpg - sblock.fs_frag;
- continue;
- }
- baseblk = dtogd(&sblock, i + j);
- for (k = 0; k < frags; k++) {
- setbmap(i + j + k);
- clrbit(cg_blksfree(cgp), baseblk + k);
- }
- n_blks += frags;
- if (frags == sblock.fs_frag)
- cgp->cg_cs.cs_nbfree--;
- else
- cgp->cg_cs.cs_nffree -= frags;
- cgdirty(cgbp);
- return (i + j);
+ for (j = 0; j <= sblock.fs_frag - frags; j++) {
+ if (testbmap(blkno + j))
+ continue;
+ for (k = 1; k < frags; k++)
+ if (testbmap(blkno + j + k))
+ break;
+ if (k < frags) {
+ j += k;
+ continue;
+ }
+ cg = dtog(&sblock, blkno + j);
+ cgbp = cglookup(cg);
+ cgp = cgbp->b_un.b_cg;
+ if (!check_cgmagic(cg, cgbp))
+ return (-((cg + 1) * sblock.fs_fpg - sblock.fs_frag));
+ baseblk = dtogd(&sblock, blkno + j);
+ for (k = 0; k < frags; k++) {
+ setbmap(blkno + j + k);
+ clrbit(cg_blksfree(cgp), baseblk + k);
}
+ n_blks += frags;
+ if (frags == sblock.fs_frag)
+ cgp->cg_cs.cs_nbfree--;
+ else
+ cgp->cg_cs.cs_nffree -= frags;
+ cgdirty(cgbp);
+ return (blkno + j);
}
return (0);
}
/*
+ * Check whether a file size is within the limits for the filesystem.
+ * Return 1 when valid and 0 when too big.
+ *
+ * This should match the file size limit in ffs_mountfs().
+ */
+int
+chkfilesize(mode_t mode, u_int64_t filesize)
+{
+ u_int64_t kernmaxfilesize;
+
+ if (sblock.fs_magic == FS_UFS1_MAGIC)
+ kernmaxfilesize = (off_t)0x40000000 * sblock.fs_bsize - 1;
+ else
+ kernmaxfilesize = sblock.fs_maxfilesize;
+ if (filesize > kernmaxfilesize ||
+ filesize > sblock.fs_maxfilesize ||
+ (mode == IFDIR && filesize > MAXDIRSIZE)) {
+ if (debug)
+ printf("bad file size %ju:", (uintmax_t)filesize);
+ return (0);
+ }
+ return (1);
+}
+
+/*
* Slow down IO so as to leave some disk bandwidth for other processes
*/
void
@@ -1152,9 +1294,11 @@ getpathname(char *namebuf, ino_t curdir, ino_t ino)
ginode(ino, &ip);
if ((ckinode(ip.i_dp, &idesc) & FOUND) == 0) {
irelse(&ip);
+ free(idesc.id_name);
break;
}
irelse(&ip);
+ free(idesc.id_name);
namelookup:
idesc.id_number = idesc.id_parent;
idesc.id_parent = ino;
@@ -1246,12 +1390,17 @@ dofix(struct inodesc *idesc, const char *msg)
/*
* Print details about a buffer.
*/
-static void
-prtbuf(const char *msg, struct bufarea *bp)
+void
+prtbuf(struct bufarea *bp, const char *fmt, ...)
{
-
- printf("%s: bp %p, type %s, bno %jd, size %d, refcnt %d, flags %s, "
- "index %jd\n", msg, bp, BT_BUFTYPE(bp->b_type), (intmax_t) bp->b_bno,
+ va_list ap;
+ va_start(ap, fmt);
+ if (preen)
+ (void)fprintf(stdout, "%s: ", cdevname);
+ (void)vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ printf(": bp %p, type %s, bno %jd, size %d, refcnt %d, flags %s, "
+ "index %jd\n", bp, BT_BUFTYPE(bp->b_type), (intmax_t) bp->b_bno,
bp->b_size, bp->b_refcnt, bp->b_flags & B_DIRTY ? "dirty" : "clean",
(intmax_t) bp->b_index);
}
diff --git a/sbin/fsck_ffs/gjournal.c b/sbin/fsck_ffs/gjournal.c
index b65589b5bd08..b94f27758315 100644
--- a/sbin/fsck_ffs/gjournal.c
+++ b/sbin/fsck_ffs/gjournal.c
@@ -1,5 +1,5 @@
/*-
- * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause-FreeBSD
+ * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause
*
* Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
@@ -54,8 +54,6 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <string.h>
#include <sys/stat.h>
#include <ufs/ffs/fs.h>
@@ -77,7 +75,6 @@ gjournal_check(const char *filesys)
fs = &sblock;
/* Are there any unreferenced inodes in this file system? */
if (fs->fs_unrefs == 0) {
- //printf("No unreferenced inodes.\n");
sbdirty();
ckfini(1);
return;
@@ -97,7 +94,7 @@ gjournal_check(const char *filesys)
}
cgbp = cglookup(cg);
cgp = cgbp->b_un.b_cg;
- if (!check_cgmagic(cg, cgbp, 0)) {
+ if (!check_cgmagic(cg, cgbp)) {
rerun = 1;
ckfini(0);
return;
@@ -105,7 +102,6 @@ gjournal_check(const char *filesys)
/* Are there any unreferenced inodes in this cylinder group? */
if (cgp->cg_unrefs == 0)
continue;
- //printf("Analizing cylinder group %d (count=%d)\n", cg, cgp->cg_unrefs);
/*
* Now go through the list of all inodes in this cylinder group
* to find unreferenced ones.
@@ -150,20 +146,15 @@ gjournal_check(const char *filesys)
inodirty(&ip);
irelse(&ip);
cgdirty(cgbp);
- if (cgp->cg_unrefs == 0) {
- //printf("No more unreferenced inodes in cg=%d.\n", cg);
+ if (cgp->cg_unrefs == 0)
break;
- }
}
/*
- * If there are no more unreferenced inodes, there is no need to
- * check other cylinder groups.
+ * If there are no more unreferenced inodes, there is no
+ * need to check other cylinder groups.
*/
- if (fs->fs_unrefs == 0) {
- //printf("No more unreferenced inodes (cg=%d/%d).\n", cg,
- // fs->fs_ncg);
+ if (fs->fs_unrefs == 0)
break;
- }
}
/* Write back updated statistics and super-block. */
sbdirty();
diff --git a/sbin/fsck_ffs/globs.c b/sbin/fsck_ffs/globs.c
index 09dbcc6694a2..1f04311e2e5c 100644
--- a/sbin/fsck_ffs/globs.c
+++ b/sbin/fsck_ffs/globs.c
@@ -29,20 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char copyright[] =
-"@(#) Copyright (c) 1980, 1986, 1993\n\
- The Regents of the University of California. All rights reserved.\n";
-#endif /* not lint */
-
-#ifndef lint
-static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <ufs/ufs/dinode.h>
#include <ufs/ffs/fs.h>
@@ -60,19 +46,19 @@ ino_t cursnapshot;
long dirhash, inplast;
unsigned long numdirs, listmax;
long countdirs; /* number of directories we actually found */
-int adjrefcnt[MIBSIZE]; /* MIB command to adjust inode reference cnt */
-int adjblkcnt[MIBSIZE]; /* MIB command to adjust inode block count */
-int setsize[MIBSIZE]; /* MIB command to set inode size */
-int adjndir[MIBSIZE]; /* MIB command to adjust number of directories */
-int adjnbfree[MIBSIZE]; /* MIB command to adjust number of free blocks */
-int adjnifree[MIBSIZE]; /* MIB command to adjust number of free inodes */
-int adjnffree[MIBSIZE]; /* MIB command to adjust number of free frags */
-int adjnumclusters[MIBSIZE]; /* MIB command to adjust number of free clusters */
-int freefiles[MIBSIZE]; /* MIB command to free a set of files */
-int freedirs[MIBSIZE]; /* MIB command to free a set of directories */
-int freeblks[MIBSIZE]; /* MIB command to free a set of data blocks */
+int adjrefcnt[MIBSIZE]; /* MIB cmd to adjust inode reference cnt */
+int adjblkcnt[MIBSIZE]; /* MIB cmd to adjust inode block count */
+int setsize[MIBSIZE]; /* MIB cmd to set inode size */
+int adjndir[MIBSIZE]; /* MIB cmd to adjust number of directories */
+int adjnbfree[MIBSIZE]; /* MIB cmd to adjust number of free blocks */
+int adjnifree[MIBSIZE]; /* MIB cmd to adjust number of free inodes */
+int adjnffree[MIBSIZE]; /* MIB cmd to adjust number of free frags */
+int adjnumclusters[MIBSIZE]; /* MIB cmd to adjust number of free clusters */
+int adjdepth[MIBSIZE]; /* MIB cmd to adjust directory depth count */
+int freefiles[MIBSIZE]; /* MIB cmd to free a set of files */
+int freedirs[MIBSIZE]; /* MIB cmd to free a set of directories */
+int freeblks[MIBSIZE]; /* MIB cmd to free a set of data blocks */
struct fsck_cmd cmd; /* sysctl file system update commands */
-char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */
char *cdevname; /* name of device being checked */
long dev_bsize; /* computed value of DEV_BSIZE */
long secsize; /* actual disk sector size */
@@ -90,13 +76,12 @@ char ckclean; /* only do work if not cleanly unmounted */
int cvtlevel; /* convert to newer file system format */
int ckhashadd; /* check hashes to be added */
int bkgrdcheck; /* determine if background check is possible */
-int bkgrdsumadj; /* whether the kernel have ability to adjust superblock summary */
+int bkgrdsumadj; /* kernel able to adjust superblock summary */
char usedsoftdep; /* just fix soft dependency inconsistencies */
char preen; /* just fix normal inconsistencies */
char rerun; /* rerun fsck. Only used in non-preen mode */
int returntosingle; /* 1 => return to single user mode on exit */
char resolved; /* cleared if unresolved changes => not clean */
-int sbhashfailed; /* when reading superblock check hash failed */
char havesb; /* superblock has been read */
char skipclean; /* skip clean file systems if preening */
int fsmodified; /* 1 => write done to file system */
@@ -111,6 +96,7 @@ ino_t lfdir; /* lost & found directory inode number */
const char *lfname; /* lost & found directory name */
int lfmode; /* lost & found directory creation mode */
ufs2_daddr_t n_blks; /* number of blocks in use */
+int cgheader_corrupt; /* one or more CG headers are corrupt */
ino_t n_files; /* number of files in use */
volatile sig_atomic_t got_siginfo; /* received a SIGINFO */
volatile sig_atomic_t got_sigalarm; /* received a SIGALRM */
@@ -140,11 +126,11 @@ fsckinit(void)
bzero(adjnifree, sizeof(int) * MIBSIZE);
bzero(adjnffree, sizeof(int) * MIBSIZE);
bzero(adjnumclusters, sizeof(int) * MIBSIZE);
+ bzero(adjdepth, sizeof(int) * MIBSIZE);
bzero(freefiles, sizeof(int) * MIBSIZE);
bzero(freedirs, sizeof(int) * MIBSIZE);
bzero(freeblks, sizeof(int) * MIBSIZE);
bzero(&cmd, sizeof(struct fsck_cmd));
- bzero(snapname, sizeof(char) * BUFSIZ);
cdevname = NULL;
dev_bsize = 0;
secsize = 0;
@@ -156,7 +142,6 @@ fsckinit(void)
resolved = 0;
havesb = 0;
fsmodified = 0;
- sbhashfailed = 0;
fsreadfd = -1;
fswritefd = -1;
maxfsblock = 0;
@@ -166,6 +151,7 @@ fsckinit(void)
lfmode = 0700;
n_blks = 0;
n_files = 0;
+ cgheader_corrupt = 0;
got_siginfo = 0;
got_sigalarm = 0;
bzero(&zino.dp1, sizeof(struct ufs1_dinode));
diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c
index 47f72c84a1f2..5b004cd29c90 100644
--- a/sbin/fsck_ffs/inode.c
+++ b/sbin/fsck_ffs/inode.c
@@ -29,15 +29,8 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char sccsid[] = "@(#)inode.c 8.8 (Berkeley) 4/28/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
+#include <sys/stat.h>
#include <sys/stdint.h>
#include <sys/sysctl.h>
@@ -49,7 +42,6 @@ __FBSDID("$FreeBSD$");
#include <pwd.h>
#include <string.h>
#include <time.h>
-#include <libufs.h>
#include "fsck.h"
@@ -58,6 +50,9 @@ struct bufarea *icachebp; /* inode cache buffer */
static int iblock(struct inodesc *, off_t isize, int type);
static ufs2_daddr_t indir_blkatoff(ufs2_daddr_t, ino_t, ufs_lbn_t, ufs_lbn_t,
struct bufarea **);
+static int snapclean(struct inodesc *idesc);
+static void chkcopyonwrite(struct fs *, ufs2_daddr_t,
+ ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t, long));
int
ckinode(union dinode *dp, struct inodesc *idesc)
@@ -86,6 +81,10 @@ ckinode(union dinode *dp, struct inodesc *idesc)
dino.dp1 = dp->dp1;
else
dino.dp2 = dp->dp2;
+ if (DIP(&dino, di_size) < 0) {
+ pfatal("NEGATIVE INODE SIZE %jd\n", DIP(&dino, di_size));
+ return (STOP);
+ }
ndb = howmany(DIP(&dino, di_size), sblock.fs_bsize);
for (i = 0; i < UFS_NDADDR; i++) {
idesc->id_lbn++;
@@ -112,6 +111,7 @@ ckinode(union dinode *dp, struct inodesc *idesc)
inodirty(&ip);
irelse(&ip);
}
+ return (STOP);
}
continue;
}
@@ -377,9 +377,13 @@ chkrange(ufs2_daddr_t blk, int cnt)
{
int c;
- if (cnt <= 0 || blk <= 0 || blk > maxfsblock ||
- cnt - 1 > maxfsblock - blk)
+ if (cnt <= 0 || blk <= 0 || blk >= maxfsblock ||
+ cnt > maxfsblock - blk) {
+ if (debug)
+ printf("out of range: blk %ld, offset %i, size %d\n",
+ (long)blk, (int)fragnum(&sblock, blk), cnt);
return (1);
+ }
if (cnt > sblock.fs_frag ||
fragnum(&sblock, blk) + cnt > sblock.fs_frag) {
if (debug)
@@ -415,21 +419,33 @@ chkrange(ufs2_daddr_t blk, int cnt)
/*
* General purpose interface for reading inodes.
+ *
+ * firstinum and lastinum track contents of getnextino() cache (below).
*/
+static ino_t firstinum, lastinum;
+static struct bufarea inobuf;
+
void
ginode(ino_t inumber, struct inode *ip)
{
ufs2_daddr_t iblk;
+ struct ufs2_dinode *dp;
- if (inumber < UFS_ROOTINO || inumber > maxino)
+ if (inumber < UFS_ROOTINO || inumber >= maxino)
errx(EEXIT, "bad inode number %ju to ginode",
(uintmax_t)inumber);
ip->i_number = inumber;
- if (icachebp != NULL &&
+ if (inumber >= firstinum && inumber < lastinum) {
+ /* contents in getnextino() cache */
+ ip->i_bp = &inobuf;
+ inobuf.b_refcnt++;
+ inobuf.b_index = firstinum;
+ } else if (icachebp != NULL &&
inumber >= icachebp->b_index &&
inumber < icachebp->b_index + INOPB(&sblock)) {
/* take an additional reference for the returned inode */
icachebp->b_refcnt++;
+ ip->i_bp = icachebp;
} else {
iblk = ino_to_fsba(&sblock, inumber);
/* release our cache-hold reference on old icachebp */
@@ -445,23 +461,24 @@ ginode(ino_t inumber, struct inode *ip)
/* take a cache-hold reference on new icachebp */
icachebp->b_refcnt++;
icachebp->b_index = rounddown(inumber, INOPB(&sblock));
+ ip->i_bp = icachebp;
}
- ip->i_bp = icachebp;
if (sblock.fs_magic == FS_UFS1_MAGIC) {
ip->i_dp = (union dinode *)
- &icachebp->b_un.b_dinode1[inumber % INOPB(&sblock)];
+ &ip->i_bp->b_un.b_dinode1[inumber - ip->i_bp->b_index];
return;
}
ip->i_dp = (union dinode *)
- &icachebp->b_un.b_dinode2[inumber % INOPB(&sblock)];
- if (ffs_verify_dinode_ckhash(&sblock, (struct ufs2_dinode *)ip->i_dp)) {
+ &ip->i_bp->b_un.b_dinode2[inumber - ip->i_bp->b_index];
+ dp = (struct ufs2_dinode *)ip->i_dp;
+ /* Do not check hash of inodes being created */
+ if (dp->di_mode != 0 && ffs_verify_dinode_ckhash(&sblock, dp)) {
pwarn("INODE CHECK-HASH FAILED");
prtinode(ip);
if (preen || reply("FIX") != 0) {
if (preen)
printf(" (FIXED)\n");
- ffs_update_dinode_ckhash(&sblock,
- (struct ufs2_dinode *)ip->i_dp);
+ ffs_update_dinode_ckhash(&sblock, dp);
inodirty(ip);
}
}
@@ -474,6 +491,14 @@ void
irelse(struct inode *ip)
{
+ /* Check for failed inode read */
+ if (ip->i_bp == NULL)
+ return;
+ if (debug && sblock.fs_magic == FS_UFS2_MAGIC &&
+ ffs_verify_dinode_ckhash(&sblock, (struct ufs2_dinode *)ip->i_dp)) {
+ pwarn("irelse: releasing inode with bad check-hash");
+ prtinode(ip);
+ }
if (ip->i_bp->b_refcnt <= 0)
pfatal("irelse: releasing unreferenced ino %ju\n",
(uintmax_t) ip->i_number);
@@ -484,12 +509,11 @@ irelse(struct inode *ip)
* Special purpose version of ginode used to optimize first pass
* over all the inodes in numerical order.
*/
-static ino_t nextino, lastinum, lastvalidinum;
+static ino_t nextinum, lastvalidinum;
static long readcount, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
-static struct bufarea inobuf;
union dinode *
-getnextinode(ino_t inumber, int rebuildcg)
+getnextinode(ino_t inumber, int rebuiltcg)
{
int j;
long size;
@@ -499,11 +523,12 @@ getnextinode(ino_t inumber, int rebuildcg)
struct inode ip;
static caddr_t nextinop;
- if (inumber != nextino++ || inumber > lastvalidinum)
+ if (inumber != nextinum++ || inumber > lastvalidinum)
errx(EEXIT, "bad inode number %ju to nextinode",
(uintmax_t)inumber);
if (inumber >= lastinum) {
readcount++;
+ firstinum = lastinum;
blk = ino_to_fsba(&sblock, lastinum);
if (readcount % readpercg == 0) {
size = partialsize;
@@ -517,6 +542,9 @@ getnextinode(ino_t inumber, int rebuildcg)
* If getblk encounters an error, it will already have zeroed
* out the buffer, so we do not need to do so here.
*/
+ if (inobuf.b_refcnt != 0)
+ pfatal("Non-zero getnextinode() ref count %d\n",
+ inobuf.b_refcnt);
flush(fswritefd, &inobuf);
getblk(&inobuf, blk, size);
nextinop = inobuf.b_un.b_buf;
@@ -544,7 +572,7 @@ getnextinode(ino_t inumber, int rebuildcg)
dirty(&inobuf);
}
}
- if (rebuildcg && (char *)dp == inobuf.b_un.b_buf) {
+ if (rebuiltcg && (char *)dp == inobuf.b_un.b_buf) {
/*
* Try to determine if we have reached the end of the
* allocated inodes.
@@ -601,7 +629,7 @@ setinodebuf(int cg, ino_t inosused)
inum = cg * sblock.fs_ipg;
lastvalidinum = inum + inosused - 1;
- nextino = inum;
+ nextinum = inum;
lastinum = inum;
readcount = 0;
/* Flush old contents in case they have been updated */
@@ -611,7 +639,7 @@ setinodebuf(int cg, ino_t inosused)
inobufsize = blkroundup(&sblock,
MAX(INOBUFSIZE, sblock.fs_bsize));
initbarea(&inobuf, BT_INODES);
- if ((inobuf.b_un.b_buf = Malloc((unsigned)inobufsize)) == NULL)
+ if ((inobuf.b_un.b_buf = Balloc((unsigned)inobufsize)) == NULL)
errx(EEXIT, "cannot allocate space for inode buffer");
}
fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ?
@@ -633,14 +661,23 @@ int
freeblock(struct inodesc *idesc)
{
struct dups *dlp;
+ struct bufarea *cgbp;
+ struct cg *cgp;
ufs2_daddr_t blkno;
- long nfrags, res;
+ long size, nfrags;
- res = KEEPON;
blkno = idesc->id_blkno;
+ if (idesc->id_type == SNAP) {
+ pfatal("clearing a snapshot dinode\n");
+ return (STOP);
+ }
+ size = lfragtosize(&sblock, idesc->id_numfrags);
+ if (snapblkfree(&sblock, blkno, size, idesc->id_number,
+ std_checkblkavail))
+ return (KEEPON);
for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
if (chkrange(blkno, 1)) {
- res = SKIP;
+ return (SKIP);
} else if (testbmap(blkno)) {
for (dlp = duplist; dlp; dlp = dlp->next) {
if (dlp->dup != blkno)
@@ -657,12 +694,401 @@ freeblock(struct inodesc *idesc)
}
}
}
- return (res);
+ /*
+ * If all successfully returned, account for them.
+ */
+ if (nfrags == 0) {
+ cgbp = cglookup(dtog(&sblock, idesc->id_blkno));
+ cgp = cgbp->b_un.b_cg;
+ if (idesc->id_numfrags == sblock.fs_frag)
+ cgp->cg_cs.cs_nbfree++;
+ else
+ cgp->cg_cs.cs_nffree += idesc->id_numfrags;
+ cgdirty(cgbp);
+ }
+ return (KEEPON);
+}
+
+/*
+ * Prepare a snapshot file for being removed.
+ */
+void
+snapremove(ino_t inum)
+{
+ struct inodesc idesc;
+ struct inode ip;
+ int i;
+
+ for (i = 0; i < snapcnt; i++)
+ if (snaplist[i].i_number == inum)
+ break;
+ if (i == snapcnt)
+ ginode(inum, &ip);
+ else
+ ip = snaplist[i];
+ if ((DIP(ip.i_dp, di_flags) & SF_SNAPSHOT) == 0) {
+ printf("snapremove: inode %jd is not a snapshot\n",
+ (intmax_t)inum);
+ if (i == snapcnt)
+ irelse(&ip);
+ return;
+ }
+ if (debug)
+ printf("snapremove: remove %sactive snapshot %jd\n",
+ i == snapcnt ? "in" : "", (intmax_t)inum);
+ /*
+ * If on active snapshot list, remove it.
+ */
+ if (i < snapcnt) {
+ for (i++; i < FSMAXSNAP; i++) {
+ if (sblock.fs_snapinum[i] == 0)
+ break;
+ snaplist[i - 1] = snaplist[i];
+ sblock.fs_snapinum[i - 1] = sblock.fs_snapinum[i];
+ }
+ sblock.fs_snapinum[i - 1] = 0;
+ bzero(&snaplist[i - 1], sizeof(struct inode));
+ snapcnt--;
+ }
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = SNAP;
+ idesc.id_func = snapclean;
+ idesc.id_number = inum;
+ (void)ckinode(ip.i_dp, &idesc);
+ DIP_SET(ip.i_dp, di_flags, DIP(ip.i_dp, di_flags) & ~SF_SNAPSHOT);
+ inodirty(&ip);
+ irelse(&ip);
+}
+
+static int
+snapclean(struct inodesc *idesc)
+{
+ ufs2_daddr_t blkno;
+ struct bufarea *bp;
+ union dinode *dp;
+
+ blkno = idesc->id_blkno;
+ if (blkno == 0)
+ return (KEEPON);
+
+ dp = idesc->id_dp;
+ if (blkno == BLK_NOCOPY || blkno == BLK_SNAP) {
+ if (idesc->id_lbn < UFS_NDADDR) {
+ DIP_SET(dp, di_db[idesc->id_lbn], 0);
+ } else {
+ bp = idesc->id_bp;
+ IBLK_SET(bp, bp->b_index, 0);
+ dirty(bp);
+ }
+ }
+ return (KEEPON);
+}
+
+/*
+ * Notification that a block is being freed. Return zero if the free
+ * should be allowed to proceed. Return non-zero if the snapshot file
+ * wants to claim the block. The block will be claimed if it is an
+ * uncopied part of one of the snapshots. It will be freed if it is
+ * either a BLK_NOCOPY or has already been copied in all of the snapshots.
+ * If a fragment is being freed, then all snapshots that care about
+ * it must make a copy since a snapshot file can only claim full sized
+ * blocks. Note that if more than one snapshot file maps the block,
+ * we can pick one at random to claim it. Since none of the snapshots
+ * can change, we are assurred that they will all see the same unmodified
+ * image. When deleting a snapshot file (see ino_trunc above), we
+ * must push any of these claimed blocks to one of the other snapshots
+ * that maps it. These claimed blocks are easily identified as they will
+ * have a block number equal to their logical block number within the
+ * snapshot. A copied block can never have this property because they
+ * must always have been allocated from a BLK_NOCOPY location.
+ */
+int
+snapblkfree(struct fs *fs, ufs2_daddr_t bno, long size, ino_t inum,
+ ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t blkno, long frags))
+{
+ union dinode *dp;
+ struct inode ip;
+ struct bufarea *snapbp;
+ ufs_lbn_t lbn;
+ ufs2_daddr_t blkno, relblkno;
+ int i, frags, claimedblk, copydone;
+
+ /* If no snapshots, nothing to do */
+ if (snapcnt == 0)
+ return (0);
+ if (debug)
+ printf("snapblkfree: in ino %jd free blkno %jd, size %jd\n",
+ (intmax_t)inum, (intmax_t)bno, (intmax_t)size);
+ relblkno = blknum(fs, bno);
+ lbn = fragstoblks(fs, relblkno);
+ /* Direct blocks are always pre-copied */
+ if (lbn < UFS_NDADDR)
+ return (0);
+ copydone = 0;
+ claimedblk = 0;
+ for (i = 0; i < snapcnt; i++) {
+ /*
+ * Lookup block being freed.
+ */
+ ip = snaplist[i];
+ dp = ip.i_dp;
+ blkno = ino_blkatoff(dp, inum != 0 ? inum : ip.i_number,
+ lbn, &frags, &snapbp);
+ /*
+ * Check to see if block needs to be copied.
+ */
+ if (blkno == 0) {
+ /*
+ * A block that we map is being freed. If it has not
+ * been claimed yet, we will claim or copy it (below).
+ */
+ claimedblk = 1;
+ } else if (blkno == BLK_SNAP) {
+ /*
+ * No previous snapshot claimed the block,
+ * so it will be freed and become a BLK_NOCOPY
+ * (don't care) for us.
+ */
+ if (claimedblk)
+ pfatal("snapblkfree: inconsistent block type");
+ IBLK_SET(snapbp, snapbp->b_index, BLK_NOCOPY);
+ dirty(snapbp);
+ brelse(snapbp);
+ continue;
+ } else /* BLK_NOCOPY or default */ {
+ /*
+ * If the snapshot has already copied the block
+ * (default), or does not care about the block,
+ * it is not needed.
+ */
+ brelse(snapbp);
+ continue;
+ }
+ /*
+ * If this is a full size block, we will just grab it
+ * and assign it to the snapshot inode. Otherwise we
+ * will proceed to copy it. See explanation for this
+ * routine as to why only a single snapshot needs to
+ * claim this block.
+ */
+ if (size == fs->fs_bsize) {
+ if (debug)
+ printf("Grabonremove snapshot %ju lbn %jd "
+ "from inum %ju\n", (intmax_t)ip.i_number,
+ (intmax_t)lbn, (uintmax_t)inum);
+ IBLK_SET(snapbp, snapbp->b_index, relblkno);
+ dirty(snapbp);
+ brelse(snapbp);
+ DIP_SET(dp, di_blocks,
+ DIP(dp, di_blocks) + btodb(size));
+ inodirty(&ip);
+ return (1);
+ }
+
+ /* First time through, read the contents of the old block. */
+ if (copydone == 0) {
+ copydone = 1;
+ if (blread(fsreadfd, copybuf, fsbtodb(fs, relblkno),
+ fs->fs_bsize) != 0) {
+ pfatal("Could not read snapshot %ju block "
+ "%jd\n", (intmax_t)ip.i_number,
+ (intmax_t)relblkno);
+ continue;
+ }
+ }
+ /*
+ * This allocation will never require any additional
+ * allocations for the snapshot inode.
+ */
+ blkno = allocblk(dtog(fs, relblkno), fs->fs_frag,
+ checkblkavail);
+ if (blkno == 0) {
+ pfatal("Could not allocate block for snapshot %ju\n",
+ (intmax_t)ip.i_number);
+ continue;
+ }
+ if (debug)
+ printf("Copyonremove: snapino %jd lbn %jd for inum %ju "
+ "size %ld new blkno %jd\n", (intmax_t)ip.i_number,
+ (intmax_t)lbn, (uintmax_t)inum, size,
+ (intmax_t)blkno);
+ blwrite(fswritefd, copybuf, fsbtodb(fs, blkno), fs->fs_bsize);
+ IBLK_SET(snapbp, snapbp->b_index, blkno);
+ dirty(snapbp);
+ brelse(snapbp);
+ DIP_SET(dp, di_blocks,
+ DIP(dp, di_blocks) + btodb(fs->fs_bsize));
+ inodirty(&ip);
+ }
+ return (0);
+}
+
+/*
+ * Notification that a block is being written. Return if the block
+ * is part of a snapshot as snapshots never track other snapshots.
+ * The block will be copied in all of the snapshots that are tracking
+ * it and have not yet copied it. Some buffers may hold more than one
+ * block. Here we need to check each block in the buffer.
+ */
+void
+copyonwrite(struct fs *fs, struct bufarea *bp,
+ ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t blkno, long frags))
+{
+ ufs2_daddr_t copyblkno;
+ long i, numblks;
+
+ /* If no snapshots, nothing to do. */
+ if (snapcnt == 0)
+ return;
+ numblks = blkroundup(fs, bp->b_size) / fs->fs_bsize;
+ if (debug)
+ prtbuf(bp, "copyonwrite: checking %jd block%s in buffer",
+ (intmax_t)numblks, numblks > 1 ? "s" : "");
+ copyblkno = blknum(fs, dbtofsb(fs, bp->b_bno));
+ for (i = 0; i < numblks; i++) {
+ chkcopyonwrite(fs, copyblkno, checkblkavail);
+ copyblkno += fs->fs_frag;
+ }
+}
+
+static void
+chkcopyonwrite(struct fs *fs, ufs2_daddr_t copyblkno,
+ ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t blkno, long frags))
+{
+ struct inode ip;
+ union dinode *dp;
+ struct bufarea *snapbp;
+ ufs2_daddr_t blkno;
+ int i, frags, copydone;
+ ufs_lbn_t lbn;
+
+ lbn = fragstoblks(fs, copyblkno);
+ /* Direct blocks are always pre-copied */
+ if (lbn < UFS_NDADDR)
+ return;
+ copydone = 0;
+ for (i = 0; i < snapcnt; i++) {
+ /*
+ * Lookup block being freed.
+ */
+ ip = snaplist[i];
+ dp = ip.i_dp;
+ blkno = ino_blkatoff(dp, ip.i_number, lbn, &frags, &snapbp);
+ /*
+ * Check to see if block needs to be copied.
+ */
+ if (blkno != 0) {
+ /*
+ * A block that we have already copied or don't track.
+ */
+ brelse(snapbp);
+ continue;
+ }
+ /* First time through, read the contents of the old block. */
+ if (copydone == 0) {
+ copydone = 1;
+ if (blread(fsreadfd, copybuf, fsbtodb(fs, copyblkno),
+ fs->fs_bsize) != 0) {
+ pfatal("Could not read snapshot %ju block "
+ "%jd\n", (intmax_t)ip.i_number,
+ (intmax_t)copyblkno);
+ continue;
+ }
+ }
+ /*
+ * This allocation will never require any additional
+ * allocations for the snapshot inode.
+ */
+ if ((blkno = allocblk(dtog(fs, copyblkno), fs->fs_frag,
+ checkblkavail)) == 0) {
+ pfatal("Could not allocate block for snapshot %ju\n",
+ (intmax_t)ip.i_number);
+ continue;
+ }
+ if (debug)
+ prtbuf(snapbp, "Copyonwrite: snapino %jd lbn %jd using "
+ "blkno %ju setting in buffer",
+ (intmax_t)ip.i_number, (intmax_t)lbn,
+ (intmax_t)blkno);
+ blwrite(fswritefd, copybuf, fsbtodb(fs, blkno), fs->fs_bsize);
+ IBLK_SET(snapbp, snapbp->b_index, blkno);
+ dirty(snapbp);
+ brelse(snapbp);
+ DIP_SET(dp, di_blocks,
+ DIP(dp, di_blocks) + btodb(fs->fs_bsize));
+ inodirty(&ip);
+ }
+ return;
+}
+
+/*
+ * Traverse an inode and check that its block count is correct
+ * fixing it if necessary.
+ */
+void
+check_blkcnt(struct inode *ip)
+{
+ struct inodesc idesc;
+ union dinode *dp;
+ ufs2_daddr_t ndb;
+ int j, ret, offset;
+
+ dp = ip->i_dp;
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_func = pass1check;
+ idesc.id_number = ip->i_number;
+ idesc.id_type = (DIP(dp, di_flags) & SF_SNAPSHOT) == 0 ? ADDR : SNAP;
+ (void)ckinode(dp, &idesc);
+ if (sblock.fs_magic == FS_UFS2_MAGIC && dp->dp2.di_extsize > 0) {
+ ndb = howmany(dp->dp2.di_extsize, sblock.fs_bsize);
+ for (j = 0; j < UFS_NXADDR; j++) {
+ if (--ndb == 0 &&
+ (offset = blkoff(&sblock, dp->dp2.di_extsize)) != 0)
+ idesc.id_numfrags = numfrags(&sblock,
+ fragroundup(&sblock, offset));
+ else
+ idesc.id_numfrags = sblock.fs_frag;
+ if (dp->dp2.di_extb[j] == 0)
+ continue;
+ idesc.id_blkno = dp->dp2.di_extb[j];
+ ret = (*idesc.id_func)(&idesc);
+ if (ret & STOP)
+ break;
+ }
+ }
+ idesc.id_entryno *= btodb(sblock.fs_fsize);
+ if (DIP(dp, di_blocks) != idesc.id_entryno) {
+ if (!(sujrecovery && preen)) {
+ pwarn("INCORRECT BLOCK COUNT I=%lu (%ju should be %ju)",
+ (u_long)idesc.id_number,
+ (uintmax_t)DIP(dp, di_blocks),
+ (uintmax_t)idesc.id_entryno);
+ if (preen)
+ printf(" (CORRECTED)\n");
+ else if (reply("CORRECT") == 0)
+ return;
+ }
+ if (bkgrdflag == 0) {
+ DIP_SET(dp, di_blocks, idesc.id_entryno);
+ inodirty(ip);
+ } else {
+ cmd.value = idesc.id_number;
+ cmd.size = idesc.id_entryno - DIP(dp, di_blocks);
+ if (debug)
+ printf("adjblkcnt ino %ju amount %lld\n",
+ (uintmax_t)cmd.value, (long long)cmd.size);
+ if (sysctl(adjblkcnt, MIBSIZE, 0, 0,
+ &cmd, sizeof cmd) == -1)
+ rwerror("ADJUST INODE BLOCK COUNT", cmd.value);
+ }
+ }
}
void
freeinodebuf(void)
{
+ struct bufarea *bp;
+ int i;
/*
* Flush old contents in case they have been updated.
@@ -671,6 +1097,15 @@ freeinodebuf(void)
if (inobuf.b_un.b_buf != NULL)
free((char *)inobuf.b_un.b_buf);
inobuf.b_un.b_buf = NULL;
+ firstinum = lastinum = 0;
+ /*
+ * Reload the snapshot inodes in case any of them changed.
+ */
+ for (i = 0; i < snapcnt; i++) {
+ bp = snaplist[i].i_bp;
+ bp->b_errs = blread(fsreadfd, bp->b_un.b_buf, bp->b_bno,
+ bp->b_size);
+ }
}
/*
@@ -680,12 +1115,15 @@ freeinodebuf(void)
*
* Enter inodes into the cache.
*/
-void
+struct inoinfo *
cacheino(union dinode *dp, ino_t inumber)
{
- struct inoinfo *inp, **inpp;
+ struct inoinfo *inp;
int i, blks;
+ if (getinoinfo(inumber) != NULL)
+ pfatal("cacheino: duplicate entry for ino %jd\n",
+ (intmax_t)inumber);
if (howmany(DIP(dp, di_size), sblock.fs_bsize) > UFS_NDADDR)
blks = UFS_NDADDR + UFS_NIADDR;
else if (DIP(dp, di_size) > 0)
@@ -696,13 +1134,13 @@ cacheino(union dinode *dp, ino_t inumber)
Malloc(sizeof(*inp) + (blks - 1) * sizeof(ufs2_daddr_t));
if (inp == NULL)
errx(EEXIT, "cannot increase directory list");
- inpp = &inphead[inumber % dirhash];
- inp->i_nexthash = *inpp;
- *inpp = inp;
+ SLIST_INSERT_HEAD(&inphash[inumber % dirhash], inp, i_hash);
+ inp->i_flags = 0;
inp->i_parent = inumber == UFS_ROOTINO ? UFS_ROOTINO : (ino_t)0;
inp->i_dotdot = (ino_t)0;
inp->i_number = inumber;
inp->i_isize = DIP(dp, di_size);
+ inp->i_depth = DIP(dp, di_dirdepth);
inp->i_numblks = blks;
for (i = 0; i < MIN(blks, UFS_NDADDR); i++)
inp->i_blks[i] = DIP(dp, di_db[i]);
@@ -717,6 +1155,7 @@ cacheino(union dinode *dp, ino_t inumber)
errx(EEXIT, "cannot increase directory list");
}
inpsort[inplast++] = inp;
+ return (inp);
}
/*
@@ -727,13 +1166,43 @@ getinoinfo(ino_t inumber)
{
struct inoinfo *inp;
- for (inp = inphead[inumber % dirhash]; inp; inp = inp->i_nexthash) {
+ SLIST_FOREACH(inp, &inphash[inumber % dirhash], i_hash) {
if (inp->i_number != inumber)
continue;
return (inp);
}
- errx(EEXIT, "cannot find inode %ju", (uintmax_t)inumber);
- return ((struct inoinfo *)0);
+ return (NULL);
+}
+
+/*
+ * Remove an entry from the inode cache and disk-order sorted list.
+ * Return 0 on success and 1 on failure.
+ */
+int
+removecachedino(ino_t inumber)
+{
+ struct inoinfo *inp, **inpp;
+ char *listtype;
+
+ listtype = "hash";
+ SLIST_FOREACH(inp, &inphash[inumber % dirhash], i_hash) {
+ if (inp->i_number != inumber)
+ continue;
+ SLIST_REMOVE(&inphash[inumber % dirhash], inp, inoinfo, i_hash);
+ for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) {
+ if (*inpp != inp)
+ continue;
+ *inpp = inpsort[inplast - 1];
+ inplast--;
+ free(inp);
+ return (0);
+ }
+ listtype = "sort";
+ break;
+ }
+ pfatal("removecachedino: entry for ino %jd not found on %s list\n",
+ (intmax_t)inumber, listtype);
+ return (1);
}
/*
@@ -744,13 +1213,14 @@ inocleanup(void)
{
struct inoinfo **inpp;
- if (inphead == NULL)
+ if (inphash == NULL)
return;
for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
free((char *)(*inpp));
- free((char *)inphead);
+ free((char *)inphash);
+ inphash = NULL;
free((char *)inpsort);
- inphead = inpsort = NULL;
+ inpsort = NULL;
}
void
@@ -782,6 +1252,10 @@ clri(struct inodesc *idesc, const char *type, int flag)
printf(" (CLEARED)\n");
n_files--;
if (bkgrdflag == 0) {
+ if (idesc->id_type == SNAP) {
+ snapremove(idesc->id_number);
+ idesc->id_type = ADDR;
+ }
(void)ckinode(dp, idesc);
inoinfo(idesc->id_number)->ino_state = USTATE;
clearinode(dp);
@@ -821,7 +1295,7 @@ findino(struct inodesc *idesc)
if (dirp->d_ino == 0)
return (KEEPON);
if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
- dirp->d_ino >= UFS_ROOTINO && dirp->d_ino <= maxino) {
+ dirp->d_ino >= UFS_ROOTINO && dirp->d_ino < maxino) {
idesc->id_parent = dirp->d_ino;
return (STOP|FOUND);
}
@@ -851,7 +1325,7 @@ prtinode(struct inode *ip)
dp = ip->i_dp;
printf(" I=%lu ", (u_long)ip->i_number);
- if (ip->i_number < UFS_ROOTINO || ip->i_number > maxino)
+ if (ip->i_number < UFS_ROOTINO || ip->i_number >= maxino)
return;
printf(" OWNER=");
if ((pw = getpwuid((int)DIP(dp, di_uid))) != NULL)
@@ -863,8 +1337,8 @@ prtinode(struct inode *ip)
printf("%s: ", cdevname);
printf("SIZE=%ju ", (uintmax_t)DIP(dp, di_size));
t = DIP(dp, di_mtime);
- p = ctime(&t);
- printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+ if ((p = ctime(&t)) != NULL)
+ printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
}
void
@@ -923,7 +1397,7 @@ retry:
cg = ino_to_cg(&sblock, ino);
cgbp = cglookup(cg);
cgp = cgbp->b_un.b_cg;
- if (!check_cgmagic(cg, cgbp, 0)) {
+ if (!check_cgmagic(cg, cgbp)) {
if (anyino == 0)
return (0);
request = (cg + 1) * sblock.fs_ipg;
@@ -946,20 +1420,20 @@ retry:
cgdirty(cgbp);
ginode(ino, &ip);
dp = ip.i_dp;
- DIP_SET(dp, di_db[0], allocblk((long)1));
+ memset(dp, 0, ((sblock.fs_magic == FS_UFS1_MAGIC) ?
+ sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)));
+ DIP_SET(dp, di_db[0], allocblk(ino_to_cg(&sblock, ino), (long)1,
+ std_checkblkavail));
if (DIP(dp, di_db[0]) == 0) {
inoinfo(ino)->ino_state = USTATE;
+ inodirty(&ip);
irelse(&ip);
return (0);
}
DIP_SET(dp, di_mode, type);
- DIP_SET(dp, di_flags, 0);
DIP_SET(dp, di_atime, time(NULL));
DIP_SET(dp, di_ctime, DIP(dp, di_atime));
DIP_SET(dp, di_mtime, DIP(dp, di_ctime));
- DIP_SET(dp, di_mtimensec, 0);
- DIP_SET(dp, di_ctimensec, 0);
- DIP_SET(dp, di_atimensec, 0);
DIP_SET(dp, di_size, sblock.fs_fsize);
DIP_SET(dp, di_blocks, btodb(sblock.fs_fsize));
n_files++;
@@ -980,7 +1454,7 @@ freeino(ino_t ino)
struct inode ip;
memset(&idesc, 0, sizeof(struct inodesc));
- idesc.id_type = inoinfo(ino)->ino_idtype;
+ idesc.id_type = ADDR;
idesc.id_func = freeblock;
idesc.id_number = ino;
ginode(ino, &ip);
diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c
index 18634a93c05c..aabab489573e 100644
--- a/sbin/fsck_ffs/main.c
+++ b/sbin/fsck_ffs/main.c
@@ -29,21 +29,7 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char copyright[] =
-"@(#) Copyright (c) 1980, 1986, 1993\n\
- The Regents of the University of California. All rights reserved.\n";
-#endif /* not lint */
-
-#ifndef lint
-static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#define IN_RTLD /* So we pickup the P_OSREL defines */
+#define _WANT_P_OSREL
#include <sys/param.h>
#include <sys/file.h>
#include <sys/mount.h>
@@ -61,7 +47,6 @@ __FBSDID("$FreeBSD$");
#include <fstab.h>
#include <grp.h>
#include <inttypes.h>
-#include <libufs.h>
#include <mntopts.h>
#include <paths.h>
#include <stdint.h>
@@ -70,14 +55,13 @@ __FBSDID("$FreeBSD$");
#include "fsck.h"
-static int restarts;
+static int restarts;
+static char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */
static void usage(void) __dead2;
static intmax_t argtoimax(int flag, const char *req, const char *str, int base);
static int checkfilesys(char *filesys);
static int setup_bkgrdchk(struct statfs *mntp, int sbrdfailed, char **filesys);
-static int chkdoreload(struct statfs *mntp);
-static struct statfs *getmntpt(const char *);
int
main(int argc, char *argv[])
@@ -258,7 +242,7 @@ checkfilesys(char *filesys)
* if it is listed among the mounted file systems. Failing that
* check to see if it is listed in /etc/fstab.
*/
- mntp = getmntpt(filesys);
+ mntp = getmntpoint(filesys);
if (mntp != NULL)
filesys = mntp->f_mntfromname;
else
@@ -271,14 +255,21 @@ checkfilesys(char *filesys)
*/
sblock_init();
sbreadfailed = 0;
- if (openfilesys(filesys) == 0 || readsb(0) == 0)
+ if (openfilesys(filesys) == 0 || readsb() == 0)
sbreadfailed = 1;
if (bkgrdcheck) {
if (sbreadfailed)
exit(3); /* Cannot read superblock */
- /* Earlier background failed or journaled */
- if (sblock.fs_flags & (FS_NEEDSFSCK | FS_SUJ))
- exit(4);
+ if ((sblock.fs_flags & FS_NEEDSFSCK) == FS_NEEDSFSCK)
+ exit(4); /* Earlier background failed */
+ if ((sblock.fs_flags & FS_SUJ) == FS_SUJ) {
+ maxino = sblock.fs_ncg * sblock.fs_ipg;
+ maxfsblock = sblock.fs_size;
+ bufinit();
+ preen = 1;
+ if (suj_check(filesys) == 0)
+ exit(4); /* Journal good, run it now */
+ }
if ((sblock.fs_flags & FS_DOSOFTDEP) == 0)
exit(5); /* Not running soft updates */
size = MIBSIZE;
@@ -311,7 +302,7 @@ checkfilesys(char *filesys)
(FS_UNCLEAN | FS_NEEDSFSCK)) == 0) {
bufinit();
gjournal_check(filesys);
- if (chkdoreload(mntp) == 0)
+ if (chkdoreload(mntp, pwarn) == 0)
exit(0);
exit(4);
} else {
@@ -352,17 +343,19 @@ checkfilesys(char *filesys)
/*
* Determine if we can and should do journal recovery.
*/
- if ((sblock.fs_flags & FS_SUJ) == FS_SUJ) {
- if ((sblock.fs_flags & FS_NEEDSFSCK) != FS_NEEDSFSCK && skipclean) {
+ if (bkgrdflag == 0 && (sblock.fs_flags & FS_SUJ) == FS_SUJ) {
+ if ((sblock.fs_flags & FS_NEEDSFSCK) != FS_NEEDSFSCK &&
+ skipclean) {
sujrecovery = 1;
if (suj_check(filesys) == 0) {
- printf("\n***** FILE SYSTEM MARKED CLEAN *****\n");
- if (chkdoreload(mntp) == 0)
+ pwarn("\n**** FILE SYSTEM MARKED CLEAN ****\n");
+ if (chkdoreload(mntp, pwarn) == 0)
exit(0);
exit(4);
}
sujrecovery = 0;
- printf("** Skipping journal, falling through to full fsck\n\n");
+ pwarn("Skipping journal, "
+ "falling through to full fsck\n");
}
if (fswritefd != -1) {
/*
@@ -438,7 +431,7 @@ checkfilesys(char *filesys)
/*
* 1: scan inodes tallying blocks used
*/
- if (preen == 0) {
+ if (preen == 0 || debug) {
printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
if (mntp != NULL && mntp->f_flags & MNT_ROOTFS)
printf("** Root file system\n");
@@ -457,7 +450,8 @@ checkfilesys(char *filesys)
preen ? "-p" : "",
(preen && usedsoftdep) ? " AND " : "",
usedsoftdep ? "SOFTUPDATES" : "");
- printf("** Phase 1b - Rescan For More DUPS\n");
+ if (preen == 0 || debug)
+ printf("** Phase 1b - Rescan For More DUPS\n");
pass1b();
IOstats("Pass1b");
}
@@ -465,7 +459,7 @@ checkfilesys(char *filesys)
/*
* 2: traverse directories from root to mark all connected directories
*/
- if (preen == 0)
+ if (preen == 0 || debug)
printf("** Phase 2 - Check Pathnames\n");
pass2();
IOstats("Pass2");
@@ -473,7 +467,7 @@ checkfilesys(char *filesys)
/*
* 3: scan inodes looking for disconnected directories
*/
- if (preen == 0)
+ if (preen == 0 || debug)
printf("** Phase 3 - Check Connectivity\n");
pass3();
IOstats("Pass3");
@@ -481,7 +475,7 @@ checkfilesys(char *filesys)
/*
* 4: scan inodes looking for disconnected files; check reference counts
*/
- if (preen == 0)
+ if (preen == 0 || debug)
printf("** Phase 4 - Check Reference Counts\n");
pass4();
IOstats("Pass4");
@@ -489,10 +483,16 @@ checkfilesys(char *filesys)
/*
* 5: check and repair resource counts in cylinder groups
*/
- if (preen == 0)
+ if (preen == 0 || debug)
printf("** Phase 5 - Check Cyl groups\n");
- pass5();
- IOstats("Pass5");
+ snapflush(std_checkblkavail);
+ if (cgheader_corrupt) {
+ printf("PHASE 5 SKIPPED DUE TO CORRUPT CYLINDER GROUP "
+ "HEADER(S)\n\n");
+ } else {
+ pass5();
+ IOstats("Pass5");
+ }
/*
* print out summary statistics
@@ -560,7 +560,7 @@ checkfilesys(char *filesys)
return (ERESTART);
printf("\n***** PLEASE RERUN FSCK *****\n");
}
- if (chkdoreload(mntp) != 0) {
+ if (chkdoreload(mntp, pwarn) != 0) {
if (!fsmodified)
return (0);
if (!preen)
@@ -612,10 +612,6 @@ setup_bkgrdchk(struct statfs *mntp, int sbreadfailed, char **filesys)
pwarn("FULL FSCK NEEDED, CANNOT RUN IN BACKGROUND\n");
return (0);
}
- if ((sblock.fs_flags & FS_SUJ) != 0) {
- pwarn("JOURNALED FILESYSTEM, CANNOT RUN IN BACKGROUND\n");
- return (0);
- }
if (skipclean && ckclean &&
(sblock.fs_flags & (FS_UNCLEAN|FS_NEEDSFSCK)) == 0) {
/*
@@ -656,8 +652,7 @@ setup_bkgrdchk(struct statfs *mntp, int sbreadfailed, char **filesys)
"SUPPORT\n");
}
/* Find or create the snapshot directory */
- snprintf(snapname, sizeof snapname, "%s/.snap",
- mntp->f_mntonname);
+ snprintf(snapname, sizeof snapname, "%s/.snap", mntp->f_mntonname);
if (stat(snapname, &snapdir) < 0) {
if (errno != ENOENT) {
pwarn("CANNOT FIND SNAPSHOT DIRECTORY %s: %s, CANNOT "
@@ -705,101 +700,20 @@ setup_bkgrdchk(struct statfs *mntp, int sbreadfailed, char **filesys)
"BACKGROUND\n", snapname, strerror(errno));
return (0);
}
+ /* Immediately unlink snapshot so that it will be deleted when closed */
+ unlink(snapname);
free(sblock.fs_csp);
free(sblock.fs_si);
- havesb = 0;
+ if (readsb() == 0) {
+ pwarn("CANNOT READ SNAPSHOT SUPERBLOCK\n");
+ return (0);
+ }
*filesys = snapname;
cmd.version = FFS_CMD_VERSION;
cmd.handle = fsreadfd;
return (1);
}
-static int
-chkdoreload(struct statfs *mntp)
-{
- struct iovec *iov;
- int iovlen;
- char errmsg[255];
-
- if (mntp == NULL)
- return (0);
-
- iov = NULL;
- iovlen = 0;
- errmsg[0] = '\0';
- /*
- * We modified a mounted file system. Do a mount update on
- * it unless it is read-write, so we can continue using it
- * as safely as possible.
- */
- if (mntp->f_flags & MNT_RDONLY) {
- build_iovec(&iov, &iovlen, "fstype", "ffs", 4);
- build_iovec(&iov, &iovlen, "from", mntp->f_mntfromname,
- (size_t)-1);
- build_iovec(&iov, &iovlen, "fspath", mntp->f_mntonname,
- (size_t)-1);
- build_iovec(&iov, &iovlen, "errmsg", errmsg,
- sizeof(errmsg));
- build_iovec(&iov, &iovlen, "update", NULL, 0);
- build_iovec(&iov, &iovlen, "reload", NULL, 0);
- /*
- * XX: We need the following line until we clean up
- * nmount parsing of root mounts and NFS root mounts.
- */
- build_iovec(&iov, &iovlen, "ro", NULL, 0);
- if (nmount(iov, iovlen, mntp->f_flags) == 0) {
- return (0);
- }
- pwarn("mount reload of '%s' failed: %s %s\n\n",
- mntp->f_mntonname, strerror(errno), errmsg);
- return (1);
- }
- return (0);
-}
-
-/*
- * Get the mount point information for name.
- */
-static struct statfs *
-getmntpt(const char *name)
-{
- struct stat devstat, mntdevstat;
- char device[sizeof(_PATH_DEV) - 1 + MNAMELEN];
- char *ddevname;
- struct statfs *mntbuf, *statfsp;
- int i, mntsize, isdev;
-
- if (stat(name, &devstat) != 0)
- return (NULL);
- if (S_ISCHR(devstat.st_mode) || S_ISBLK(devstat.st_mode))
- isdev = 1;
- else
- isdev = 0;
- mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
- for (i = 0; i < mntsize; i++) {
- statfsp = &mntbuf[i];
- ddevname = statfsp->f_mntfromname;
- if (*ddevname != '/') {
- if (strlen(_PATH_DEV) + strlen(ddevname) + 1 >
- sizeof(statfsp->f_mntfromname))
- continue;
- strcpy(device, _PATH_DEV);
- strcat(device, ddevname);
- strcpy(statfsp->f_mntfromname, device);
- }
- if (isdev == 0) {
- if (strcmp(name, statfsp->f_mntonname))
- continue;
- return (statfsp);
- }
- if (stat(ddevname, &mntdevstat) == 0 &&
- mntdevstat.st_rdev == devstat.st_rdev)
- return (statfsp);
- }
- statfsp = NULL;
- return (statfsp);
-}
-
static void
usage(void)
{
diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c
index 319a324cc070..f4f97a38097b 100644
--- a/sbin/fsck_ffs/pass1.c
+++ b/sbin/fsck_ffs/pass1.c
@@ -29,14 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char sccsid[] = "@(#)pass1.c 8.6 (Berkeley) 4/28/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
@@ -56,7 +48,7 @@ static ufs2_daddr_t badblk;
static ufs2_daddr_t dupblk;
static ino_t lastino; /* last inode in use */
-static int checkinode(ino_t inumber, struct inodesc *, int rebuildcg);
+static int checkinode(ino_t inumber, struct inodesc *, int rebuiltcg);
void
pass1(void)
@@ -68,7 +60,7 @@ pass1(void)
ino_t inumber, inosused, mininos;
ufs2_daddr_t i, cgd;
u_int8_t *cp;
- int c, rebuildcg;
+ int c, rebuiltcg;
badblk = dupblk = lastino = 0;
@@ -99,10 +91,20 @@ pass1(void)
inumber = c * sblock.fs_ipg;
cgbp = cglookup(c);
cgp = cgbp->b_un.b_cg;
- rebuildcg = 0;
- if (!check_cgmagic(c, cgbp, 1))
- rebuildcg = 1;
- if (!rebuildcg && sblock.fs_magic == FS_UFS2_MAGIC) {
+ rebuiltcg = 0;
+ if (!check_cgmagic(c, cgbp)) {
+ if (!reply("REBUILD CYLINDER GROUP")) {
+ cgheader_corrupt = 1;
+ if (!nflag) {
+ pwarn("YOU WILL NEED TO RERUN FSCK.\n");
+ rerun = 1;
+ }
+ } else {
+ rebuild_cg(c, cgbp);
+ rebuiltcg = 1;
+ }
+ }
+ if (!rebuiltcg && sblock.fs_magic == FS_UFS2_MAGIC) {
inosused = cgp->cg_initediblk;
if (inosused > sblock.fs_ipg) {
pfatal("Too many initialized inodes (%ju > %d) "
@@ -132,7 +134,7 @@ pass1(void)
* to find the inodes that are really in use, and then
* read only those inodes in from disk.
*/
- if ((preen || inoopt) && usedsoftdep && !rebuildcg) {
+ if ((preen || inoopt) && usedsoftdep && !rebuiltcg) {
cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT];
for ( ; inosused != 0; cp--) {
if (*cp == 0) {
@@ -169,7 +171,7 @@ pass1(void)
setinodebuf(c, inosused);
for (i = 0; i < inosused; i++, inumber++) {
if (inumber < UFS_ROOTINO) {
- (void)getnextinode(inumber, rebuildcg);
+ (void)getnextinode(inumber, rebuiltcg);
continue;
}
/*
@@ -178,7 +180,7 @@ pass1(void)
* We always keep trying until we get to the minimum
* valid number for this cylinder group.
*/
- if (checkinode(inumber, &idesc, rebuildcg) == 0 &&
+ if (checkinode(inumber, &idesc, rebuiltcg) == 0 &&
i > cgp->cg_initediblk)
break;
}
@@ -189,7 +191,7 @@ pass1(void)
* fewer in use.
*/
mininos = roundup(inosused + INOPB(&sblock), INOPB(&sblock));
- if (inoopt && !preen && !rebuildcg &&
+ if (inoopt && !preen && !rebuiltcg &&
sblock.fs_magic == FS_UFS2_MAGIC &&
cgp->cg_initediblk > 2 * INOPB(&sblock) &&
mininos < cgp->cg_initediblk) {
@@ -209,7 +211,7 @@ pass1(void)
inosused = 0;
else
inosused = lastino - (c * sblock.fs_ipg);
- if (rebuildcg && inosused > cgp->cg_initediblk &&
+ if (rebuiltcg && inosused > cgp->cg_initediblk &&
sblock.fs_magic == FS_UFS2_MAGIC) {
cgp->cg_initediblk = roundup(inosused, INOPB(&sblock));
pwarn("CYLINDER GROUP %d: FOUND %d VALID INODES\n", c,
@@ -219,9 +221,10 @@ pass1(void)
* If we were not able to determine in advance which inodes
* were in use, then reduce the size of the inoinfo structure
* to the size necessary to describe the inodes that we
- * really found.
+ * really found. Always leave map space in the first cylinder
+ * group in case we need to a root or lost+found directory.
*/
- if (inumber == lastino)
+ if (inumber == lastino || c == 0)
continue;
inostathead[c].il_numalloced = inosused;
if (inosused == 0) {
@@ -241,18 +244,19 @@ pass1(void)
}
static int
-checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
+checkinode(ino_t inumber, struct inodesc *idesc, int rebuiltcg)
{
struct inode ip;
union dinode *dp;
- off_t kernmaxfilesize;
ufs2_daddr_t ndb;
mode_t mode;
intmax_t size, fixsize;
int j, ret, offset;
- if ((dp = getnextinode(inumber, rebuildcg)) == NULL)
+ if ((dp = getnextinode(inumber, rebuiltcg)) == NULL) {
+ pfatal("INVALID INODE");
goto unknown;
+ }
mode = DIP(dp, di_mode) & IFMT;
if (mode == 0) {
if ((sblock.fs_magic == FS_UFS1_MAGIC &&
@@ -280,16 +284,8 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
return (1);
}
lastino = inumber;
- /* This should match the file size limit in ffs_mountfs(). */
- if (sblock.fs_magic == FS_UFS1_MAGIC)
- kernmaxfilesize = (off_t)0x40000000 * sblock.fs_bsize - 1;
- else
- kernmaxfilesize = sblock.fs_maxfilesize;
- if (DIP(dp, di_size) > kernmaxfilesize ||
- DIP(dp, di_size) > sblock.fs_maxfilesize ||
- (mode == IFDIR && DIP(dp, di_size) > MAXDIRSIZE)) {
- if (debug)
- printf("bad size %ju:", (uintmax_t)DIP(dp, di_size));
+ if (chkfilesize(mode, DIP(dp, di_size)) == 0) {
+ pfatal("BAD FILE SIZE");
goto unknown;
}
if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) {
@@ -305,19 +301,22 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
if (debug)
printf("bad special-file size %ju:",
(uintmax_t)DIP(dp, di_size));
+ pfatal("BAD SPECIAL-FILE SIZE");
goto unknown;
}
if ((mode == IFBLK || mode == IFCHR) &&
(dev_t)DIP(dp, di_rdev) == NODEV) {
if (debug)
printf("bad special-file rdev NODEV:");
+ pfatal("BAD SPECIAL-FILE RDEV");
goto unknown;
}
ndb = howmany(DIP(dp, di_size), sblock.fs_bsize);
if (ndb < 0) {
if (debug)
- printf("bad size %ju ndb %ju:",
+ printf("negative size %ju ndb %ju:",
(uintmax_t)DIP(dp, di_size), (uintmax_t)ndb);
+ pfatal("NEGATIVE FILE SIZE");
goto unknown;
}
if (mode == IFBLK || mode == IFCHR)
@@ -342,33 +341,52 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
}
}
}
- for (j = ndb; ndb < UFS_NDADDR && j < UFS_NDADDR; j++)
- if (DIP(dp, di_db[j]) != 0) {
- if (debug)
- printf("bad direct addr[%d]: %ju\n", j,
- (uintmax_t)DIP(dp, di_db[j]));
- goto unknown;
+ for (j = ndb; ndb < UFS_NDADDR && j < UFS_NDADDR; j++) {
+ if (DIP(dp, di_db[j]) == 0)
+ continue;
+ if (debug)
+ printf("invalid direct addr[%d]: %ju\n", j,
+ (uintmax_t)DIP(dp, di_db[j]));
+ pfatal("INVALID DIRECT BLOCK");
+ ginode(inumber, &ip);
+ prtinode(&ip);
+ if (reply("CLEAR") == 1) {
+ DIP_SET(ip.i_dp, di_db[j], 0);
+ inodirty(&ip);
}
+ irelse(&ip);
+ }
for (j = 0, ndb -= UFS_NDADDR; ndb > 0; j++)
ndb /= NINDIR(&sblock);
- for (; j < UFS_NIADDR; j++)
- if (DIP(dp, di_ib[j]) != 0) {
- if (debug)
- printf("bad indirect addr: %ju\n",
- (uintmax_t)DIP(dp, di_ib[j]));
- goto unknown;
+ for (; j < UFS_NIADDR; j++) {
+ if (DIP(dp, di_ib[j]) == 0)
+ continue;
+ if (debug)
+ printf("invalid indirect addr: %ju\n",
+ (uintmax_t)DIP(dp, di_ib[j]));
+ pfatal("INVALID INDIRECT BLOCK");
+ ginode(inumber, &ip);
+ prtinode(&ip);
+ if (reply("CLEAR") == 1) {
+ DIP_SET(ip.i_dp, di_ib[j], 0);
+ inodirty(&ip);
}
- if (ftypeok(dp) == 0)
+ irelse(&ip);
+ }
+ if (ftypeok(dp) == 0) {
+ pfatal("UNKNOWN FILE TYPE");
goto unknown;
+ }
n_files++;
inoinfo(inumber)->ino_linkcnt = DIP(dp, di_nlink);
if (mode == IFDIR) {
- if (DIP(dp, di_size) == 0)
+ if (DIP(dp, di_size) == 0) {
inoinfo(inumber)->ino_state = DCLEAR;
- else if (DIP(dp, di_nlink) <= 0)
+ } else if (DIP(dp, di_nlink) == 0) {
inoinfo(inumber)->ino_state = DZLINK;
- else
+ } else {
inoinfo(inumber)->ino_state = DSTATE;
+ }
cacheino(dp, inumber);
countdirs++;
} else if (DIP(dp, di_nlink) <= 0)
@@ -483,15 +501,14 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
}
return (1);
unknown:
- pfatal("UNKNOWN FILE TYPE I=%lu", (u_long)inumber);
- inoinfo(inumber)->ino_state = FCLEAR;
+ ginode(inumber, &ip);
+ prtinode(&ip);
+ inoinfo(inumber)->ino_state = USTATE;
if (reply("CLEAR") == 1) {
- inoinfo(inumber)->ino_state = USTATE;
- ginode(inumber, &ip);
clearinode(ip.i_dp);
inodirty(&ip);
- irelse(&ip);
}
+ irelse(&ip);
return (1);
}
diff --git a/sbin/fsck_ffs/pass1b.c b/sbin/fsck_ffs/pass1b.c
index 17a3b6495dc4..3fb9cab89078 100644
--- a/sbin/fsck_ffs/pass1b.c
+++ b/sbin/fsck_ffs/pass1b.c
@@ -29,14 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char sccsid[] = "@(#)pass1b.c 8.4 (Berkeley) 4/28/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <ufs/ufs/dinode.h>
@@ -88,10 +80,12 @@ pass1b(void)
if (inoinfo(inumber)->ino_state != USTATE &&
(ckinode(dp, &idesc) & STOP)) {
rerun = 1;
+ freeinodebuf();
return;
}
}
}
+ freeinodebuf();
}
static int
diff --git a/sbin/fsck_ffs/pass2.c b/sbin/fsck_ffs/pass2.c
index 8632cf4878b1..1755a1295a9e 100644
--- a/sbin/fsck_ffs/pass2.c
+++ b/sbin/fsck_ffs/pass2.c
@@ -29,14 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/sysctl.h>
@@ -85,7 +77,7 @@ pass2(void)
case DCLEAR:
pfatal("DUPS/BAD IN ROOT INODE");
if (reply("REALLOCATE")) {
- freeino(UFS_ROOTINO);
+ freedirino(UFS_ROOTINO, UFS_ROOTINO);
if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) !=
UFS_ROOTINO)
errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
@@ -210,16 +202,18 @@ pass2(void)
if (inp->i_parent == 0 || inp->i_isize == 0)
continue;
if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
- INO_IS_DUNFOUND(inp->i_number))
+ INO_IS_DUNFOUND(inp->i_number)) {
inoinfo(inp->i_number)->ino_state = DFOUND;
+ check_dirdepth(inp);
+ }
if (inp->i_dotdot == inp->i_parent ||
inp->i_dotdot == (ino_t)-1)
continue;
if (inp->i_dotdot == 0) {
inp->i_dotdot = inp->i_parent;
- fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
- if (reply("FIX") == 0)
- continue;
+ if (debug)
+ fileerror(inp->i_parent, inp->i_number,
+ "DEFERRED MISSING '..' FIX");
(void)makeentry(inp->i_number, inp->i_parent, "..");
inoinfo(inp->i_parent)->ino_linkcnt--;
continue;
@@ -271,7 +265,8 @@ pass2(void)
inoinfo(inp->i_dotdot)->ino_linkcnt++;
inoinfo(inp->i_parent)->ino_linkcnt--;
inp->i_dotdot = inp->i_parent;
- (void)changeino(inp->i_number, "..", inp->i_parent);
+ (void)changeino(inp->i_number, "..", inp->i_parent,
+ getinoinfo(inp->i_parent)->i_depth + 1);
}
/*
* Mark all the directories that can be found from the root.
@@ -289,57 +284,62 @@ pass2check(struct inodesc *idesc)
struct inode ip;
union dinode *dp;
const char *errmsg;
- struct direct proto;
+ struct direct proto, *newdirp;
/*
* check for "."
*/
- if (dirp->d_ino > maxino)
- goto chk2;
if (idesc->id_entryno != 0)
goto chk1;
if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
if (dirp->d_ino != idesc->id_number) {
direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
- dirp->d_ino = idesc->id_number;
- if (reply("FIX") == 1)
+ if (reply("FIX") == 1) {
+ dirp->d_ino = idesc->id_number;
ret |= ALTERED;
+ }
}
if (dirp->d_type != DT_DIR) {
direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
- dirp->d_type = DT_DIR;
- if (reply("FIX") == 1)
+ if (reply("FIX") == 1) {
+ dirp->d_type = DT_DIR;
ret |= ALTERED;
+ }
}
goto chk1;
}
- direrror(idesc->id_number, "MISSING '.'");
proto.d_ino = idesc->id_number;
proto.d_type = DT_DIR;
proto.d_namlen = 1;
(void)strcpy(proto.d_name, ".");
entrysize = DIRSIZ(0, &proto);
- if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
- pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
- dirp->d_name);
- } else if (dirp->d_reclen < entrysize) {
- pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
- } else if (dirp->d_reclen < 2 * entrysize) {
+ direrror(idesc->id_number, "MISSING '.'");
+ errmsg = "ADD '.' ENTRY";
+ if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) {
+ /* Not enough space to add '.', replace first entry with '.' */
+ if (dirp->d_ino != 0) {
+ pwarn("\nFIRST ENTRY IN DIRECTORY CONTAINS %s\n",
+ dirp->d_name);
+ errmsg = "REPLACE WITH '.'";
+ }
+ if (reply(errmsg) == 0)
+ goto chk1;
proto.d_reclen = dirp->d_reclen;
memmove(dirp, &proto, (size_t)entrysize);
- if (reply("FIX") == 1)
- ret |= ALTERED;
+ ret |= ALTERED;
} else {
- n = dirp->d_reclen - entrysize;
+ /* Move over first entry and add '.' entry */
+ if (reply(errmsg) == 0)
+ goto chk1;
+ newdirp = (struct direct *)((char *)(dirp) + entrysize);
+ dirp->d_reclen -= entrysize;
+ memmove(newdirp, dirp, dirp->d_reclen);
proto.d_reclen = entrysize;
memmove(dirp, &proto, (size_t)entrysize);
idesc->id_entryno++;
- inoinfo(dirp->d_ino)->ino_linkcnt--;
- dirp = (struct direct *)((char *)(dirp) + entrysize);
- memset(dirp, 0, (size_t)n);
- dirp->d_reclen = n;
- if (reply("FIX") == 1)
- ret |= ALTERED;
+ inoinfo(idesc->id_number)->ino_linkcnt--;
+ dirp = newdirp;
+ ret |= ALTERED;
}
chk1:
if (idesc->id_entryno > 1)
@@ -363,6 +363,20 @@ chk1:
dirp->d_reclen = proto.d_reclen;
}
if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
+ if (dirp->d_ino >= maxino) {
+ direrror(idesc->id_number, "BAD INODE NUMBER FOR '..'");
+ /*
+ * If we know parent set it now, otherwise let it
+ * point to the root inode and it will get cleaned
+ * up later if that is not correct.
+ */
+ if (inp->i_parent != 0)
+ dirp->d_ino = inp->i_parent;
+ else
+ dirp->d_ino = UFS_ROOTINO;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
inp->i_dotdot = dirp->d_ino;
if (dirp->d_type != DT_DIR) {
direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
@@ -372,30 +386,60 @@ chk1:
}
goto chk2;
}
- if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
- fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
- pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
- dirp->d_name);
- inp->i_dotdot = (ino_t)-1;
- } else if (dirp->d_reclen < entrysize) {
- fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
- pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
- inp->i_dotdot = (ino_t)-1;
- } else if (inp->i_parent != 0) {
- /*
- * We know the parent, so fix now.
- */
- inp->i_dotdot = inp->i_parent;
- fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ fileerror(inp->i_parent != 0 ? inp->i_parent : idesc->id_number,
+ idesc->id_number, "MISSING '..'");
+ errmsg = "ADD '..' ENTRY";
+ if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) {
+ /* No space to add '..', replace second entry with '..' */
+ if (dirp->d_ino != 0) {
+ pfatal("SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
+ dirp->d_name);
+ errmsg = "REPLACE WITH '..'";
+ }
+ if (reply(errmsg) == 0) {
+ inp->i_dotdot = (ino_t)-1;
+ goto chk2;
+ }
+ if (proto.d_ino == 0) {
+ /* Defer processing until parent known */
+ idesc->id_entryno++;
+ if (debug)
+ printf("(FIX DEFERRED)\n");
+ }
+ inp->i_dotdot = proto.d_ino;
proto.d_reclen = dirp->d_reclen;
memmove(dirp, &proto, (size_t)entrysize);
- if (reply("FIX") == 1)
- ret |= ALTERED;
+ ret |= ALTERED;
+ } else {
+ /* Move over second entry and add '..' entry */
+ if (reply(errmsg) == 0) {
+ inp->i_dotdot = (ino_t)-1;
+ goto chk2;
+ }
+ if (proto.d_ino == 0) {
+ /* Defer processing until parent known */
+ idesc->id_entryno++;
+ if (debug)
+ printf("(FIX DEFERRED)\n");
+ }
+ inp->i_dotdot = proto.d_ino;
+ if (dirp->d_ino == 0) {
+ proto.d_reclen = dirp->d_reclen;
+ memmove(dirp, &proto, (size_t)entrysize);
+ } else {
+ newdirp = (struct direct *)((char *)(dirp) + entrysize);
+ dirp->d_reclen -= entrysize;
+ memmove(newdirp, dirp, dirp->d_reclen);
+ proto.d_reclen = entrysize;
+ memmove(dirp, &proto, (size_t)entrysize);
+ if (dirp->d_ino != 0) {
+ idesc->id_entryno++;
+ inoinfo(dirp->d_ino)->ino_linkcnt--;
+ }
+ dirp = newdirp;
+ }
+ ret |= ALTERED;
}
- idesc->id_entryno++;
- if (dirp->d_ino != 0)
- inoinfo(dirp->d_ino)->ino_linkcnt--;
- return (ret|KEEPON);
chk2:
if (dirp->d_ino == 0)
return (ret|KEEPON);
@@ -419,7 +463,7 @@ chk2:
}
idesc->id_entryno++;
n = 0;
- if (dirp->d_ino > maxino) {
+ if (dirp->d_ino >= maxino) {
fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
n = reply("REMOVE");
} else if (((dirp->d_ino == UFS_WINO && dirp->d_type != DT_WHT) ||
@@ -499,10 +543,12 @@ again:
case DFOUND:
inp = getinoinfo(dirp->d_ino);
if (idesc->id_entryno > 2) {
- if (inp->i_parent == 0)
+ if (inp->i_parent == 0) {
inp->i_parent = idesc->id_number;
- else if ((n = fix_extraneous(inp, idesc)) == 1)
+ check_dirdepth(inp);
+ } else if ((n = fix_extraneous(inp, idesc))) {
break;
+ }
}
/* FALLTHROUGH */
@@ -553,6 +599,7 @@ fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
if ((ckinode(ip.i_dp, &dotdesc) & FOUND))
inp->i_dotdot = dotdesc.id_parent;
irelse(&ip);
+ free(dotdesc.id_name);
}
/*
* We have the previously found old name (inp->i_parent) and the
diff --git a/sbin/fsck_ffs/pass3.c b/sbin/fsck_ffs/pass3.c
index 22cb0393905b..853f5d62382f 100644
--- a/sbin/fsck_ffs/pass3.c
+++ b/sbin/fsck_ffs/pass3.c
@@ -29,14 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char sccsid[] = "@(#)pass3.c 8.2 (Berkeley) 4/27/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <ufs/ufs/dinode.h>
@@ -74,7 +66,7 @@ pass3(void)
if (inp->i_number == UFS_ROOTINO ||
(inp->i_parent != 0 && !S_IS_DUNFOUND(state)))
continue;
- if (state == DCLEAR)
+ if (state == DCLEAR || state == DZLINK)
continue;
/*
* If we are running with soft updates and we come
@@ -100,9 +92,10 @@ pass3(void)
if (linkup(orphan, inp->i_dotdot, NULL)) {
inp->i_parent = inp->i_dotdot = lfdir;
inoinfo(lfdir)->ino_linkcnt--;
+ inoinfo(orphan)->ino_state = DFOUND;
+ check_dirdepth(inp);
+ propagate();
}
- inoinfo(orphan)->ino_state = DFOUND;
- propagate();
continue;
}
pfatal("ORPHANED DIRECTORY LOOP DETECTED I=%lu",
@@ -127,6 +120,7 @@ pass3(void)
}
irelse(&ip);
inoinfo(orphan)->ino_state = DFOUND;
+ check_dirdepth(inp);
propagate();
}
}
diff --git a/sbin/fsck_ffs/pass4.c b/sbin/fsck_ffs/pass4.c
index cfcd36296887..a261051c432a 100644
--- a/sbin/fsck_ffs/pass4.c
+++ b/sbin/fsck_ffs/pass4.c
@@ -29,14 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char sccsid[] = "@(#)pass4.c 8.4 (Berkeley) 4/28/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/stat.h>
diff --git a/sbin/fsck_ffs/pass5.c b/sbin/fsck_ffs/pass5.c
index 324e725929f6..528a6956c4a1 100644
--- a/sbin/fsck_ffs/pass5.c
+++ b/sbin/fsck_ffs/pass5.c
@@ -29,14 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char sccsid[] = "@(#)pass5.c 8.9 (Berkeley) 4/28/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/sysctl.h>
@@ -47,7 +39,6 @@ __FBSDID("$FreeBSD$");
#include <inttypes.h>
#include <limits.h>
#include <string.h>
-#include <libufs.h>
#include "fsck.h"
@@ -116,7 +107,7 @@ pass5(void)
}
}
}
- basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_firstfield);
+ basesize = sizeof(*newcg);
if (sblock.fs_magic == FS_UFS2_MAGIC) {
newcg->cg_iusedoff = basesize;
} else {
@@ -131,7 +122,7 @@ pass5(void)
fs->fs_old_cpg * sizeof(int32_t);
newcg->cg_iusedoff = newcg->cg_old_boff +
fs->fs_old_cpg * fs->fs_old_nrpos * sizeof(u_int16_t);
- memset(&newcg->cg_space[0], 0, newcg->cg_iusedoff - basesize);
+ memset(&newcg[1], 0, newcg->cg_iusedoff - basesize);
}
inomapsize = howmany(fs->fs_ipg, CHAR_BIT);
newcg->cg_freeoff = newcg->cg_iusedoff + inomapsize;
@@ -375,6 +366,22 @@ pass5(void)
if (cursnapshot == 0 &&
memcmp(&cstotal, &fs->fs_cstotal, sizeof cstotal) != 0
&& dofix(&idesc[0], "SUMMARY BLK COUNT(S) WRONG IN SUPERBLK")) {
+ if (debug) {
+ printf("cstotal is currently: %jd dirs, %jd blks free, "
+ "%jd frags free, %jd inos free, %jd clusters\n",
+ (intmax_t)fs->fs_cstotal.cs_ndir,
+ (intmax_t)fs->fs_cstotal.cs_nbfree,
+ (intmax_t)fs->fs_cstotal.cs_nffree,
+ (intmax_t)fs->fs_cstotal.cs_nifree,
+ (intmax_t)fs->fs_cstotal.cs_numclusters);
+ printf("cstotal ought to be: %jd dirs, %jd blks free, "
+ "%jd frags free, %jd inos free, %jd clusters\n",
+ (intmax_t)cstotal.cs_ndir,
+ (intmax_t)cstotal.cs_nbfree,
+ (intmax_t)cstotal.cs_nffree,
+ (intmax_t)cstotal.cs_nifree,
+ (intmax_t)cstotal.cs_numclusters);
+ }
memmove(&fs->fs_cstotal, &cstotal, sizeof cstotal);
fs->fs_ronly = 0;
fs->fs_fmod = 0;
@@ -395,43 +402,53 @@ pass5(void)
printf("adjndir by %+" PRIi64 "\n", cmd.value);
if (bkgrdsumadj == 0 || sysctl(adjndir, MIBSIZE, 0, 0,
&cmd, sizeof cmd) == -1)
- rwerror("ADJUST NUMBER OF DIRECTORIES", cmd.value);
+ rwerror("ADJUST NUMBER OF DIRECTORIES",
+ cmd.value);
}
cmd.value = cstotal.cs_nbfree - fs->fs_cstotal.cs_nbfree;
if (cmd.value != 0) {
if (debug)
- printf("adjnbfree by %+" PRIi64 "\n", cmd.value);
+ printf("adjnbfree by %+" PRIi64 "\n",
+ cmd.value);
if (bkgrdsumadj == 0 || sysctl(adjnbfree, MIBSIZE, 0, 0,
&cmd, sizeof cmd) == -1)
- rwerror("ADJUST NUMBER OF FREE BLOCKS", cmd.value);
+ rwerror("ADJUST NUMBER OF FREE BLOCKS",
+ cmd.value);
}
cmd.value = cstotal.cs_nifree - fs->fs_cstotal.cs_nifree;
if (cmd.value != 0) {
if (debug)
- printf("adjnifree by %+" PRIi64 "\n", cmd.value);
+ printf("adjnifree by %+" PRIi64 "\n",
+ cmd.value);
if (bkgrdsumadj == 0 || sysctl(adjnifree, MIBSIZE, 0, 0,
&cmd, sizeof cmd) == -1)
- rwerror("ADJUST NUMBER OF FREE INODES", cmd.value);
+ rwerror("ADJUST NUMBER OF FREE INODES",
+ cmd.value);
}
cmd.value = cstotal.cs_nffree - fs->fs_cstotal.cs_nffree;
if (cmd.value != 0) {
if (debug)
- printf("adjnffree by %+" PRIi64 "\n", cmd.value);
+ printf("adjnffree by %+" PRIi64 "\n",
+ cmd.value);
if (bkgrdsumadj == 0 || sysctl(adjnffree, MIBSIZE, 0, 0,
&cmd, sizeof cmd) == -1)
- rwerror("ADJUST NUMBER OF FREE FRAGS", cmd.value);
+ rwerror("ADJUST NUMBER OF FREE FRAGS",
+ cmd.value);
}
- cmd.value = cstotal.cs_numclusters - fs->fs_cstotal.cs_numclusters;
+ cmd.value = cstotal.cs_numclusters -
+ fs->fs_cstotal.cs_numclusters;
if (cmd.value != 0) {
if (debug)
- printf("adjnumclusters by %+" PRIi64 "\n", cmd.value);
- if (bkgrdsumadj == 0 || sysctl(adjnumclusters, MIBSIZE, 0, 0,
- &cmd, sizeof cmd) == -1)
- rwerror("ADJUST NUMBER OF FREE CLUSTERS", cmd.value);
+ printf("adjnumclusters by %+" PRIi64 "\n",
+ cmd.value);
+ if (bkgrdsumadj == 0 || sysctl(adjnumclusters, MIBSIZE,
+ 0, 0, &cmd, sizeof cmd) == -1)
+ rwerror("ADJUST NUMBER OF FREE CLUSTERS",
+ cmd.value);
}
}
}
diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c
index cb95d18859b0..f10f02d159c3 100644
--- a/sbin/fsck_ffs/setup.c
+++ b/sbin/fsck_ffs/setup.c
@@ -29,14 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char sccsid[] = "@(#)setup.c 8.10 (Berkeley) 5/9/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/disk.h>
#include <sys/stat.h>
@@ -54,17 +46,23 @@ __FBSDID("$FreeBSD$");
#include <limits.h>
#include <stdint.h>
#include <string.h>
-#include <libufs.h>
#include "fsck.h"
-struct inoinfo **inphead, **inpsort; /* info about all inodes */
+struct inohash *inphash; /* hash list of directory inode info */
+struct inoinfo **inpsort; /* disk order list of directory inodes */
+struct inode snaplist[FSMAXSNAP + 1]; /* list of active snapshots */
+int snapcnt; /* number of active snapshots */
+char *copybuf; /* buffer to copy snapshot blocks */
+static int sbhashfailed;
#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
static int calcsb(char *dev, int devfd, struct fs *fs);
static void saverecovery(int readfd, int writefd);
static int chkrecovery(int devfd);
+static int getlbnblkno(struct inodesc *);
+static int checksnapinfo(struct inode *);
/*
* Read in a superblock finding an alternate if necessary.
@@ -74,39 +72,20 @@ static int chkrecovery(int devfd);
int
setup(char *dev)
{
- long cg, bmapsize;
- struct fs proto;
+ long i, bmapsize;
+ struct inode ip;
/*
- * We are expected to have an open file descriptor
+ * We are expected to have an open file descriptor and a superblock.
*/
- if (fsreadfd < 0)
- return (0);
- /*
- * If we do not yet have a superblock, read it in looking
- * for alternates if necessary.
- */
- if (havesb == 0 && readsb(1) == 0) {
- skipclean = 0;
- if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
- return(0);
- if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0)
- return (0);
- for (cg = 0; cg < proto.fs_ncg; cg++) {
- bflag = fsbtodb(&proto, cgsblock(&proto, cg));
- if (readsb(0) != 0)
- break;
- }
- if (cg >= proto.fs_ncg) {
- printf("SEARCH FOR ALTERNATE SUPER-BLOCK FAILED. "
- "YOU MUST USE THE\n-b OPTION TO FSCK TO SPECIFY "
- "THE LOCATION OF AN ALTERNATE\nSUPER-BLOCK TO "
- "SUPPLY NEEDED INFORMATION; SEE fsck_ffs(8).\n");
- bflag = 0;
- return(0);
+ if (fsreadfd < 0 || havesb == 0) {
+ if (debug) {
+ if (fsreadfd < 0)
+ printf("setup: missing fsreadfd\n");
+ else
+ printf("setup: missing superblock\n");
}
- pwarn("USING ALTERNATE SUPERBLOCK AT %jd\n", bflag);
- bflag = 0;
+ return (0);
}
if (preen == 0)
printf("** %s", dev);
@@ -168,6 +147,7 @@ setup(char *dev)
/*
* allocate and initialize the necessary maps
*/
+ bufinit();
bmapsize = roundup(howmany(maxfsblock, CHAR_BIT), sizeof(short));
blockmap = Calloc((unsigned)bmapsize, sizeof (char));
if (blockmap == NULL) {
@@ -181,22 +161,57 @@ setup(char *dev)
(unsigned)(sizeof(struct inostatlist) * (sblock.fs_ncg)));
goto badsb;
}
- numdirs = MAX(sblock.fs_cstotal.cs_ndir, 128);
- dirhash = numdirs;
+ numdirs = sblock.fs_cstotal.cs_ndir;
+ dirhash = MAX(numdirs / 2, 1);
inplast = 0;
listmax = numdirs + 10;
inpsort = (struct inoinfo **)Calloc(listmax, sizeof(struct inoinfo *));
- inphead = (struct inoinfo **)Calloc(numdirs, sizeof(struct inoinfo *));
- if (inpsort == NULL || inphead == NULL) {
- printf("cannot alloc %ju bytes for inphead\n",
+ inphash = (struct inohash *)Calloc(dirhash, sizeof(struct inohash));
+ if (inpsort == NULL || inphash == NULL) {
+ printf("cannot alloc %ju bytes for inphash\n",
(uintmax_t)numdirs * sizeof(struct inoinfo *));
goto badsb;
}
- bufinit();
if (sblock.fs_flags & FS_DOSOFTDEP)
usedsoftdep = 1;
else
usedsoftdep = 0;
+ /*
+ * Collect any snapshot inodes so that we can allow them to
+ * claim any blocks that we free. The code for doing this is
+ * imported here and into inode.c from sys/ufs/ffs/ffs_snapshot.c.
+ */
+ for (snapcnt = 0; snapcnt < FSMAXSNAP; snapcnt++) {
+ if (sblock.fs_snapinum[snapcnt] == 0)
+ break;
+ ginode(sblock.fs_snapinum[snapcnt], &ip);
+ if ((DIP(ip.i_dp, di_mode) & IFMT) == IFREG &&
+ (DIP(ip.i_dp, di_flags) & SF_SNAPSHOT) != 0 &&
+ checksnapinfo(&ip)) {
+ if (debug)
+ printf("Load snapshot %jd\n",
+ (intmax_t)sblock.fs_snapinum[snapcnt]);
+ snaplist[snapcnt] = ip;
+ continue;
+ }
+ printf("Removing non-snapshot inode %ju from snapshot list\n",
+ (uintmax_t)sblock.fs_snapinum[snapcnt]);
+ irelse(&ip);
+ for (i = snapcnt + 1; i < FSMAXSNAP; i++) {
+ if (sblock.fs_snapinum[i] == 0)
+ break;
+ sblock.fs_snapinum[i - 1] = sblock.fs_snapinum[i];
+ }
+ sblock.fs_snapinum[i - 1] = 0;
+ snapcnt--;
+ sbdirty();
+ }
+ if (snapcnt > 0 && copybuf == NULL) {
+ copybuf = Balloc(sblock.fs_bsize);
+ if (copybuf == NULL)
+ errx(EEXIT, "cannot allocate space for snapshot "
+ "copy buffer");
+ }
return (1);
badsb:
@@ -205,6 +220,146 @@ badsb:
}
/*
+ * Check for valid snapshot information.
+ *
+ * Each snapshot has a list of blocks that have been copied. This list
+ * is consulted before checking the snapshot inode. Its purpose is to
+ * speed checking of commonly checked blocks and to avoid recursive
+ * checks of the snapshot inode. In particular, the list must contain
+ * the superblock, the superblock summary information, and all the
+ * cylinder group blocks. The list may contain other commonly checked
+ * pointers such as those of the blocks that contain the snapshot inodes.
+ * The list is sorted into block order to allow binary search lookup.
+ *
+ * The twelve direct direct block pointers of the snapshot are always
+ * copied, so we test for them first before checking the list itself
+ * (i.e., they are not in the list).
+ *
+ * The checksnapinfo() routine needs to ensure that the list contains at
+ * least the super block, its summary information, and the cylinder groups.
+ * Here we check the list first for the superblock, zero or more cylinder
+ * groups up to the location of the superblock summary information, the
+ * summary group information, and any remaining cylinder group maps that
+ * follow it. We skip over any other entries in the list.
+ */
+#define CHKBLKINLIST(chkblk) \
+ /* All UFS_NDADDR blocks are copied */ \
+ if ((chkblk) >= UFS_NDADDR) { \
+ /* Skip over blocks that are not of interest */ \
+ while (*blkp < (chkblk) && blkp < lastblkp) \
+ blkp++; \
+ /* Fail if end of list and not all blocks found */ \
+ if (blkp >= lastblkp) { \
+ pwarn("UFS%d snapshot inode %jd failed: " \
+ "improper block list length (%jd)\n", \
+ sblock.fs_magic == FS_UFS1_MAGIC ? 1 : 2, \
+ (intmax_t)snapip->i_number, \
+ (intmax_t)(lastblkp - &snapblklist[0])); \
+ status = 0; \
+ } \
+ /* Fail if block we seek is missing */ \
+ else if (*blkp++ != (chkblk)) { \
+ pwarn("UFS%d snapshot inode %jd failed: " \
+ "block list (%jd) != %s (%jd)\n", \
+ sblock.fs_magic == FS_UFS1_MAGIC ? 1 : 2, \
+ (intmax_t)snapip->i_number, \
+ (intmax_t)blkp[-1], #chkblk, \
+ (intmax_t)chkblk); \
+ status = 0; \
+ } \
+ }
+
+static int
+checksnapinfo(struct inode *snapip)
+{
+ struct fs *fs;
+ struct bufarea *bp;
+ struct inodesc idesc;
+ daddr_t *snapblklist, *blkp, *lastblkp, csblkno;
+ int cg, loc, len, status;
+ ufs_lbn_t lbn;
+ size_t size;
+
+ fs = &sblock;
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = getlbnblkno;
+ idesc.id_number = snapip->i_number;
+ lbn = howmany(fs->fs_size, fs->fs_frag);
+ idesc.id_parent = lbn; /* sought after blkno */
+ if ((ckinode(snapip->i_dp, &idesc) & FOUND) == 0)
+ return (0);
+ size = fragroundup(fs,
+ DIP(snapip->i_dp, di_size) - lblktosize(fs, lbn));
+ bp = getdatablk(idesc.id_parent, size, BT_DATA);
+ if (bp->b_errs != 0)
+ return (0);
+ snapblklist = (daddr_t *)bp->b_un.b_buf;
+ /*
+ * snapblklist[0] is the size of the list
+ * snapblklist[1] is the first element of the list
+ *
+ * We need to be careful to bound the size of the list and verify
+ * that we have not run off the end of it if it or its size has
+ * been corrupted.
+ */
+ blkp = &snapblklist[1];
+ lastblkp = &snapblklist[MAX(0,
+ MIN(snapblklist[0] + 1, size / sizeof(daddr_t)))];
+ status = 1;
+ /* Check that the superblock is listed. */
+ CHKBLKINLIST(lblkno(fs, fs->fs_sblockloc));
+ if (status == 0)
+ goto out;
+ /*
+ * Calculate where the summary information is located.
+ * Usually it is in the first cylinder group, but growfs
+ * may move it to the first cylinder group that it adds.
+ *
+ * Check all cylinder groups up to the summary information.
+ */
+ csblkno = fragstoblks(fs, fs->fs_csaddr);
+ for (cg = 0; cg < fs->fs_ncg; cg++) {
+ if (fragstoblks(fs, cgtod(fs, cg)) > csblkno)
+ break;
+ CHKBLKINLIST(fragstoblks(fs, cgtod(fs, cg)));
+ if (status == 0)
+ goto out;
+ }
+ /* Check the summary information block(s). */
+ len = howmany(fs->fs_cssize, fs->fs_bsize);
+ for (loc = 0; loc < len; loc++) {
+ CHKBLKINLIST(csblkno + loc);
+ if (status == 0)
+ goto out;
+ }
+ /* Check the remaining cylinder groups. */
+ for (; cg < fs->fs_ncg; cg++) {
+ CHKBLKINLIST(fragstoblks(fs, cgtod(fs, cg)));
+ if (status == 0)
+ goto out;
+ }
+out:
+ brelse(bp);
+ return (status);
+}
+
+/*
+ * Return the block number associated with a specified inode lbn.
+ * Requested lbn is in id_parent. If found, block is returned in
+ * id_parent.
+ */
+static int
+getlbnblkno(struct inodesc *idesc)
+{
+
+ if (idesc->id_lbn < idesc->id_parent)
+ return (KEEPON);
+ idesc->id_parent = idesc->id_blkno;
+ return (STOP | FOUND);
+}
+
+/*
* Open a device or file to be checked by fsck.
*/
int
@@ -218,14 +373,14 @@ openfilesys(char *dev)
if ((statb.st_mode & S_IFMT) != S_IFCHR &&
(statb.st_mode & S_IFMT) != S_IFBLK) {
if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) {
- pfatal("BACKGROUND FSCK LACKS A SNAPSHOT\n");
- exit(EEXIT);
+ pwarn("BACKGROUND FSCK LACKS A SNAPSHOT\n");
+ return (0);
}
if (bkgrdflag != 0) {
cursnapshot = statb.st_ino;
} else {
- pfatal("%s IS NOT A DISK DEVICE\n", dev);
- if (reply("CONTINUE") == 0)
+ pwarn("%s IS NOT A DISK DEVICE\n", dev);
+ if (preen || reply("CONTINUE") == 0)
return (0);
}
}
@@ -243,40 +398,64 @@ openfilesys(char *dev)
* Read in the super block and its summary info.
*/
int
-readsb(int listerr)
+readsb(void)
{
- off_t super;
- int ret, flags;
struct fs *fs;
- super = bflag ? bflag * dev_bsize : UFS_STDSB;
- flags = sbhashfailed ? UFS_NOHASHFAIL | UFS_NOMSG : UFS_NOMSG;
+ sbhashfailed = 0;
readcnt[sblk.b_type]++;
- while ((ret = sbget(fsreadfd, &fs, super, flags)) != 0) {
- switch (ret) {
+ /*
+ * If bflag is given, then check just that superblock.
+ */
+ if (bflag) {
+ switch (sbget(fsreadfd, &fs, bflag * dev_bsize, 0)) {
+ case 0:
+ goto goodsb;
case EINTEGRITY:
- if (bflag || (super == UFS_STDSB &&
- flags == (UFS_NOHASHFAIL | UFS_NOMSG)))
- return (0);
- super = UFS_STDSB;
- flags = UFS_NOHASHFAIL | UFS_NOMSG;
- sbhashfailed = 1;
- continue;
+ printf("Check hash failed for superblock at %jd\n",
+ bflag);
+ return (0);
case ENOENT:
- if (bflag)
- printf("%jd is not a file system "
- "superblock\n", super / dev_bsize);
- else
- printf("Cannot find file system "
- "superblock\n");
+ printf("%jd is not a file system superblock\n", bflag);
return (0);
case EIO:
default:
- printf("I/O error reading %jd\n",
- super / dev_bsize);
+ printf("I/O error reading %jd\n", bflag);
return (0);
}
}
+ /*
+ * Check for the standard superblock and use it if good.
+ */
+ if (sbget(fsreadfd, &fs, UFS_STDSB, UFS_NOMSG) == 0)
+ goto goodsb;
+ /*
+ * Check if the only problem is a check-hash failure.
+ */
+ skipclean = 0;
+ if (sbget(fsreadfd, &fs, UFS_STDSB, UFS_NOMSG | UFS_NOHASHFAIL) == 0) {
+ sbhashfailed = 1;
+ goto goodsb;
+ }
+ /*
+ * Do an exhaustive search for a usable superblock.
+ */
+ switch (sbsearch(fsreadfd, &fs, 0)) {
+ case 0:
+ goto goodsb;
+ case ENOENT:
+ printf("SEARCH FOR ALTERNATE SUPER-BLOCK FAILED. "
+ "YOU MUST USE THE\n-b OPTION TO FSCK TO SPECIFY "
+ "THE LOCATION OF AN ALTERNATE\nSUPER-BLOCK TO "
+ "SUPPLY NEEDED INFORMATION; SEE fsck_ffs(8).\n");
+ return (0);
+ case EIO:
+ default:
+ printf("I/O error reading a usable superblock\n");
+ return (0);
+ }
+
+goodsb:
memcpy(&sblock, fs, fs->fs_sbsize);
free(fs);
/*
@@ -315,7 +494,7 @@ sblock_init(void)
fsmodified = 0;
lfdir = 0;
initbarea(&sblk, BT_SUPERBLK);
- sblk.b_un.b_buf = Malloc(SBLOCKSIZE);
+ sblk.b_un.b_buf = Balloc(SBLOCKSIZE);
if (sblk.b_un.b_buf == NULL)
errx(EEXIT, "cannot allocate space for superblock");
dev_bsize = secsize = DEV_BSIZE;
@@ -344,7 +523,7 @@ calcsb(char *dev, int devfd, struct fs *fs)
*/
if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1)
return (0);
- fsrbuf = Malloc(secsize);
+ fsrbuf = Balloc(secsize);
if (fsrbuf == NULL)
errx(EEXIT, "calcsb: cannot allocate recovery buffer");
if (blread(devfd, fsrbuf,
@@ -387,7 +566,7 @@ chkrecovery(int devfd)
rdsize = sblock.fs_fsize;
if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1 ||
rdsize % secsize != 0 ||
- (fsrbuf = Malloc(rdsize)) == NULL ||
+ (fsrbuf = Balloc(rdsize)) == NULL ||
blread(devfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize,
rdsize) != 0) {
free(fsrbuf);
@@ -426,7 +605,7 @@ saverecovery(int readfd, int writefd)
if (sblock.fs_magic != FS_UFS2_MAGIC ||
ioctl(readfd, DIOCGSECTORSIZE, &secsize) == -1 ||
rdsize % secsize != 0 ||
- (fsrbuf = Malloc(rdsize)) == NULL ||
+ (fsrbuf = Balloc(rdsize)) == NULL ||
blread(readfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize,
rdsize) != 0) {
printf("RECOVERY DATA COULD NOT BE CREATED\n");
diff --git a/sbin/fsck_ffs/suj.c b/sbin/fsck_ffs/suj.c
index 4ad86b0dc14d..73be60c5e5b4 100644
--- a/sbin/fsck_ffs/suj.c
+++ b/sbin/fsck_ffs/suj.c
@@ -1,5 +1,5 @@
/*-
- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ * SPDX-License-Identifier: BSD-2-Clause
*
* Copyright 2009, 2010 Jeffrey W. Roberson <jeff@FreeBSD.org>
* All rights reserved.
@@ -26,9 +26,6 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
@@ -49,7 +46,6 @@ __FBSDID("$FreeBSD$");
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
-#include <libufs.h>
#include <string.h>
#include <strings.h>
#include <sysexits.h>
@@ -115,6 +111,7 @@ static TAILQ_HEAD(seghd, suj_seg) allsegs;
static uint64_t oldseq;
static struct fs *fs = NULL;
static ino_t sujino;
+static char *joptype[JOP_NUMJOPTYPES] = JOP_NAMES;
/*
* Summary statistics.
@@ -183,7 +180,7 @@ cg_lookup(int cgx)
if (lastcg && lastcg->sc_cgx == cgx)
return (lastcg);
cgbp = cglookup(cgx);
- if (!check_cgmagic(cgx, cgbp, 0))
+ if (!check_cgmagic(cgx, cgbp))
err_suj("UNABLE TO REBUILD CYLINDER GROUP %d", cgx);
hd = &cghash[HASH(cgx)];
LIST_FOREACH(sc, hd, sc_next)
@@ -321,7 +318,7 @@ blk_freemask(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn, int frags)
* To be certain we're not freeing a reallocated block we lookup
* this block in the blk hash and see if there is an allocation
* journal record that overlaps with any fragments in the block
- * we're concerned with. If any fragments have ben reallocated
+ * we're concerned with. If any fragments have been reallocated
* the block has already been freed and re-used for another purpose.
*/
mask = 0;
@@ -379,6 +376,50 @@ blk_isindir(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn)
}
/*
+ * Check to see if the requested block is available.
+ * We can just check in the cylinder-group maps as
+ * they will only have usable blocks in them.
+ */
+ufs2_daddr_t
+suj_checkblkavail(ufs2_daddr_t blkno, long frags)
+{
+ struct bufarea *cgbp;
+ struct cg *cgp;
+ ufs2_daddr_t j, k, baseblk;
+ long cg;
+
+ if ((u_int64_t)blkno > sblock.fs_size)
+ return (0);
+ cg = dtog(&sblock, blkno);
+ cgbp = cglookup(cg);
+ cgp = cgbp->b_un.b_cg;
+ if (!check_cgmagic(cg, cgbp))
+ return (-((cg + 1) * sblock.fs_fpg - sblock.fs_frag));
+ baseblk = dtogd(&sblock, blkno);
+ for (j = 0; j <= sblock.fs_frag - frags; j++) {
+ if (!isset(cg_blksfree(cgp), baseblk + j))
+ continue;
+ for (k = 1; k < frags; k++)
+ if (!isset(cg_blksfree(cgp), baseblk + j + k))
+ break;
+ if (k < frags) {
+ j += k;
+ continue;
+ }
+ for (k = 0; k < frags; k++)
+ clrbit(cg_blksfree(cgp), baseblk + j + k);
+ n_blks += frags;
+ if (frags == sblock.fs_frag)
+ cgp->cg_cs.cs_nbfree--;
+ else
+ cgp->cg_cs.cs_nffree -= frags;
+ cgdirty(cgbp);
+ return ((cg * sblock.fs_fpg) + baseblk + j);
+ }
+ return (0);
+}
+
+/*
* Clear an inode from the cg bitmap. If the inode was already clear return
* 0 so the caller knows it does not have to check the inode contents.
*/
@@ -420,7 +461,7 @@ ino_free(ino_t ino, int mode)
* set in the mask.
*/
static void
-blk_free(ufs2_daddr_t bno, int mask, int frags)
+blk_free(ino_t ino, ufs2_daddr_t bno, int mask, int frags)
{
ufs1_daddr_t fragno, cgbno;
struct suj_cg *sc;
@@ -431,6 +472,13 @@ blk_free(ufs2_daddr_t bno, int mask, int frags)
if (debug)
printf("Freeing %d frags at blk %jd mask 0x%x\n",
frags, bno, mask);
+ /*
+ * Check to see if the block needs to be claimed by a snapshot.
+ * If wanted, the snapshot references it. Otherwise we free it.
+ */
+ if (snapblkfree(fs, bno, lfragtosize(fs, frags), ino,
+ suj_checkblkavail))
+ return;
cg = dtog(fs, bno);
sc = cg_lookup(cg);
cgp = sc->sc_cgp;
@@ -451,7 +499,8 @@ blk_free(ufs2_daddr_t bno, int mask, int frags)
* deallocate the fragment
*/
for (i = 0; i < frags; i++)
- if ((mask & (1 << i)) == 0 && isclr(blksfree, cgbno +i)) {
+ if ((mask & (1 << i)) == 0 &&
+ isclr(blksfree, cgbno +i)) {
freefrags++;
setbit(blksfree, cgbno + i);
}
@@ -685,7 +734,7 @@ indir_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, uint64_t *frags,
lbnadd *= NINDIR(fs);
bp = getdatablk(blk, fs->fs_bsize, BT_LEVEL1 + level);
if (bp->b_errs != 0)
- err_suj("indir_visit: UNRECOVERABLE I/O ERROR");
+ err_suj("indir_visit: UNRECOVERABLE I/O ERROR\n");
for (i = 0; i < NINDIR(fs); i++) {
if ((nblk = IBLK(bp, i)) == 0)
continue;
@@ -846,7 +895,7 @@ static void
blk_free_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
{
- blk_free(blk, blk_freemask(blk, ino, lbn, frags), frags);
+ blk_free(ino, blk, blk_freemask(blk, ino, lbn, frags), frags);
}
/*
@@ -865,7 +914,7 @@ blk_free_lbn(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn, int frags, int follow)
if (lbn <= -UFS_NDADDR && follow && mask == 0)
indir_visit(ino, lbn, blk, &resid, blk_free_visit, VISIT_INDIR);
else
- blk_free(blk, mask, frags);
+ blk_free(ino, blk, mask, frags);
}
static void
@@ -997,6 +1046,8 @@ ino_reclaim(struct inode *ip, ino_t ino, int mode)
if ((DIP(dp, di_mode) & IFMT) == IFDIR)
ino_visit(dp, ino, ino_free_children, 0);
DIP_SET(dp, di_nlink, 0);
+ if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0)
+ snapremove(ino);
ino_visit(dp, ino, blk_free_visit, VISIT_EXT | VISIT_INDIR);
/* Here we have to clear the inode and release any blocks it holds. */
gen = DIP(dp, di_gen);
@@ -1209,7 +1260,7 @@ indir_trunc(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, ufs_lbn_t lastlbn,
continue;
}
isdirty = 1;
- blk_free(nblk, 0, fs->fs_frag);
+ blk_free(ino, nblk, 0, fs->fs_frag);
IBLK_SET(bp, i, 0);
}
if (isdirty)
@@ -1245,9 +1296,14 @@ ino_trunc(ino_t ino, off_t size)
dp = ip.i_dp;
mode = DIP(dp, di_mode) & IFMT;
cursize = DIP(dp, di_size);
+ /* If no size change, nothing to do */
+ if (size == cursize) {
+ irelse(&ip);
+ return;
+ }
if (debug)
- printf("Truncating ino %ju, mode %o to size %jd from size %jd\n",
- (uintmax_t)ino, mode, size, cursize);
+ printf("Truncating ino %ju, mode %o to size %jd from "
+ "size %jd\n", (uintmax_t)ino, mode, size, cursize);
/* Skip datablocks for short links and devices. */
if (mode == 0 || mode == IFBLK || mode == IFCHR ||
@@ -1264,13 +1320,14 @@ ino_trunc(ino_t ino, off_t size)
if (size > 0)
err_suj("Partial truncation of ino %ju snapshot file\n",
(uintmax_t)ino);
+ snapremove(ino);
}
lastlbn = lblkno(fs, blkroundup(fs, size));
for (i = lastlbn; i < UFS_NDADDR; i++) {
if ((bn = DIP(dp, di_db[i])) == 0)
continue;
blksize = sblksize(fs, cursize, i);
- blk_free(bn, 0, numfrags(fs, blksize));
+ blk_free(ino, bn, 0, numfrags(fs, blksize));
DIP_SET(dp, di_db[i], 0);
}
/*
@@ -1283,13 +1340,13 @@ ino_trunc(ino_t ino, off_t size)
/* If we're not freeing any in this indirect range skip it. */
if (lastlbn >= nextlbn)
continue;
- if (DIP(dp, di_ib[i]) == 0)
- continue;
- indir_trunc(ino, -lbn - i, DIP(dp, di_ib[i]), lastlbn, dp);
- /* If we freed everything in this indirect free the indir. */
- if (lastlbn > lbn)
- continue;
- blk_free(DIP(dp, di_ib[i]), 0, fs->fs_frag);
+ if ((bn = DIP(dp, di_ib[i])) == 0)
+ continue;
+ indir_trunc(ino, -lbn - i, bn, lastlbn, dp);
+ /* If we freed everything in this indirect free the indir. */
+ if (lastlbn > lbn)
+ continue;
+ blk_free(ino, bn, 0, fs->fs_frag);
DIP_SET(dp, di_ib[i], 0);
}
/*
@@ -1319,7 +1376,7 @@ ino_trunc(ino_t ino, off_t size)
if (oldspace != newspace) {
bn += numfrags(fs, newspace);
frags = numfrags(fs, oldspace - newspace);
- blk_free(bn, 0, frags);
+ blk_free(ino, bn, 0, frags);
totalfrags -= frags;
}
}
@@ -1386,9 +1443,9 @@ ino_check(struct suj_ino *sino)
err_suj("Inode mode/directory type mismatch %o != %o\n",
mode, rrec->jr_mode);
if (debug)
- printf("jrefrec: op %d ino %ju, nlink %ju, parent %ju, "
+ printf("jrefrec: op %s ino %ju, nlink %ju, parent %ju, "
"diroff %jd, mode %o, isat %d, isdot %d\n",
- rrec->jr_op, (uintmax_t)rrec->jr_ino,
+ JOP_OPTYPE(rrec->jr_op), (uintmax_t)rrec->jr_ino,
(uintmax_t)rrec->jr_nlink,
(uintmax_t)rrec->jr_parent,
(uintmax_t)rrec->jr_diroff,
@@ -1452,9 +1509,10 @@ blk_check(struct suj_blk *sblk)
sino->si_blkadj = 1;
}
if (debug)
- printf("op %d blk %jd ino %ju lbn %jd frags %d isat %d (%d)\n",
- brec->jb_op, blk, (uintmax_t)brec->jb_ino,
- brec->jb_lbn, brec->jb_frags, isat, frags);
+ printf("op %s blk %jd ino %ju lbn %jd frags %d isat %d "
+ "(%d)\n", JOP_OPTYPE(brec->jb_op), blk,
+ (uintmax_t)brec->jb_ino, brec->jb_lbn,
+ brec->jb_frags, isat, frags);
/*
* If we found the block at this address we still have to
* determine if we need to free the tail end that was
@@ -1468,7 +1526,7 @@ blk_check(struct suj_blk *sblk)
mask >>= frags;
blk += frags;
frags = brec->jb_frags - frags;
- blk_free(blk, mask, frags);
+ blk_free(brec->jb_ino, blk, mask, frags);
continue;
}
/*
@@ -1636,9 +1694,9 @@ ino_append(union jrec *rec)
(uintmax_t)mvrec->jm_newoff, (uintmax_t)mvrec->jm_oldoff);
else if (debug &&
(refrec->jr_op == JOP_ADDREF || refrec->jr_op == JOP_REMREF))
- printf("ino ref: op %d, ino %ju, nlink %ju, "
+ printf("ino ref: op %s, ino %ju, nlink %ju, "
"parent %ju, diroff %jd\n",
- refrec->jr_op, (uintmax_t)refrec->jr_ino,
+ JOP_OPTYPE(refrec->jr_op), (uintmax_t)refrec->jr_ino,
(uintmax_t)refrec->jr_nlink,
(uintmax_t)refrec->jr_parent, (uintmax_t)refrec->jr_diroff);
sino = ino_lookup(((struct jrefrec *)rec)->jr_ino, 1);
@@ -1798,8 +1856,8 @@ ino_build_ref(struct suj_ino *sino, struct suj_rec *srec)
TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next);
break;
default:
- err_suj("ino_build_ref: Unknown op %d\n",
- srn->sr_rec->rec_jrefrec.jr_op);
+ err_suj("ino_build_ref: Unknown op %s\n",
+ JOP_OPTYPE(srn->sr_rec->rec_jrefrec.jr_op));
}
}
ino_add_ref(sino, srec);
@@ -1828,8 +1886,8 @@ ino_build(struct suj_ino *sino)
TAILQ_INSERT_TAIL(&sino->si_movs, srec, sr_next);
break;
default:
- err_suj("ino_build: Unknown op %d\n",
- srec->sr_rec->rec_jrefrec.jr_op);
+ err_suj("ino_build: Unknown op %s\n",
+ JOP_OPTYPE(srec->sr_rec->rec_jrefrec.jr_op));
}
}
if (TAILQ_EMPTY(&sino->si_recs))
@@ -1851,14 +1909,17 @@ blk_build(struct jblkrec *blkrec)
int frag;
if (debug)
- printf("blk_build: op %d blkno %jd frags %d oldfrags %d "
+ printf("blk_build: op %s blkno %jd frags %d oldfrags %d "
"ino %ju lbn %jd\n",
- blkrec->jb_op, (uintmax_t)blkrec->jb_blkno,
+ JOP_OPTYPE(blkrec->jb_op), (uintmax_t)blkrec->jb_blkno,
blkrec->jb_frags, blkrec->jb_oldfrags,
(uintmax_t)blkrec->jb_ino, (uintmax_t)blkrec->jb_lbn);
blk = blknum(fs, blkrec->jb_blkno);
frag = fragnum(fs, blkrec->jb_blkno);
+ if (blkrec->jb_blkno < 0 || blk + fs->fs_frag - frag > fs->fs_size)
+ err_suj("Out-of-bounds journal block number %jd\n",
+ blkrec->jb_blkno);
sblk = blk_lookup(blk, 1);
/*
* Rewrite the record using oldfrags to indicate the offset into
@@ -1906,6 +1967,9 @@ ino_build_trunc(struct jtrncrec *rec)
printf("ino_build_trunc: op %d ino %ju, size %jd\n",
rec->jt_op, (uintmax_t)rec->jt_ino,
(uintmax_t)rec->jt_size);
+ if (chkfilesize(IFREG, rec->jt_size) == 0)
+ err_suj("ino_build: truncation size too large %ju\n",
+ (intmax_t)rec->jt_size);
sino = ino_lookup(rec->jt_ino, 1);
if (rec->jt_op == JOP_SYNC) {
sino->si_trunc = NULL;
@@ -1952,8 +2016,8 @@ suj_build(void)
ino_build_trunc((struct jtrncrec *)rec);
break;
default:
- err_suj("Unknown journal operation %d (%d)\n",
- rec->rec_jrefrec.jr_op, off);
+ err_suj("Unknown journal operation %s at %d\n",
+ JOP_OPTYPE(rec->rec_jrefrec.jr_op), off);
}
i++;
}
@@ -2067,7 +2131,9 @@ suj_verifyino(union dinode *dp)
}
if (DIP(dp, di_modrev) != fs->fs_mtime) {
- printf("Journal timestamp does not match fs mount time\n");
+ if (!bkgrdcheck || debug)
+ printf("Journal timestamp does not match "
+ "fs mount time\n");
return (-1);
}
@@ -2194,7 +2260,7 @@ jblocks_add(struct jblocks *jblocks, ufs2_daddr_t daddr, int blocks)
/*
* Add a file block from the journal to the extent map. We can't read
* each file block individually because the kernel treats it as a circular
- * buffer and segments may span mutliple contiguous blocks.
+ * buffer and segments may span multiple contiguous blocks.
*/
static void
suj_add_block(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
@@ -2206,7 +2272,7 @@ suj_add_block(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
static void
suj_read(void)
{
- uint8_t block[1 * 1024 * 1024];
+ uint8_t block[1 * 1024 * 1024] __aligned(LIBUFS_BUFALIGN);
struct suj_seg *seg;
struct jsegrec *recn;
struct jsegrec *rec;
@@ -2270,8 +2336,8 @@ restart:
recsize <= fs->fs_bsize)
goto restart;
if (debug)
- printf("Found invalid segsize %d > %d\n",
- recsize, size);
+ printf("Found invalid segsize "
+ "%d > %d\n", recsize, size);
recsize = real_dev_bsize;
jblocks_advance(suj_jblocks, recsize);
continue;
@@ -2312,7 +2378,7 @@ suj_check(const char *filesys)
{
struct inodesc idesc;
struct csum *cgsum;
- union dinode *jip;
+ union dinode *dp, *jip;
struct inode ip;
uint64_t blocks;
int i, retval;
@@ -2354,11 +2420,23 @@ suj_check(const char *filesys)
idesc.id_func = findino;
idesc.id_name = SUJ_FILE;
ginode(UFS_ROOTINO, &ip);
- if ((ckinode(ip.i_dp, &idesc) & FOUND) == FOUND) {
+ dp = ip.i_dp;
+ if ((DIP(dp, di_mode) & IFMT) != IFDIR) {
+ irelse(&ip);
+ err_suj("root inode is not a directory\n");
+ }
+ if (DIP(dp, di_size) < 0 || DIP(dp, di_size) > MAXDIRSIZE) {
+ irelse(&ip);
+ err_suj("negative or oversized root directory %jd\n",
+ (uintmax_t)DIP(dp, di_size));
+ }
+ if ((ckinode(dp, &idesc) & FOUND) == FOUND) {
sujino = idesc.id_parent;
irelse(&ip);
} else {
- printf("Journal inode removed. Use tunefs to re-create.\n");
+ if (!bkgrdcheck || debug)
+ printf("Journal inode removed. "
+ "Use tunefs to re-create.\n");
sblock.fs_flags &= ~FS_SUJ;
sblock.fs_sujfree = 0;
irelse(&ip);
@@ -2369,7 +2447,8 @@ suj_check(const char *filesys)
*/
ginode(sujino, &ip);
jip = ip.i_dp;
- printf("** SU+J Recovering %s\n", filesys);
+ if (!bkgrdcheck || debug)
+ printf("** SU+J Recovering %s\n", filesys);
if (suj_verifyino(jip) != 0 || (!preen && !reply("USE JOURNAL"))) {
irelse(&ip);
return (-1);
@@ -2378,15 +2457,23 @@ suj_check(const char *filesys)
* Build a list of journal blocks in jblocks before parsing the
* available journal blocks in with suj_read().
*/
- printf("** Reading %jd byte journal from inode %ju.\n",
- DIP(jip, di_size), (uintmax_t)sujino);
+ if (!bkgrdcheck || debug)
+ printf("** Reading %jd byte journal from inode %ju.\n",
+ DIP(jip, di_size), (uintmax_t)sujino);
suj_jblocks = jblocks_create();
blocks = ino_visit(jip, sujino, suj_add_block, 0);
if (blocks != numfrags(fs, DIP(jip, di_size))) {
- printf("Sparse journal inode %ju.\n", (uintmax_t)sujino);
+ if (!bkgrdcheck || debug)
+ printf("Sparse journal inode %ju.\n",
+ (uintmax_t)sujino);
irelse(&ip);
return (-1);
}
+ /* If journal is valid then do journal check rather than background */
+ if (bkgrdcheck) {
+ irelse(&ip);
+ return (0);
+ }
irelse(&ip);
suj_read();
jblocks_destroy(suj_jblocks);
@@ -2404,9 +2491,17 @@ suj_check(const char *filesys)
cg_apply(cg_adj_blk);
cg_apply(cg_check_ino);
}
- if (preen == 0 && (jrecs > 0 || jbytes > 0) && reply("WRITE CHANGES") == 0)
+ if (preen == 0 && (jrecs > 0 || jbytes > 0) &&
+ reply("WRITE CHANGES") == 0)
return (0);
/*
+ * Check block counts of snapshot inodes and
+ * make copies of any needed snapshot blocks.
+ */
+ for (i = 0; i < snapcnt; i++)
+ check_blkcnt(&snaplist[i]);
+ snapflush(suj_checkblkavail);
+ /*
* Recompute the fs summary info from correct cs summaries.
*/
bzero(&fs->fs_cstotal, sizeof(struct csum_total));
@@ -2425,10 +2520,11 @@ suj_check(const char *filesys)
sbdirty();
ckfini(1);
if (jrecs > 0 || jbytes > 0) {
- printf("** %jd journal records in %jd bytes for %.2f%% utilization\n",
- jrecs, jbytes, ((float)jrecs / (float)(jbytes / JREC_SIZE)) * 100);
- printf("** Freed %jd inodes (%jd dirs) %jd blocks, and %jd frags.\n",
- freeinos, freedir, freeblocks, freefrags);
+ printf("** %jd journal records in %jd bytes for %.2f%% "
+ "utilization\n", jrecs, jbytes,
+ ((float)jrecs / (float)(jbytes / JREC_SIZE)) * 100);
+ printf("** Freed %jd inodes (%jd dirs) %jd blocks, and %jd "
+ "frags.\n", freeinos, freedir, freeblocks, freefrags);
}
return (0);
diff --git a/sbin/fsck_ffs/utilities.c b/sbin/fsck_ffs/utilities.c
index 4f56f1ed8c26..56655089b9d6 100644
--- a/sbin/fsck_ffs/utilities.c
+++ b/sbin/fsck_ffs/utilities.c
@@ -29,14 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>