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 */ } }
/* * Get a dinode of a given inum. * XXX combine this function with vget. */ struct ufs1_dinode * ginode(ino_t ino) { struct uvnode *vp; struct ubuf *bp; IFILE *ifp; vp = vget(fs, ino); if (vp == NULL) return NULL; if (din_table[ino] == 0x0) { LFS_IENTRY(ifp, fs, ino, bp); din_table[ino] = ifp->if_daddr; seg_table[dtosn(fs, ifp->if_daddr)].su_nbytes += DINODE1_SIZE; brelse(bp, 0); } return (VTOI(vp)->i_din.ffs1_din); }
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; }
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(); } } }