/* * 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; }
int lfs_markv(struct proc *p, fsid_t *fsidp, BLOCK_INFO *blkiov, int blkcnt) { BLOCK_INFO *blkp; IFILE *ifp; struct buf *bp; struct inode *ip = NULL; struct lfs *fs; struct mount *mntp; struct vnode *vp = NULL; ino_t lastino; daddr_t b_daddr, v_daddr; int cnt, error; int do_again = 0; int numrefed = 0; ino_t maxino; size_t obsize; /* number of blocks/inodes that we have already bwrite'ed */ int nblkwritten, ninowritten; if ((mntp = vfs_getvfs(fsidp)) == NULL) return (ENOENT); fs = VFSTOULFS(mntp)->um_lfs; if (fs->lfs_ronly) return EROFS; maxino = (lfs_fragstoblks(fs, VTOI(fs->lfs_ivnode)->i_ffs1_blocks) - fs->lfs_cleansz - fs->lfs_segtabsz) * fs->lfs_ifpb; cnt = blkcnt; if ((error = vfs_busy(mntp, NULL)) != 0) return (error); /* * This seglock is just to prevent the fact that we might have to sleep * from allowing the possibility that our blocks might become * invalid. * * It is also important to note here that unless we specify SEGM_CKP, * any Ifile blocks that we might be asked to clean will never get * to the disk. */ lfs_seglock(fs, SEGM_CLEAN | SEGM_CKP | SEGM_SYNC); /* Mark blocks/inodes dirty. */ error = 0; /* these were inside the initialization for the for loop */ v_daddr = LFS_UNUSED_DADDR; lastino = LFS_UNUSED_INUM; nblkwritten = ninowritten = 0; for (blkp = blkiov; cnt--; ++blkp) { /* Bounds-check incoming data, avoid panic for failed VGET */ if (blkp->bi_inode <= 0 || blkp->bi_inode >= maxino) { error = EINVAL; goto err3; } /* * Get the IFILE entry (only once) and see if the file still * exists. */ if (lastino != blkp->bi_inode) { /* * Finish the old file, if there was one. The presence * of a usable vnode in vp is signaled by a valid v_daddr. */ if (v_daddr != LFS_UNUSED_DADDR) { lfs_vunref(vp); numrefed--; } /* * Start a new file */ lastino = blkp->bi_inode; if (blkp->bi_inode == LFS_IFILE_INUM) v_daddr = fs->lfs_idaddr; else { LFS_IENTRY(ifp, fs, blkp->bi_inode, bp); /* XXX fix for force write */ v_daddr = ifp->if_daddr; brelse(bp, 0); } if (v_daddr == LFS_UNUSED_DADDR) continue; /* Get the vnode/inode. */ error = lfs_fastvget(mntp, blkp->bi_inode, v_daddr, &vp, (blkp->bi_lbn == LFS_UNUSED_LBN ? blkp->bi_bp : NULL)); if (!error) { numrefed++; } if (error) { DLOG((DLOG_CLEAN, "lfs_markv: lfs_fastvget" " failed with %d (ino %d, segment %d)\n", error, blkp->bi_inode, lfs_dtosn(fs, blkp->bi_daddr))); /* * If we got EAGAIN, that means that the * Inode was locked. This is * recoverable: just clean the rest of * this segment, and let the cleaner try * again with another. (When the * cleaner runs again, this segment will * sort high on the list, since it is * now almost entirely empty.) But, we * still set v_daddr = LFS_UNUSED_ADDR * so as not to test this over and over * again. */ if (error == EAGAIN) { error = 0; do_again++; } #ifdef DIAGNOSTIC else if (error != ENOENT) panic("lfs_markv VFS_VGET FAILED"); #endif /* lastino = LFS_UNUSED_INUM; */ v_daddr = LFS_UNUSED_DADDR; vp = NULL; ip = NULL; continue; } ip = VTOI(vp); ninowritten++; } else if (v_daddr == LFS_UNUSED_DADDR) { /* * This can only happen if the vnode is dead (or * in any case we can't get it...e.g., it is * inlocked). Keep going. */ continue; } /* Past this point we are guaranteed that vp, ip are valid. */ /* Can't clean VU_DIROP directories in case of truncation */ /* XXX - maybe we should mark removed dirs specially? */ if (vp->v_type == VDIR && (vp->v_uflag & VU_DIROP)) { do_again++; continue; } /* If this BLOCK_INFO didn't contain a block, keep going. */ if (blkp->bi_lbn == LFS_UNUSED_LBN) { /* XXX need to make sure that the inode gets written in this case */ /* XXX but only write the inode if it's the right one */ if (blkp->bi_inode != LFS_IFILE_INUM) { LFS_IENTRY(ifp, fs, blkp->bi_inode, bp); if (ifp->if_daddr == blkp->bi_daddr) { mutex_enter(&lfs_lock); LFS_SET_UINO(ip, IN_CLEANING); mutex_exit(&lfs_lock); } brelse(bp, 0); } continue; } b_daddr = 0; if (VOP_BMAP(vp, blkp->bi_lbn, NULL, &b_daddr, NULL) || LFS_DBTOFSB(fs, b_daddr) != blkp->bi_daddr) { if (lfs_dtosn(fs, LFS_DBTOFSB(fs, b_daddr)) == lfs_dtosn(fs, blkp->bi_daddr)) { DLOG((DLOG_CLEAN, "lfs_markv: wrong da same seg: %llx vs %llx\n", (long long)blkp->bi_daddr, (long long)LFS_DBTOFSB(fs, b_daddr))); } do_again++; continue; } /* * Check block sizes. The blocks being cleaned come from * disk, so they should have the same size as their on-disk * counterparts. */ if (blkp->bi_lbn >= 0) obsize = lfs_blksize(fs, ip, blkp->bi_lbn); else obsize = fs->lfs_bsize; /* Check for fragment size change */ if (blkp->bi_lbn >= 0 && blkp->bi_lbn < ULFS_NDADDR) { obsize = ip->i_lfs_fragsize[blkp->bi_lbn]; } if (obsize != blkp->bi_size) { DLOG((DLOG_CLEAN, "lfs_markv: ino %d lbn %lld wrong" " size (%ld != %d), try again\n", blkp->bi_inode, (long long)blkp->bi_lbn, (long) obsize, blkp->bi_size)); do_again++; continue; } /* * If we get to here, then we are keeping the block. If * it is an indirect block, we want to actually put it * in the buffer cache so that it can be updated in the * finish_meta section. If it's not, we need to * allocate a fake buffer so that writeseg can perform * the copyin and write the buffer. */ if (ip->i_number != LFS_IFILE_INUM && blkp->bi_lbn >= 0) { /* Data Block */ bp = lfs_fakebuf(fs, vp, blkp->bi_lbn, blkp->bi_size, blkp->bi_bp); /* Pretend we used bread() to get it */ bp->b_blkno = LFS_FSBTODB(fs, blkp->bi_daddr); } else { /* Indirect block or ifile */ if (blkp->bi_size != fs->lfs_bsize && ip->i_number != LFS_IFILE_INUM) panic("lfs_markv: partial indirect block?" " size=%d\n", blkp->bi_size); bp = getblk(vp, blkp->bi_lbn, blkp->bi_size, 0, 0); if (!(bp->b_oflags & (BO_DONE|BO_DELWRI))) { /* * The block in question was not found * in the cache; i.e., the block that * getblk() returned is empty. So, we * can (and should) copy in the * contents, because we've already * determined that this was the right * version of this block on disk. * * And, it can't have changed underneath * us, because we have the segment lock. */ error = copyin(blkp->bi_bp, bp->b_data, blkp->bi_size); if (error) goto err2; } } if ((error = lfs_bwrite_ext(bp, BW_CLEAN)) != 0) goto err2; nblkwritten++; /* * XXX should account indirect blocks and ifile pages as well */ if (nblkwritten + lfs_lblkno(fs, ninowritten * sizeof (struct ulfs1_dinode)) > LFS_MARKV_MAX_BLOCKS) { DLOG((DLOG_CLEAN, "lfs_markv: writing %d blks %d inos\n", nblkwritten, ninowritten)); lfs_segwrite(mntp, SEGM_CLEAN); nblkwritten = ninowritten = 0; } } /* * Finish the old file, if there was one */ if (v_daddr != LFS_UNUSED_DADDR) { lfs_vunref(vp); numrefed--; } #ifdef DIAGNOSTIC if (numrefed != 0) panic("lfs_markv: numrefed=%d", numrefed); #endif DLOG((DLOG_CLEAN, "lfs_markv: writing %d blks %d inos (check point)\n", nblkwritten, ninowritten)); /* * The last write has to be SEGM_SYNC, because of calling semantics. * It also has to be SEGM_CKP, because otherwise we could write * over the newly cleaned data contained in a checkpoint, and then * we'd be unhappy at recovery time. */ lfs_segwrite(mntp, SEGM_CLEAN | SEGM_CKP | SEGM_SYNC); lfs_segunlock(fs); vfs_unbusy(mntp, false, NULL); if (error) return (error); else if (do_again) return EAGAIN; return 0; err2: DLOG((DLOG_CLEAN, "lfs_markv err2\n")); /* * XXX we're here because copyin() failed. * XXX it means that we can't trust the cleanerd. too bad. * XXX how can we recover from this? */ err3: /* * XXX should do segwrite here anyway? */ if (v_daddr != LFS_UNUSED_DADDR) { lfs_vunref(vp); --numrefed; } lfs_segunlock(fs); vfs_unbusy(mntp, false, NULL); #ifdef DIAGNOSTIC if (numrefed != 0) panic("lfs_markv: numrefed=%d", numrefed); #endif 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); }