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. @@ -132,16 +132,35 @@ extern struct inostatlist { struct inostat *il_stat;/* inostat info for this cylinder group */ } *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 -flush(int fd, struct bufarea *bp) +brelse(struct bufarea *bp) { - if (!bp->b_dirty) + 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_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,13 +581,101 @@ 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; } + /* + * 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; + /* 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); + free((char *)bp); + } + if (numbufs != cnt) + errx(EEXIT, "panic: lost %d buffers", numbufs - cnt); + /* 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 && @@ -438,28 +686,7 @@ ckfini(int markclean) sbdirty(); flush(fswritefd, &sblk); } - 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); - cnt++; - flush(fswritefd, bp); - free(bp->b_un.b_buf); - free((char *)bp); - } - 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); - } - 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); @@ -955,6 +1204,19 @@ dofix(struct inodesc *idesc, const char *msg) #include +/* + * 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, 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 __FBSDID("$FreeBSD$"); -#include -#include -#include +#include #include - -#include -#include #include - -#include -#include -#include -#include -#include -#include -#include - #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,76 +95,66 @@ 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,25 +229,141 @@ 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 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg) 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 @@ pass2check(struct inodesc *idesc) 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 +#include #include #include @@ -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) @@ -190,38 +165,6 @@ err_suj(const char * restrict fmt, ...) longjmp(jmpbuf, -1); } -/* - * 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); } /* @@ -712,110 +470,6 @@ blk_isfree(ufs2_daddr_t bno) return ffs_isblock(fs, cg_blksfree(sc->sc_cgp), dtogd(fs, 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 @@ -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 @@ indir_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, uint64_t *frags, * 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 @@ suj_read(void) /* * 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 @@ suj_read(void) 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; } @@ -2662,54 +2302,24 @@ suj_read(void) } } -/* - * 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;