Beispiel #1
0
/* Search a block for a specific dinode. */
union lfs_dinode *
lfs_ifind(struct lfs *fs, ino_t ino, struct buf *bp)
{
	union lfs_dinode *ldip;
	unsigned num, i;

	ASSERT_NO_SEGLOCK(fs);
	/*
	 * Read the inode block backwards, since later versions of the
	 * inode will supercede earlier ones.  Though it is unlikely, it is
	 * possible that the same inode will appear in the same inode block.
	 */
	num = LFS_INOPB(fs);
	for (i = num; i-- > 0; ) {
		ldip = DINO_IN_BLOCK(fs, bp->b_data, i);
		if (lfs_dino_getinumber(fs, ldip) == ino)
			return (ldip);
	}

	printf("searched %u entries for %ju\n", num, (uintmax_t)ino);
	printf("offset is 0x%jx (seg %d)\n", (uintmax_t)lfs_sb_getoffset(fs),
	       lfs_dtosn(fs, lfs_sb_getoffset(fs)));
	printf("block is 0x%jx (seg %d)\n",
	       (uintmax_t)LFS_DBTOFSB(fs, bp->b_blkno),
	       lfs_dtosn(fs, LFS_DBTOFSB(fs, bp->b_blkno)));

	return NULL;
}
Beispiel #2
0
/* Update segment and avail usage information when removing a block. */
static int
lfs_blkfree(struct lfs *fs, struct inode *ip, daddr_t daddr,
	    size_t bsize, long *lastseg, size_t *num)
{
	long seg;
	int error = 0;

	ASSERT_SEGLOCK(fs);
	bsize = lfs_fragroundup(fs, bsize);
	if (daddr > 0) {
		if (*lastseg != (seg = lfs_dtosn(fs, daddr))) {
			error = lfs_update_seguse(fs, ip, *lastseg, *num);
			*num = bsize;
			*lastseg = seg;
		} else
			*num += bsize;
	}

	return error;
}
Beispiel #3
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);
}
Beispiel #4
0
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);
}
Beispiel #5
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);
}
Beispiel #6
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);
}
Beispiel #7
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);
}
Beispiel #8
0
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 ulfs1_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, LFS_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 ulfs1_dinode *)(dbp->b_data)) + LFS_INOPB(fs);
	while (--dip >= (struct ulfs1_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 ulfs1_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 */
			ulfs_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 = LFS_DBTOFSB(fs, dbp->b_blkno);
			error = LFS_BWRITE_LOG(ibp); /* Ifile */
			/* And do segment accounting */
			if (lfs_dtosn(fs, daddr) != lfs_dtosn(fs, LFS_DBTOFSB(fs, dbp->b_blkno))) {
				if (daddr > 0) {
					LFS_SEGENTRY(sup, fs, lfs_dtosn(fs, daddr),
						     ibp);
					sup->su_nbytes -= sizeof (struct ulfs1_dinode);
					LFS_WRITESEGENTRY(sup, fs,
							  lfs_dtosn(fs, daddr),
							  ibp);
				}
				LFS_SEGENTRY(sup, fs, lfs_dtosn(fs, LFS_DBTOFSB(fs, dbp->b_blkno)),
					     ibp);
				sup->su_nbytes += sizeof (struct ulfs1_dinode);
				LFS_WRITESEGENTRY(sup, fs,
						  lfs_dtosn(fs, LFS_DBTOFSB(fs, dbp->b_blkno)),
						  ibp);
			}
		}
	}
Beispiel #9
0
/*
 * 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[ULFS_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 += lfs_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 < ULFS_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, lfs_dtosn(fs, ndaddr), bp);
	sup->su_nbytes += size;
	LFS_WRITESEGENTRY(sup, fs, lfs_dtosn(fs, ndaddr), bp);

	/* differences here should be due to UNWRITTEN indirect blocks. */
	KASSERT((lfs_lblkno(fs, ip->i_size) > ULFS_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 */
	ulfs_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 (LFS_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;
}