/* * Allocate a particular inode with a particular version number, freeing * any previous versions of this inode that may have gone before. * Used by the roll-forward code. * * XXX this function does not have appropriate locking to be used on a live fs; * XXX but something similar could probably be used for an "undelete" call. * * Called with the Ifile inode locked. */ int lfs_rf_valloc(struct lfs *fs, ino_t ino, int vers, struct lwp *l, struct vnode **vpp) { IFILE *ifp; struct buf *bp, *cbp; struct vnode *vp; struct inode *ip; ino_t tino, oldnext; int error; CLEANERINFO *cip; ASSERT_SEGLOCK(fs); /* XXX it doesn't, really */ /* * First, just try a vget. If the version number is the one we want, * we don't have to do anything else. If the version number is wrong, * take appropriate action. */ error = VFS_VGET(fs->lfs_ivnode->v_mount, ino, &vp); if (error == 0) { DLOG((DLOG_RF, "lfs_rf_valloc[1]: ino %d vp %p\n", ino, vp)); *vpp = vp; ip = VTOI(vp); if (ip->i_gen == vers) return 0; else if (ip->i_gen < vers) { lfs_truncate(vp, (off_t)0, 0, NOCRED); ip->i_gen = ip->i_ffs1_gen = vers; LFS_SET_UINO(ip, IN_CHANGE | IN_UPDATE); return 0; } else { DLOG((DLOG_RF, "ino %d: sought version %d, got %d\n", ino, vers, ip->i_ffs1_gen)); vput(vp); *vpp = NULLVP; return EEXIST; } } /* * The inode is not in use. Find it on the free list. */ /* If the Ifile is too short to contain this inum, extend it */ while (VTOI(fs->lfs_ivnode)->i_size <= (ino / fs->lfs_ifpb + fs->lfs_cleansz + fs->lfs_segtabsz) << fs->lfs_bshift) { lfs_extend_ifile(fs, NOCRED); } LFS_IENTRY(ifp, fs, ino, bp); oldnext = ifp->if_nextfree; ifp->if_version = vers; brelse(bp, 0); LFS_GET_HEADFREE(fs, cip, cbp, &ino); if (ino) { LFS_PUT_HEADFREE(fs, cip, cbp, oldnext); } else { tino = ino; while (1) { LFS_IENTRY(ifp, fs, tino, bp); if (ifp->if_nextfree == ino || ifp->if_nextfree == LFS_UNUSED_INUM) break; tino = ifp->if_nextfree; brelse(bp, 0); } if (ifp->if_nextfree == LFS_UNUSED_INUM) { brelse(bp, 0); return ENOENT; } ifp->if_nextfree = oldnext; LFS_BWRITE_LOG(bp); } error = lfs_ialloc(fs, fs->lfs_ivnode, ino, vers, &vp); if (error == 0) { /* * Make it VREG so we can put blocks on it. We will change * this later if it turns out to be some other kind of file. */ ip = VTOI(vp); ip->i_mode = ip->i_ffs1_mode = IFREG; ip->i_nlink = ip->i_ffs1_nlink = 1; ufs_vinit(vp->v_mount, lfs_specop_p, lfs_fifoop_p, &vp); ip = VTOI(vp); DLOG((DLOG_RF, "lfs_rf_valloc: ino %d vp %p\n", ino, vp)); /* The dirop-nature of this vnode is past */ lfs_unmark_vnode(vp); (void)lfs_vunref(vp); vp->v_uflag &= ~VU_DIROP; mutex_enter(&lfs_lock); --lfs_dirvcount; --fs->lfs_dirvcount; TAILQ_REMOVE(&fs->lfs_dchainhd, ip, i_lfs_dchain); wakeup(&lfs_dirvcount); wakeup(&fs->lfs_dirvcount); mutex_exit(&lfs_lock); } *vpp = vp; return error; }
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); } } }
void lfs_itimes(struct inode *ip, const struct timespec *acc, const struct timespec *mod, const struct timespec *cre) { #ifdef _KERNEL struct timespec now; KASSERT(ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY)); vfs_timestamp(&now); #endif if (ip->i_flag & IN_ACCESS) { #ifdef _KERNEL if (acc == NULL) acc = &now; #endif ip->i_ffs1_atime = acc->tv_sec; ip->i_ffs1_atimensec = acc->tv_nsec; if (ip->i_lfs->lfs_is64 || lfs_sb_getversion(ip->i_lfs) > 1) { struct lfs *fs = ip->i_lfs; struct buf *ibp; IFILE *ifp; LFS_IENTRY(ifp, ip->i_lfs, ip->i_number, ibp); lfs_if_setatime_sec(fs, ifp, acc->tv_sec); lfs_if_setatime_nsec(fs, ifp, acc->tv_nsec); LFS_BWRITE_LOG(ibp); mutex_enter(&lfs_lock); fs->lfs_flags |= LFS_IFDIRTY; mutex_exit(&lfs_lock); } else { mutex_enter(&lfs_lock); LFS_SET_UINO(ip, IN_ACCESSED); mutex_exit(&lfs_lock); } } if (ip->i_flag & (IN_CHANGE | IN_UPDATE | IN_MODIFY)) { if (ip->i_flag & (IN_UPDATE | IN_MODIFY)) { #ifdef _KERNEL if (mod == NULL) mod = &now; #endif ip->i_ffs1_mtime = mod->tv_sec; ip->i_ffs1_mtimensec = mod->tv_nsec; ip->i_modrev++; } if (ip->i_flag & (IN_CHANGE | IN_MODIFY)) { #ifdef _KERNEL if (cre == NULL) cre = &now; #endif ip->i_ffs1_ctime = cre->tv_sec; ip->i_ffs1_ctimensec = cre->tv_nsec; } mutex_enter(&lfs_lock); if (ip->i_flag & (IN_CHANGE | IN_UPDATE)) LFS_SET_UINO(ip, IN_MODIFIED); if (ip->i_flag & IN_MODIFY) LFS_SET_UINO(ip, IN_ACCESSED); mutex_exit(&lfs_lock); } ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY); }
/* * 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); }
/* * 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); }