void clearinode(ino_t inumber) { struct ubuf *bp; IFILE *ifp; daddr_t daddr; /* Send cleared inode to the free list */ LFS_IENTRY(ifp, fs, inumber, bp); daddr = ifp->if_daddr; if (daddr == LFS_UNUSED_DADDR) { brelse(bp, 0); return; } ifp->if_daddr = LFS_UNUSED_DADDR; ifp->if_nextfree = fs->lfs_freehd; fs->lfs_freehd = inumber; sbdirty(); VOP_BWRITE(bp); /* * update segment usage. */ if (daddr != LFS_UNUSED_DADDR) { SEGUSE *sup; u_int32_t oldsn = dtosn(fs, daddr); seg_table[oldsn].su_nbytes -= DINODE1_SIZE; LFS_SEGENTRY(sup, fs, oldsn, bp); sup->su_nbytes -= DINODE1_SIZE; LFS_WRITESEGENTRY(sup, fs, oldsn, bp); /* Ifile */ } }
static void lfs_finalize_seguse(struct lfs *fs, void *v) { SEGUSE *sup; struct buf *bp; struct segdelta *sd; LIST_HEAD(, segdelta) *hd = v; ASSERT_SEGLOCK(fs); while((sd = LIST_FIRST(hd)) != NULL) { LIST_REMOVE(sd, list); LFS_SEGENTRY(sup, fs, sd->segnum, bp); if (sd->num > sup->su_nbytes) { printf("lfs_finalize_seguse: segment %ld short by %ld\n", sd->segnum, (long)(sd->num - sup->su_nbytes)); panic("lfs_finalize_seguse: negative bytes"); sup->su_nbytes = sd->num; } sup->su_nbytes -= sd->num; LFS_WRITESEGENTRY(sup, fs, sd->segnum, bp); free(sd, M_SEGMENT); } }
static int update_inoblk(struct lfs *fs, daddr_t offset, kauth_cred_t cred, struct lwp *l) { struct vnode *devvp, *vp; struct inode *ip; struct ufs1_dinode *dip; struct buf *dbp, *ibp; int error; daddr_t daddr; IFILE *ifp; SEGUSE *sup; devvp = VTOI(fs->lfs_ivnode)->i_devvp; /* * Get the inode, update times and perms. * DO NOT update disk blocks, we do that separately. */ error = bread(devvp, fsbtodb(fs, offset), fs->lfs_ibsize, cred, 0, &dbp); if (error) { DLOG((DLOG_RF, "update_inoblk: bread returned %d\n", error)); return error; } dip = ((struct ufs1_dinode *)(dbp->b_data)) + INOPB(fs); while (--dip >= (struct ufs1_dinode *)dbp->b_data) { if (dip->di_inumber > LFS_IFILE_INUM) { error = lfs_rf_valloc(fs, dip->di_inumber, dip->di_gen, l, &vp); if (error) { DLOG((DLOG_RF, "update_inoblk: lfs_rf_valloc" " returned %d\n", error)); continue; } ip = VTOI(vp); if (dip->di_size != ip->i_size) lfs_truncate(vp, dip->di_size, 0, NOCRED); /* Get mode, link count, size, and times */ memcpy(ip->i_din.ffs1_din, dip, offsetof(struct ufs1_dinode, di_db[0])); /* Then the rest, except di_blocks */ ip->i_flags = ip->i_ffs1_flags = dip->di_flags; ip->i_gen = ip->i_ffs1_gen = dip->di_gen; ip->i_uid = ip->i_ffs1_uid = dip->di_uid; ip->i_gid = ip->i_ffs1_gid = dip->di_gid; ip->i_mode = ip->i_ffs1_mode; ip->i_nlink = ip->i_ffs1_nlink; ip->i_size = ip->i_ffs1_size; LFS_SET_UINO(ip, IN_CHANGE | IN_UPDATE); /* Re-initialize to get type right */ ufs_vinit(vp->v_mount, lfs_specop_p, lfs_fifoop_p, &vp); vput(vp); /* Record change in location */ LFS_IENTRY(ifp, fs, dip->di_inumber, ibp); daddr = ifp->if_daddr; ifp->if_daddr = dbtofsb(fs, dbp->b_blkno); error = LFS_BWRITE_LOG(ibp); /* Ifile */ /* And do segment accounting */ if (dtosn(fs, daddr) != dtosn(fs, dbtofsb(fs, dbp->b_blkno))) { if (daddr > 0) { LFS_SEGENTRY(sup, fs, dtosn(fs, daddr), ibp); sup->su_nbytes -= sizeof (struct ufs1_dinode); LFS_WRITESEGENTRY(sup, fs, dtosn(fs, daddr), ibp); } LFS_SEGENTRY(sup, fs, dtosn(fs, dbtofsb(fs, dbp->b_blkno)), ibp); sup->su_nbytes += sizeof (struct ufs1_dinode); LFS_WRITESEGENTRY(sup, fs, dtosn(fs, dbtofsb(fs, dbp->b_blkno)), ibp); } } }
/* * Load the appropriate indirect block, and change the appropriate pointer. * Mark the block dirty. Do segment and avail accounting. */ static int update_meta(struct lfs *fs, ino_t ino, int vers, daddr_t lbn, daddr_t ndaddr, size_t size, struct lwp *l) { int error; struct vnode *vp; struct inode *ip; #ifdef DEBUG daddr_t odaddr; struct indir a[NIADDR]; int num; int i; #endif /* DEBUG */ struct buf *bp; SEGUSE *sup; KASSERT(lbn >= 0); /* no indirect blocks */ if ((error = lfs_rf_valloc(fs, ino, vers, l, &vp)) != 0) { DLOG((DLOG_RF, "update_meta: ino %d: lfs_rf_valloc" " returned %d\n", ino, error)); return error; } if ((error = lfs_balloc(vp, (lbn << fs->lfs_bshift), size, NOCRED, 0, &bp)) != 0) { vput(vp); return (error); } /* No need to write, the block is already on disk */ if (bp->b_oflags & BO_DELWRI) { LFS_UNLOCK_BUF(bp); fs->lfs_avail += btofsb(fs, bp->b_bcount); } brelse(bp, BC_INVAL); /* * Extend the file, if it is not large enough already. * XXX this is not exactly right, we don't know how much of the * XXX last block is actually used. We hope that an inode will * XXX appear later to give the correct size. */ ip = VTOI(vp); if (ip->i_size <= (lbn << fs->lfs_bshift)) { u_int64_t newsize; if (lbn < NDADDR) newsize = ip->i_ffs1_size = (lbn << fs->lfs_bshift) + (size - fs->lfs_fsize) + 1; else newsize = ip->i_ffs1_size = (lbn << fs->lfs_bshift) + 1; if (ip->i_size < newsize) { ip->i_size = newsize; /* * tell vm our new size for the case the inode won't * appear later. */ uvm_vnp_setsize(vp, newsize); } } lfs_update_single(fs, NULL, vp, lbn, ndaddr, size); LFS_SEGENTRY(sup, fs, dtosn(fs, ndaddr), bp); sup->su_nbytes += size; LFS_WRITESEGENTRY(sup, fs, dtosn(fs, ndaddr), bp); /* differences here should be due to UNWRITTEN indirect blocks. */ KASSERT((lblkno(fs, ip->i_size) > NDADDR && ip->i_lfs_effnblks == ip->i_ffs1_blocks) || ip->i_lfs_effnblks >= ip->i_ffs1_blocks); #ifdef DEBUG /* Now look again to make sure it worked */ ufs_bmaparray(vp, lbn, &odaddr, &a[0], &num, NULL, NULL); for (i = num; i > 0; i--) { if (!a[i].in_exists) panic("update_meta: absent %d lv indirect block", i); } if (dbtofsb(fs, odaddr) != ndaddr) DLOG((DLOG_RF, "update_meta: failed setting ino %d lbn %" PRId64 " to %" PRId64 "\n", ino, lbn, ndaddr)); #endif /* DEBUG */ vput(vp); return 0; }
/* * Actually mark the segment clean. * Must be called with the segment lock held. */ int lfs_do_segclean(struct lfs *fs, unsigned long segnum) { extern int lfs_dostats; struct buf *bp; CLEANERINFO *cip; SEGUSE *sup; if (lfs_dtosn(fs, fs->lfs_curseg) == segnum) { return (EBUSY); } LFS_SEGENTRY(sup, fs, segnum, bp); if (sup->su_nbytes) { DLOG((DLOG_CLEAN, "lfs_segclean: not cleaning segment %lu:" " %d live bytes\n", segnum, sup->su_nbytes)); brelse(bp, 0); return (EBUSY); } if (sup->su_flags & SEGUSE_ACTIVE) { DLOG((DLOG_CLEAN, "lfs_segclean: not cleaning segment %lu:" " segment is active\n", segnum)); brelse(bp, 0); return (EBUSY); } if (!(sup->su_flags & SEGUSE_DIRTY)) { DLOG((DLOG_CLEAN, "lfs_segclean: not cleaning segment %lu:" " segment is already clean\n", segnum)); brelse(bp, 0); return (EALREADY); } fs->lfs_avail += lfs_segtod(fs, 1); if (sup->su_flags & SEGUSE_SUPERBLOCK) fs->lfs_avail -= lfs_btofsb(fs, LFS_SBPAD); if (fs->lfs_version > 1 && segnum == 0 && fs->lfs_start < lfs_btofsb(fs, LFS_LABELPAD)) fs->lfs_avail -= lfs_btofsb(fs, LFS_LABELPAD) - fs->lfs_start; mutex_enter(&lfs_lock); fs->lfs_bfree += sup->su_nsums * lfs_btofsb(fs, fs->lfs_sumsize) + lfs_btofsb(fs, sup->su_ninos * fs->lfs_ibsize); fs->lfs_dmeta -= sup->su_nsums * lfs_btofsb(fs, fs->lfs_sumsize) + lfs_btofsb(fs, sup->su_ninos * fs->lfs_ibsize); if (fs->lfs_dmeta < 0) fs->lfs_dmeta = 0; mutex_exit(&lfs_lock); sup->su_flags &= ~SEGUSE_DIRTY; LFS_WRITESEGENTRY(sup, fs, segnum, bp); LFS_CLEANERINFO(cip, fs, bp); ++cip->clean; --cip->dirty; fs->lfs_nclean = cip->clean; cip->bfree = fs->lfs_bfree; mutex_enter(&lfs_lock); cip->avail = fs->lfs_avail - fs->lfs_ravail - fs->lfs_favail; wakeup(&fs->lfs_avail); mutex_exit(&lfs_lock); (void) LFS_BWRITE_LOG(bp); if (lfs_dostats) ++lfs_stats.segs_reclaimed; return (0); }
int setup(const char *dev) { long bmapsize; struct stat statb; int doskipclean; u_int64_t maxfilesize; int open_flags; struct uvnode *ivp; struct ubuf *bp; int i, isdirty; long sn, curseg; SEGUSE *sup; havesb = 0; doskipclean = skipclean; if (stat(dev, &statb) < 0) { pfatal("Can't stat %s: %s\n", dev, strerror(errno)); return (0); } if (!S_ISCHR(statb.st_mode) && skipclean) { pfatal("%s is not a character device", dev); if (reply("CONTINUE") == 0) return (0); } if (nflag) open_flags = O_RDONLY; else open_flags = O_RDWR; if ((fsreadfd = open(dev, open_flags)) < 0) { pfatal("Can't open %s: %s\n", dev, strerror(errno)); return (0); } if (nflag) { if (preen) pfatal("NO WRITE ACCESS"); printf("** %s (NO WRITE)\n", dev); quiet = 0; } else if (!preen && !quiet) printf("** %s\n", dev); fsmodified = 0; lfdir = 0; /* Initialize time in case we have to write */ time(&write_time); bufinit(0); /* XXX we could make a better guess */ fs = lfs_init(fsreadfd, bflag, idaddr, 0, debug); if (fs == NULL) { if (preen) printf("%s: ", cdevname()); errexit("BAD SUPER BLOCK OR IFILE INODE NOT FOUND"); } /* Resize buffer cache now that we have a superblock to guess from. */ bufrehash((fs->lfs_segtabsz + maxino / fs->lfs_ifpb) << 4); if (fs->lfs_pflags & LFS_PF_CLEAN) { if (doskipclean) { if (!quiet) pwarn("%sile system is clean; not checking\n", preen ? "f" : "** F"); return (-1); } if (!preen) pwarn("** File system is already clean\n"); } if (idaddr) { daddr_t tdaddr; SEGSUM *sp; FINFO *fp; int bc; if (debug) pwarn("adjusting offset, serial for -i 0x%lx\n", (unsigned long)idaddr); tdaddr = lfs_sntod(fs, lfs_dtosn(fs, idaddr)); if (lfs_sntod(fs, lfs_dtosn(fs, tdaddr)) == tdaddr) { if (tdaddr == fs->lfs_start) tdaddr += lfs_btofsb(fs, LFS_LABELPAD); for (i = 0; i < LFS_MAXNUMSB; i++) { if (fs->lfs_sboffs[i] == tdaddr) tdaddr += lfs_btofsb(fs, LFS_SBPAD); if (fs->lfs_sboffs[i] > tdaddr) break; } } fs->lfs_offset = tdaddr; if (debug) pwarn("begin with offset/serial 0x%x/%d\n", (int)fs->lfs_offset, (int)fs->lfs_serial); while (tdaddr < idaddr) { bread(fs->lfs_devvp, LFS_FSBTODB(fs, tdaddr), fs->lfs_sumsize, NULL, 0, &bp); sp = (SEGSUM *)bp->b_data; if (sp->ss_sumsum != cksum(&sp->ss_datasum, fs->lfs_sumsize - sizeof(sp->ss_sumsum))) { brelse(bp, 0); if (debug) printf("bad cksum at %x\n", (unsigned)tdaddr); break; } fp = (FINFO *)(sp + 1); bc = howmany(sp->ss_ninos, LFS_INOPB(fs)) << (fs->lfs_version > 1 ? fs->lfs_ffshift : fs->lfs_bshift); for (i = 0; i < sp->ss_nfinfo; i++) { bc += fp->fi_lastlength + ((fp->fi_nblocks - 1) << fs->lfs_bshift); fp = (FINFO *)(fp->fi_blocks + fp->fi_nblocks); } tdaddr += lfs_btofsb(fs, bc) + 1; fs->lfs_offset = tdaddr; fs->lfs_serial = sp->ss_serial + 1; brelse(bp, 0); } /* * Set curseg, nextseg appropriately -- inlined from * lfs_newseg() */ curseg = lfs_dtosn(fs, fs->lfs_offset); fs->lfs_curseg = lfs_sntod(fs, curseg); for (sn = curseg + fs->lfs_interleave;;) { sn = (sn + 1) % fs->lfs_nseg; if (sn == curseg) errx(1, "init: no clean segments"); LFS_SEGENTRY(sup, fs, sn, bp); isdirty = sup->su_flags & SEGUSE_DIRTY; brelse(bp, 0); if (!isdirty) break; } /* Skip superblock if necessary */ for (i = 0; i < LFS_MAXNUMSB; i++) if (fs->lfs_offset == fs->lfs_sboffs[i]) fs->lfs_offset += lfs_btofsb(fs, LFS_SBPAD); ++fs->lfs_nactive; fs->lfs_nextseg = lfs_sntod(fs, sn); if (debug) { pwarn("offset = 0x%" PRIx32 ", serial = %" PRId64 "\n", fs->lfs_offset, fs->lfs_serial); pwarn("curseg = %" PRIx32 ", nextseg = %" PRIx32 "\n", fs->lfs_curseg, fs->lfs_nextseg); } if (!nflag && !skipclean) { fs->lfs_idaddr = idaddr; fsmodified = 1; sbdirty(); } } if (debug) { pwarn("idaddr = 0x%lx\n", idaddr ? (unsigned long)idaddr : (unsigned long)fs->lfs_idaddr); pwarn("dev_bsize = %lu\n", dev_bsize); pwarn("lfs_bsize = %lu\n", (unsigned long) fs->lfs_bsize); pwarn("lfs_fsize = %lu\n", (unsigned long) fs->lfs_fsize); pwarn("lfs_frag = %lu\n", (unsigned long) fs->lfs_frag); pwarn("lfs_inopb = %lu\n", (unsigned long) fs->lfs_inopb); } if (fs->lfs_version == 1) maxfsblock = fs->lfs_size * (fs->lfs_bsize / dev_bsize); else maxfsblock = fs->lfs_size; maxfilesize = calcmaxfilesize(fs->lfs_bshift); if (/* fs->lfs_minfree < 0 || */ fs->lfs_minfree > 99) { pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK", fs->lfs_minfree); if (reply("SET TO DEFAULT") == 1) { fs->lfs_minfree = 10; sbdirty(); } } if (fs->lfs_bmask != fs->lfs_bsize - 1) { pwarn("INCORRECT BMASK=0x%x IN SUPERBLOCK (SHOULD BE 0x%x)", (unsigned int) fs->lfs_bmask, (unsigned int) fs->lfs_bsize - 1); fs->lfs_bmask = fs->lfs_bsize - 1; if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { sbdirty(); } } if (fs->lfs_ffmask != fs->lfs_fsize - 1) { pwarn("INCORRECT FFMASK=%" PRId64 " IN SUPERBLOCK", fs->lfs_ffmask); fs->lfs_ffmask = fs->lfs_fsize - 1; if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { sbdirty(); } } if (fs->lfs_fbmask != (1 << fs->lfs_fbshift) - 1) { pwarn("INCORRECT FBMASK=%" PRId64 " IN SUPERBLOCK", fs->lfs_fbmask); fs->lfs_fbmask = (1 << fs->lfs_fbshift) - 1; if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { sbdirty(); } } if (fs->lfs_maxfilesize != maxfilesize) { pwarn( "INCORRECT MAXFILESIZE=%llu IN SUPERBLOCK (SHOULD BE %llu WITH BSHIFT %d)", (unsigned long long) fs->lfs_maxfilesize, (unsigned long long) maxfilesize, (int)fs->lfs_bshift); if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { fs->lfs_maxfilesize = maxfilesize; sbdirty(); } } if (fs->lfs_maxsymlinklen != ULFS1_MAXSYMLINKLEN) { pwarn("INCORRECT MAXSYMLINKLEN=%d IN SUPERBLOCK", fs->lfs_maxsymlinklen); fs->lfs_maxsymlinklen = ULFS1_MAXSYMLINKLEN; if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { sbdirty(); } } /* * Read in the Ifile; we'll be using it a lot. * XXX If the Ifile is corrupted we are in bad shape. We need to * XXX run through the segment headers of the entire disk to * XXX reconstruct the inode table, then pretend all segments are * XXX dirty while we do the rest. */ ivp = fs->lfs_ivnode; maxino = ((VTOI(ivp)->i_ffs1_size - (fs->lfs_cleansz + fs->lfs_segtabsz) * fs->lfs_bsize) / fs->lfs_bsize) * fs->lfs_ifpb; if (debug) pwarn("maxino = %llu\n", (unsigned long long)maxino); for (i = 0; i < VTOI(ivp)->i_ffs1_size; i += fs->lfs_bsize) { bread(ivp, i >> fs->lfs_bshift, fs->lfs_bsize, NOCRED, 0, &bp); /* XXX check B_ERROR */ brelse(bp, 0); } /* * allocate and initialize the necessary maps */ din_table = ecalloc(maxino, sizeof(*din_table)); seg_table = ecalloc(fs->lfs_nseg, sizeof(SEGUSE)); /* Get segment flags */ for (i = 0; i < fs->lfs_nseg; i++) { LFS_SEGENTRY(sup, fs, i, bp); seg_table[i].su_flags = sup->su_flags & ~SEGUSE_ACTIVE; if (preen) seg_table[i].su_nbytes = sup->su_nbytes; brelse(bp, 0); } /* Initialize Ifile entry */ din_table[fs->lfs_ifile] = fs->lfs_idaddr; seg_table[lfs_dtosn(fs, fs->lfs_idaddr)].su_nbytes += LFS_DINODE1_SIZE; #ifndef VERBOSE_BLOCKMAP bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(int16_t)); blockmap = ecalloc(bmapsize, sizeof(char)); #else bmapsize = maxfsblock * sizeof(ino_t); blockmap = ecalloc(maxfsblock, sizeof(ino_t)); #endif statemap = ecalloc(maxino, sizeof(char)); typemap = ecalloc(maxino, sizeof(char)); lncntp = ecalloc(maxino, sizeof(int16_t)); if (preen) { n_files = fs->lfs_nfiles; n_blks = fs->lfs_dsize - fs->lfs_bfree; numdirs = maxino; inplast = 0; listmax = numdirs + 10; inpsort = ecalloc(listmax, sizeof(struct inoinfo *)); inphead = ecalloc(numdirs, sizeof(struct inoinfo *)); } return (1); ckfini(0); return (0); }
int setup(const char *dev) { #ifndef VERBOSE_BLOCKMAP long bmapsize; #endif struct stat statb; int doskipclean; u_int64_t maxfilesize; int open_flags; struct uvnode *ivp; struct ubuf *bp; int i, isdirty; long sn, curseg; SEGUSE *sup; size_t sumstart; havesb = 0; doskipclean = skipclean; if (stat(dev, &statb) < 0) { pfatal("Can't stat %s: %s\n", dev, strerror(errno)); return (0); } if (!S_ISCHR(statb.st_mode) && skipclean) { pfatal("%s is not a character device", dev); if (reply("CONTINUE") == 0) return (0); } if (nflag) open_flags = O_RDONLY; else open_flags = O_RDWR; if ((fsreadfd = open(dev, open_flags)) < 0) { pfatal("Can't open %s: %s\n", dev, strerror(errno)); return (0); } if (nflag) { if (preen) pfatal("NO WRITE ACCESS"); printf("** %s (NO WRITE)\n", dev); quiet = 0; } else if (!preen && !quiet) printf("** %s\n", dev); fsmodified = 0; lfdir = 0; /* Initialize time in case we have to write */ time(&write_time); bufinit(0); /* XXX we could make a better guess */ fs = lfs_init(fsreadfd, bflag, idaddr, 0, debug); if (fs == NULL) { if (preen) printf("%s: ", cdevname()); errexit("BAD SUPER BLOCK OR IFILE INODE NOT FOUND"); } /* Resize buffer cache now that we have a superblock to guess from. */ bufrehash((lfs_sb_getsegtabsz(fs) + maxino / lfs_sb_getifpb(fs)) << 4); if (lfs_sb_getpflags(fs) & LFS_PF_CLEAN) { if (doskipclean) { if (!quiet) pwarn("%sile system is clean; not checking\n", preen ? "f" : "** F"); return (-1); } if (!preen) pwarn("** File system is already clean\n"); } if (idaddr) { daddr_t tdaddr; SEGSUM *sp; FINFO *fp; int bc; if (debug) pwarn("adjusting offset, serial for -i 0x%jx\n", (uintmax_t)idaddr); tdaddr = lfs_sntod(fs, lfs_dtosn(fs, idaddr)); if (lfs_sntod(fs, lfs_dtosn(fs, tdaddr)) == tdaddr) { if (tdaddr == lfs_sb_gets0addr(fs)) tdaddr += lfs_btofsb(fs, LFS_LABELPAD); for (i = 0; i < LFS_MAXNUMSB; i++) { if (lfs_sb_getsboff(fs, i) == tdaddr) tdaddr += lfs_btofsb(fs, LFS_SBPAD); if (lfs_sb_getsboff(fs, i) > tdaddr) break; } } lfs_sb_setoffset(fs, tdaddr); if (debug) pwarn("begin with offset/serial 0x%jx/%jd\n", (uintmax_t)lfs_sb_getoffset(fs), (intmax_t)lfs_sb_getserial(fs)); while (tdaddr < idaddr) { bread(fs->lfs_devvp, LFS_FSBTODB(fs, tdaddr), lfs_sb_getsumsize(fs), 0, &bp); sp = (SEGSUM *)bp->b_data; sumstart = lfs_ss_getsumstart(fs); if (lfs_ss_getsumsum(fs, sp) != cksum((char *)sp + sumstart, lfs_sb_getsumsize(fs) - sumstart)) { brelse(bp, 0); if (debug) printf("bad cksum at %jx\n", (uintmax_t)tdaddr); break; } fp = SEGSUM_FINFOBASE(fs, sp); bc = howmany(lfs_ss_getninos(fs, sp), LFS_INOPB(fs)) << (lfs_sb_getversion(fs) > 1 ? lfs_sb_getffshift(fs) : lfs_sb_getbshift(fs)); for (i = 0; i < lfs_ss_getnfinfo(fs, sp); i++) { bc += lfs_fi_getlastlength(fs, fp) + ((lfs_fi_getnblocks(fs, fp) - 1) << lfs_sb_getbshift(fs)); fp = NEXT_FINFO(fs, fp); } tdaddr += lfs_btofsb(fs, bc) + 1; lfs_sb_setoffset(fs, tdaddr); lfs_sb_setserial(fs, lfs_ss_getserial(fs, sp) + 1); brelse(bp, 0); } /* * Set curseg, nextseg appropriately -- inlined from * lfs_newseg() */ curseg = lfs_dtosn(fs, lfs_sb_getoffset(fs)); lfs_sb_setcurseg(fs, lfs_sntod(fs, curseg)); for (sn = curseg + lfs_sb_getinterleave(fs);;) { sn = (sn + 1) % lfs_sb_getnseg(fs); if (sn == curseg) errx(1, "init: no clean segments"); LFS_SEGENTRY(sup, fs, sn, bp); isdirty = sup->su_flags & SEGUSE_DIRTY; brelse(bp, 0); if (!isdirty) break; } /* Skip superblock if necessary */ for (i = 0; i < LFS_MAXNUMSB; i++) if (lfs_sb_getoffset(fs) == lfs_sb_getsboff(fs, i)) lfs_sb_addoffset(fs, lfs_btofsb(fs, LFS_SBPAD)); ++fs->lfs_nactive; lfs_sb_setnextseg(fs, lfs_sntod(fs, sn)); if (debug) { pwarn("offset = 0x%" PRIx64 ", serial = %" PRIu64 "\n", lfs_sb_getoffset(fs), lfs_sb_getserial(fs)); pwarn("curseg = %" PRIx64 ", nextseg = %" PRIx64 "\n", lfs_sb_getcurseg(fs), lfs_sb_getnextseg(fs)); } if (!nflag && !skipclean) { lfs_sb_setidaddr(fs, idaddr); fsmodified = 1; sbdirty(); } } if (debug) { pwarn("idaddr = 0x%jx\n", idaddr ? (uintmax_t)idaddr : (uintmax_t)lfs_sb_getidaddr(fs)); pwarn("dev_bsize = %lu\n", dev_bsize); pwarn("lfs_bsize = %lu\n", (unsigned long) lfs_sb_getbsize(fs)); pwarn("lfs_fsize = %lu\n", (unsigned long) lfs_sb_getfsize(fs)); pwarn("lfs_frag = %lu\n", (unsigned long) lfs_sb_getfrag(fs)); pwarn("lfs_inopb = %lu\n", (unsigned long) lfs_sb_getinopb(fs)); } if (lfs_sb_getversion(fs) == 1) maxfsblock = lfs_blkstofrags(fs, lfs_sb_getsize(fs)); else maxfsblock = lfs_sb_getsize(fs); maxfilesize = calcmaxfilesize(lfs_sb_getbshift(fs)); if (/* lfs_sb_getminfree(fs) < 0 || */ lfs_sb_getminfree(fs) > 99) { pfatal("IMPOSSIBLE MINFREE=%u IN SUPERBLOCK", lfs_sb_getminfree(fs)); if (reply("SET TO DEFAULT") == 1) { lfs_sb_setminfree(fs, 10); sbdirty(); } } if (lfs_sb_getbmask(fs) != lfs_sb_getbsize(fs) - 1) { pwarn("INCORRECT BMASK=0x%jx IN SUPERBLOCK (SHOULD BE 0x%x)", (uintmax_t)lfs_sb_getbmask(fs), lfs_sb_getbsize(fs) - 1); lfs_sb_setbmask(fs, lfs_sb_getbsize(fs) - 1); if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { sbdirty(); } } if (lfs_sb_getffmask(fs) != lfs_sb_getfsize(fs) - 1) { pwarn("INCORRECT FFMASK=0x%jx IN SUPERBLOCK (SHOULD BE 0x%x)", (uintmax_t)lfs_sb_getffmask(fs), lfs_sb_getfsize(fs) - 1); lfs_sb_setffmask(fs, lfs_sb_getfsize(fs) - 1); if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { sbdirty(); } } if (lfs_sb_getfbmask(fs) != (1U << lfs_sb_getfbshift(fs)) - 1) { pwarn("INCORRECT FBMASK=0x%jx IN SUPERBLOCK (SHOULD BE 0x%x)", (uintmax_t)lfs_sb_getfbmask(fs), (1U << lfs_sb_getfbshift(fs)) - 1); lfs_sb_setfbmask(fs, (1U << lfs_sb_getfbshift(fs)) - 1); if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { sbdirty(); } } if (lfs_sb_getmaxfilesize(fs) != maxfilesize) { pwarn( "INCORRECT MAXFILESIZE=%ju IN SUPERBLOCK (SHOULD BE %ju WITH BSHIFT %u)", (uintmax_t) lfs_sb_getmaxfilesize(fs), (uintmax_t) maxfilesize, lfs_sb_getbshift(fs)); if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { lfs_sb_setmaxfilesize(fs, maxfilesize); sbdirty(); } } if (lfs_sb_getmaxsymlinklen(fs) != LFS_MAXSYMLINKLEN(fs)) { pwarn("INCORRECT MAXSYMLINKLEN=%d IN SUPERBLOCK (SHOULD BE %zu)", lfs_sb_getmaxsymlinklen(fs), LFS_MAXSYMLINKLEN(fs)); lfs_sb_setmaxsymlinklen(fs, LFS_MAXSYMLINKLEN(fs)); if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { sbdirty(); } } /* * Read in the Ifile; we'll be using it a lot. * XXX If the Ifile is corrupted we are in bad shape. We need to * XXX run through the segment headers of the entire disk to * XXX reconstruct the inode table, then pretend all segments are * XXX dirty while we do the rest. */ ivp = fs->lfs_ivnode; maxino = ((lfs_dino_getsize(fs, VTOI(ivp)->i_din) - (lfs_sb_getcleansz(fs) + lfs_sb_getsegtabsz(fs)) * lfs_sb_getbsize(fs)) / lfs_sb_getbsize(fs)) * lfs_sb_getifpb(fs); if (debug) pwarn("maxino = %llu\n", (unsigned long long)maxino); for (i = 0; i < lfs_dino_getsize(fs, VTOI(ivp)->i_din); i += lfs_sb_getbsize(fs)) { bread(ivp, i >> lfs_sb_getbshift(fs), lfs_sb_getbsize(fs), 0, &bp); /* XXX check B_ERROR */ brelse(bp, 0); } /* * allocate and initialize the necessary maps */ din_table = ecalloc(maxino, sizeof(*din_table)); seg_table = ecalloc(lfs_sb_getnseg(fs), sizeof(SEGUSE)); /* Get segment flags */ for (i = 0; i < lfs_sb_getnseg(fs); i++) { LFS_SEGENTRY(sup, fs, i, bp); seg_table[i].su_flags = sup->su_flags & ~SEGUSE_ACTIVE; if (preen) seg_table[i].su_nbytes = sup->su_nbytes; brelse(bp, 0); } /* Initialize Ifile entry */ din_table[LFS_IFILE_INUM] = lfs_sb_getidaddr(fs); seg_table[lfs_dtosn(fs, lfs_sb_getidaddr(fs))].su_nbytes += DINOSIZE(fs); #ifndef VERBOSE_BLOCKMAP bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(int16_t)); blockmap = ecalloc(bmapsize, sizeof(char)); #else blockmap = ecalloc(maxfsblock, sizeof(ino_t)); #endif statemap = ecalloc(maxino, sizeof(char)); typemap = ecalloc(maxino, sizeof(char)); lncntp = ecalloc(maxino, sizeof(int16_t)); if (preen) { n_files = lfs_sb_getnfiles(fs); n_blks = lfs_sb_getdsize(fs) - lfs_sb_getbfree(fs); numdirs = maxino; inplast = 0; listmax = numdirs + 10; inpsort = ecalloc(listmax, sizeof(struct inoinfo *)); inphead = ecalloc(numdirs, sizeof(struct inoinfo *)); } return (1); ckfini(0); return (0); }
/* * Actually mark the segment clean. * Must be called with the segment lock held. */ int lfs_do_segclean(struct lfs *fs, unsigned long segnum) { extern int lfs_dostats; struct buf *bp; CLEANERINFO *cip; SEGUSE *sup; if (lfs_dtosn(fs, lfs_sb_getcurseg(fs)) == segnum) { return (EBUSY); } LFS_SEGENTRY(sup, fs, segnum, bp); if (sup->su_nbytes) { DLOG((DLOG_CLEAN, "lfs_segclean: not cleaning segment %lu:" " %d live bytes\n", segnum, sup->su_nbytes)); brelse(bp, 0); return (EBUSY); } if (sup->su_flags & SEGUSE_ACTIVE) { DLOG((DLOG_CLEAN, "lfs_segclean: not cleaning segment %lu:" " segment is active\n", segnum)); brelse(bp, 0); return (EBUSY); } if (!(sup->su_flags & SEGUSE_DIRTY)) { DLOG((DLOG_CLEAN, "lfs_segclean: not cleaning segment %lu:" " segment is already clean\n", segnum)); brelse(bp, 0); return (EALREADY); } lfs_sb_addavail(fs, lfs_segtod(fs, 1)); if (sup->su_flags & SEGUSE_SUPERBLOCK) lfs_sb_subavail(fs, lfs_btofsb(fs, LFS_SBPAD)); if (lfs_sb_getversion(fs) > 1 && segnum == 0 && lfs_sb_gets0addr(fs) < lfs_btofsb(fs, LFS_LABELPAD)) lfs_sb_subavail(fs, lfs_btofsb(fs, LFS_LABELPAD) - lfs_sb_gets0addr(fs)); mutex_enter(&lfs_lock); lfs_sb_addbfree(fs, sup->su_nsums * lfs_btofsb(fs, lfs_sb_getsumsize(fs)) + lfs_btofsb(fs, sup->su_ninos * lfs_sb_getibsize(fs))); lfs_sb_subdmeta(fs, sup->su_nsums * lfs_btofsb(fs, lfs_sb_getsumsize(fs)) + lfs_btofsb(fs, sup->su_ninos * lfs_sb_getibsize(fs))); if (lfs_sb_getdmeta(fs) < 0) lfs_sb_setdmeta(fs, 0); mutex_exit(&lfs_lock); sup->su_flags &= ~SEGUSE_DIRTY; LFS_WRITESEGENTRY(sup, fs, segnum, bp); LFS_CLEANERINFO(cip, fs, bp); lfs_ci_shiftdirtytoclean(fs, cip, 1); lfs_sb_setnclean(fs, lfs_ci_getclean(fs, cip)); mutex_enter(&lfs_lock); lfs_ci_setbfree(fs, cip, lfs_sb_getbfree(fs)); lfs_ci_setavail(fs, cip, lfs_sb_getavail(fs) - fs->lfs_ravail - fs->lfs_favail); wakeup(&fs->lfs_availsleep); mutex_exit(&lfs_lock); (void) LFS_BWRITE_LOG(bp); if (lfs_dostats) ++lfs_stats.segs_reclaimed; return (0); }
void pass5(void) { SEGUSE *su; struct ubuf *bp; int i; unsigned long bb; /* total number of used blocks (lower bound) */ unsigned long ubb; /* upper bound number of used blocks */ unsigned long avail; /* blocks available for writing */ unsigned long dmeta; /* blocks in segsums and inodes */ int nclean; /* clean segments */ size_t labelskew; int diddirty; /* * Check segment holdings against actual holdings. Check for * "clean" segments that contain live data. If we are only * rolling forward, we can't check the segment holdings, but * we can still check the cleanerinfo data. */ nclean = 0; avail = 0; bb = ubb = 0; dmeta = 0; for (i = 0; i < fs->lfs_nseg; i++) { diddirty = 0; LFS_SEGENTRY(su, fs, i, bp); if (!preen && !(su->su_flags & SEGUSE_DIRTY) && seg_table[i].su_nbytes > 0) { pwarn("CLEAN SEGMENT %d CONTAINS %d BYTES\n", i, seg_table[i].su_nbytes); if (reply("MARK SEGMENT DIRTY")) { su->su_flags |= SEGUSE_DIRTY; ++diddirty; } } if (!preen && su->su_nbytes != seg_table[i].su_nbytes) { pwarn("SEGMENT %d CLAIMS %d BYTES BUT HAS %d", i, su->su_nbytes, seg_table[i].su_nbytes); if ((int32_t)su->su_nbytes > (int32_t)seg_table[i].su_nbytes) pwarn(" (HIGH BY %d)\n", su->su_nbytes - seg_table[i].su_nbytes); else pwarn(" (LOW BY %d)\n", -su->su_nbytes + seg_table[i].su_nbytes); if (reply("FIX")) { su->su_nbytes = seg_table[i].su_nbytes; ++diddirty; } } if (su->su_flags & SEGUSE_DIRTY) { bb += btofsb(fs, su->su_nbytes + su->su_nsums * fs->lfs_sumsize); ubb += btofsb(fs, su->su_nbytes + su->su_nsums * fs->lfs_sumsize + su->su_ninos * fs->lfs_ibsize); dmeta += btofsb(fs, fs->lfs_sumsize * su->su_nsums); dmeta += btofsb(fs, fs->lfs_ibsize * su->su_ninos); } else { nclean++; avail += segtod(fs, 1); if (su->su_flags & SEGUSE_SUPERBLOCK) avail -= btofsb(fs, LFS_SBPAD); if (i == 0 && fs->lfs_version > 1 && fs->lfs_start < btofsb(fs, LFS_LABELPAD)) avail -= btofsb(fs, LFS_LABELPAD) - fs->lfs_start; } if (diddirty) VOP_BWRITE(bp); else brelse(bp, 0); } /* Also may be available bytes in current seg */ i = dtosn(fs, fs->lfs_offset); avail += sntod(fs, i + 1) - fs->lfs_offset; /* But do not count minfreesegs */ avail -= segtod(fs, (fs->lfs_minfreeseg - (fs->lfs_minfreeseg / 2))); /* Note we may have bytes to write yet */ avail -= btofsb(fs, locked_queue_bytes); if (idaddr) pwarn("NOTE: when using -i, expect discrepancies in dmeta," " avail, nclean, bfree\n"); if (dmeta != fs->lfs_dmeta) { pwarn("DMETA GIVEN AS %d, SHOULD BE %ld\n", fs->lfs_dmeta, dmeta); if (preen || reply("FIX")) { fs->lfs_dmeta = dmeta; sbdirty(); } } if (avail != fs->lfs_avail) { pwarn("AVAIL GIVEN AS %d, SHOULD BE %ld\n", fs->lfs_avail, avail); if (preen || reply("FIX")) { fs->lfs_avail = avail; sbdirty(); } } if (nclean != fs->lfs_nclean) { pwarn("NCLEAN GIVEN AS %d, SHOULD BE %d\n", fs->lfs_nclean, nclean); if (preen || reply("FIX")) { fs->lfs_nclean = nclean; sbdirty(); } } labelskew = 0; if (fs->lfs_version > 1 && fs->lfs_start < btofsb(fs, LFS_LABELPAD)) labelskew = btofsb(fs, LFS_LABELPAD); if (fs->lfs_bfree > fs->lfs_dsize - bb - labelskew || fs->lfs_bfree < fs->lfs_dsize - ubb - labelskew) { pwarn("BFREE GIVEN AS %d, SHOULD BE BETWEEN %ld AND %ld\n", fs->lfs_bfree, (fs->lfs_dsize - ubb - labelskew), fs->lfs_dsize - bb - labelskew); if (preen || reply("FIX")) { fs->lfs_bfree = ((fs->lfs_dsize - labelskew - ubb) + fs->lfs_dsize - labelskew - bb) / 2; sbdirty(); } } }