aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKirk McKusick <mckusick@FreeBSD.org>2021-01-07 01:37:08 +0000
committerKirk McKusick <mckusick@FreeBSD.org>2021-01-07 23:03:15 +0000
commit5cc52631b3b88dfc36d8049dc8bece8573c5f9af (patch)
tree6c06ba397fcabfe6a58a2498ce80809cbc31e06b
parent3c5c39c7ad8f010cfa5fc0db43d15d1964b4cf16 (diff)
downloadsrc-5cc52631b3b8.tar.gz
src-5cc52631b3b8.zip
Rewrite the disk I/O management system in fsck_ffs(8). Other than
making fsck_ffs(8) run faster, there should be no functional change. The original fsck_ffs(8) had its own disk I/O management system. When gjournal(8) was added to FreeBSD 7, code was added to fsck_ffs(8) to do the necessary gjournal rollback. Rather than use the existing fsck_ffs(8) disk I/O system, it wrote its own from scratch. Similarly when journalled soft updates were added in FreeBSD 9, code was added to fsck_ffs(8) to do the necessary journal rollback. And once again, rather than using either of the existing fsck_ffs(8) disk I/O systems, it wrote its own from scratch. Lastly the fsdb(8) utility uses the fsck_ffs(8) disk I/O management system. In preparation for making the changes necessary to enable snapshots to be taken when using journalled soft updates, it was necessary to have a single disk I/O system used by all the various subsystems in fsck_ffs(8). This commit merges the functionality required by all the different subsystems into a single disk I/O system that supports all of their needs. In so doing it picks up optimizations from each of them with the results that each of the subsystems does fewer reads and writes than it did with its own customized I/O system. It also greatly simplifies making changes to fsck_ffs(8) since everything goes through a single place. For example the ginode() function fetches an inode from the disk. When inode check hashes were added, they previously had to be checked in the code implementing inode fetch in each of the three different disk I/O systems. Now they need only be checked in ginode(). Tested by: Peter Holm Sponsored by: Netflix
-rw-r--r--sbin/fsck_ffs/dir.c139
-rw-r--r--sbin/fsck_ffs/ea.c1
-rw-r--r--sbin/fsck_ffs/fsck.h81
-rw-r--r--sbin/fsck_ffs/fsutil.c424
-rw-r--r--sbin/fsck_ffs/gjournal.c411
-rw-r--r--sbin/fsck_ffs/globs.c9
-rw-r--r--sbin/fsck_ffs/inode.c368
-rw-r--r--sbin/fsck_ffs/main.c16
-rw-r--r--sbin/fsck_ffs/pass1.c61
-rw-r--r--sbin/fsck_ffs/pass1b.c14
-rw-r--r--sbin/fsck_ffs/pass2.c35
-rw-r--r--sbin/fsck_ffs/pass3.c7
-rw-r--r--sbin/fsck_ffs/pass4.c11
-rw-r--r--sbin/fsck_ffs/setup.c18
-rw-r--r--sbin/fsck_ffs/suj.c914
-rw-r--r--sbin/fsdb/fsdb.c89
-rw-r--r--sbin/fsdb/fsdb.h1
17 files changed, 1215 insertions, 1384 deletions
diff --git a/sbin/fsck_ffs/dir.c b/sbin/fsck_ffs/dir.c
index a86d65a9f183..e88d1650ce5a 100644
--- a/sbin/fsck_ffs/dir.c
+++ b/sbin/fsck_ffs/dir.c
@@ -62,7 +62,7 @@ static struct dirtemplate dirhead = {
static int chgino(struct inodesc *);
static int dircheck(struct inodesc *, struct bufarea *, struct direct *);
-static int expanddir(union dinode *dp, char *name);
+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);
@@ -126,6 +126,8 @@ dirscan(struct inodesc *idesc)
idesc->id_dirp = (struct direct *)dbuf;
if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
bp = getdirblk(idesc->id_blkno, blksiz);
+ if (bp->b_errs != 0)
+ return (STOP);
memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
(size_t)dsize);
dirty(bp);
@@ -155,6 +157,8 @@ fsck_readdir(struct inodesc *idesc)
if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
return (NULL);
bp = getdirblk(idesc->id_blkno, blksiz);
+ if (bp->b_errs != 0)
+ return (NULL);
dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
/*
* Only need to check current entry if it is the first in the
@@ -330,6 +334,7 @@ direrror(ino_t ino, const char *errmesg)
void
fileerror(ino_t cwd, ino_t ino, const char *errmesg)
{
+ struct inode ip;
union dinode *dp;
char pathbuf[MAXPATHLEN + 1];
@@ -338,8 +343,9 @@ fileerror(ino_t cwd, ino_t ino, const char *errmesg)
pfatal("out-of-range inode number %ju", (uintmax_t)ino);
return;
}
- dp = ginode(ino);
- prtinode(ino, dp);
+ ginode(ino, &ip);
+ dp = ip.i_dp;
+ prtinode(&ip);
printf("\n");
getpathname(pathbuf, cwd, ino);
if (ftypeok(dp))
@@ -348,15 +354,18 @@ fileerror(ino_t cwd, ino_t ino, const char *errmesg)
pathbuf);
else
pfatal("NAME=%s\n", pathbuf);
+ irelse(&ip);
}
void
adjust(struct inodesc *idesc, int lcnt)
{
+ struct inode ip;
union dinode *dp;
int saveresolved;
- dp = ginode(idesc->id_number);
+ ginode(idesc->id_number, &ip);
+ dp = ip.i_dp;
if (DIP(dp, di_nlink) == lcnt) {
/*
* If we have not hit any unresolved problems, are running
@@ -365,6 +374,7 @@ adjust(struct inodesc *idesc, int lcnt)
*/
if (resolved && (preen || bkgrdflag) && usedsoftdep) {
clri(idesc, "UNREF", 1);
+ irelse(&ip);
return;
} else {
/*
@@ -377,19 +387,19 @@ adjust(struct inodesc *idesc, int lcnt)
if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) {
resolved = saveresolved;
clri(idesc, "UNREF", 0);
+ irelse(&ip);
return;
}
/*
* Account for the new reference created by linkup().
*/
- dp = ginode(idesc->id_number);
lcnt--;
}
}
if (lcnt != 0) {
pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE"));
- prtinode(idesc->id_number, dp);
+ prtinode(&ip);
printf(" COUNT %d SHOULD BE %d",
DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt);
if (preen || usedsoftdep) {
@@ -403,7 +413,7 @@ adjust(struct inodesc *idesc, int lcnt)
if (preen || reply("ADJUST") == 1) {
if (bkgrdflag == 0) {
DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt);
- inodirty(dp);
+ inodirty(&ip);
} else {
cmd.value = idesc->id_number;
cmd.size = -lcnt;
@@ -417,6 +427,7 @@ adjust(struct inodesc *idesc, int lcnt)
}
}
}
+ irelse(&ip);
}
static int
@@ -460,6 +471,7 @@ chgino(struct inodesc *idesc)
int
linkup(ino_t orphan, ino_t parentdir, char *name)
{
+ struct inode ip;
union dinode *dp;
int lostdir;
ino_t oldlfdir;
@@ -467,29 +479,32 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
char tempname[BUFSIZ];
memset(&idesc, 0, sizeof(struct inodesc));
- dp = ginode(orphan);
+ ginode(orphan, &ip);
+ dp = ip.i_dp;
lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR;
pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
- prtinode(orphan, dp);
+ prtinode(&ip);
printf("\n");
- if (preen && DIP(dp, di_size) == 0)
+ if (preen && DIP(dp, di_size) == 0) {
+ irelse(&ip);
return (0);
+ }
+ irelse(&ip);
if (cursnapshot != 0) {
pfatal("FILE LINKUP IN SNAPSHOT");
return (0);
}
if (preen)
printf(" (RECONNECTED)\n");
- else
- if (reply("RECONNECT") == 0)
- return (0);
+ else if (reply("RECONNECT") == 0)
+ return (0);
if (lfdir == 0) {
- dp = ginode(UFS_ROOTINO);
+ ginode(UFS_ROOTINO, &ip);
idesc.id_name = strdup(lfname);
idesc.id_type = DATA;
idesc.id_func = findino;
idesc.id_number = UFS_ROOTINO;
- if ((ckinode(dp, &idesc) & FOUND) != 0) {
+ if ((ckinode(ip.i_dp, &idesc) & FOUND) != 0) {
lfdir = idesc.id_parent;
} else {
pwarn("NO lost+found DIRECTORY");
@@ -510,42 +525,52 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
}
}
}
+ irelse(&ip);
if (lfdir == 0) {
pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
printf("\n\n");
return (0);
}
}
- dp = ginode(lfdir);
+ ginode(lfdir, &ip);
+ dp = ip.i_dp;
if ((DIP(dp, di_mode) & IFMT) != IFDIR) {
pfatal("lost+found IS NOT A DIRECTORY");
- if (reply("REALLOCATE") == 0)
+ if (reply("REALLOCATE") == 0) {
+ irelse(&ip);
return (0);
+ }
oldlfdir = lfdir;
if ((lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode)) == 0) {
pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+ irelse(&ip);
return (0);
}
if ((changeino(UFS_ROOTINO, lfname, lfdir) & ALTERED) == 0) {
pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+ irelse(&ip);
return (0);
}
- inodirty(dp);
- idesc.id_type = ADDR;
+ idesc.id_type = inoinfo(oldlfdir)->ino_idtype;
idesc.id_func = freeblock;
idesc.id_number = oldlfdir;
adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
inoinfo(oldlfdir)->ino_linkcnt = 0;
- dp = ginode(lfdir);
+ inodirty(&ip);
+ irelse(&ip);
+ ginode(lfdir, &ip);
+ dp = ip.i_dp;
}
if (inoinfo(lfdir)->ino_state != DFOUND) {
pfatal("SORRY. NO lost+found DIRECTORY\n\n");
+ irelse(&ip);
return (0);
}
(void)lftempname(tempname, orphan);
if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
printf("\n\n");
+ irelse(&ip);
return (0);
}
inoinfo(orphan)->ino_linkcnt--;
@@ -553,9 +578,8 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
parentdir != (ino_t)-1)
(void)makeentry(orphan, lfdir, "..");
- dp = ginode(lfdir);
DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
- inodirty(dp);
+ inodirty(&ip);
inoinfo(lfdir)->ino_linkcnt++;
pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan);
if (parentdir != (ino_t)-1) {
@@ -572,6 +596,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
if (preen == 0)
printf("\n");
}
+ irelse(&ip);
return (1);
}
@@ -582,6 +607,8 @@ int
changeino(ino_t dir, const char *name, ino_t newnum)
{
struct inodesc idesc;
+ struct inode ip;
+ int error;
memset(&idesc, 0, sizeof(struct inodesc));
idesc.id_type = DATA;
@@ -590,7 +617,10 @@ changeino(ino_t dir, const char *name, ino_t newnum)
idesc.id_fix = DONTKNOW;
idesc.id_name = strdup(name);
idesc.id_parent = newnum; /* new value for name */
- return (ckinode(ginode(dir), &idesc));
+ ginode(dir, &ip);
+ error = ckinode(ip.i_dp, &idesc);
+ irelse(&ip);
+ return (error);
}
/*
@@ -599,8 +629,10 @@ changeino(ino_t dir, const char *name, ino_t newnum)
int
makeentry(ino_t parent, ino_t ino, const char *name)
{
+ struct inode ip;
union dinode *dp;
struct inodesc idesc;
+ int retval;
char pathbuf[MAXPATHLEN + 1];
if (parent < UFS_ROOTINO || parent >= maxino ||
@@ -613,30 +645,37 @@ makeentry(ino_t parent, ino_t ino, const char *name)
idesc.id_parent = ino; /* this is the inode to enter */
idesc.id_fix = DONTKNOW;
idesc.id_name = strdup(name);
- dp = ginode(parent);
+ ginode(parent, &ip);
+ dp = ip.i_dp;
if (DIP(dp, di_size) % DIRBLKSIZ) {
DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ));
- inodirty(dp);
+ inodirty(&ip);
}
- if ((ckinode(dp, &idesc) & ALTERED) != 0)
+ if ((ckinode(dp, &idesc) & ALTERED) != 0) {
+ irelse(&ip);
return (1);
+ }
getpathname(pathbuf, parent, parent);
- dp = ginode(parent);
- if (expanddir(dp, pathbuf) == 0)
+ if (expanddir(&ip, pathbuf) == 0) {
+ irelse(&ip);
return (0);
- return (ckinode(dp, &idesc) & ALTERED);
+ }
+ retval = ckinode(dp, &idesc) & ALTERED;
+ irelse(&ip);
+ return (retval);
}
/*
* Attempt to expand the size of a directory
*/
static int
-expanddir(union dinode *dp, char *name)
+expanddir(struct inode *ip, char *name)
{
ufs2_daddr_t lastlbn, oldblk, newblk, indirblk;
size_t filesize, lastlbnsize;
struct bufarea *bp, *nbp;
struct inodesc idesc;
+ union dinode *dp;
int indiralloced;
char *cp;
@@ -645,6 +684,7 @@ expanddir(union dinode *dp, char *name)
pwarn("NO SPACE LEFT IN %s", name);
if (!preen && reply("EXPAND") == 0)
return (0);
+ dp = ip->i_dp;
filesize = DIP(dp, di_size);
lastlbn = lblkno(&sblock, filesize);
/*
@@ -671,7 +711,7 @@ expanddir(union dinode *dp, char *name)
DIP_SET(dp, di_size, filesize + sblock.fs_bsize - lastlbnsize);
DIP_SET(dp, di_blocks, DIP(dp, di_blocks) +
btodb(sblock.fs_bsize - lastlbnsize));
- inodirty(dp);
+ inodirty(ip);
memmove(nbp->b_un.b_buf, bp->b_un.b_buf, lastlbnsize);
memset(&nbp->b_un.b_buf[lastlbnsize], 0,
sblock.fs_bsize - lastlbnsize);
@@ -680,10 +720,12 @@ expanddir(union dinode *dp, char *name)
cp += DIRBLKSIZ)
memmove(cp, &emptydir, sizeof emptydir);
dirty(nbp);
- nbp->b_flags &= ~B_INUSE;
+ brelse(nbp);
idesc.id_blkno = oldblk;
idesc.id_numfrags = numfrags(&sblock, lastlbnsize);
(void)freeblock(&idesc);
+ if (preen)
+ printf(" (EXPANDED)\n");
return (1);
}
if ((newblk = allocblk(sblock.fs_frag)) == 0)
@@ -719,18 +761,18 @@ expanddir(union dinode *dp, char *name)
}
IBLK_SET(nbp, lastlbn - UFS_NDADDR, newblk);
dirty(nbp);
- nbp->b_flags &= ~B_INUSE;
+ brelse(nbp);
}
DIP_SET(dp, di_size, filesize + sblock.fs_bsize);
DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize));
- inodirty(dp);
+ inodirty(ip);
if (preen)
printf(" (EXPANDED)\n");
return (1);
bad:
pfatal(" (EXPANSION FAILED)\n");
if (nbp != NULL)
- nbp->b_flags &= ~B_INUSE;
+ brelse(nbp);
if (newblk != 0) {
idesc.id_blkno = newblk;
idesc.id_numfrags = sblock.fs_frag;
@@ -752,6 +794,7 @@ allocdir(ino_t parent, ino_t request, int mode)
{
ino_t ino;
char *cp;
+ struct inode ip;
union dinode *dp;
struct bufarea *bp;
struct inoinfo *inp;
@@ -761,10 +804,12 @@ allocdir(ino_t parent, ino_t request, int mode)
dirp = &dirhead;
dirp->dot_ino = ino;
dirp->dotdot_ino = parent;
- dp = ginode(ino);
+ ginode(ino, &ip);
+ dp = ip.i_dp;
bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize);
if (bp->b_errs) {
freeino(ino);
+ irelse(&ip);
return (0);
}
memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
@@ -774,14 +819,16 @@ allocdir(ino_t parent, ino_t request, int mode)
memmove(cp, &emptydir, sizeof emptydir);
dirty(bp);
DIP_SET(dp, di_nlink, 2);
- inodirty(dp);
+ inodirty(&ip);
if (ino == UFS_ROOTINO) {
inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
cacheino(dp, ino);
+ irelse(&ip);
return(ino);
}
if (!INO_IS_DVALID(parent)) {
freeino(ino);
+ irelse(&ip);
return (0);
}
cacheino(dp, ino);
@@ -793,9 +840,12 @@ allocdir(ino_t parent, ino_t request, int mode)
inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
inoinfo(parent)->ino_linkcnt++;
}
- dp = ginode(parent);
+ irelse(&ip);
+ ginode(parent, &ip);
+ dp = ip.i_dp;
DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
- inodirty(dp);
+ inodirty(&ip);
+ irelse(&ip);
return (ino);
}
@@ -805,12 +855,15 @@ allocdir(ino_t parent, ino_t request, int mode)
static void
freedir(ino_t ino, ino_t parent)
{
+ struct inode ip;
union dinode *dp;
if (ino != parent) {
- dp = ginode(parent);
+ ginode(parent, &ip);
+ dp = ip.i_dp;
DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1);
- inodirty(dp);
+ inodirty(&ip);
+ irelse(&ip);
}
freeino(ino);
}
@@ -847,8 +900,8 @@ static struct bufarea *
getdirblk(ufs2_daddr_t blkno, long size)
{
- if (pdirbp != NULL)
- pdirbp->b_flags &= ~B_INUSE;
+ if (pdirbp != NULL && pdirbp->b_errs == 0)
+ brelse(pdirbp);
pdirbp = getdatablk(blkno, size, BT_DIRDATA);
return (pdirbp);
}
diff --git a/sbin/fsck_ffs/ea.c b/sbin/fsck_ffs/ea.c
index 29e5f46d7651..7cf20196dfae 100644
--- a/sbin/fsck_ffs/ea.c
+++ b/sbin/fsck_ffs/ea.c
@@ -82,6 +82,7 @@ eascan(struct inodesc *idesc, struct ufs2_dinode *dp)
if ((n & 31) == 31)
printf("\n");
}
+ brelse(bp);
return (STOP);
#endif
}
diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h
index a471d1979438..676350b75767 100644
--- a/sbin/fsck_ffs/fsck.h
+++ b/sbin/fsck_ffs/fsck.h
@@ -73,8 +73,7 @@
#define MAXDUP 10 /* limit on dup blks (per inode) */
#define MAXBAD 10 /* limit on bad blks (per inode) */
-#define MINBUFS 10 /* minimum number of buffers required */
-#define MAXBUFS 40 /* maximum space to allocate to buffers */
+#define MINBUFS 100 /* minimum number of buffers required */
#define INOBUFSIZE 64*1024 /* size of buffer to read inodes in pass1 */
#define ZEROBUFSIZE (dev_bsize * 128) /* size of zero buffer used by -Z */
@@ -101,9 +100,10 @@ union dinode {
* have its link count adjusted by the value remaining in ino_linkcnt.
*/
struct inostat {
- char ino_state; /* state of inode, see below */
- char ino_type; /* type of inode */
- short ino_linkcnt; /* number of links not found */
+ u_char ino_state; /* state of inode, see below */
+ u_char ino_type:4; /* type of inode */
+ u_char ino_idtype:4; /* idesc id_type, SNAP or ADDR */
+ u_short ino_linkcnt; /* number of links not found */
};
/*
* Inode states.
@@ -133,15 +133,34 @@ extern struct inostatlist {
} *inostathead;
/*
+ * Structure to reference a dinode.
+ */
+struct inode {
+ struct bufarea *i_bp; /* buffer containing the dinode */
+ union dinode *i_dp; /* pointer to dinode in buffer */
+ ino_t i_number; /* inode number */
+};
+
+/*
+ * Size of hash tables
+ */
+#define HASHSIZE 2048
+#define HASH(x) ((x * 2654435761) & (HASHSIZE - 1))
+
+/*
* buffer cache structure.
*/
struct bufarea {
- TAILQ_ENTRY(bufarea) b_list; /* buffer list */
+ TAILQ_ENTRY(bufarea) b_list; /* LRU buffer queue */
+ LIST_ENTRY(bufarea) b_hash; /* hash list */
ufs2_daddr_t b_bno; /* disk block number */
int b_size; /* size of I/O */
int b_errs; /* I/O error */
int b_flags; /* B_ flags below */
int b_type; /* BT_ type below */
+ int b_refcnt; /* ref count of users */
+ int b_index; /* for BT_LEVEL, ptr index */
+ /* for BT_INODES, first inum */
union {
char *b_buf; /* buffer space */
ufs1_daddr_t *b_indir1; /* UFS1 indirect block */
@@ -151,7 +170,6 @@ struct bufarea {
struct ufs1_dinode *b_dinode1; /* UFS1 inode block */
struct ufs2_dinode *b_dinode2; /* UFS2 inode block */
} b_un;
- char b_dirty;
};
#define IBLK(bp, i) \
@@ -168,7 +186,7 @@ struct bufarea {
/*
* Buffer flags
*/
-#define B_INUSE 0x00000001 /* Buffer is in use */
+#define B_DIRTY 0x00000001 /* Buffer is dirty */
/*
* Type of data in buffer
*/
@@ -182,7 +200,8 @@ 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_NUMBUFTYPES 10
+#define BT_EMPTY 10 /* Buffer allocated but not filled */
+#define BT_NUMBUFTYPES 11
#define BT_NAMES { \
"unknown", \
"Superblock", \
@@ -193,27 +212,33 @@ struct bufarea {
"External Attribute", \
"Inode Block", \
"Directory Contents", \
- "User Data" }
+ "User Data", \
+ "Allocated but not filled" }
+extern char *buftype[];
+#define BT_BUFTYPE(type) \
+ type < BT_NUMBUFTYPES ? buftype[type] : buftype[BT_UNKNOWN]
extern long readcnt[BT_NUMBUFTYPES];
extern long totalreadcnt[BT_NUMBUFTYPES];
extern struct timespec readtime[BT_NUMBUFTYPES];
extern struct timespec totalreadtime[BT_NUMBUFTYPES];
extern struct timespec startprog;
+extern struct bufarea *icachebp; /* inode cache buffer */
extern struct bufarea sblk; /* file system superblock */
extern struct bufarea *pdirbp; /* current directory contents */
-extern struct bufarea *pbp; /* current inode block */
+extern int sujrecovery; /* 1 => doing check using the journal */
#define dirty(bp) do { \
if (fswritefd < 0) \
pfatal("SETTING DIRTY FLAG IN READ_ONLY MODE\n"); \
else \
- (bp)->b_dirty = 1; \
+ (bp)->b_flags |= B_DIRTY; \
} while (0)
#define initbarea(bp, type) do { \
- (bp)->b_dirty = 0; \
(bp)->b_bno = (ufs2_daddr_t)-1; \
(bp)->b_flags = 0; \
+ (bp)->b_refcnt = 0; \
+ (bp)->b_index = 0; \
(bp)->b_type = type; \
} while (0)
@@ -227,6 +252,8 @@ struct inodesc {
enum fixstate id_fix; /* policy on fixing errors */
int (*id_func)(struct inodesc *);
/* function to be applied to blocks of inode */
+ struct bufarea *id_bp; /* ckinode: buffer with indirect pointers */
+ union dinode *id_dp; /* ckinode: dinode being traversed */
ino_t id_number; /* inode number described */
ino_t id_parent; /* for DATA nodes, their parent */
ufs_lbn_t id_lbn; /* logical block number of current block */
@@ -239,7 +266,7 @@ struct inodesc {
int id_loc; /* for DATA nodes, current location in dir */
struct direct *id_dirp; /* for DATA nodes, ptr to current entry */
char *id_name; /* for DATA nodes, name to find or enter */
- char id_type; /* type of descriptor, DATA or ADDR */
+ char id_type; /* type of descriptor, DATA, ADDR, or SNAP */
};
/* file types */
#define DATA 1 /* a directory */
@@ -332,7 +359,6 @@ extern char skipclean; /* skip clean file systems if preening */
extern int fsmodified; /* 1 => write done to file system */
extern int fsreadfd; /* file descriptor for reading file system */
extern int fswritefd; /* file descriptor for writing file system */
-extern struct uufsd disk; /* libufs user-ufs disk structure */
extern int surrender; /* Give up if reads fail */
extern int wantrestart; /* Restart fsck on early termination */
@@ -352,12 +378,11 @@ extern volatile sig_atomic_t got_sigalarm; /* received a SIGALRM */
#define clearinode(dp) \
if (sblock.fs_magic == FS_UFS1_MAGIC) { \
- (dp)->dp1 = ufs1_zino; \
+ (dp)->dp1 = zino.dp1; \
} else { \
- (dp)->dp2 = ufs2_zino; \
+ (dp)->dp2 = zino.dp2; \
}
-extern struct ufs1_dinode ufs1_zino;
-extern struct ufs2_dinode ufs2_zino;
+extern union dinode zino;
#define setbmap(blkno) setbit(blockmap, blkno)
#define testbmap(blkno) isset(blockmap, blkno)
@@ -408,6 +433,7 @@ struct fstab;
void adjust(struct inodesc *, int lcnt);
+void alarmhandler(int sig);
ufs2_daddr_t allocblk(long frags);
ino_t allocdir(ino_t parent, ino_t request, int mode);
ino_t allocino(ino_t request, int type);
@@ -418,12 +444,14 @@ void bufinit(void);
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);
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 check_cgmagic(int cg, struct bufarea *cgbp, int requestrebuild);
int chkrange(ufs2_daddr_t blk, int cnt);
void ckfini(int markclean);
int ckinode(union dinode *dp, struct inodesc *);
@@ -444,16 +472,17 @@ void freeinodebuf(void);
void fsutilinit(void);
int ftypeok(union dinode *dp);
void getblk(struct bufarea *bp, ufs2_daddr_t blk, long size);
-struct bufarea *cglookup(int cg);
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);
void getpathname(char *namebuf, ino_t curdir, ino_t ino);
-union dinode *ginode(ino_t inumber);
+void ginode(ino_t, struct inode *);
void infohandler(int sig);
-void alarmhandler(int sig);
+void irelse(struct inode *);
+ufs2_daddr_t ino_blkatoff(union dinode *, ino_t, ufs_lbn_t, int *,
+ struct bufarea **);
void inocleanup(void);
-void inodirty(union dinode *);
+void inodirty(struct inode *);
struct inostat *inoinfo(ino_t inum);
void IOstats(char *what);
int linkup(ino_t orphan, ino_t parentdir, char *name);
@@ -468,13 +497,13 @@ void pass4(void);
void pass5(void);
void pfatal(const char *fmt, ...) __printflike(1, 2);
void propagate(void);
-void prtinode(ino_t ino, union dinode *dp);
+void prtinode(struct inode *);
void pwarn(const char *fmt, ...) __printflike(1, 2);
int readsb(int listerr);
int reply(const char *question);
void rwerror(const char *mesg, ufs2_daddr_t blk);
void sblock_init(void);
-void setinodebuf(ino_t);
+void setinodebuf(int, ino_t);
int setup(char *dev);
void gjournal_check(const char *filesys);
int suj_check(const char *filesys);
diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c
index 11d2ebd598fd..64c4701d9b7f 100644
--- a/sbin/fsck_ffs/fsutil.c
+++ b/sbin/fsck_ffs/fsutil.c
@@ -64,9 +64,14 @@ __FBSDID("$FreeBSD$");
#include "fsck.h"
+int sujrecovery = 0;
+
+static struct bufarea *allocbuf(const char *);
+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;
@@ -74,12 +79,16 @@ 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 TAILQ_HEAD(buflist, bufarea) bufhead; /* head of buffer cache list */
+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 char *buftype[BT_NUMBUFTYPES] = BT_NAMES;
+static int cachelookups; /* number of cache lookups */
+static int cachereads; /* number of cache reads */
static struct bufarea *cgbufs; /* header for cylinder group cache */
static int flushtries; /* number of tries to reclaim memory */
+char *buftype[BT_NUMBUFTYPES] = BT_NAMES;
+
void
fsutilinit(void)
{
@@ -89,11 +98,6 @@ fsutilinit(void)
bzero(&slowio_starttime, sizeof(struct timeval));
slowio_delay_usec = 10000;
slowio_pollcnt = 0;
- bzero(&cgblk, sizeof(struct bufarea));
- TAILQ_INIT(&bufhead);
- numbufs = 0;
- /* buftype ? */
- cgbufs = NULL;
flushtries = 0;
}
@@ -181,33 +185,19 @@ inoinfo(ino_t inum)
void
bufinit(void)
{
- struct bufarea *bp;
- long bufcnt, i;
- char *bufp;
+ int i;
- pbp = pdirbp = (struct bufarea *)0;
- bufp = Malloc((unsigned int)sblock.fs_bsize);
- if (bufp == NULL)
- errx(EEXIT, "cannot allocate buffer pool");
- cgblk.b_un.b_buf = bufp;
+ pdirbp = (struct bufarea *)0;
+ bzero(&cgblk, sizeof(struct bufarea));
+ cgblk.b_un.b_buf = Malloc((unsigned int)sblock.fs_bsize);
+ if (cgblk.b_un.b_buf == NULL)
+ errx(EEXIT, "Initial malloc(%d) failed", sblock.fs_bsize);
initbarea(&cgblk, BT_CYLGRP);
- TAILQ_INIT(&bufhead);
- bufcnt = MAXBUFS;
- if (bufcnt < MINBUFS)
- bufcnt = MINBUFS;
- for (i = 0; i < bufcnt; i++) {
- bp = (struct bufarea *)Malloc(sizeof(struct bufarea));
- bufp = Malloc((unsigned int)sblock.fs_bsize);
- if (bp == NULL || bufp == NULL) {
- if (i >= MINBUFS)
- break;
- errx(EEXIT, "cannot allocate buffer pool");
- }
- bp->b_un.b_buf = bufp;
- TAILQ_INSERT_HEAD(&bufhead, bp, b_list);
- initbarea(bp, BT_UNKNOWN);
- }
- numbufs = i; /* save number of buffers */
+ cgbufs = NULL;
+ numbufs = cachelookups = cachereads = 0;
+ TAILQ_INIT(&bufqueuehd);
+ for (i = 0; i < HASHSIZE; i++)
+ LIST_INIT(&bufhashhd[i]);
for (i = 0; i < BT_NUMBUFTYPES; i++) {
readtime[i].tv_sec = totalreadtime[i].tv_sec = 0;
readtime[i].tv_nsec = totalreadtime[i].tv_nsec = 0;
@@ -215,6 +205,25 @@ bufinit(void)
}
}
+static struct bufarea *
+allocbuf(const char *failreason)
+{
+ struct bufarea *bp;
+ char *bufp;
+
+ bp = (struct bufarea *)Malloc(sizeof(struct bufarea));
+ bufp = Malloc((unsigned int)sblock.fs_bsize);
+ if (bp == NULL || bufp == NULL) {
+ errx(EEXIT, "%s", failreason);
+ /* NOTREACHED */
+ }
+ numbufs++;
+ bp->b_un.b_buf = bufp;
+ TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list);
+ initbarea(bp, BT_UNKNOWN);
+ return (bp);
+}
+
/*
* Manage cylinder group buffers.
*
@@ -230,18 +239,22 @@ cglookup(int cg)
struct bufarea *cgbp;
struct cg *cgp;
+ 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));
if (cgbufs == NULL)
- errx(EEXIT, "cannot allocate cylinder group buffers");
+ errx(EEXIT, "Cannot allocate cylinder group buffers");
}
cgbp = &cgbufs[cg];
if (cgbp->b_un.b_cg != NULL)
return (cgbp);
cgp = NULL;
if (flushtries == 0)
- cgp = malloc((unsigned int)sblock.fs_cgsize);
+ cgp = Malloc((unsigned int)sblock.fs_cgsize);
if (cgp == NULL) {
+ if (sujrecovery)
+ errx(EEXIT,"Ran out of memory during journal recovery");
getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
return (&cgblk);
}
@@ -278,7 +291,7 @@ flushentry(void)
{
struct bufarea *cgbp;
- if (flushtries == sblock.fs_ncg || cgbufs == NULL)
+ if (sujrecovery || flushtries == sblock.fs_ncg || cgbufs == NULL)
return (0);
cgbp = &cgbufs[flushtries++];
if (cgbp->b_un.b_cg == NULL)
@@ -296,25 +309,84 @@ struct bufarea *
getdatablk(ufs2_daddr_t blkno, long size, int type)
{
struct bufarea *bp;
+ struct bufhash *bhdp;
- TAILQ_FOREACH(bp, &bufhead, b_list)
- if (bp->b_bno == fsbtodb(&sblock, blkno))
+ cachelookups++;
+ /* If out of range, return empty buffer with b_err == -1 */
+ if (type != BT_INODES && chkrange(blkno, size / sblock.fs_fsize)) {
+ blkno = -1;
+ type = BT_EMPTY;
+ }
+ 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);
+ pfatal("getdatablk: b_size %d != size %ld\n",
+ bp->b_size, size);
+ }
goto foundit;
- TAILQ_FOREACH_REVERSE(bp, &bufhead, buflist, b_list)
- if ((bp->b_flags & B_INUSE) == 0)
- break;
- if (bp == NULL)
- errx(EEXIT, "deadlocked buffer pool");
+ }
+ /*
+ * Move long-term busy buffer back to the front of the LRU so we
+ * do not endless inspect them for recycling.
+ */
+ bp = TAILQ_LAST(&bufqueuehd, bufqueue);
+ if (bp != NULL && bp->b_refcnt != 0) {
+ TAILQ_REMOVE(&bufqueuehd, bp, b_list);
+ TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list);
+ }
+ /*
+ * Allocate up to the minimum number of buffers before
+ * considering recycling any of them.
+ */
+ if (size > sblock.fs_bsize)
+ errx(EEXIT, "Excessive buffer size %ld > %d\n", size,
+ sblock.fs_bsize);
+ if (numbufs < MINBUFS) {
+ bp = allocbuf("cannot create minimal buffer pool");
+ } else if (sujrecovery) {
+ /*
+ * SUJ recovery does not want anything written until it
+ * has successfully completed (so it can fail back to
+ * full fsck). Thus, we can only recycle clean buffers.
+ */
+ TAILQ_FOREACH_REVERSE(bp, &bufqueuehd, bufqueue, b_list)
+ if ((bp->b_flags & B_DIRTY) == 0 && bp->b_refcnt == 0)
+ break;
+ if (bp == NULL)
+ bp = allocbuf("Ran out of memory during "
+ "journal recovery");
+ else
+ LIST_REMOVE(bp, b_hash);
+ } else {
+ /*
+ * Recycle oldest non-busy buffer.
+ */
+ TAILQ_FOREACH_REVERSE(bp, &bufqueuehd, bufqueue, b_list)
+ if (bp->b_refcnt == 0)
+ break;
+ if (bp == NULL)
+ bp = allocbuf("Ran out of memory for buffers");
+ else
+ LIST_REMOVE(bp, b_hash);
+ }
+ flush(fswritefd, bp);
bp->b_type = type;
+ LIST_INSERT_HEAD(bhdp, bp, b_hash);
getblk(bp, blkno, size);
+ cachereads++;
/* fall through */
foundit:
- if (debug && bp->b_type != type)
- printf("Buffer type changed from %s to %s\n",
- buftype[bp->b_type], buftype[type]);
- TAILQ_REMOVE(&bufhead, bp, b_list);
- TAILQ_INSERT_HEAD(&bufhead, bp, b_list);
- bp->b_flags |= B_INUSE;
+ if (debug && bp->b_type != type) {
+ printf("getdatablk: buffer type changed to %s",
+ BT_BUFTYPE(type));
+ 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);
}
@@ -328,12 +400,15 @@ getblk(struct bufarea *bp, ufs2_daddr_t blk, long size)
if (bp->b_bno == dblk) {
totalreads++;
} else {
- flush(fswritefd, bp);
if (debug) {
readcnt[bp->b_type]++;
clock_gettime(CLOCK_REALTIME_PRECISE, &start);
}
- bp->b_errs = blread(fsreadfd, bp->b_un.b_buf, dblk, size);
+ if (bp->b_type != BT_EMPTY)
+ bp->b_errs =
+ blread(fsreadfd, bp->b_un.b_buf, dblk, size);
+ else
+ bp->b_errs = -1;
if (debug) {
clock_gettime(CLOCK_REALTIME_PRECISE, &finish);
timespecsub(&finish, &start, &finish);
@@ -346,12 +421,22 @@ getblk(struct bufarea *bp, ufs2_daddr_t blk, long size)
}
void
+brelse(struct bufarea *bp)
+{
+
+ if (bp->b_refcnt <= 0)
+ prtbuf("brelse: buffer with negative reference count", bp);
+ bp->b_refcnt--;
+}
+
+void
flush(int fd, struct bufarea *bp)
{
+ struct inode ip;
- if (!bp->b_dirty)
+ if ((bp->b_flags & B_DIRTY) == 0)
return;
- bp->b_dirty = 0;
+ bp->b_flags &= ~B_DIRTY;
if (fswritefd < 0) {
pfatal("WRITING IN READ_ONLY MODE.\n");
return;
@@ -373,15 +458,90 @@ flush(int fd, struct bufarea *bp)
fsmodified = 1;
break;
case BT_CYLGRP:
+ if (sujrecovery)
+ cg_write(bp);
if (cgput(fswritefd, &sblock, bp->b_un.b_cg) == 0)
fsmodified = 1;
break;
+ case BT_INODES:
+ if (debug && sblock.fs_magic == FS_UFS2_MAGIC) {
+ struct ufs2_dinode *dp = bp->b_un.b_dinode2;
+ int i;
+
+ for (i = 0; i < INOPB(&sblock); dp++, i++) {
+ 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;
+ prtinode(&ip);
+ if (preen || reply("FIX") != 0) {
+ if (preen)
+ printf(" (FIXED)\n");
+ ffs_update_dinode_ckhash(&sblock, dp);
+ inodirty(&ip);
+ }
+ }
+ }
+ /* FALLTHROUGH */
default:
blwrite(fd, bp->b_un.b_buf, bp->b_bno, bp->b_size);
break;
}
}
+/*
+ * 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
+ * writing out the cylinder group.
+ */
+static void
+cg_write(struct bufarea *bp)
+{
+ ufs1_daddr_t fragno, cgbno, maxbno;
+ u_int8_t *blksfree;
+ struct cg *cgp;
+ int blk;
+ int i;
+
+ /*
+ * Fix the frag and cluster summary.
+ */
+ cgp = bp->b_un.b_cg;
+ cgp->cg_cs.cs_nbfree = 0;
+ cgp->cg_cs.cs_nffree = 0;
+ bzero(&cgp->cg_frsum, sizeof(cgp->cg_frsum));
+ maxbno = fragstoblks(&sblock, sblock.fs_fpg);
+ if (sblock.fs_contigsumsize > 0) {
+ for (i = 1; i <= sblock.fs_contigsumsize; i++)
+ cg_clustersum(cgp)[i] = 0;
+ bzero(cg_clustersfree(cgp), howmany(maxbno, CHAR_BIT));
+ }
+ blksfree = cg_blksfree(cgp);
+ for (cgbno = 0; cgbno < maxbno; cgbno++) {
+ if (ffs_isfreeblock(&sblock, blksfree, cgbno))
+ continue;
+ if (ffs_isblock(&sblock, blksfree, cgbno)) {
+ ffs_clusteracct(&sblock, cgp, cgbno, 1);
+ cgp->cg_cs.cs_nbfree++;
+ continue;
+ }
+ fragno = blkstofrags(&sblock, cgbno);
+ blk = blkmap(&sblock, blksfree, fragno);
+ ffs_fragacct(&sblock, blk, cgp->cg_frsum, 1);
+ for (i = 0; i < sblock.fs_frag; i++)
+ if (isset(blksfree, fragno + i))
+ cgp->cg_cs.cs_nffree++;
+ }
+ /*
+ * Update the superblock cg summary from our now correct values
+ * before writing the block.
+ */
+ sblock.fs_cs(&sblock, cgp->cg_cgx) = cgp->cg_cs;
+}
+
void
rwerror(const char *mesg, ufs2_daddr_t blk)
{
@@ -421,28 +581,91 @@ ckfini(int markclean)
}
}
if (debug && totalreads > 0)
- printf("cache with %d buffers missed %ld of %ld (%d%%)\n",
- numbufs, totaldiskreads, totalreads,
- (int)(totaldiskreads * 100 / totalreads));
+ printf("cache with %d buffers missed %d of %d (%d%%)\n",
+ numbufs, cachereads, cachelookups,
+ (int)(cachereads * 100 / cachelookups));
if (fswritefd < 0) {
(void)close(fsreadfd);
return;
}
- 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);
+ /*
+ * To remain idempotent with partial truncations the buffers
+ * must be flushed in this order:
+ * 1) cylinder groups (bitmaps)
+ * 2) indirect, directory, external attribute, and data blocks
+ * 3) inode blocks
+ * 4) superblock
+ * This ordering preserves access to the modified pointers
+ * until they are freed.
+ */
+ /* Step 1: cylinder groups */
+ if (debug)
+ printf("Flush Cylinder groups\n");
+ if (cgbufs != NULL) {
+ for (cnt = 0; cnt < sblock.fs_ncg; cnt++) {
+ if (cgbufs[cnt].b_un.b_cg == NULL)
+ continue;
+ flush(fswritefd, &cgbufs[cnt]);
+ free(cgbufs[cnt].b_un.b_cg);
+ }
+ free(cgbufs);
}
flush(fswritefd, &cgblk);
free(cgblk.b_un.b_buf);
cnt = 0;
- TAILQ_FOREACH_REVERSE_SAFE(bp, &bufhead, buflist, b_list, nbp) {
- TAILQ_REMOVE(&bufhead, bp, b_list);
+ /* Step 2: indirect, directory, external attribute, and data blocks */
+ if (debug)
+ printf("Flush indirect, directory, external attribute, "
+ "and data blocks\n");
+ if (pdirbp != NULL)
+ brelse(pdirbp);
+ TAILQ_FOREACH_REVERSE_SAFE(bp, &bufqueuehd, bufqueue, b_list, nbp) {
+ switch (bp->b_type) {
+ /* These should not be in the buffer cache list */
+ case BT_UNKNOWN:
+ case BT_SUPERBLK:
+ case BT_CYLGRP:
+ default:
+ prtbuf("ckfini: improper buffer type on cache list",bp);
+ 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:
+ case BT_EXTATTR:
+ case BT_DIRDATA:
+ case BT_DATA:
+ break;
+ /* These are the ones to flush in the next step */
+ case BT_INODES:
+ continue;
+ }
+ if (debug && bp->b_refcnt != 0) {
+ prtbuf("ckfini: clearing in-use buffer", bp);
+ pfatal("ckfini: clearing in-use buffer\n");
+ }
+ TAILQ_REMOVE(&bufqueuehd, bp, b_list);
+ cnt++;
+ flush(fswritefd, bp);
+ free(bp->b_un.b_buf);
+ free((char *)bp);
+ }
+ /* Step 3: inode blocks */
+ if (debug)
+ printf("Flush inode blocks\n");
+ if (icachebp != NULL)
+ brelse(icachebp);
+ 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");
+ }
+ TAILQ_REMOVE(&bufqueuehd, bp, b_list);
cnt++;
flush(fswritefd, bp);
free(bp->b_un.b_buf);
@@ -450,16 +673,20 @@ ckfini(int markclean)
}
if (numbufs != cnt)
errx(EEXIT, "panic: lost %d buffers", numbufs - cnt);
- if (cgbufs != NULL) {
- for (cnt = 0; cnt < sblock.fs_ncg; cnt++) {
- if (cgbufs[cnt].b_un.b_cg == NULL)
- continue;
- flush(fswritefd, &cgbufs[cnt]);
- free(cgbufs[cnt].b_un.b_cg);
- }
- free(cgbufs);
+ /* Step 4: superblock */
+ 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);
}
- pbp = pdirbp = (struct bufarea *)0;
+ pdirbp = (struct bufarea *)0;
if (cursnapshot == 0 && sblock.fs_clean != markclean) {
if ((sblock.fs_clean = markclean) != 0) {
sblock.fs_flags &= ~(FS_UNCLEAN | FS_NEEDSFSCK);
@@ -484,6 +711,7 @@ ckfini(int markclean)
rerun = 1;
}
}
+ finalIOstats();
(void)close(fsreadfd);
(void)close(fswritefd);
}
@@ -689,14 +917,24 @@ blzero(int fd, ufs2_daddr_t blk, long size)
* test fails, offer an option to rebuild the whole cylinder group.
*/
int
-check_cgmagic(int cg, struct bufarea *cgbp)
+check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild)
{
struct cg *cgp = cgbp->b_un.b_cg;
+ uint32_t cghash, calchash;
/*
* Extended cylinder group checks.
*/
- if (cg_chkmagic(cgp) &&
+ calchash = cgp->cg_ckhash;
+ if ((sblock.fs_metackhash & CK_CYLGRP) != 0) {
+ cghash = cgp->cg_ckhash;
+ cgp->cg_ckhash = 0;
+ 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 &&
@@ -707,7 +945,9 @@ check_cgmagic(int cg, struct bufarea *cgbp)
cgp->cg_initediblk <= sblock.fs_ipg))) {
return (1);
}
- pfatal("CYLINDER GROUP %d: BAD MAGIC NUMBER", cg);
+ pfatal("CYLINDER GROUP %d: INTEGRITY CHECK FAILED", cg);
+ if (!request_rebuild)
+ return (0);
if (!reply("REBUILD CYLINDER GROUP")) {
printf("YOU WILL NEED TO RERUN FSCK.\n");
rerun = 1;
@@ -780,7 +1020,7 @@ allocblk(long frags)
cg = dtog(&sblock, i + j);
cgbp = cglookup(cg);
cgp = cgbp->b_un.b_cg;
- if (!check_cgmagic(cg, cgbp))
+ if (!check_cgmagic(cg, cgbp, 0))
return (0);
baseblk = dtogd(&sblock, i + j);
for (k = 0; k < frags; k++) {
@@ -844,6 +1084,7 @@ getpathname(char *namebuf, ino_t curdir, ino_t ino)
{
int len;
char *cp;
+ struct inode ip;
struct inodesc idesc;
static int busy = 0;
@@ -869,15 +1110,23 @@ getpathname(char *namebuf, ino_t curdir, ino_t ino)
idesc.id_number = ino;
idesc.id_func = findino;
idesc.id_name = strdup("..");
- if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
+ ginode(ino, &ip);
+ if ((ckinode(ip.i_dp, &idesc) & FOUND) == 0) {
+ irelse(&ip);
break;
+ }
+ irelse(&ip);
namelookup:
idesc.id_number = idesc.id_parent;
idesc.id_parent = ino;
idesc.id_func = findname;
idesc.id_name = namebuf;
- if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
+ ginode(idesc.id_number, &ip);
+ if ((ckinode(ip.i_dp, &idesc) & FOUND) == 0) {
+ irelse(&ip);
break;
+ }
+ irelse(&ip);
len = strlen(namebuf);
cp -= len;
memmove(cp, namebuf, (size_t)len);
@@ -956,6 +1205,19 @@ dofix(struct inodesc *idesc, const char *msg)
#include <stdarg.h>
/*
+ * Print details about a buffer.
+ */
+static void
+prtbuf(const char *msg, struct bufarea *bp)
+{
+
+ printf("%s: bp %p, type %s, bno %ld, size %d, refcnt %d, flags %s, "
+ "index %d\n", msg, bp, BT_BUFTYPE(bp->b_type), bp->b_bno,
+ bp->b_size, bp->b_refcnt, bp->b_flags & B_DIRTY ? "dirty" : "clean",
+ bp->b_index);
+}
+
+/*
* An unexpected inconsistency occurred.
* Die if preening or file system is running with soft dependency protocol,
* otherwise just print message and continue.
diff --git a/sbin/fsck_ffs/gjournal.c b/sbin/fsck_ffs/gjournal.c
index 6e3dbd2da41e..b65589b5bd08 100644
--- a/sbin/fsck_ffs/gjournal.c
+++ b/sbin/fsck_ffs/gjournal.c
@@ -56,354 +56,30 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include <sys/param.h>
-#include <sys/disklabel.h>
-#include <sys/mount.h>
+#include <string.h>
#include <sys/stat.h>
-
-#include <ufs/ufs/ufsmount.h>
-#include <ufs/ufs/dinode.h>
#include <ufs/ffs/fs.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <libufs.h>
-#include <strings.h>
-#include <err.h>
-#include <assert.h>
-
#include "fsck.h"
-struct cgchain {
- union {
- struct cg cgcu_cg;
- char cgcu_buf[MAXBSIZE];
- } cgc_union;
- int cgc_busy;
- int cgc_dirty;
- LIST_ENTRY(cgchain) cgc_next;
-};
-#define cgc_cg cgc_union.cgcu_cg
-
-#define MAX_CACHED_CGS 1024
-static unsigned ncgs = 0;
-static LIST_HEAD(, cgchain) cglist = LIST_HEAD_INITIALIZER(cglist);
-
-static const char *devnam;
-static struct uufsd *diskp = NULL;
-static struct fs *fs = NULL;
-
-static void putcgs(void);
-
-/*
- * Return cylinder group from the cache or load it if it is not in the
- * cache yet.
- * Don't cache more than MAX_CACHED_CGS cylinder groups.
- */
-static struct cgchain *
-getcg(int cg)
-{
- struct cgchain *cgc;
-
- assert(diskp != NULL && fs != NULL);
- LIST_FOREACH(cgc, &cglist, cgc_next) {
- if (cgc->cgc_cg.cg_cgx == cg) {
- //printf("%s: Found cg=%d\n", __func__, cg);
- return (cgc);
- }
- }
- /*
- * Our cache is full? Let's clean it up.
- */
- if (ncgs >= MAX_CACHED_CGS) {
- //printf("%s: Flushing CGs.\n", __func__);
- putcgs();
- }
- cgc = malloc(sizeof(*cgc));
- if (cgc == NULL) {
- /*
- * Cannot allocate memory?
- * Let's put all currently loaded and not busy cylinder groups
- * on disk and try again.
- */
- //printf("%s: No memory, flushing CGs.\n", __func__);
- putcgs();
- cgc = malloc(sizeof(*cgc));
- if (cgc == NULL)
- err(1, "malloc(%zu)", sizeof(*cgc));
- }
- if (cgget(fsreadfd, fs, cg, &cgc->cgc_cg) == -1)
- err(1, "cgget(%d)", cg);
- cgc->cgc_busy = 0;
- cgc->cgc_dirty = 0;
- LIST_INSERT_HEAD(&cglist, cgc, cgc_next);
- ncgs++;
- //printf("%s: Read cg=%d\n", __func__, cg);
- return (cgc);
-}
-
-/*
- * Mark cylinder group as dirty - it will be written back on putcgs().
- */
-static void
-dirtycg(struct cgchain *cgc)
-{
-
- cgc->cgc_dirty = 1;
-}
-
-/*
- * Mark cylinder group as busy - it will not be freed on putcgs().
- */
-static void
-busycg(struct cgchain *cgc)
-{
-
- cgc->cgc_busy = 1;
-}
-
-/*
- * Unmark the given cylinder group as busy.
- */
-static void
-unbusycg(struct cgchain *cgc)
-{
-
- cgc->cgc_busy = 0;
-}
-
-/*
- * Write back all dirty cylinder groups.
- * Free all non-busy cylinder groups.
- */
-static void
-putcgs(void)
-{
- struct cgchain *cgc, *cgc2;
-
- assert(diskp != NULL && fs != NULL);
- LIST_FOREACH_SAFE(cgc, &cglist, cgc_next, cgc2) {
- if (cgc->cgc_busy)
- continue;
- LIST_REMOVE(cgc, cgc_next);
- ncgs--;
- if (cgc->cgc_dirty) {
- if (cgput(fswritefd, fs, &cgc->cgc_cg) == -1)
- err(1, "cgput(%d)", cgc->cgc_cg.cg_cgx);
- //printf("%s: Wrote cg=%d\n", __func__,
- // cgc->cgc_cg.cg_cgx);
- }
- free(cgc);
- }
-}
-
-#if 0
-/*
- * Free all non-busy cylinder groups without storing the dirty ones.
- */
-static void
-cancelcgs(void)
-{
- struct cgchain *cgc;
-
- assert(diskp != NULL && fs != NULL);
- while ((cgc = LIST_FIRST(&cglist)) != NULL) {
- if (cgc->cgc_busy)
- continue;
- LIST_REMOVE(cgc, cgc_next);
- //printf("%s: Canceled cg=%d\n", __func__, cgc->cgc_cg.cg_cgx);
- free(cgc);
- }
-}
-#endif
-
-/*
- * Open the given provider, load superblock.
- */
-static void
-opendisk(void)
-{
- if (diskp != NULL)
- return;
- diskp = &disk;
- if (ufs_disk_fillout(diskp, devnam) == -1) {
- err(1, "ufs_disk_fillout(%s) failed: %s", devnam,
- diskp->d_error);
- }
- fs = &diskp->d_fs;
-}
-
-/*
- * Mark file system as clean, write the super-block back, close the disk.
- */
-static void
-closedisk(void)
-{
-
- fs->fs_clean = 1;
- if (sbwrite(diskp, 0) == -1)
- err(1, "sbwrite(%s)", devnam);
- if (ufs_disk_close(diskp) == -1)
- err(1, "ufs_disk_close(%s)", devnam);
- free(diskp);
- diskp = NULL;
- fs = NULL;
-}
-
-static void
-blkfree(ufs2_daddr_t bno, long size)
-{
- struct cgchain *cgc;
- struct cg *cgp;
- ufs1_daddr_t fragno, cgbno;
- int i, cg, blk, frags, bbase;
- u_int8_t *blksfree;
-
- cg = dtog(fs, bno);
- cgc = getcg(cg);
- dirtycg(cgc);
- cgp = &cgc->cgc_cg;
- cgbno = dtogd(fs, bno);
- blksfree = cg_blksfree(cgp);
- if (size == fs->fs_bsize) {
- fragno = fragstoblks(fs, cgbno);
- if (!ffs_isfreeblock(fs, blksfree, fragno))
- assert(!"blkfree: freeing free block");
- ffs_setblock(fs, blksfree, fragno);
- ffs_clusteracct(fs, cgp, fragno, 1);
- cgp->cg_cs.cs_nbfree++;
- fs->fs_cstotal.cs_nbfree++;
- fs->fs_cs(fs, cg).cs_nbfree++;
- } else {
- bbase = cgbno - fragnum(fs, cgbno);
- /*
- * decrement the counts associated with the old frags
- */
- blk = blkmap(fs, blksfree, bbase);
- ffs_fragacct(fs, blk, cgp->cg_frsum, -1);
- /*
- * deallocate the fragment
- */
- frags = numfrags(fs, size);
- for (i = 0; i < frags; i++) {
- if (isset(blksfree, cgbno + i))
- assert(!"blkfree: freeing free frag");
- setbit(blksfree, cgbno + i);
- }
- cgp->cg_cs.cs_nffree += i;
- fs->fs_cstotal.cs_nffree += i;
- fs->fs_cs(fs, cg).cs_nffree += i;
- /*
- * add back in counts associated with the new frags
- */
- blk = blkmap(fs, blksfree, bbase);
- ffs_fragacct(fs, blk, cgp->cg_frsum, 1);
- /*
- * if a complete block has been reassembled, account for it
- */
- fragno = fragstoblks(fs, bbase);
- if (ffs_isblock(fs, blksfree, fragno)) {
- cgp->cg_cs.cs_nffree -= fs->fs_frag;
- fs->fs_cstotal.cs_nffree -= fs->fs_frag;
- fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag;
- ffs_clusteracct(fs, cgp, fragno, 1);
- cgp->cg_cs.cs_nbfree++;
- fs->fs_cstotal.cs_nbfree++;
- fs->fs_cs(fs, cg).cs_nbfree++;
- }
- }
-}
-
-/*
- * Recursively free all indirect blocks.
- */
-static void
-freeindir(ufs2_daddr_t blk, int level)
-{
- char sblks[MAXBSIZE];
- ufs2_daddr_t *blks;
- int i;
-
- if (bread(diskp, fsbtodb(fs, blk), (void *)&sblks, (size_t)fs->fs_bsize) == -1)
- err(1, "bread: %s", diskp->d_error);
- blks = (ufs2_daddr_t *)&sblks;
- for (i = 0; i < NINDIR(fs); i++) {
- if (blks[i] == 0)
- break;
- if (level == 0)
- blkfree(blks[i], fs->fs_bsize);
- else
- freeindir(blks[i], level - 1);
- }
- blkfree(blk, fs->fs_bsize);
-}
-
-#define dblksize(fs, dino, lbn) \
- ((dino)->di_size >= smalllblktosize(fs, (lbn) + 1) \
- ? (fs)->fs_bsize \
- : fragroundup(fs, blkoff(fs, (dino)->di_size)))
-
-/*
- * Free all blocks associated with the given inode.
- */
-static void
-clear_inode(struct ufs2_dinode *dino)
-{
- ufs2_daddr_t bn;
- int extblocks, i, level;
- off_t osize;
- long bsize;
-
- extblocks = 0;
- if (fs->fs_magic == FS_UFS2_MAGIC && dino->di_extsize > 0)
- extblocks = btodb(fragroundup(fs, dino->di_extsize));
- /* deallocate external attributes blocks */
- if (extblocks > 0) {
- osize = dino->di_extsize;
- dino->di_blocks -= extblocks;
- dino->di_extsize = 0;
- for (i = 0; i < UFS_NXADDR; i++) {
- if (dino->di_extb[i] == 0)
- continue;
- blkfree(dino->di_extb[i], sblksize(fs, osize, i));
- }
- }
-#define SINGLE 0 /* index of single indirect block */
-#define DOUBLE 1 /* index of double indirect block */
-#define TRIPLE 2 /* index of triple indirect block */
- /* deallocate indirect blocks */
- for (level = SINGLE; level <= TRIPLE; level++) {
- if (dino->di_ib[level] == 0)
- break;
- freeindir(dino->di_ib[level], level);
- }
- /* deallocate direct blocks and fragments */
- for (i = 0; i < UFS_NDADDR; i++) {
- bn = dino->di_db[i];
- if (bn == 0)
- continue;
- bsize = dblksize(fs, dino, i);
- blkfree(bn, bsize);
- }
-}
-
void
gjournal_check(const char *filesys)
{
- union dinodep dp;
- struct cgchain *cgc;
+ struct fs *fs;
+ struct inode ip;
+ union dinode *dp;
+ struct bufarea *cgbp;
struct cg *cgp;
+ struct inodesc idesc;
uint8_t *inosused;
ino_t cino, ino;
int cg;
- devnam = filesys;
- opendisk();
+ fs = &sblock;
/* Are there any unreferenced inodes in this file system? */
if (fs->fs_unrefs == 0) {
//printf("No unreferenced inodes.\n");
- closedisk();
+ sbdirty();
+ ckfini(1);
return;
}
@@ -419,77 +95,67 @@ gjournal_check(const char *filesys)
cg * 100 / fs->fs_ncg);
got_sigalarm = 0;
}
- cgc = getcg(cg);
- cgp = &cgc->cgc_cg;
+ cgbp = cglookup(cg);
+ cgp = cgbp->b_un.b_cg;
+ if (!check_cgmagic(cg, cgbp, 0)) {
+ rerun = 1;
+ ckfini(0);
+ return;
+ }
/* 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);
/*
- * We are going to modify this cylinder group, so we want it to
- * be written back.
- */
- dirtycg(cgc);
- /* We don't want it to be freed in the meantime. */
- busycg(cgc);
- inosused = cg_inosused(cgp);
- /*
* Now go through the list of all inodes in this cylinder group
* to find unreferenced ones.
*/
+ inosused = cg_inosused(cgp);
for (cino = 0; cino < fs->fs_ipg; cino++) {
ino = fs->fs_ipg * cg + cino;
/* Unallocated? Skip it. */
if (isclr(inosused, cino))
continue;
- if (getinode(diskp, &dp, ino) == -1)
- err(1, "getinode (cg=%d ino=%ju) %s",
- cg, (uintmax_t)ino, diskp->d_error);
+ ginode(ino, &ip);
+ dp = ip.i_dp;
/* Not a regular file nor directory? Skip it. */
- if (!S_ISREG(dp.dp2->di_mode) &&
- !S_ISDIR(dp.dp2->di_mode))
+ if (!S_ISREG(dp->dp2.di_mode) &&
+ !S_ISDIR(dp->dp2.di_mode)) {
+ irelse(&ip);
continue;
+ }
/* Has reference(s)? Skip it. */
- if (dp.dp2->di_nlink > 0)
+ if (dp->dp2.di_nlink > 0) {
+ irelse(&ip);
continue;
+ }
/* printf("Clearing inode=%d (size=%jd)\n", ino,
- (intmax_t)dp.dp2->di_size); */
- /* Free inode's blocks. */
- clear_inode(dp.dp2);
+ (intmax_t)dp->dp2->di_size); */
/* Deallocate it. */
+ memset(&idesc, 0, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = freeblock;
+ idesc.id_number = ino;
+ clri(&idesc, "UNREF", 1);
clrbit(inosused, cino);
/* Update position of last used inode. */
if (ino < cgp->cg_irotor)
cgp->cg_irotor = ino;
/* Update statistics. */
- cgp->cg_cs.cs_nifree++;
- fs->fs_cs(fs, cg).cs_nifree++;
- fs->fs_cstotal.cs_nifree++;
cgp->cg_unrefs--;
fs->fs_unrefs--;
- /* If this is directory, update related statistics. */
- if (S_ISDIR(dp.dp2->di_mode)) {
- cgp->cg_cs.cs_ndir--;
- fs->fs_cs(fs, cg).cs_ndir--;
- fs->fs_cstotal.cs_ndir--;
- }
/* Zero-fill the inode. */
- *dp.dp2 = ufs2_zino;
+ dp->dp2 = zino.dp2;
/* Write the inode back. */
- if (putinode(diskp) == -1)
- err(1, "putinode (cg=%d ino=%ju) %s",
- cg, (uintmax_t)ino, diskp->d_error);
+ inodirty(&ip);
+ irelse(&ip);
+ cgdirty(cgbp);
if (cgp->cg_unrefs == 0) {
//printf("No more unreferenced inodes in cg=%d.\n", cg);
break;
}
}
/*
- * We don't need this cylinder group anymore, so feel free to
- * free it if needed.
- */
- unbusycg(cgc);
- /*
* If there are no more unreferenced inodes, there is no need to
* check other cylinder groups.
*/
@@ -499,8 +165,7 @@ gjournal_check(const char *filesys)
break;
}
}
- /* Write back modified cylinder groups. */
- putcgs();
/* Write back updated statistics and super-block. */
- closedisk();
+ sbdirty();
+ ckfini(1);
}
diff --git a/sbin/fsck_ffs/globs.c b/sbin/fsck_ffs/globs.c
index 9324133bf544..45d6b80d8fe8 100644
--- a/sbin/fsck_ffs/globs.c
+++ b/sbin/fsck_ffs/globs.c
@@ -56,7 +56,6 @@ struct timespec totalreadtime[BT_NUMBUFTYPES];
struct timespec startprog;
struct bufarea sblk; /* file system superblock */
struct bufarea *pdirbp; /* current directory contents */
-struct bufarea *pbp; /* current inode block */
ino_t cursnapshot;
long dirhash, inplast;
unsigned long numdirs, listmax;
@@ -114,8 +113,7 @@ ufs2_daddr_t n_blks; /* number of blocks in use */
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 */
-struct ufs1_dinode ufs1_zino;
-struct ufs2_dinode ufs2_zino;
+union dinode zino;
struct dups *duplist;
struct dups *muldup;
@@ -131,7 +129,6 @@ fsckinit(void)
bzero(&startprog, sizeof(struct timespec));
bzero(&sblk, sizeof(struct bufarea));
pdirbp = NULL;
- pbp = NULL;
cursnapshot = 0;
listmax = numdirs = dirhash = inplast = 0;
countdirs = 0;
@@ -171,6 +168,6 @@ fsckinit(void)
n_files = 0;
got_siginfo = 0;
got_sigalarm = 0;
- bzero(&ufs1_zino, sizeof(struct ufs1_dinode));
- bzero(&ufs2_zino, sizeof(struct ufs2_dinode));
+ bzero(&zino.dp1, sizeof(struct ufs1_dinode));
+ bzero(&zino.dp2, sizeof(struct ufs2_dinode));
}
diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c
index 2fcc6414918b..18a015f8187e 100644
--- a/sbin/fsck_ffs/inode.c
+++ b/sbin/fsck_ffs/inode.c
@@ -53,15 +53,18 @@ __FBSDID("$FreeBSD$");
#include "fsck.h"
-static ino_t startinum;
+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 **);
int
ckinode(union dinode *dp, struct inodesc *idesc)
{
off_t remsize, sizepb;
int i, offset, ret;
+ struct inode ip;
union dinode dino;
ufs2_daddr_t ndb;
mode_t mode;
@@ -69,6 +72,7 @@ ckinode(union dinode *dp, struct inodesc *idesc)
if (idesc->id_fix != IGNORE)
idesc->id_fix = DONTKNOW;
+ idesc->id_dp = dp;
idesc->id_lbn = -1;
idesc->id_lballoc = -1;
idesc->id_level = 0;
@@ -99,14 +103,14 @@ ckinode(union dinode *dp, struct inodesc *idesc)
pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
pathbuf);
if (reply("ADJUST LENGTH") == 1) {
- dp = ginode(idesc->id_number);
- DIP_SET(dp, di_size,
+ ginode(idesc->id_number, &ip);
+ DIP_SET(ip.i_dp, di_size,
i * sblock.fs_bsize);
printf(
"YOU MUST RERUN FSCK AFTERWARDS\n");
rerun = 1;
- inodirty(dp);
-
+ inodirty(&ip);
+ irelse(&ip);
}
}
continue;
@@ -139,14 +143,15 @@ ckinode(union dinode *dp, struct inodesc *idesc)
pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
pathbuf);
if (reply("ADJUST LENGTH") == 1) {
- dp = ginode(idesc->id_number);
- DIP_SET(dp, di_size,
- DIP(dp, di_size) - remsize);
+ ginode(idesc->id_number, &ip);
+ DIP_SET(ip.i_dp, di_size,
+ DIP(ip.i_dp, di_size) - remsize);
remsize = 0;
printf(
"YOU MUST RERUN FSCK AFTERWARDS\n");
rerun = 1;
- inodirty(dp);
+ inodirty(&ip);
+ irelse(&ip);
break;
}
}
@@ -159,12 +164,12 @@ ckinode(union dinode *dp, struct inodesc *idesc)
static int
iblock(struct inodesc *idesc, off_t isize, int type)
{
+ struct inode ip;
struct bufarea *bp;
int i, n, (*func)(struct inodesc *), nif;
off_t sizepb;
char buf[BUFSIZ];
char pathbuf[MAXPATHLEN + 1];
- union dinode *dp;
if (idesc->id_type != DATA) {
func = idesc->id_func;
@@ -172,9 +177,12 @@ iblock(struct inodesc *idesc, off_t isize, int type)
return (n);
} else
func = dirscan;
- if (chkrange(idesc->id_blkno, idesc->id_numfrags))
- return (SKIP);
bp = getdatablk(idesc->id_blkno, sblock.fs_bsize, type);
+ if (bp->b_errs != 0) {
+ brelse(bp);
+ return (SKIP);
+ }
+ idesc->id_bp = bp;
idesc->id_level--;
for (sizepb = sblock.fs_bsize, i = 0; i < idesc->id_level; i++)
sizepb *= NINDIR(&sblock);
@@ -200,6 +208,7 @@ iblock(struct inodesc *idesc, off_t isize, int type)
for (i = 0; i < nif; i++) {
if (IBLK(bp, i)) {
idesc->id_blkno = IBLK(bp, i);
+ bp->b_index = i;
if (idesc->id_level == 0) {
idesc->id_lbn++;
n = (*func)(idesc);
@@ -208,7 +217,7 @@ iblock(struct inodesc *idesc, off_t isize, int type)
idesc->id_level++;
}
if (n & STOP) {
- bp->b_flags &= ~B_INUSE;
+ brelse(bp);
return (n);
}
} else {
@@ -220,26 +229,142 @@ iblock(struct inodesc *idesc, off_t isize, int type)
pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
pathbuf);
if (reply("ADJUST LENGTH") == 1) {
- dp = ginode(idesc->id_number);
- DIP_SET(dp, di_size,
- DIP(dp, di_size) - isize);
+ ginode(idesc->id_number, &ip);
+ DIP_SET(ip.i_dp, di_size,
+ DIP(ip.i_dp, di_size) - isize);
isize = 0;
printf(
"YOU MUST RERUN FSCK AFTERWARDS\n");
rerun = 1;
- inodirty(dp);
- bp->b_flags &= ~B_INUSE;
+ inodirty(&ip);
+ brelse(bp);
return(STOP);
}
}
}
isize -= sizepb;
}
- bp->b_flags &= ~B_INUSE;
+ brelse(bp);
return (KEEPON);
}
/*
+ * Finds the disk block address at the specified lbn within the inode
+ * specified by dp. This follows the whole tree and honors di_size and
+ * di_extsize so it is a true test of reachability. The lbn may be
+ * negative if an extattr or indirect block is requested.
+ */
+ufs2_daddr_t
+ino_blkatoff(union dinode *dp, ino_t ino, ufs_lbn_t lbn, int *frags,
+ struct bufarea **bpp)
+{
+ ufs_lbn_t tmpval;
+ ufs_lbn_t cur;
+ ufs_lbn_t next;
+ int i;
+
+ *frags = 0;
+ /*
+ * Handle extattr blocks first.
+ */
+ if (lbn < 0 && lbn >= -UFS_NXADDR) {
+ lbn = -1 - lbn;
+ if (lbn > lblkno(&sblock, dp->dp2.di_extsize - 1))
+ return (0);
+ *frags = numfrags(&sblock,
+ sblksize(&sblock, dp->dp2.di_extsize, lbn));
+ return (dp->dp2.di_extb[lbn]);
+ }
+ /*
+ * Now direct and indirect.
+ */
+ if (DIP(dp, di_mode) == IFLNK &&
+ DIP(dp, di_size) < sblock.fs_maxsymlinklen)
+ return (0);
+ if (lbn >= 0 && lbn < UFS_NDADDR) {
+ *frags = numfrags(&sblock,
+ sblksize(&sblock, DIP(dp, di_size), lbn));
+ return (DIP(dp, di_db[lbn]));
+ }
+ *frags = sblock.fs_frag;
+
+ for (i = 0, tmpval = NINDIR(&sblock), cur = UFS_NDADDR; i < UFS_NIADDR;
+ i++, tmpval *= NINDIR(&sblock), cur = next) {
+ next = cur + tmpval;
+ if (lbn == -cur - i)
+ return (DIP(dp, di_ib[i]));
+ /*
+ * Determine whether the lbn in question is within this tree.
+ */
+ if (lbn < 0 && -lbn >= next)
+ continue;
+ if (lbn > 0 && lbn >= next)
+ continue;
+ return (indir_blkatoff(DIP(dp, di_ib[i]), ino, -cur - i, lbn,
+ bpp));
+ }
+ pfatal("lbn %jd not in ino %ju\n", lbn, (uintmax_t)ino);
+ return (0);
+}
+
+/*
+ * Fetch an indirect block to find the block at a given lbn. The lbn
+ * may be negative to fetch a specific indirect block pointer or positive
+ * to fetch a specific block.
+ */
+static ufs2_daddr_t
+indir_blkatoff(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t cur, ufs_lbn_t lbn,
+ struct bufarea **bpp)
+{
+ struct bufarea *bp;
+ ufs_lbn_t lbnadd;
+ ufs_lbn_t base;
+ int i, level;
+
+ if (blk == 0)
+ return (0);
+ level = lbn_level(cur);
+ if (level == -1)
+ pfatal("Invalid indir lbn %jd in ino %ju\n",
+ lbn, (uintmax_t)ino);
+ if (level == 0 && lbn < 0)
+ pfatal("Invalid lbn %jd in ino %ju\n",
+ lbn, (uintmax_t)ino);
+ lbnadd = 1;
+ base = -(cur + level);
+ for (i = level; i > 0; i--)
+ lbnadd *= NINDIR(&sblock);
+ if (lbn > 0)
+ i = (lbn - base) / lbnadd;
+ else
+ i = (-lbn - base) / lbnadd;
+ if (i < 0 || i >= NINDIR(&sblock)) {
+ pfatal("Invalid indirect index %d produced by lbn %jd "
+ "in ino %ju\n", i, lbn, (uintmax_t)ino);
+ return (0);
+ }
+ if (level == 0)
+ cur = base + (i * lbnadd);
+ else
+ cur = -(base + (i * lbnadd)) - (level - 1);
+ bp = getdatablk(blk, sblock.fs_bsize, BT_LEVEL1 + level);
+ if (bp->b_errs != 0)
+ return (0);
+ blk = IBLK(bp, i);
+ bp->b_index = i;
+ if (bpp != NULL)
+ *bpp = bp;
+ else
+ brelse(bp);
+ if (cur == lbn)
+ return (blk);
+ if (level == 0)
+ pfatal("Invalid lbn %jd at level 0 for ino %ju\n", lbn,
+ (uintmax_t)ino);
+ return (indir_blkatoff(blk, ino, cur, lbn, bpp));
+}
+
+/*
* Check that a block in a legal block number.
* Return 0 if in range, 1 if out of range.
*/
@@ -287,39 +412,68 @@ chkrange(ufs2_daddr_t blk, int cnt)
/*
* General purpose interface for reading inodes.
*/
-union dinode *
-ginode(ino_t inumber)
+void
+ginode(ino_t inumber, struct inode *ip)
{
ufs2_daddr_t iblk;
- union dinode *dp;
+ static ino_t startinum = -1;
if (inumber < UFS_ROOTINO || inumber > maxino)
errx(EEXIT, "bad inode number %ju to ginode",
(uintmax_t)inumber);
- if (startinum == 0 ||
- inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
+ ip->i_number = inumber;
+ if (startinum != -1 &&
+ inumber >= startinum && inumber < startinum + INOPB(&sblock)) {
+ /* take an additional reference for the returned inode */
+ icachebp->b_refcnt++;
+ } else {
iblk = ino_to_fsba(&sblock, inumber);
- if (pbp != NULL)
- pbp->b_flags &= ~B_INUSE;
- pbp = getdatablk(iblk, sblock.fs_bsize, BT_INODES);
+ /* release our cache-hold reference on old icachebp */
+ if (icachebp != NULL)
+ brelse(icachebp);
+ icachebp = getdatablk(iblk, sblock.fs_bsize, BT_INODES);
+ if (icachebp->b_errs != 0) {
+ ip->i_bp = NULL;
+ ip->i_dp = &zino;
+ return;
+ }
startinum = rounddown(inumber, INOPB(&sblock));
+ /* take a cache-hold reference on new icachebp */
+ icachebp->b_refcnt++;
+ icachebp->b_index = startinum;
}
- if (sblock.fs_magic == FS_UFS1_MAGIC)
- return ((union dinode *)
- &pbp->b_un.b_dinode1[inumber % INOPB(&sblock)]);
- dp = (union dinode *)&pbp->b_un.b_dinode2[inumber % INOPB(&sblock)];
- if (ffs_verify_dinode_ckhash(&sblock, (struct ufs2_dinode *)dp) != 0) {
+ ip->i_bp = icachebp;
+ if (sblock.fs_magic == FS_UFS1_MAGIC) {
+ ip->i_dp = (union dinode *)
+ &icachebp->b_un.b_dinode1[inumber % INOPB(&sblock)];
+ 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)) {
pwarn("INODE CHECK-HASH FAILED");
- prtinode(inumber, dp);
+ prtinode(ip);
if (preen || reply("FIX") != 0) {
if (preen)
printf(" (FIXED)\n");
ffs_update_dinode_ckhash(&sblock,
- (struct ufs2_dinode *)dp);
- inodirty(dp);
+ (struct ufs2_dinode *)ip->i_dp);
+ inodirty(ip);
}
}
- return (dp);
+}
+
+/*
+ * Release a held inode.
+ */
+void
+irelse(struct inode *ip)
+{
+
+ if (ip->i_bp->b_refcnt <= 0)
+ pfatal("irelse: releasing unreferenced ino %ju\n",
+ (uintmax_t) ip->i_number);
+ brelse(ip->i_bp);
}
/*
@@ -338,6 +492,7 @@ getnextinode(ino_t inumber, int rebuildcg)
mode_t mode;
ufs2_daddr_t ndb, blk;
union dinode *dp;
+ struct inode ip;
static caddr_t nextinop;
if (inumber != nextino++ || inumber > lastvalidinum)
@@ -373,7 +528,10 @@ getnextinode(ino_t inumber, int rebuildcg)
}
if (ffs_verify_dinode_ckhash(&sblock, (struct ufs2_dinode *)dp) != 0) {
pwarn("INODE CHECK-HASH FAILED");
- prtinode(inumber, dp);
+ ip.i_bp = NULL;
+ ip.i_dp = dp;
+ ip.i_number = inumber;
+ prtinode(&ip);
if (preen || reply("FIX") != 0) {
if (preen)
printf(" (FIXED)\n");
@@ -389,9 +547,9 @@ getnextinode(ino_t inumber, int rebuildcg)
*/
mode = DIP(dp, di_mode) & IFMT;
if (mode == 0) {
- if (memcmp(dp->dp2.di_db, ufs2_zino.di_db,
+ if (memcmp(dp->dp2.di_db, zino.dp2.di_db,
UFS_NDADDR * sizeof(ufs2_daddr_t)) ||
- memcmp(dp->dp2.di_ib, ufs2_zino.di_ib,
+ memcmp(dp->dp2.di_ib, zino.dp2.di_ib,
UFS_NIADDR * sizeof(ufs2_daddr_t)) ||
dp->dp2.di_mode || dp->dp2.di_size)
return (NULL);
@@ -433,24 +591,26 @@ getnextinode(ino_t inumber, int rebuildcg)
}
void
-setinodebuf(ino_t inum)
+setinodebuf(int cg, ino_t inosused)
{
+ ino_t inum;
- if (inum % sblock.fs_ipg != 0)
- errx(EEXIT, "bad inode number %ju to setinodebuf",
- (uintmax_t)inum);
- lastvalidinum = inum + sblock.fs_ipg - 1;
- startinum = 0;
+ inum = cg * sblock.fs_ipg;
+ lastvalidinum = inum + inosused - 1;
nextino = inum;
lastinum = inum;
readcount = 0;
- if (inobuf.b_un.b_buf != NULL)
- return;
- inobufsize = blkroundup(&sblock, INOBUFSIZE);
+ if (inobuf.b_un.b_buf == NULL) {
+ inobufsize = blkroundup(&sblock,
+ MAX(INOBUFSIZE, sblock.fs_bsize));
+ initbarea(&inobuf, BT_INODES);
+ if ((inobuf.b_un.b_buf = Malloc((unsigned)inobufsize)) == NULL)
+ errx(EEXIT, "cannot allocate space for inode buffer");
+ }
fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ?
sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
- readpercg = sblock.fs_ipg / fullcnt;
- partialcnt = sblock.fs_ipg % fullcnt;
+ readpercg = inosused / fullcnt;
+ partialcnt = inosused % fullcnt;
partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ?
sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
if (partialcnt != 0) {
@@ -459,9 +619,37 @@ setinodebuf(ino_t inum)
partialcnt = fullcnt;
partialsize = inobufsize;
}
- initbarea(&inobuf, BT_INODES);
- if ((inobuf.b_un.b_buf = Malloc((unsigned)inobufsize)) == NULL)
- errx(EEXIT, "cannot allocate space for inode buffer");
+}
+
+int
+freeblock(struct inodesc *idesc)
+{
+ struct dups *dlp;
+ ufs2_daddr_t blkno;
+ long nfrags, res;
+
+ res = KEEPON;
+ blkno = idesc->id_blkno;
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (testbmap(blkno)) {
+ for (dlp = duplist; dlp; dlp = dlp->next) {
+ if (dlp->dup != blkno)
+ continue;
+ dlp->dup = duplist->dup;
+ dlp = duplist;
+ duplist = duplist->next;
+ free((char *)dlp);
+ break;
+ }
+ if (dlp == NULL) {
+ clrbmap(blkno);
+ n_blks--;
+ }
+ }
+ }
+ return (res);
}
void
@@ -558,24 +746,27 @@ inocleanup(void)
}
void
-inodirty(union dinode *dp)
+inodirty(struct inode *ip)
{
if (sblock.fs_magic == FS_UFS2_MAGIC)
- ffs_update_dinode_ckhash(&sblock, (struct ufs2_dinode *)dp);
- dirty(pbp);
+ ffs_update_dinode_ckhash(&sblock,
+ (struct ufs2_dinode *)ip->i_dp);
+ dirty(ip->i_bp);
}
void
clri(struct inodesc *idesc, const char *type, int flag)
{
union dinode *dp;
+ struct inode ip;
- dp = ginode(idesc->id_number);
+ ginode(idesc->id_number, &ip);
+ dp = ip.i_dp;
if (flag == 1) {
pwarn("%s %s", type,
(DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE");
- prtinode(idesc->id_number, dp);
+ prtinode(&ip);
printf("\n");
}
if (preen || reply("CLEAR") == 1) {
@@ -586,7 +777,7 @@ clri(struct inodesc *idesc, const char *type, int flag)
(void)ckinode(dp, idesc);
inoinfo(idesc->id_number)->ino_state = USTATE;
clearinode(dp);
- inodirty(dp);
+ inodirty(&ip);
} else {
cmd.value = idesc->id_number;
cmd.size = -DIP(dp, di_nlink);
@@ -598,6 +789,7 @@ clri(struct inodesc *idesc, const char *type, int flag)
rwerror("ADJUST INODE", cmd.value);
}
}
+ irelse(&ip);
}
int
@@ -641,46 +833,17 @@ clearentry(struct inodesc *idesc)
return (STOP|FOUND|ALTERED);
}
-int
-freeblock(struct inodesc *idesc)
-{
- struct dups *dlp;
- ufs2_daddr_t blkno;
- long nfrags, res;
-
- res = KEEPON;
- blkno = idesc->id_blkno;
- for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
- if (chkrange(blkno, 1)) {
- res = SKIP;
- } else if (testbmap(blkno)) {
- for (dlp = duplist; dlp; dlp = dlp->next) {
- if (dlp->dup != blkno)
- continue;
- dlp->dup = duplist->dup;
- dlp = duplist;
- duplist = duplist->next;
- free((char *)dlp);
- break;
- }
- if (dlp == NULL) {
- clrbmap(blkno);
- n_blks--;
- }
- }
- }
- return (res);
-}
-
void
-prtinode(ino_t ino, union dinode *dp)
+prtinode(struct inode *ip)
{
char *p;
+ union dinode *dp;
struct passwd *pw;
time_t t;
- printf(" I=%lu ", (u_long)ino);
- if (ino < UFS_ROOTINO || ino > maxino)
+ dp = ip->i_dp;
+ printf(" I=%lu ", (u_long)ip->i_number);
+ if (ip->i_number < UFS_ROOTINO || ip->i_number > maxino)
return;
printf(" OWNER=");
if ((pw = getpwuid((int)DIP(dp, di_uid))) != NULL)
@@ -731,6 +894,7 @@ ino_t
allocino(ino_t request, int type)
{
ino_t ino;
+ struct inode ip;
union dinode *dp;
struct bufarea *cgbp;
struct cg *cgp;
@@ -748,7 +912,7 @@ allocino(ino_t request, int type)
cg = ino_to_cg(&sblock, ino);
cgbp = cglookup(cg);
cgp = cgbp->b_un.b_cg;
- if (!check_cgmagic(cg, cgbp))
+ if (!check_cgmagic(cg, cgbp, 0))
return (0);
setbit(cg_inosused(cgp), ino % sblock.fs_ipg);
cgp->cg_cs.cs_nifree--;
@@ -765,10 +929,12 @@ allocino(ino_t request, int type)
return (0);
}
cgdirty(cgbp);
- dp = ginode(ino);
+ ginode(ino, &ip);
+ dp = ip.i_dp;
DIP_SET(dp, di_db[0], allocblk((long)1));
if (DIP(dp, di_db[0]) == 0) {
inoinfo(ino)->ino_state = USTATE;
+ irelse(&ip);
return (0);
}
DIP_SET(dp, di_mode, type);
@@ -782,7 +948,8 @@ allocino(ino_t request, int type)
DIP_SET(dp, di_size, sblock.fs_fsize);
DIP_SET(dp, di_blocks, btodb(sblock.fs_fsize));
n_files++;
- inodirty(dp);
+ inodirty(&ip);
+ irelse(&ip);
inoinfo(ino)->ino_type = IFTODT(type);
return (ino);
}
@@ -795,15 +962,18 @@ freeino(ino_t ino)
{
struct inodesc idesc;
union dinode *dp;
+ struct inode ip;
memset(&idesc, 0, sizeof(struct inodesc));
- idesc.id_type = ADDR;
+ idesc.id_type = inoinfo(ino)->ino_idtype;
idesc.id_func = freeblock;
idesc.id_number = ino;
- dp = ginode(ino);
+ ginode(ino, &ip);
+ dp = ip.i_dp;
(void)ckinode(dp, &idesc);
clearinode(dp);
- inodirty(dp);
+ inodirty(&ip);
+ irelse(&ip);
inoinfo(ino)->ino_state = USTATE;
n_files--;
}
diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c
index 6a67bbe3feb1..65cee9b7b8c6 100644
--- a/sbin/fsck_ffs/main.c
+++ b/sbin/fsck_ffs/main.c
@@ -70,7 +70,7 @@ __FBSDID("$FreeBSD$");
#include "fsck.h"
-int restarts;
+static int restarts;
static void usage(void) __dead2;
static intmax_t argtoimax(int flag, const char *req, const char *str, int base);
@@ -296,7 +296,12 @@ checkfilesys(char *filesys)
*/
if ((fsreadfd = open(filesys, O_RDONLY)) < 0 || readsb(0) == 0)
exit(3); /* Cannot read superblock */
- close(fsreadfd);
+ if (nflag || (fswritefd = open(filesys, O_WRONLY)) < 0) {
+ fswritefd = -1;
+ if (preen)
+ pfatal("NO WRITE ACCESS");
+ printf(" (NO WRITE)");
+ }
if ((sblock.fs_flags & FS_GJOURNAL) != 0) {
//printf("GJournaled file system detected on %s.\n",
// filesys);
@@ -312,6 +317,8 @@ checkfilesys(char *filesys)
} else {
pfatal(
"UNEXPECTED INCONSISTENCY, CANNOT RUN FAST FSCK\n");
+ close(fsreadfd);
+ close(fswritefd);
}
}
}
@@ -424,12 +431,14 @@ checkfilesys(char *filesys)
*/
if ((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)
exit(0);
exit(4);
}
+ sujrecovery = 0;
printf("** Skipping journal, falling through to full fsck\n\n");
}
/*
@@ -601,7 +610,7 @@ checkfilesys(char *filesys)
sblock.fs_time = time(NULL);
sbdirty();
}
- if (cvtlevel && sblk.b_dirty) {
+ if (cvtlevel && (sblk.b_flags & B_DIRTY) != 0) {
/*
* Write out the duplicate super blocks
*/
@@ -610,7 +619,6 @@ checkfilesys(char *filesys)
}
if (rerun)
resolved = 0;
- finalIOstats();
/*
* Check to see if the file system is mounted read-write.
diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c
index 0e9a232c76fb..c1f1b1ec04f5 100644
--- a/sbin/fsck_ffs/pass1.c
+++ b/sbin/fsck_ffs/pass1.c
@@ -97,19 +97,18 @@ pass1(void)
n_files = n_blks = 0;
for (c = 0; c < sblock.fs_ncg; c++) {
inumber = c * sblock.fs_ipg;
- setinodebuf(inumber);
cgbp = cglookup(c);
cgp = cgbp->b_un.b_cg;
rebuildcg = 0;
- if (!check_cgmagic(c, cgbp))
+ if (!check_cgmagic(c, cgbp, 1))
rebuildcg = 1;
if (!rebuildcg && sblock.fs_magic == FS_UFS2_MAGIC) {
inosused = cgp->cg_initediblk;
if (inosused > sblock.fs_ipg) {
- pfatal(
-"Too many initialized inodes (%ju > %d) in cylinder group %d\nReset to %d\n",
- (uintmax_t)inosused,
- sblock.fs_ipg, c, sblock.fs_ipg);
+ pfatal("Too many initialized inodes (%ju > %d) "
+ "in cylinder group %d\nReset to %d\n",
+ (uintmax_t)inosused, sblock.fs_ipg, c,
+ sblock.fs_ipg);
inosused = sblock.fs_ipg;
}
} else {
@@ -167,6 +166,7 @@ pass1(void)
/*
* Scan the allocated inodes.
*/
+ setinodebuf(c, inosused);
for (i = 0; i < inosused; i++, inumber++) {
if (inumber < UFS_ROOTINO) {
(void)getnextinode(inumber, rebuildcg);
@@ -243,6 +243,7 @@ pass1(void)
static int
checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
{
+ struct inode ip;
union dinode *dp;
off_t kernmaxfilesize;
ufs2_daddr_t ndb;
@@ -255,23 +256,24 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
mode = DIP(dp, di_mode) & IFMT;
if (mode == 0) {
if ((sblock.fs_magic == FS_UFS1_MAGIC &&
- (memcmp(dp->dp1.di_db, ufs1_zino.di_db,
+ (memcmp(dp->dp1.di_db, zino.dp1.di_db,
UFS_NDADDR * sizeof(ufs1_daddr_t)) ||
- memcmp(dp->dp1.di_ib, ufs1_zino.di_ib,
+ memcmp(dp->dp1.di_ib, zino.dp1.di_ib,
UFS_NIADDR * sizeof(ufs1_daddr_t)) ||
dp->dp1.di_mode || dp->dp1.di_size)) ||
(sblock.fs_magic == FS_UFS2_MAGIC &&
- (memcmp(dp->dp2.di_db, ufs2_zino.di_db,
+ (memcmp(dp->dp2.di_db, zino.dp2.di_db,
UFS_NDADDR * sizeof(ufs2_daddr_t)) ||
- memcmp(dp->dp2.di_ib, ufs2_zino.di_ib,
+ memcmp(dp->dp2.di_ib, zino.dp2.di_ib,
UFS_NIADDR * sizeof(ufs2_daddr_t)) ||
dp->dp2.di_mode || dp->dp2.di_size))) {
pfatal("PARTIALLY ALLOCATED INODE I=%lu",
(u_long)inumber);
if (reply("CLEAR") == 1) {
- dp = ginode(inumber);
- clearinode(dp);
- inodirty(dp);
+ ginode(inumber, &ip);
+ clearinode(ip.i_dp);
+ inodirty(&ip);
+ irelse(&ip);
}
}
inoinfo(inumber)->ino_state = USTATE;
@@ -291,10 +293,12 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
goto unknown;
}
if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) {
- dp = ginode(inumber);
+ ginode(inumber, &ip);
+ dp = ip.i_dp;
DIP_SET(dp, di_size, sblock.fs_fsize);
DIP_SET(dp, di_mode, IFREG|0600);
- inodirty(dp);
+ inodirty(&ip);
+ irelse(&ip);
}
if ((mode == IFBLK || mode == IFCHR || mode == IFIFO ||
mode == IFSOCK) && DIP(dp, di_size) != 0) {
@@ -375,12 +379,12 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
badblk = dupblk = 0;
idesc->id_number = inumber;
if (DIP(dp, di_flags) & SF_SNAPSHOT)
- idesc->id_type = SNAP;
+ inoinfo(inumber)->ino_idtype = SNAP;
else
- idesc->id_type = ADDR;
+ inoinfo(inumber)->ino_idtype = ADDR;
+ idesc->id_type = inoinfo(inumber)->ino_idtype;
(void)ckinode(dp, idesc);
if (sblock.fs_magic == FS_UFS2_MAGIC && dp->dp2.di_extsize > 0) {
- idesc->id_type = ADDR;
ndb = howmany(dp->dp2.di_extsize, sblock.fs_bsize);
for (j = 0; j < UFS_NXADDR; j++) {
if (--ndb == 0 &&
@@ -409,9 +413,10 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
else if (reply("CORRECT") == 0)
return (1);
if (bkgrdflag == 0) {
- dp = ginode(inumber);
- DIP_SET(dp, di_blocks, idesc->id_entryno);
- inodirty(dp);
+ ginode(inumber, &ip);
+ DIP_SET(ip.i_dp, di_blocks, idesc->id_entryno);
+ inodirty(&ip);
+ irelse(&ip);
} else {
cmd.value = idesc->id_number;
cmd.size = idesc->id_entryno - DIP(dp, di_blocks);
@@ -448,9 +453,10 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
else if (reply("ADJUST") == 0)
return (1);
if (bkgrdflag == 0) {
- dp = ginode(inumber);
- DIP_SET(dp, di_size, fixsize);
- inodirty(dp);
+ ginode(inumber, &ip);
+ DIP_SET(ip.i_dp, di_size, fixsize);
+ inodirty(&ip);
+ irelse(&ip);
} else {
cmd.value = idesc->id_number;
cmd.size = fixsize;
@@ -469,9 +475,10 @@ unknown:
inoinfo(inumber)->ino_state = FCLEAR;
if (reply("CLEAR") == 1) {
inoinfo(inumber)->ino_state = USTATE;
- dp = ginode(inumber);
- clearinode(dp);
- inodirty(dp);
+ ginode(inumber, &ip);
+ clearinode(ip.i_dp);
+ inodirty(&ip);
+ irelse(&ip);
}
return (1);
}
diff --git a/sbin/fsck_ffs/pass1b.c b/sbin/fsck_ffs/pass1b.c
index 4db754371b0b..8c09ef36acad 100644
--- a/sbin/fsck_ffs/pass1b.c
+++ b/sbin/fsck_ffs/pass1b.c
@@ -55,10 +55,9 @@ pass1b(void)
int c, i;
union dinode *dp;
struct inodesc idesc;
- ino_t inumber;
+ ino_t inumber, inosused;
memset(&idesc, 0, sizeof(struct inodesc));
- idesc.id_type = ADDR;
idesc.id_func = pass1bcheck;
duphead = duplist;
inumber = 0;
@@ -74,13 +73,16 @@ pass1b(void)
c * 100 / sblock.fs_ncg);
got_sigalarm = 0;
}
- for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
+ inosused = inostathead[c].il_numalloced;
+ if (inosused == 0)
+ continue;
+ setinodebuf(c, inosused);
+ for (i = 0; i < inosused; i++, inumber++) {
if (inumber < UFS_ROOTINO)
continue;
- dp = ginode(inumber);
- if (dp == NULL)
- continue;
+ dp = getnextinode(inumber, 0);
idesc.id_number = inumber;
+ idesc.id_type = inoinfo(inumber)->ino_idtype;
if (inoinfo(inumber)->ino_state != USTATE &&
(ckinode(dp, &idesc) & STOP)) {
rerun = 1;
diff --git a/sbin/fsck_ffs/pass2.c b/sbin/fsck_ffs/pass2.c
index f0e1321afcfe..8632cf4878b1 100644
--- a/sbin/fsck_ffs/pass2.c
+++ b/sbin/fsck_ffs/pass2.c
@@ -61,6 +61,7 @@ static int pass2check(struct inodesc *);
void
pass2(void)
{
+ struct inode ip;
union dinode *dp;
struct inoinfo **inpp, *inp;
struct inoinfo **inpend;
@@ -111,10 +112,12 @@ pass2(void)
ckfini(0);
exit(EEXIT);
}
- dp = ginode(UFS_ROOTINO);
+ ginode(UFS_ROOTINO, &ip);
+ dp = ip.i_dp;
DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
- inodirty(dp);
+ inodirty(&ip);
+ irelse(&ip);
break;
case DSTATE:
@@ -158,9 +161,10 @@ pass2(void)
direrror(inp->i_number, "DIRECTORY TOO SHORT");
inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
if (reply("FIX") == 1) {
- dp = ginode(inp->i_number);
- DIP_SET(dp, di_size, inp->i_isize);
- inodirty(dp);
+ ginode(inp->i_number, &ip);
+ DIP_SET(ip.i_dp, di_size, inp->i_isize);
+ inodirty(&ip);
+ irelse(&ip);
}
} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
getpathname(pathbuf, inp->i_number, inp->i_number);
@@ -176,10 +180,11 @@ pass2(void)
printf(" (ADJUSTED)\n");
inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
if (preen || reply("ADJUST") == 1) {
- dp = ginode(inp->i_number);
- DIP_SET(dp, di_size,
+ ginode(inp->i_number, &ip);
+ DIP_SET(ip.i_dp, di_size,
roundup(inp->i_isize, DIRBLKSIZ));
- inodirty(dp);
+ inodirty(&ip);
+ irelse(&ip);
}
}
dp = &dino;
@@ -281,6 +286,7 @@ pass2check(struct inodesc *idesc)
char dirname[MAXPATHLEN + 1];
struct inoinfo *inp;
int n, entrysize, ret = 0;
+ struct inode ip;
union dinode *dp;
const char *errmsg;
struct direct proto;
@@ -476,10 +482,12 @@ again:
fileerror(idesc->id_number, dirp->d_ino, errmsg);
if ((n = reply("REMOVE")) == 1)
break;
- dp = ginode(dirp->d_ino);
+ ginode(dirp->d_ino, &ip);
+ dp = ip.i_dp;
inoinfo(dirp->d_ino)->ino_state =
(DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
+ irelse(&ip);
goto again;
case DSTATE:
@@ -526,6 +534,7 @@ static int
fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
{
char *cp;
+ struct inode ip;
struct inodesc dotdesc;
char oldname[MAXPATHLEN + 1];
char newname[MAXPATHLEN + 1];
@@ -540,8 +549,10 @@ fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
dotdesc.id_number = idesc->id_dirp->d_ino;
dotdesc.id_func = findino;
dotdesc.id_name = strdup("..");
- if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND))
+ ginode(dotdesc.id_number, &ip);
+ if ((ckinode(ip.i_dp, &dotdesc) & FOUND))
inp->i_dotdot = dotdesc.id_parent;
+ irelse(&ip);
}
/*
* We have the previously found old name (inp->i_parent) and the
@@ -641,8 +652,10 @@ fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
dotdesc.id_number = inp->i_parent; /* directory in which name appears */
dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
dotdesc.id_func = deleteentry;
- if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen)
+ ginode(dotdesc.id_number, &ip);
+ if ((ckinode(ip.i_dp, &dotdesc) & FOUND) && preen)
printf(" (REMOVED)\n");
+ irelse(&ip);
inp->i_parent = idesc->id_number; /* reparent to correct directory */
inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
return (0);
diff --git a/sbin/fsck_ffs/pass3.c b/sbin/fsck_ffs/pass3.c
index 38eb9a4cdfc6..22cb0393905b 100644
--- a/sbin/fsck_ffs/pass3.c
+++ b/sbin/fsck_ffs/pass3.c
@@ -53,6 +53,7 @@ pass3(void)
struct inoinfo *inp;
int loopcnt, inpindex, state;
ino_t orphan;
+ struct inode ip;
struct inodesc idesc;
char namebuf[UFS_MAXNAMLEN+1];
@@ -114,15 +115,17 @@ pass3(void)
idesc.id_parent = orphan;
idesc.id_func = findname;
idesc.id_name = namebuf;
- if ((ckinode(ginode(inp->i_parent), &idesc) & FOUND) == 0)
+ ginode(inp->i_parent, &ip);
+ if ((ckinode(ip.i_dp, &idesc) & FOUND) == 0)
pfatal("COULD NOT FIND NAME IN PARENT DIRECTORY");
if (linkup(orphan, inp->i_parent, namebuf)) {
idesc.id_func = clearentry;
- if (ckinode(ginode(inp->i_parent), &idesc) & FOUND)
+ if (ckinode(ip.i_dp, &idesc) & FOUND)
inoinfo(orphan)->ino_linkcnt++;
inp->i_parent = inp->i_dotdot = lfdir;
inoinfo(lfdir)->ino_linkcnt--;
}
+ irelse(&ip);
inoinfo(orphan)->ino_state = DFOUND;
propagate();
}
diff --git a/sbin/fsck_ffs/pass4.c b/sbin/fsck_ffs/pass4.c
index 925be9f4fb4a..cfcd36296887 100644
--- a/sbin/fsck_ffs/pass4.c
+++ b/sbin/fsck_ffs/pass4.c
@@ -38,6 +38,7 @@ static const char sccsid[] = "@(#)pass4.c 8.4 (Berkeley) 4/28/95";
__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#include <sys/stat.h>
#include <ufs/ufs/dinode.h>
#include <ufs/ffs/fs.h>
@@ -52,12 +53,11 @@ void
pass4(void)
{
ino_t inumber;
- union dinode *dp;
+ struct inode ip;
struct inodesc idesc;
int i, n, cg;
memset(&idesc, 0, sizeof(struct inodesc));
- idesc.id_type = ADDR;
idesc.id_func = freeblock;
for (cg = 0; cg < sblock.fs_ncg; cg++) {
if (got_siginfo) {
@@ -76,6 +76,7 @@ pass4(void)
if (inumber < UFS_ROOTINO)
continue;
idesc.id_number = inumber;
+ idesc.id_type = inoinfo(inumber)->ino_idtype;
switch (inoinfo(inumber)->ino_state) {
case FZLINK:
@@ -103,11 +104,13 @@ pass4(void)
/* if on snapshot, already cleared */
if (cursnapshot != 0)
break;
- dp = ginode(inumber);
- if (DIP(dp, di_size) == 0) {
+ ginode(inumber, &ip);
+ if (DIP(ip.i_dp, di_size) == 0) {
clri(&idesc, "ZERO LENGTH", 1);
+ irelse(&ip);
break;
}
+ irelse(&ip);
/* fall through */
case FCLEAR:
clri(&idesc, "BAD/DUP", 1);
diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c
index deb920e00e7c..0ae7f1bbb28f 100644
--- a/sbin/fsck_ffs/setup.c
+++ b/sbin/fsck_ffs/setup.c
@@ -58,9 +58,8 @@ __FBSDID("$FreeBSD$");
#include "fsck.h"
-struct inoinfo **inphead, **inpsort;
+struct inoinfo **inphead, **inpsort; /* info about all inodes */
-struct uufsd disk;
struct bufarea asblk;
#define altsblock (*asblk.b_un.b_fs)
#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
@@ -128,8 +127,7 @@ setup(char *dev)
}
}
}
- if ((fsreadfd = open(dev, O_RDONLY)) < 0 ||
- ufs_disk_fillout_blank(&disk, dev) < 0) {
+ if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
if (bkgrdflag) {
unlink(snapname);
bkgrdflag = 0;
@@ -174,8 +172,7 @@ setup(char *dev)
if (preen == 0)
printf("** %s", dev);
if (bkgrdflag == 0 &&
- (nflag || ufs_disk_write(&disk) < 0 ||
- (fswritefd = dup(disk.d_fd)) < 0)) {
+ (nflag || (fswritefd = open(dev, O_WRONLY)) < 0)) {
fswritefd = -1;
if (preen)
pfatal("NO WRITE ACCESS");
@@ -211,13 +208,6 @@ setup(char *dev)
pwarn("USING ALTERNATE SUPERBLOCK AT %jd\n", bflag);
bflag = 0;
}
- /* Save copy of things needed by libufs */
- memcpy(&disk.d_fs, &sblock, sblock.fs_sbsize);
- disk.d_ufs = (sblock.fs_magic == FS_UFS1_MAGIC) ? 1 : 2;
- disk.d_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
- disk.d_sblock = sblock.fs_sblockloc / disk.d_bsize;
- disk.d_si = sblock.fs_si;
-
if (skipclean && ckclean && sblock.fs_clean) {
pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
return (-1);
@@ -249,7 +239,7 @@ setup(char *dev)
pfatal("from before 2002 with the command ``fsck -c 2''\n");
exit(EEXIT);
}
- if (asblk.b_dirty && !bflag) {
+ if ((asblk.b_flags & B_DIRTY) != 0 && !bflag) {
memmove(&altsblock, &sblock, (size_t)sblock.fs_sbsize);
flush(fswritefd, &asblk);
}
diff --git a/sbin/fsck_ffs/suj.c b/sbin/fsck_ffs/suj.c
index 8ecd1f686ea7..aa2085e2fa77 100644
--- a/sbin/fsck_ffs/suj.c
+++ b/sbin/fsck_ffs/suj.c
@@ -56,9 +56,6 @@ __FBSDID("$FreeBSD$");
#include "fsck.h"
#define DOTDOT_OFFSET DIRECTSIZ(1)
-#define SUJ_HASHSIZE 2048
-#define SUJ_HASHMASK (SUJ_HASHSIZE - 1)
-#define SUJ_HASH(x) ((x * 2654435761) & SUJ_HASHMASK)
struct suj_seg {
TAILQ_ENTRY(suj_seg) ss_next;
@@ -97,41 +94,20 @@ struct suj_blk {
};
LIST_HEAD(blkhd, suj_blk);
-struct data_blk {
- LIST_ENTRY(data_blk) db_next;
- uint8_t *db_buf;
- ufs2_daddr_t db_blk;
- int db_size;
- int db_dirty;
-};
-
-struct ino_blk {
- LIST_ENTRY(ino_blk) ib_next;
- uint8_t *ib_buf;
- int ib_dirty;
- ino_t ib_startinginum;
- ufs2_daddr_t ib_blk;
-};
-LIST_HEAD(iblkhd, ino_blk);
-
struct suj_cg {
LIST_ENTRY(suj_cg) sc_next;
- struct blkhd sc_blkhash[SUJ_HASHSIZE];
- struct inohd sc_inohash[SUJ_HASHSIZE];
- struct iblkhd sc_iblkhash[SUJ_HASHSIZE];
+ struct blkhd sc_blkhash[HASHSIZE];
+ struct inohd sc_inohash[HASHSIZE];
struct ino_blk *sc_lastiblk;
struct suj_ino *sc_lastino;
struct suj_blk *sc_lastblk;
- uint8_t *sc_cgbuf;
+ struct bufarea *sc_cgbp;
struct cg *sc_cgp;
- int sc_dirty;
int sc_cgx;
};
-static LIST_HEAD(cghd, suj_cg) cghash[SUJ_HASHSIZE];
-static LIST_HEAD(dblkhd, data_blk) dbhash[SUJ_HASHSIZE];
+static LIST_HEAD(cghd, suj_cg) cghash[HASHSIZE];
static struct suj_cg *lastcg;
-static struct data_blk *lastblk;
static TAILQ_HEAD(seghd, suj_seg) allsegs;
static uint64_t oldseq;
@@ -158,7 +134,6 @@ static void ino_adjust(struct suj_ino *);
static void ino_build(struct suj_ino *);
static int blk_isfree(ufs2_daddr_t);
static void initsuj(void);
-static void ino_dirty(ino_t);
static void *
errmalloc(size_t n)
@@ -191,38 +166,6 @@ err_suj(const char * restrict fmt, ...)
}
/*
- * Mark file system as clean, write the super-block back, close the disk.
- */
-static void
-closedisk(const char *devnam)
-{
- struct csum *cgsum;
- uint32_t i;
-
- /*
- * Recompute the fs summary info from correct cs summaries.
- */
- bzero(&fs->fs_cstotal, sizeof(struct csum_total));
- for (i = 0; i < fs->fs_ncg; i++) {
- cgsum = &fs->fs_cs(fs, i);
- fs->fs_cstotal.cs_nffree += cgsum->cs_nffree;
- fs->fs_cstotal.cs_nbfree += cgsum->cs_nbfree;
- fs->fs_cstotal.cs_nifree += cgsum->cs_nifree;
- fs->fs_cstotal.cs_ndir += cgsum->cs_ndir;
- }
- fs->fs_pendinginodes = 0;
- fs->fs_pendingblocks = 0;
- fs->fs_clean = 1;
- fs->fs_time = time(NULL);
- fs->fs_mtime = time(NULL);
- if (sbput(disk.d_fd, fs, 0) == -1)
- err(EX_OSERR, "sbput(%s)", devnam);
- if (ufs_disk_close(&disk) == -1)
- err(EX_OSERR, "ufs_disk_close(%s)", devnam);
- fs = NULL;
-}
-
-/*
* Lookup a cg by number in the hash so we can keep track of which cgs
* need stats rebuilt.
*/
@@ -231,31 +174,29 @@ cg_lookup(int cgx)
{
struct cghd *hd;
struct suj_cg *sc;
+ struct bufarea *cgbp;
if (cgx < 0 || cgx >= fs->fs_ncg)
err_suj("Bad cg number %d\n", cgx);
if (lastcg && lastcg->sc_cgx == cgx)
return (lastcg);
- hd = &cghash[SUJ_HASH(cgx)];
+ cgbp = cglookup(cgx);
+ if (!check_cgmagic(cgx, cgbp, 0))
+ err_suj("UNABLE TO REBUILD CYLINDER GROUP %d", cgx);
+ hd = &cghash[HASH(cgx)];
LIST_FOREACH(sc, hd, sc_next)
if (sc->sc_cgx == cgx) {
+ sc->sc_cgbp = cgbp;
+ sc->sc_cgp = sc->sc_cgbp->b_un.b_cg;
lastcg = sc;
return (sc);
}
sc = errmalloc(sizeof(*sc));
bzero(sc, sizeof(*sc));
- sc->sc_cgbuf = errmalloc(fs->fs_bsize);
- sc->sc_cgp = (struct cg *)sc->sc_cgbuf;
+ sc->sc_cgbp = cgbp;
+ sc->sc_cgp = sc->sc_cgbp->b_un.b_cg;
sc->sc_cgx = cgx;
LIST_INSERT_HEAD(hd, sc, sc_next);
- /*
- * Use bread() here rather than cgget() because the cylinder group
- * may be corrupted but we want it anyway so we can fix it.
- */
- if (bread(&disk, fsbtodb(fs, cgtod(fs, sc->sc_cgx)), sc->sc_cgbuf,
- fs->fs_bsize) == -1)
- err_suj("Unable to read cylinder group %d\n", sc->sc_cgx);
-
return (sc);
}
@@ -273,7 +214,7 @@ ino_lookup(ino_t ino, int creat)
sc = cg_lookup(ino_to_cg(fs, ino));
if (sc->sc_lastino && sc->sc_lastino->si_ino == ino)
return (sc->sc_lastino);
- hd = &sc->sc_inohash[SUJ_HASH(ino)];
+ hd = &sc->sc_inohash[HASH(ino)];
LIST_FOREACH(sino, hd, si_next)
if (sino->si_ino == ino)
return (sino);
@@ -304,7 +245,7 @@ blk_lookup(ufs2_daddr_t blk, int creat)
sc = cg_lookup(dtog(fs, blk));
if (sc->sc_lastblk && sc->sc_lastblk->sb_blk == blk)
return (sc->sc_lastblk);
- hd = &sc->sc_blkhash[SUJ_HASH(fragstoblks(fs, blk))];
+ hd = &sc->sc_blkhash[HASH(fragstoblks(fs, blk))];
LIST_FOREACH(sblk, hd, sb_next)
if (sblk->sb_blk == blk)
return (sblk);
@@ -319,189 +260,6 @@ blk_lookup(ufs2_daddr_t blk, int creat)
return (sblk);
}
-static struct data_blk *
-dblk_lookup(ufs2_daddr_t blk)
-{
- struct data_blk *dblk;
- struct dblkhd *hd;
-
- hd = &dbhash[SUJ_HASH(fragstoblks(fs, blk))];
- if (lastblk && lastblk->db_blk == blk)
- return (lastblk);
- LIST_FOREACH(dblk, hd, db_next)
- if (dblk->db_blk == blk)
- return (dblk);
- /*
- * The inode block wasn't located, allocate a new one.
- */
- dblk = errmalloc(sizeof(*dblk));
- bzero(dblk, sizeof(*dblk));
- LIST_INSERT_HEAD(hd, dblk, db_next);
- dblk->db_blk = blk;
- return (dblk);
-}
-
-static uint8_t *
-dblk_read(ufs2_daddr_t blk, int size)
-{
- struct data_blk *dblk;
-
- dblk = dblk_lookup(blk);
- /*
- * I doubt size mismatches can happen in practice but it is trivial
- * to handle.
- */
- if (size != dblk->db_size) {
- if (dblk->db_buf)
- free(dblk->db_buf);
- dblk->db_buf = errmalloc(size);
- dblk->db_size = size;
- if (bread(&disk, fsbtodb(fs, blk), dblk->db_buf, size) == -1)
- err_suj("Failed to read data block %jd\n", blk);
- }
- return (dblk->db_buf);
-}
-
-static void
-dblk_dirty(ufs2_daddr_t blk)
-{
- struct data_blk *dblk;
-
- dblk = dblk_lookup(blk);
- dblk->db_dirty = 1;
-}
-
-static void
-dblk_write(void)
-{
- struct data_blk *dblk;
- int i;
-
- for (i = 0; i < SUJ_HASHSIZE; i++) {
- LIST_FOREACH(dblk, &dbhash[i], db_next) {
- if (dblk->db_dirty == 0 || dblk->db_size == 0)
- continue;
- if (bwrite(&disk, fsbtodb(fs, dblk->db_blk),
- dblk->db_buf, dblk->db_size) == -1)
- err_suj("Unable to write block %jd\n",
- dblk->db_blk);
- }
- }
-}
-
-static union dinode *
-ino_read(ino_t ino)
-{
- struct ino_blk *iblk;
- struct iblkhd *hd;
- struct suj_cg *sc;
- ufs2_daddr_t blk;
- union dinode *dp;
- int off;
-
- blk = ino_to_fsba(fs, ino);
- sc = cg_lookup(ino_to_cg(fs, ino));
- iblk = sc->sc_lastiblk;
- if (iblk && iblk->ib_blk == blk)
- goto found;
- hd = &sc->sc_iblkhash[SUJ_HASH(fragstoblks(fs, blk))];
- LIST_FOREACH(iblk, hd, ib_next)
- if (iblk->ib_blk == blk)
- goto found;
- /*
- * The inode block wasn't located, allocate a new one.
- */
- iblk = errmalloc(sizeof(*iblk));
- bzero(iblk, sizeof(*iblk));
- iblk->ib_buf = errmalloc(fs->fs_bsize);
- iblk->ib_blk = blk;
- iblk->ib_startinginum = rounddown(ino, INOPB(fs));
- LIST_INSERT_HEAD(hd, iblk, ib_next);
- if (bread(&disk, fsbtodb(fs, blk), iblk->ib_buf, fs->fs_bsize) == -1)
- err_suj("Failed to read inode block %jd\n", blk);
-found:
- sc->sc_lastiblk = iblk;
- off = ino_to_fsbo(fs, ino);
- if (fs->fs_magic == FS_UFS1_MAGIC)
- return (union dinode *)&((struct ufs1_dinode *)iblk->ib_buf)[off];
- dp = (union dinode *)&((struct ufs2_dinode *)iblk->ib_buf)[off];
- if (debug &&
- ffs_verify_dinode_ckhash(fs, (struct ufs2_dinode *)dp) != 0) {
- pwarn("ino_read: INODE CHECK-HASH FAILED");
- prtinode(ino, dp);
- if (preen || reply("FIX") != 0) {
- if (preen)
- printf(" (FIXED)\n");
- ino_dirty(ino);
- }
- }
- return (dp);
-}
-
-static void
-ino_dirty(ino_t ino)
-{
- struct ino_blk *iblk;
- struct iblkhd *hd;
- struct suj_cg *sc;
- ufs2_daddr_t blk;
- int off;
-
- blk = ino_to_fsba(fs, ino);
- sc = cg_lookup(ino_to_cg(fs, ino));
- iblk = sc->sc_lastiblk;
- if (iblk && iblk->ib_blk == blk) {
- if (fs->fs_magic == FS_UFS2_MAGIC) {
- off = ino_to_fsbo(fs, ino);
- ffs_update_dinode_ckhash(fs,
- &((struct ufs2_dinode *)iblk->ib_buf)[off]);
- }
- iblk->ib_dirty = 1;
- return;
- }
- hd = &sc->sc_iblkhash[SUJ_HASH(fragstoblks(fs, blk))];
- LIST_FOREACH(iblk, hd, ib_next) {
- if (iblk->ib_blk == blk) {
- if (fs->fs_magic == FS_UFS2_MAGIC) {
- off = ino_to_fsbo(fs, ino);
- ffs_update_dinode_ckhash(fs,
- &((struct ufs2_dinode *)iblk->ib_buf)[off]);
- }
- iblk->ib_dirty = 1;
- return;
- }
- }
- ino_read(ino);
- ino_dirty(ino);
-}
-
-static void
-iblk_write(struct ino_blk *iblk)
-{
- struct ufs2_dinode *dp;
- int i;
-
- if (iblk->ib_dirty == 0)
- return;
- if (debug && fs->fs_magic == FS_UFS2_MAGIC) {
- dp = (struct ufs2_dinode *)iblk->ib_buf;
- for (i = 0; i < INOPB(fs); dp++, i++) {
- if (ffs_verify_dinode_ckhash(fs, dp) == 0)
- continue;
- pwarn("iblk_write: INODE CHECK-HASH FAILED");
- prtinode(iblk->ib_startinginum + i, (union dinode *)dp);
- if (preen || reply("FIX") != 0) {
- if (preen)
- printf(" (FIXED)\n");
- ino_dirty(iblk->ib_startinginum + i);
- }
- }
- }
- if (bwrite(&disk, fsbtodb(fs, iblk->ib_blk), iblk->ib_buf,
- fs->fs_bsize) == -1)
- err_suj("Failed to write inode block %jd\n", iblk->ib_blk);
-}
-
static int
blk_overlaps(struct jblkrec *brec, ufs2_daddr_t start, int frags)
{
@@ -650,7 +408,7 @@ ino_free(ino_t ino, int mode)
freedir++;
cgp->cg_cs.cs_ndir--;
}
- sc->sc_dirty = 1;
+ cgdirty(sc->sc_cgbp);
return (1);
}
@@ -696,7 +454,7 @@ blk_free(ufs2_daddr_t bno, int mask, int frags)
setbit(blksfree, cgbno + i);
}
}
- sc->sc_dirty = 1;
+ cgdirty(sc->sc_cgbp);
}
/*
@@ -713,110 +471,6 @@ blk_isfree(ufs2_daddr_t bno)
}
/*
- * Fetch an indirect block to find the block at a given lbn. The lbn
- * may be negative to fetch a specific indirect block pointer or positive
- * to fetch a specific block.
- */
-static ufs2_daddr_t
-indir_blkatoff(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t cur, ufs_lbn_t lbn)
-{
- ufs2_daddr_t *bap2;
- ufs2_daddr_t *bap1;
- ufs_lbn_t lbnadd;
- ufs_lbn_t base;
- int level;
- int i;
-
- if (blk == 0)
- return (0);
- level = lbn_level(cur);
- if (level == -1)
- err_suj("Invalid indir lbn %jd\n", lbn);
- if (level == 0 && lbn < 0)
- err_suj("Invalid lbn %jd\n", lbn);
- bap2 = (void *)dblk_read(blk, fs->fs_bsize);
- bap1 = (void *)bap2;
- lbnadd = 1;
- base = -(cur + level);
- for (i = level; i > 0; i--)
- lbnadd *= NINDIR(fs);
- if (lbn > 0)
- i = (lbn - base) / lbnadd;
- else
- i = (-lbn - base) / lbnadd;
- if (i < 0 || i >= NINDIR(fs))
- err_suj("Invalid indirect index %d produced by lbn %jd\n",
- i, lbn);
- if (level == 0)
- cur = base + (i * lbnadd);
- else
- cur = -(base + (i * lbnadd)) - (level - 1);
- if (fs->fs_magic == FS_UFS1_MAGIC)
- blk = bap1[i];
- else
- blk = bap2[i];
- if (cur == lbn)
- return (blk);
- if (level == 0)
- err_suj("Invalid lbn %jd at level 0\n", lbn);
- return indir_blkatoff(blk, ino, cur, lbn);
-}
-
-/*
- * Finds the disk block address at the specified lbn within the inode
- * specified by ip. This follows the whole tree and honors di_size and
- * di_extsize so it is a true test of reachability. The lbn may be
- * negative if an extattr or indirect block is requested.
- */
-static ufs2_daddr_t
-ino_blkatoff(union dinode *ip, ino_t ino, ufs_lbn_t lbn, int *frags)
-{
- ufs_lbn_t tmpval;
- ufs_lbn_t cur;
- ufs_lbn_t next;
- int i;
-
- /*
- * Handle extattr blocks first.
- */
- if (lbn < 0 && lbn >= -UFS_NXADDR) {
- lbn = -1 - lbn;
- if (lbn > lblkno(fs, ip->dp2.di_extsize - 1))
- return (0);
- *frags = numfrags(fs, sblksize(fs, ip->dp2.di_extsize, lbn));
- return (ip->dp2.di_extb[lbn]);
- }
- /*
- * Now direct and indirect.
- */
- if (DIP(ip, di_mode) == IFLNK &&
- DIP(ip, di_size) < fs->fs_maxsymlinklen)
- return (0);
- if (lbn >= 0 && lbn < UFS_NDADDR) {
- *frags = numfrags(fs, sblksize(fs, DIP(ip, di_size), lbn));
- return (DIP(ip, di_db[lbn]));
- }
- *frags = fs->fs_frag;
-
- for (i = 0, tmpval = NINDIR(fs), cur = UFS_NDADDR; i < UFS_NIADDR; i++,
- tmpval *= NINDIR(fs), cur = next) {
- next = cur + tmpval;
- if (lbn == -cur - i)
- return (DIP(ip, di_ib[i]));
- /*
- * Determine whether the lbn in question is within this tree.
- */
- if (lbn < 0 && -lbn >= next)
- continue;
- if (lbn > 0 && lbn >= next)
- continue;
- return indir_blkatoff(DIP(ip, di_ib[i]), ino, -cur - i, lbn);
- }
- err_suj("lbn %jd not in ino\n", lbn);
- /* NOTREACHED */
-}
-
-/*
* Determine whether a block exists at a particular lbn in an inode.
* Returns 1 if found, 0 if not. lbn may be negative for indirects
* or ext blocks.
@@ -824,15 +478,18 @@ ino_blkatoff(union dinode *ip, ino_t ino, ufs_lbn_t lbn, int *frags)
static int
blk_isat(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int *frags)
{
- union dinode *ip;
+ struct inode ip;
+ union dinode *dp;
ufs2_daddr_t nblk;
- ip = ino_read(ino);
-
- if (DIP(ip, di_nlink) == 0 || DIP(ip, di_mode) == 0)
+ ginode(ino, &ip);
+ dp = ip.i_dp;
+ if (DIP(dp, di_nlink) == 0 || DIP(dp, di_mode) == 0) {
+ irelse(&ip);
return (0);
- nblk = ino_blkatoff(ip, ino, lbn, frags);
-
+ }
+ nblk = ino_blkatoff(dp, ino, lbn, frags, NULL);
+ irelse(&ip);
return (nblk == blk);
}
@@ -845,8 +502,9 @@ ino_clrat(ino_t parent, off_t diroff, ino_t child)
{
union dinode *dip;
struct direct *dp;
+ struct inode ip;
ufs2_daddr_t blk;
- uint8_t *block;
+ struct bufarea *bp;
ufs_lbn_t lbn;
int blksize;
int frags;
@@ -858,16 +516,21 @@ ino_clrat(ino_t parent, off_t diroff, ino_t child)
lbn = lblkno(fs, diroff);
doff = blkoff(fs, diroff);
- dip = ino_read(parent);
- blk = ino_blkatoff(dip, parent, lbn, &frags);
+ ginode(parent, &ip);
+ dip = ip.i_dp;
+ blk = ino_blkatoff(dip, parent, lbn, &frags, NULL);
blksize = sblksize(fs, DIP(dip, di_size), lbn);
- block = dblk_read(blk, blksize);
- dp = (struct direct *)&block[doff];
+ irelse(&ip);
+ bp = getdatablk(blk, blksize, BT_DIRDATA);
+ if (bp->b_errs != 0)
+ err_suj("ino_clrat: UNRECOVERABLE I/O ERROR");
+ dp = (struct direct *)&bp->b_un.b_buf[doff];
if (dp->d_ino != child)
errx(1, "Inode %ju does not exist in %ju at %jd",
(uintmax_t)child, (uintmax_t)parent, diroff);
dp->d_ino = 0;
- dblk_dirty(blk);
+ dirty(bp);
+ brelse(bp);
/*
* The actual .. reference count will already have been removed
* from the parent by the .. remref record.
@@ -881,10 +544,11 @@ ino_clrat(ino_t parent, off_t diroff, ino_t child)
static int
ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot)
{
+ struct inode ip;
union dinode *dip;
+ struct bufarea *bp;
struct direct *dp;
ufs2_daddr_t blk;
- uint8_t *block;
ufs_lbn_t lbn;
int blksize;
int frags;
@@ -892,7 +556,8 @@ ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot)
int doff;
*isdot = 0;
- dip = ino_read(parent);
+ ginode(parent, &ip);
+ dip = ip.i_dp;
*mode = DIP(dip, di_mode);
if ((*mode & IFMT) != IFDIR) {
if (debug) {
@@ -907,6 +572,7 @@ ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot)
printf("Directory %ju has zero mode\n",
(uintmax_t)parent);
}
+ irelse(&ip);
return (0);
}
lbn = lblkno(fs, diroff);
@@ -918,15 +584,19 @@ ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot)
" exceeding size %jd\n",
(uintmax_t)child, (uintmax_t)parent, diroff,
DIP(dip, di_size));
+ irelse(&ip);
return (0);
}
- blk = ino_blkatoff(dip, parent, lbn, &frags);
+ blk = ino_blkatoff(dip, parent, lbn, &frags, NULL);
+ irelse(&ip);
if (blk <= 0) {
if (debug)
printf("Sparse directory %ju", (uintmax_t)parent);
return (0);
}
- block = dblk_read(blk, blksize);
+ bp = getdatablk(blk, blksize, BT_DIRDATA);
+ if (bp->b_errs != 0)
+ err_suj("ino_isat: UNRECOVERABLE I/O ERROR");
/*
* Walk through the records from the start of the block to be
* certain we hit a valid record and not some junk in the middle
@@ -934,7 +604,7 @@ ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot)
*/
dpoff = rounddown(doff, DIRBLKSIZ);
do {
- dp = (struct direct *)&block[dpoff];
+ dp = (struct direct *)&bp->b_un.b_buf[dpoff];
if (dpoff == doff)
break;
if (dp->d_reclen == 0)
@@ -949,6 +619,7 @@ ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot)
if (debug)
printf("ino %ju not found in %ju, lbn %jd, dpoff %d\n",
(uintmax_t)child, (uintmax_t)parent, lbn, dpoff);
+ brelse(bp);
return (0);
}
/*
@@ -962,11 +633,13 @@ ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot)
dp->d_name[0] == '.' && dp->d_name[1] == '.')
*isdot = 1;
*mode = DTTOIF(dp->d_type);
+ brelse(bp);
return (1);
}
if (debug)
printf("ino %ju doesn't match dirent ino %ju in parent %ju\n",
(uintmax_t)child, (uintmax_t)dp->d_ino, (uintmax_t)parent);
+ brelse(bp);
return (0);
}
@@ -981,8 +654,7 @@ static void
indir_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, uint64_t *frags,
ino_visitor visitor, int flags)
{
- ufs2_daddr_t *bap2;
- ufs1_daddr_t *bap1;
+ struct bufarea *bp;
ufs_lbn_t lbnadd;
ufs2_daddr_t nblk;
ufs_lbn_t nlbn;
@@ -1009,14 +681,11 @@ indir_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, uint64_t *frags,
lbnadd = 1;
for (i = level; i > 0; i--)
lbnadd *= NINDIR(fs);
- bap1 = (void *)dblk_read(blk, fs->fs_bsize);
- bap2 = (void *)bap1;
+ bp = getdatablk(blk, fs->fs_bsize, BT_LEVEL1 + level);
+ if (bp->b_errs != 0)
+ err_suj("indir_visit: UNRECOVERABLE I/O ERROR");
for (i = 0; i < NINDIR(fs); i++) {
- if (fs->fs_magic == FS_UFS1_MAGIC)
- nblk = *bap1++;
- else
- nblk = *bap2++;
- if (nblk == 0)
+ if ((nblk = IBLK(bp, i)) == 0)
continue;
if (level == 0) {
nlbn = -lbn + i * lbnadd;
@@ -1027,6 +696,7 @@ indir_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, uint64_t *frags,
indir_visit(ino, nlbn, nblk, frags, visitor, flags);
}
}
+ brelse(bp);
out:
if (flags & VISIT_INDIR) {
(*frags) += fs->fs_frag;
@@ -1042,7 +712,7 @@ out:
* the correct di_blocks for a file.
*/
static uint64_t
-ino_visit(union dinode *ip, ino_t ino, ino_visitor visitor, int flags)
+ino_visit(union dinode *dp, ino_t ino, ino_visitor visitor, int flags)
{
ufs_lbn_t nextlbn;
ufs_lbn_t tmpval;
@@ -1053,18 +723,18 @@ ino_visit(union dinode *ip, ino_t ino, ino_visitor visitor, int flags)
int frags;
int i;
- size = DIP(ip, di_size);
- mode = DIP(ip, di_mode) & IFMT;
+ size = DIP(dp, di_size);
+ mode = DIP(dp, di_mode) & IFMT;
fragcnt = 0;
if ((flags & VISIT_EXT) &&
- fs->fs_magic == FS_UFS2_MAGIC && ip->dp2.di_extsize) {
+ fs->fs_magic == FS_UFS2_MAGIC && dp->dp2.di_extsize) {
for (i = 0; i < UFS_NXADDR; i++) {
- if (ip->dp2.di_extb[i] == 0)
+ if (dp->dp2.di_extb[i] == 0)
continue;
- frags = sblksize(fs, ip->dp2.di_extsize, i);
+ frags = sblksize(fs, dp->dp2.di_extsize, i);
frags = numfrags(fs, frags);
fragcnt += frags;
- visitor(ino, -1 - i, ip->dp2.di_extb[i], frags);
+ visitor(ino, -1 - i, dp->dp2.di_extb[i], frags);
}
}
/* Skip datablocks for short links and devices. */
@@ -1072,12 +742,12 @@ ino_visit(union dinode *ip, ino_t ino, ino_visitor visitor, int flags)
(mode == IFLNK && size < fs->fs_maxsymlinklen))
return (fragcnt);
for (i = 0; i < UFS_NDADDR; i++) {
- if (DIP(ip, di_db[i]) == 0)
+ if (DIP(dp, di_db[i]) == 0)
continue;
frags = sblksize(fs, size, i);
frags = numfrags(fs, frags);
fragcnt += frags;
- visitor(ino, i, DIP(ip, di_db[i]), frags);
+ visitor(ino, i, DIP(dp, di_db[i]), frags);
}
/*
* We know the following indirects are real as we're following
@@ -1088,9 +758,9 @@ ino_visit(union dinode *ip, ino_t ino, ino_visitor visitor, int flags)
lbn = nextlbn) {
nextlbn = lbn + tmpval;
tmpval *= NINDIR(fs);
- if (DIP(ip, di_ib[i]) == 0)
+ if (DIP(dp, di_ib[i]) == 0)
continue;
- indir_visit(ino, -lbn - i, DIP(ip, di_ib[i]), &fragcnt, visitor,
+ indir_visit(ino, -lbn - i, DIP(dp, di_ib[i]), &fragcnt, visitor,
flags);
}
return (fragcnt);
@@ -1117,7 +787,8 @@ null_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
static void
ino_adjblks(struct suj_ino *sino)
{
- union dinode *ip;
+ struct inode ip;
+ union dinode *dp;
uint64_t blocks;
uint64_t frags;
off_t isize;
@@ -1125,10 +796,13 @@ ino_adjblks(struct suj_ino *sino)
ino_t ino;
ino = sino->si_ino;
- ip = ino_read(ino);
+ ginode(ino, &ip);
+ dp = ip.i_dp;
/* No need to adjust zero'd inodes. */
- if (DIP(ip, di_mode) == 0)
+ if (DIP(dp, di_mode) == 0) {
+ irelse(&ip);
return;
+ }
/*
* Visit all blocks and count them as well as recording the last
* valid lbn in the file. If the file size doesn't agree with the
@@ -1136,7 +810,7 @@ ino_adjblks(struct suj_ino *sino)
* the blocks count.
*/
visitlbn = 0;
- frags = ino_visit(ip, ino, null_visit, VISIT_INDIR | VISIT_EXT);
+ frags = ino_visit(dp, ino, null_visit, VISIT_INDIR | VISIT_EXT);
blocks = fsbtodb(fs, frags);
/*
* We assume the size and direct block list is kept coherent by
@@ -1145,21 +819,25 @@ ino_adjblks(struct suj_ino *sino)
* populated indirects.
*/
if (visitlbn >= UFS_NDADDR) {
- isize = DIP(ip, di_size);
+ isize = DIP(dp, di_size);
size = lblktosize(fs, visitlbn + 1);
if (isize > size)
isize = size;
/* Always truncate to free any unpopulated indirects. */
- ino_trunc(sino->si_ino, isize);
+ ino_trunc(ino, isize);
+ irelse(&ip);
return;
}
- if (blocks == DIP(ip, di_blocks))
+ if (blocks == DIP(dp, di_blocks)) {
+ irelse(&ip);
return;
+ }
if (debug)
printf("ino %ju adjusting block count from %jd to %jd\n",
- (uintmax_t)ino, DIP(ip, di_blocks), blocks);
- DIP_SET(ip, di_blocks, blocks);
- ino_dirty(ino);
+ (uintmax_t)ino, DIP(dp, di_blocks), blocks);
+ DIP_SET(dp, di_blocks, blocks);
+ inodirty(&ip);
+ irelse(&ip);
}
static void
@@ -1229,6 +907,9 @@ ino_remref(ino_t parent, ino_t child, uint64_t diroff, int isdotdot)
* directory it will be discarded.
*/
if (sino->si_linkadj) {
+ if (sino->si_nlink == 0)
+ err_suj("ino_remref: ino %ld mode 0%o about to go "
+ "negative\n", sino->si_ino, sino->si_mode);
sino->si_nlink--;
if (isdotdot)
sino->si_dotlinks--;
@@ -1256,9 +937,9 @@ static void
ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
{
struct suj_ino *sino;
+ struct bufarea *bp;
struct direct *dp;
off_t diroff;
- uint8_t *block;
int skipparent;
int isdotdot;
int dpoff;
@@ -1270,10 +951,12 @@ ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
else
skipparent = 0;
size = lfragtosize(fs, frags);
- block = dblk_read(blk, size);
- dp = (struct direct *)&block[0];
+ bp = getdatablk(blk, size, BT_DIRDATA);
+ if (bp->b_errs != 0)
+ err_suj("ino_free_children: UNRECOVERABLE I/O ERROR");
+ dp = (struct direct *)&bp->b_un.b_buf[0];
for (dpoff = 0; dpoff < size && dp->d_reclen; dpoff += dp->d_reclen) {
- dp = (struct direct *)&block[dpoff];
+ dp = (struct direct *)&bp->b_un.b_buf[dpoff];
if (dp->d_ino == 0 || dp->d_ino == UFS_WINO)
continue;
if (dp->d_namlen == 1 && dp->d_name[0] == '.')
@@ -1288,6 +971,7 @@ ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
diroff = lblktosize(fs, lbn) + dpoff;
ino_remref(ino, dp->d_ino, diroff, isdotdot);
}
+ brelse(bp);
}
/*
@@ -1295,29 +979,31 @@ ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
* link counts. Free the inode back to the cg.
*/
static void
-ino_reclaim(union dinode *ip, ino_t ino, int mode)
+ino_reclaim(struct inode *ip, ino_t ino, int mode)
{
+ union dinode *dp;
uint32_t gen;
+ dp = ip->i_dp;
if (ino == UFS_ROOTINO)
err_suj("Attempting to free UFS_ROOTINO\n");
if (debug)
printf("Truncating and freeing ino %ju, nlink %d, mode %o\n",
- (uintmax_t)ino, DIP(ip, di_nlink), DIP(ip, di_mode));
+ (uintmax_t)ino, DIP(dp, di_nlink), DIP(dp, di_mode));
/* We are freeing an inode or directory. */
- if ((DIP(ip, di_mode) & IFMT) == IFDIR)
- ino_visit(ip, ino, ino_free_children, 0);
- DIP_SET(ip, di_nlink, 0);
- ino_visit(ip, ino, blk_free_visit, VISIT_EXT | VISIT_INDIR);
+ if ((DIP(dp, di_mode) & IFMT) == IFDIR)
+ ino_visit(dp, ino, ino_free_children, 0);
+ DIP_SET(dp, di_nlink, 0);
+ 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(ip, di_gen);
+ gen = DIP(dp, di_gen);
if (fs->fs_magic == FS_UFS1_MAGIC)
- bzero(ip, sizeof(struct ufs1_dinode));
+ bzero(dp, sizeof(struct ufs1_dinode));
else
- bzero(ip, sizeof(struct ufs2_dinode));
- DIP_SET(ip, di_gen, gen);
- ino_dirty(ino);
+ bzero(dp, sizeof(struct ufs2_dinode));
+ DIP_SET(dp, di_gen, gen);
+ inodirty(ip);
ino_free(ino, mode);
return;
}
@@ -1328,14 +1014,16 @@ ino_reclaim(union dinode *ip, ino_t ino, int mode)
static void
ino_decr(ino_t ino)
{
- union dinode *ip;
+ struct inode ip;
+ union dinode *dp;
int reqlink;
int nlink;
int mode;
- ip = ino_read(ino);
- nlink = DIP(ip, di_nlink);
- mode = DIP(ip, di_mode);
+ ginode(ino, &ip);
+ dp = ip.i_dp;
+ nlink = DIP(dp, di_nlink);
+ mode = DIP(dp, di_mode);
if (nlink < 1)
err_suj("Inode %d link count %d invalid\n", ino, nlink);
if (mode == 0)
@@ -1349,11 +1037,13 @@ ino_decr(ino_t ino)
if (debug)
printf("ino %ju not enough links to live %d < %d\n",
(uintmax_t)ino, nlink, reqlink);
- ino_reclaim(ip, ino, mode);
+ ino_reclaim(&ip, ino, mode);
+ irelse(&ip);
return;
}
- DIP_SET(ip, di_nlink, nlink);
- ino_dirty(ino);
+ DIP_SET(dp, di_nlink, nlink);
+ inodirty(&ip);
+ irelse(&ip);
}
/*
@@ -1366,7 +1056,8 @@ ino_adjust(struct suj_ino *sino)
struct jrefrec *rrec;
struct suj_rec *srec;
struct suj_ino *stmp;
- union dinode *ip;
+ union dinode *dp;
+ struct inode ip;
nlink_t nlink;
nlink_t reqlink;
int recmode;
@@ -1414,20 +1105,22 @@ ino_adjust(struct suj_ino *sino)
}
}
}
- ip = ino_read(ino);
- mode = DIP(ip, di_mode) & IFMT;
+ ginode(ino, &ip);
+ dp = ip.i_dp;
+ mode = DIP(dp, di_mode) & IFMT;
if (nlink > UFS_LINK_MAX)
err_suj("ino %ju nlink manipulation error, new %ju, old %d\n",
- (uintmax_t)ino, (uintmax_t)nlink, DIP(ip, di_nlink));
+ (uintmax_t)ino, (uintmax_t)nlink, DIP(dp, di_nlink));
if (debug)
printf("Adjusting ino %ju, nlink %ju, old link %d lastmode %o\n",
- (uintmax_t)ino, (uintmax_t)nlink, DIP(ip, di_nlink),
+ (uintmax_t)ino, (uintmax_t)nlink, DIP(dp, di_nlink),
sino->si_mode);
if (mode == 0) {
if (debug)
printf("ino %ju, zero inode freeing bitmap\n",
(uintmax_t)ino);
ino_free(ino, sino->si_mode);
+ irelse(&ip);
return;
}
/* XXX Should be an assert? */
@@ -1444,18 +1137,21 @@ ino_adjust(struct suj_ino *sino)
printf("ino %ju not enough links to live %ju < %ju\n",
(uintmax_t)ino, (uintmax_t)nlink,
(uintmax_t)reqlink);
- ino_reclaim(ip, ino, mode);
+ ino_reclaim(&ip, ino, mode);
+ irelse(&ip);
return;
}
/* If required write the updated link count. */
- if (DIP(ip, di_nlink) == nlink) {
+ if (DIP(dp, di_nlink) == nlink) {
if (debug)
printf("ino %ju, link matches, skipping.\n",
(uintmax_t)ino);
+ irelse(&ip);
return;
}
- DIP_SET(ip, di_nlink, nlink);
- ino_dirty(ino);
+ DIP_SET(dp, di_nlink, nlink);
+ inodirty(&ip);
+ irelse(&ip);
}
/*
@@ -1463,35 +1159,32 @@ ino_adjust(struct suj_ino *sino)
* and zeroing the indirect.
*/
static void
-indir_trunc(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, ufs_lbn_t lastlbn)
+indir_trunc(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, ufs_lbn_t lastlbn,
+ union dinode *dp)
{
- ufs2_daddr_t *bap2;
- ufs1_daddr_t *bap1;
+ struct bufarea *bp;
ufs_lbn_t lbnadd;
ufs2_daddr_t nblk;
ufs_lbn_t next;
ufs_lbn_t nlbn;
- int dirty;
+ int isdirty;
int level;
int i;
if (blk == 0)
return;
- dirty = 0;
+ isdirty = 0;
level = lbn_level(lbn);
if (level == -1)
err_suj("Invalid level for lbn %jd\n", lbn);
lbnadd = 1;
for (i = level; i > 0; i--)
lbnadd *= NINDIR(fs);
- bap1 = (void *)dblk_read(blk, fs->fs_bsize);
- bap2 = (void *)bap1;
+ bp = getdatablk(blk, fs->fs_bsize, BT_LEVEL1 + level);
+ if (bp->b_errs != 0)
+ err_suj("indir_trunc: UNRECOVERABLE I/O ERROR");
for (i = 0; i < NINDIR(fs); i++) {
- if (fs->fs_magic == FS_UFS1_MAGIC)
- nblk = *bap1++;
- else
- nblk = *bap2++;
- if (nblk == 0)
+ if ((nblk = IBLK(bp, i)) == 0)
continue;
if (level != 0) {
nlbn = (lbn + 1) - (i * lbnadd);
@@ -1503,7 +1196,7 @@ indir_trunc(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, ufs_lbn_t lastlbn)
next = -(lbn + level) + ((i+1) * lbnadd);
if (next <= lastlbn)
continue;
- indir_trunc(ino, nlbn, nblk, lastlbn);
+ indir_trunc(ino, nlbn, nblk, lastlbn, dp);
/* If all of this indirect was reclaimed, free it. */
nlbn = next - lbnadd;
if (nlbn < lastlbn)
@@ -1513,15 +1206,13 @@ indir_trunc(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, ufs_lbn_t lastlbn)
if (nlbn < lastlbn)
continue;
}
- dirty = 1;
+ isdirty = 1;
blk_free(nblk, 0, fs->fs_frag);
- if (fs->fs_magic == FS_UFS1_MAGIC)
- *(bap1 - 1) = 0;
- else
- *(bap2 - 1) = 0;
+ IBLK_SET(bp, i, 0);
}
- if (dirty)
- dblk_dirty(blk);
+ if (isdirty)
+ dirty(bp);
+ brelse(bp);
}
/*
@@ -1533,7 +1224,9 @@ indir_trunc(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, ufs_lbn_t lastlbn)
static void
ino_trunc(ino_t ino, off_t size)
{
- union dinode *ip;
+ struct inode ip;
+ union dinode *dp;
+ struct bufarea *bp;
ufs2_daddr_t bn;
uint64_t totalfrags;
ufs_lbn_t nextlbn;
@@ -1541,33 +1234,42 @@ ino_trunc(ino_t ino, off_t size)
ufs_lbn_t tmpval;
ufs_lbn_t lbn;
ufs_lbn_t i;
- int frags;
+ int blksize, frags;
off_t cursize;
off_t off;
int mode;
- ip = ino_read(ino);
- mode = DIP(ip, di_mode) & IFMT;
- cursize = DIP(ip, di_size);
+ ginode(ino, &ip);
+ dp = ip.i_dp;
+ mode = DIP(dp, di_mode) & IFMT;
+ cursize = DIP(dp, di_size);
if (debug)
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 ||
- (mode == IFLNK && cursize < fs->fs_maxsymlinklen))
+ (mode == IFLNK && cursize < fs->fs_maxsymlinklen)) {
+ irelse(&ip);
return;
+ }
/* Don't extend. */
- if (size > cursize)
- size = cursize;
+ if (size > cursize) {
+ irelse(&ip);
+ return;
+ }
+ if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) {
+ if (size > 0)
+ err_suj("Partial truncation of ino %ju snapshot file\n",
+ (uintmax_t)ino);
+ }
lastlbn = lblkno(fs, blkroundup(fs, size));
for (i = lastlbn; i < UFS_NDADDR; i++) {
- if (DIP(ip, di_db[i]) == 0)
+ if ((bn = DIP(dp, di_db[i])) == 0)
continue;
- frags = sblksize(fs, cursize, i);
- frags = numfrags(fs, frags);
- blk_free(DIP(ip, di_db[i]), 0, frags);
- DIP_SET(ip, di_db[i], 0);
+ blksize = sblksize(fs, cursize, i);
+ blk_free(bn, 0, numfrags(fs, blksize));
+ DIP_SET(dp, di_db[i], 0);
}
/*
* Follow indirect blocks, freeing anything required.
@@ -1579,16 +1281,15 @@ 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(ip, di_ib[i]) == 0)
+ if (DIP(dp, di_ib[i]) == 0)
continue;
- indir_trunc(ino, -lbn - i, DIP(ip, di_ib[i]), lastlbn);
+ 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(ip, di_ib[i]), 0, fs->fs_frag);
- DIP_SET(ip, di_ib[i], 0);
+ blk_free(DIP(dp, di_ib[i]), 0, fs->fs_frag);
+ DIP_SET(dp, di_ib[i], 0);
}
- ino_dirty(ino);
/*
* Now that we've freed any whole blocks that exceed the desired
* truncation size, figure out how many blocks remain and what the
@@ -1597,7 +1298,7 @@ ino_trunc(ino_t ino, off_t size)
* would've done. This is consistent with normal fsck behavior.
*/
visitlbn = 0;
- totalfrags = ino_visit(ip, ino, null_visit, VISIT_INDIR | VISIT_EXT);
+ totalfrags = ino_visit(dp, ino, null_visit, VISIT_INDIR | VISIT_EXT);
if (size > lblktosize(fs, visitlbn + 1))
size = lblktosize(fs, visitlbn + 1);
/*
@@ -1607,7 +1308,7 @@ ino_trunc(ino_t ino, off_t size)
if (visitlbn < UFS_NDADDR && totalfrags) {
long oldspace, newspace;
- bn = DIP(ip, di_db[visitlbn]);
+ bn = DIP(dp, di_db[visitlbn]);
if (bn == 0)
err_suj("Bad blk at ino %ju lbn %jd\n",
(uintmax_t)ino, visitlbn);
@@ -1620,30 +1321,32 @@ ino_trunc(ino_t ino, off_t size)
totalfrags -= frags;
}
}
- DIP_SET(ip, di_blocks, fsbtodb(fs, totalfrags));
- DIP_SET(ip, di_size, size);
- ino_dirty(ino);
+ DIP_SET(dp, di_blocks, fsbtodb(fs, totalfrags));
+ DIP_SET(dp, di_size, size);
+ inodirty(&ip);
/*
* If we've truncated into the middle of a block or frag we have
* to zero it here. Otherwise the file could extend into
* uninitialized space later.
*/
off = blkoff(fs, size);
- if (off && DIP(ip, di_mode) != IFDIR) {
- uint8_t *buf;
+ if (off && DIP(dp, di_mode) != IFDIR) {
long clrsize;
- bn = ino_blkatoff(ip, ino, visitlbn, &frags);
+ bn = ino_blkatoff(dp, ino, visitlbn, &frags, NULL);
if (bn == 0)
err_suj("Block missing from ino %ju at lbn %jd\n",
(uintmax_t)ino, visitlbn);
clrsize = frags * fs->fs_fsize;
- buf = dblk_read(bn, clrsize);
+ bp = getdatablk(bn, clrsize, BT_DATA);
+ if (bp->b_errs != 0)
+ err_suj("ino_trunc: UNRECOVERABLE I/O ERROR");
clrsize -= off;
- buf += off;
- bzero(buf, clrsize);
- dblk_dirty(bn);
+ bzero(&bp->b_un.b_buf[off], clrsize);
+ dirty(bp);
+ brelse(bp);
}
+ irelse(&ip);
return;
}
@@ -1789,7 +1492,7 @@ cg_build(struct suj_cg *sc)
struct suj_ino *sino;
int i;
- for (i = 0; i < SUJ_HASHSIZE; i++)
+ for (i = 0; i < HASHSIZE; i++)
LIST_FOREACH(sino, &sc->sc_inohash[i], si_next)
ino_build(sino);
}
@@ -1804,7 +1507,7 @@ cg_trunc(struct suj_cg *sc)
struct suj_ino *sino;
int i;
- for (i = 0; i < SUJ_HASHSIZE; i++) {
+ for (i = 0; i < HASHSIZE; i++) {
LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) {
if (sino->si_trunc) {
ino_trunc(sino->si_ino,
@@ -1824,7 +1527,7 @@ cg_adj_blk(struct suj_cg *sc)
struct suj_ino *sino;
int i;
- for (i = 0; i < SUJ_HASHSIZE; i++) {
+ for (i = 0; i < HASHSIZE; i++) {
LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) {
if (sino->si_blkadj)
ino_adjblks(sino);
@@ -1843,7 +1546,7 @@ cg_check_blk(struct suj_cg *sc)
int i;
- for (i = 0; i < SUJ_HASHSIZE; i++)
+ for (i = 0; i < HASHSIZE; i++)
LIST_FOREACH(sblk, &sc->sc_blkhash[i], sb_next)
blk_check(sblk);
}
@@ -1858,86 +1561,18 @@ cg_check_ino(struct suj_cg *sc)
struct suj_ino *sino;
int i;
- for (i = 0; i < SUJ_HASHSIZE; i++)
+ for (i = 0; i < HASHSIZE; i++)
LIST_FOREACH(sino, &sc->sc_inohash[i], si_next)
ino_check(sino);
}
-/*
- * Write a potentially dirty cg. Recalculate the summary information and
- * update the superblock summary.
- */
-static void
-cg_write(struct suj_cg *sc)
-{
- ufs1_daddr_t fragno, cgbno, maxbno;
- u_int8_t *blksfree;
- struct cg *cgp;
- int blk;
- int i;
-
- if (sc->sc_dirty == 0)
- return;
- /*
- * Fix the frag and cluster summary.
- */
- cgp = sc->sc_cgp;
- cgp->cg_cs.cs_nbfree = 0;
- cgp->cg_cs.cs_nffree = 0;
- bzero(&cgp->cg_frsum, sizeof(cgp->cg_frsum));
- maxbno = fragstoblks(fs, fs->fs_fpg);
- if (fs->fs_contigsumsize > 0) {
- for (i = 1; i <= fs->fs_contigsumsize; i++)
- cg_clustersum(cgp)[i] = 0;
- bzero(cg_clustersfree(cgp), howmany(maxbno, CHAR_BIT));
- }
- blksfree = cg_blksfree(cgp);
- for (cgbno = 0; cgbno < maxbno; cgbno++) {
- if (ffs_isfreeblock(fs, blksfree, cgbno))
- continue;
- if (ffs_isblock(fs, blksfree, cgbno)) {
- ffs_clusteracct(fs, cgp, cgbno, 1);
- cgp->cg_cs.cs_nbfree++;
- continue;
- }
- fragno = blkstofrags(fs, cgbno);
- blk = blkmap(fs, blksfree, fragno);
- ffs_fragacct(fs, blk, cgp->cg_frsum, 1);
- for (i = 0; i < fs->fs_frag; i++)
- if (isset(blksfree, fragno + i))
- cgp->cg_cs.cs_nffree++;
- }
- /*
- * Update the superblock cg summary from our now correct values
- * before writing the block.
- */
- fs->fs_cs(fs, sc->sc_cgx) = cgp->cg_cs;
- if (cgput(fswritefd, fs, cgp) == -1)
- err_suj("Unable to write cylinder group %d\n", sc->sc_cgx);
-}
-
-/*
- * Write out any modified inodes.
- */
-static void
-cg_write_inos(struct suj_cg *sc)
-{
- struct ino_blk *iblk;
- int i;
-
- for (i = 0; i < SUJ_HASHSIZE; i++)
- LIST_FOREACH(iblk, &sc->sc_iblkhash[i], ib_next)
- if (iblk->ib_dirty)
- iblk_write(iblk);
-}
-
static void
cg_apply(void (*apply)(struct suj_cg *))
{
struct suj_cg *scg;
int i;
- for (i = 0; i < SUJ_HASHSIZE; i++)
+ for (i = 0; i < HASHSIZE; i++)
LIST_FOREACH(scg, &cghash[i], sc_next)
apply(scg);
}
@@ -1948,7 +1583,8 @@ cg_apply(void (*apply)(struct suj_cg *))
static void
ino_unlinked(void)
{
- union dinode *ip;
+ struct inode ip;
+ union dinode *dp;
uint16_t mode;
ino_t inon;
ino_t ino;
@@ -1956,23 +1592,25 @@ ino_unlinked(void)
ino = fs->fs_sujfree;
fs->fs_sujfree = 0;
while (ino != 0) {
- ip = ino_read(ino);
- mode = DIP(ip, di_mode) & IFMT;
- inon = DIP(ip, di_freelink);
- DIP_SET(ip, di_freelink, 0);
- ino_dirty(ino);
+ ginode(ino, &ip);
+ dp = ip.i_dp;
+ mode = DIP(dp, di_mode) & IFMT;
+ inon = DIP(dp, di_freelink);
+ DIP_SET(dp, di_freelink, 0);
+ inodirty(&ip);
/*
* XXX Should this be an errx?
*/
- if (DIP(ip, di_nlink) == 0) {
+ if (DIP(dp, di_nlink) == 0) {
if (debug)
printf("Freeing unlinked ino %ju mode %o\n",
(uintmax_t)ino, mode);
- ino_reclaim(ip, ino, mode);
+ ino_reclaim(&ip, ino, mode);
} else if (debug)
printf("Skipping ino %ju mode %o with link %d\n",
- (uintmax_t)ino, mode, DIP(ip, di_nlink));
+ (uintmax_t)ino, mode, DIP(dp, di_nlink));
ino = inon;
+ irelse(&ip);
}
}
@@ -2398,35 +2036,35 @@ suj_prune(void)
* Verify the journal inode before attempting to read records.
*/
static int
-suj_verifyino(union dinode *ip)
+suj_verifyino(union dinode *dp)
{
- if (DIP(ip, di_nlink) != 1) {
+ if (DIP(dp, di_nlink) != 1) {
printf("Invalid link count %d for journal inode %ju\n",
- DIP(ip, di_nlink), (uintmax_t)sujino);
+ DIP(dp, di_nlink), (uintmax_t)sujino);
return (-1);
}
- if ((DIP(ip, di_flags) & (SF_IMMUTABLE | SF_NOUNLINK)) !=
+ if ((DIP(dp, di_flags) & (SF_IMMUTABLE | SF_NOUNLINK)) !=
(SF_IMMUTABLE | SF_NOUNLINK)) {
printf("Invalid flags 0x%X for journal inode %ju\n",
- DIP(ip, di_flags), (uintmax_t)sujino);
+ DIP(dp, di_flags), (uintmax_t)sujino);
return (-1);
}
- if (DIP(ip, di_mode) != (IFREG | IREAD)) {
+ if (DIP(dp, di_mode) != (IFREG | IREAD)) {
printf("Invalid mode %o for journal inode %ju\n",
- DIP(ip, di_mode), (uintmax_t)sujino);
+ DIP(dp, di_mode), (uintmax_t)sujino);
return (-1);
}
- if (DIP(ip, di_size) < SUJ_MIN) {
+ if (DIP(dp, di_size) < SUJ_MIN) {
printf("Invalid size %jd for journal inode %ju\n",
- DIP(ip, di_size), (uintmax_t)sujino);
+ DIP(dp, di_size), (uintmax_t)sujino);
return (-1);
}
- if (DIP(ip, di_modrev) != fs->fs_mtime) {
+ if (DIP(dp, di_modrev) != fs->fs_mtime) {
printf("Journal timestamp does not match fs mount time\n");
return (-1);
}
@@ -2478,7 +2116,7 @@ jblocks_next(struct jblocks *jblocks, int bytes, int *actual)
int freecnt;
int blocks;
- blocks = bytes / disk.d_bsize;
+ blocks = btodb(bytes);
jext = &jblocks->jb_extent[jblocks->jb_head];
freecnt = jext->je_blocks - jblocks->jb_off;
if (freecnt == 0) {
@@ -2490,7 +2128,7 @@ jblocks_next(struct jblocks *jblocks, int bytes, int *actual)
}
if (freecnt > blocks)
freecnt = blocks;
- *actual = freecnt * disk.d_bsize;
+ *actual = dbtob(freecnt);
daddr = jext->je_daddr + jblocks->jb_off;
return (daddr);
@@ -2504,7 +2142,7 @@ static void
jblocks_advance(struct jblocks *jblocks, int bytes)
{
- jblocks->jb_off += bytes / disk.d_bsize;
+ jblocks->jb_off += btodb(bytes);
}
static void
@@ -2595,7 +2233,7 @@ restart:
/*
* Read 1MB at a time and scan for records within this block.
*/
- if (bread(&disk, blk, &block, size) == -1) {
+ if (pread(fsreadfd, &block, size, dbtob(blk)) != size) {
err_suj("Error reading journal block %jd\n",
(intmax_t)blk);
}
@@ -2603,9 +2241,11 @@ restart:
rec = (struct jsegrec *)((uintptr_t)rec + recsize)) {
recsize = real_dev_bsize;
if (rec->jsr_time != fs->fs_mtime) {
+#ifdef notdef
if (debug)
printf("Rec time %jd != fs mtime %jd\n",
rec->jsr_time, fs->fs_mtime);
+#endif
jblocks_advance(suj_jblocks, recsize);
continue;
}
@@ -2663,53 +2303,23 @@ restart:
}
/*
- * Search a directory block for the SUJ_FILE.
- */
-static void
-suj_find(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
-{
- char block[MAXBSIZE];
- struct direct *dp;
- int bytes;
- int off;
-
- if (sujino)
- return;
- bytes = lfragtosize(fs, frags);
- if (bread(&disk, fsbtodb(fs, blk), block, bytes) <= 0)
- err_suj("Failed to read UFS_ROOTINO directory block %jd\n",
- blk);
- for (off = 0; off < bytes; off += dp->d_reclen) {
- dp = (struct direct *)&block[off];
- if (dp->d_reclen == 0)
- break;
- if (dp->d_ino == 0)
- continue;
- if (dp->d_namlen != strlen(SUJ_FILE))
- continue;
- if (bcmp(dp->d_name, SUJ_FILE, dp->d_namlen) != 0)
- continue;
- sujino = dp->d_ino;
- return;
- }
-}
-
-/*
* Orchestrate the verification of a filesystem via the softupdates journal.
*/
int
suj_check(const char *filesys)
{
+ struct inodesc idesc;
+ struct csum *cgsum;
union dinode *jip;
- union dinode *ip;
+ struct inode ip;
uint64_t blocks;
- int retval;
+ int i, retval;
struct suj_seg *seg;
struct suj_seg *segn;
initsuj();
fs = &sblock;
- if (real_dev_bsize == 0 && ioctl(disk.d_fd, DIOCGSECTORSIZE,
+ if (real_dev_bsize == 0 && ioctl(fsreadfd, DIOCGSECTORSIZE,
&real_dev_bsize) == -1)
real_dev_bsize = secsize;
if (debug)
@@ -2734,26 +2344,34 @@ suj_check(const char *filesys)
}
/*
- * Find the journal inode.
+ * Search the root directory for the SUJ_FILE.
*/
- ip = ino_read(UFS_ROOTINO);
- sujino = 0;
- ino_visit(ip, UFS_ROOTINO, suj_find, 0);
- if (sujino == 0) {
+ idesc.id_type = DATA;
+ idesc.id_fix = IGNORE;
+ idesc.id_number = UFS_ROOTINO;
+ idesc.id_func = findino;
+ idesc.id_name = SUJ_FILE;
+ ginode(UFS_ROOTINO, &ip);
+ if ((ckinode(ip.i_dp, &idesc) & FOUND) == FOUND) {
+ sujino = idesc.id_parent;
+ irelse(&ip);
+ } else {
printf("Journal inode removed. Use tunefs to re-create.\n");
sblock.fs_flags &= ~FS_SUJ;
sblock.fs_sujfree = 0;
+ irelse(&ip);
return (-1);
}
/*
* Fetch the journal inode and verify it.
*/
- jip = ino_read(sujino);
+ ginode(sujino, &ip);
+ jip = ip.i_dp;
printf("** SU+J Recovering %s\n", filesys);
- if (suj_verifyino(jip) != 0)
- return (-1);
- if (!preen && !reply("USE JOURNAL"))
+ if (suj_verifyino(jip) != 0 || (!preen && !reply("USE JOURNAL"))) {
+ irelse(&ip);
return (-1);
+ }
/*
* Build a list of journal blocks in jblocks before parsing the
* available journal blocks in with suj_read().
@@ -2764,8 +2382,10 @@ suj_check(const char *filesys)
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);
+ irelse(&ip);
return (-1);
}
+ irelse(&ip);
suj_read();
jblocks_destroy(suj_jblocks);
suj_jblocks = NULL;
@@ -2785,16 +2405,23 @@ suj_check(const char *filesys)
if (preen == 0 && (jrecs > 0 || jbytes > 0) && reply("WRITE CHANGES") == 0)
return (0);
/*
- * To remain idempotent with partial truncations the free bitmaps
- * must be written followed by indirect blocks and lastly inode
- * blocks. This preserves access to the modified pointers until
- * they are freed.
+ * Recompute the fs summary info from correct cs summaries.
*/
- cg_apply(cg_write);
- dblk_write();
- cg_apply(cg_write_inos);
- /* Write back superblock. */
- closedisk(filesys);
+ bzero(&fs->fs_cstotal, sizeof(struct csum_total));
+ for (i = 0; i < fs->fs_ncg; i++) {
+ cgsum = &fs->fs_cs(fs, i);
+ fs->fs_cstotal.cs_nffree += cgsum->cs_nffree;
+ fs->fs_cstotal.cs_nbfree += cgsum->cs_nbfree;
+ fs->fs_cstotal.cs_nifree += cgsum->cs_nifree;
+ fs->fs_cstotal.cs_ndir += cgsum->cs_ndir;
+ }
+ fs->fs_pendinginodes = 0;
+ fs->fs_pendingblocks = 0;
+ fs->fs_clean = 1;
+ fs->fs_time = time(NULL);
+ fs->fs_mtime = time(NULL);
+ 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);
@@ -2810,12 +2437,9 @@ initsuj(void)
{
int i;
- for (i = 0; i < SUJ_HASHSIZE; i++) {
+ for (i = 0; i < HASHSIZE; i++)
LIST_INIT(&cghash[i]);
- LIST_INIT(&dbhash[i]);
- }
lastcg = NULL;
- lastblk = NULL;
TAILQ_INIT(&allsegs);
oldseq = 0;
fs = NULL;
diff --git a/sbin/fsdb/fsdb.c b/sbin/fsdb/fsdb.c
index ffffa389a206..785aeb2b5a75 100644
--- a/sbin/fsdb/fsdb.c
+++ b/sbin/fsdb/fsdb.c
@@ -63,6 +63,10 @@ static int find_blks64(uint64_t *buf, int size, uint64_t *blknum);
static int find_indirblks32(uint32_t blk, int ind_level, uint32_t *blknum);
static int find_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum);
+struct inode curip;
+union dinode *curinode;
+ino_t curinum, ocurrent;
+
static void
usage(void)
{
@@ -120,7 +124,7 @@ main(int argc, char *argv[])
ckfini(0);
printf("*** FILE SYSTEM MARKED DIRTY\n");
printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
- printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
+ printf("*** IF IT IS MOUNTED, RE-MOUNT WITH -u -o reload\n");
}
exit(rval);
}
@@ -220,6 +224,16 @@ prompt(EditLine *el)
return pstring;
}
+static void
+setcurinode(ino_t inum)
+{
+
+ if (curip.i_number != 0)
+ irelse(&curip);
+ ginode(inum, &curip);
+ curinode = curip.i_dp;
+ curinum = inum;
+}
int
cmdloop(void)
@@ -234,8 +248,7 @@ cmdloop(void)
EditLine *elptr;
HistEvent he;
- curinode = ginode(UFS_ROOTINO);
- curinum = UFS_ROOTINO;
+ setcurinode(UFS_ROOTINO);
printactive(0);
hist = history_init();
@@ -287,20 +300,20 @@ cmdloop(void)
} else
rval = 0;
free(line);
- if (rval < 0)
+ if (rval < 0) {
/* user typed "quit" */
+ irelse(&curip);
return 0;
+ }
if (rval)
warnx("rval was %d", rval);
}
el_end(elptr);
history_end(hist);
+ irelse(&curip);
return rval;
}
-union dinode *curinode;
-ino_t curinum, ocurrent;
-
#define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \
if (inum < UFS_ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
printf("inode %ju out of range; range is [%ju,%ju]\n", \
@@ -317,33 +330,30 @@ CMDFUNCSTART(focus)
char *cp;
GETINUM(1,inum);
- curinode = ginode(inum);
ocurrent = curinum;
- curinum = inum;
+ setcurinode(inum);
printactive(0);
return 0;
}
CMDFUNCSTART(back)
{
- curinum = ocurrent;
- curinode = ginode(curinum);
+ setcurinode(ocurrent);
printactive(0);
return 0;
}
CMDFUNCSTART(zapi)
{
+ struct inode ip;
ino_t inum;
- union dinode *dp;
char *cp;
GETINUM(1,inum);
- dp = ginode(inum);
- clearinode(dp);
- inodirty(dp);
- if (curinode) /* re-set after potential change */
- curinode = ginode(curinum);
+ ginode(inum, &ip);
+ clearinode(ip.i_dp);
+ inodirty(&ip);
+ irelse(&ip);
return 0;
}
@@ -371,7 +381,7 @@ CMDFUNCSTART(uplink)
DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1);
printf("inode %ju link count now %d\n",
(uintmax_t)curinum, DIP(curinode, di_nlink));
- inodirty(curinode);
+ inodirty(&curip);
return 0;
}
@@ -382,7 +392,7 @@ CMDFUNCSTART(downlink)
DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1);
printf("inode %ju link count now %d\n",
(uintmax_t)curinum, DIP(curinode, di_nlink));
- inodirty(curinode);
+ inodirty(&curip);
return 0;
}
@@ -431,7 +441,6 @@ CMDFUNCSTART(ls)
idesc.id_type = DATA;
idesc.id_fix = IGNORE;
ckinode(curinode, &idesc);
- curinode = ginode(curinum);
return 0;
}
@@ -511,8 +520,7 @@ CMDFUNCSTART(findblk)
goto end;
}
/* Get on-disk inode aka dinode. */
- curinum = inum;
- curinode = ginode(inum);
+ setcurinode(inum);
/* Find IFLNK dinode with allocated data blocks. */
switch (DIP(curinode, di_mode) & IFMT) {
case IFDIR:
@@ -564,8 +572,7 @@ CMDFUNCSTART(findblk)
}
}
end:
- curinum = ocurrent;
- curinode = ginode(curinum);
+ setcurinode(ocurrent);
if (is_ufs2)
free(wantedblk64);
else
@@ -713,8 +720,7 @@ dolookup(char *name)
idesc.id_type = DATA;
idesc.id_fix = IGNORE;
if (ckinode(curinode, &idesc) & FOUND) {
- curinum = idesc.id_parent;
- curinode = ginode(curinum);
+ setcurinode(idesc.id_parent);
printactive(0);
return 1;
} else {
@@ -733,8 +739,7 @@ CMDFUNCSTART(focusname)
ocurrent = curinum;
if (argv[1][0] == '/') {
- curinum = UFS_ROOTINO;
- curinode = ginode(UFS_ROOTINO);
+ setcurinode(UFS_ROOTINO);
} else {
if (!checkactivedir())
return 1;
@@ -745,7 +750,6 @@ CMDFUNCSTART(focusname)
printf("component `%s': ", val);
fflush(stdout);
if (!dolookup(val)) {
- curinode = ginode(curinum);
return(1);
}
}
@@ -768,7 +772,6 @@ CMDFUNCSTART(ln)
printf("Ino %ju entered as `%s'\n", (uintmax_t)inum, argv[2]);
else
printf("could not enter name? weird.\n");
- curinode = ginode(curinum);
return rval;
}
@@ -920,7 +923,7 @@ CMDFUNCSTART(newtype)
}
DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT);
DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type);
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return 0;
}
@@ -941,7 +944,7 @@ CMDFUNCSTART(chlen)
}
DIP_SET(curinode, di_size, len);
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return rval;
}
@@ -963,7 +966,7 @@ CMDFUNCSTART(chmode)
DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777);
DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits);
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return rval;
}
@@ -988,7 +991,7 @@ CMDFUNCSTART(chaflags)
return(1);
}
DIP_SET(curinode, di_flags, flags);
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return rval;
}
@@ -1013,7 +1016,7 @@ CMDFUNCSTART(chgen)
return(1);
}
DIP_SET(curinode, di_gen, gen);
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return rval;
}
@@ -1038,7 +1041,7 @@ CMDFUNCSTART(chsize)
return(1);
}
DIP_SET(curinode, di_size, size);
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return rval;
}
@@ -1063,7 +1066,7 @@ CMDFUNCSTART(linkcount)
}
DIP_SET(curinode, di_nlink, lcnt);
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return rval;
}
@@ -1090,7 +1093,7 @@ CMDFUNCSTART(chowner)
}
DIP_SET(curinode, di_uid, uid);
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return rval;
}
@@ -1116,7 +1119,7 @@ CMDFUNCSTART(chgroup)
}
DIP_SET(curinode, di_gid, gid);
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return rval;
}
@@ -1185,7 +1188,7 @@ CMDFUNCSTART(chbtime)
return 1;
curinode->dp2.di_birthtime = _time_to_time64(secs);
curinode->dp2.di_birthnsec = nsecs;
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return 0;
}
@@ -1202,7 +1205,7 @@ CMDFUNCSTART(chmtime)
else
curinode->dp2.di_mtime = _time_to_time64(secs);
DIP_SET(curinode, di_mtimensec, nsecs);
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return 0;
}
@@ -1219,7 +1222,7 @@ CMDFUNCSTART(chatime)
else
curinode->dp2.di_atime = _time_to_time64(secs);
DIP_SET(curinode, di_atimensec, nsecs);
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return 0;
}
@@ -1236,7 +1239,7 @@ CMDFUNCSTART(chctime)
else
curinode->dp2.di_ctime = _time_to_time64(secs);
DIP_SET(curinode, di_ctimensec, nsecs);
- inodirty(curinode);
+ inodirty(&curip);
printactive(0);
return 0;
}
diff --git a/sbin/fsdb/fsdb.h b/sbin/fsdb/fsdb.h
index b8dc1e0a2dd6..fee35886f675 100644
--- a/sbin/fsdb/fsdb.h
+++ b/sbin/fsdb/fsdb.h
@@ -52,6 +52,7 @@ struct cmdtable {
#define FL_ST 0x0002 /* resplit final string if argc > maxargc */
int (*handler)(int argc, char *argv[]);
};
+extern struct inode curip;
extern union dinode *curinode;
extern ino_t curinum;