diff options
Diffstat (limited to 'sys/fs/ext2fs/ext2_bmap.c')
| -rw-r--r-- | sys/fs/ext2fs/ext2_bmap.c | 173 | 
1 files changed, 145 insertions, 28 deletions
| diff --git a/sys/fs/ext2fs/ext2_bmap.c b/sys/fs/ext2fs/ext2_bmap.c index a5e9a0258582..5d159b8ec152 100644 --- a/sys/fs/ext2fs/ext2_bmap.c +++ b/sys/fs/ext2fs/ext2_bmap.c @@ -139,6 +139,47 @@ ext4_bmapext(struct vnode *vp, int32_t bn, int64_t *bnp, int *runp, int *runb)  	return (error);  } +static int +readindir(struct vnode *vp, e2fs_lbn_t lbn, e2fs_daddr_t daddr, struct buf **bpp) +{ +	struct buf *bp; +	struct mount *mp; +	struct ext2mount *ump; +	int error; + +	mp = vp->v_mount; +	ump = VFSTOEXT2(mp); + +	bp = getblk(vp, lbn, mp->mnt_stat.f_iosize, 0, 0, 0); +	if ((bp->b_flags & B_CACHE) == 0) { +		KASSERT(daddr != 0, +		    ("readindir: indirect block not in cache")); + +		bp->b_blkno = blkptrtodb(ump, daddr); +		bp->b_iocmd = BIO_READ; +		bp->b_flags &= ~B_INVAL; +		bp->b_ioflags &= ~BIO_ERROR; +		vfs_busy_pages(bp, 0); +		bp->b_iooffset = dbtob(bp->b_blkno); +		bstrategy(bp); +#ifdef RACCT +		if (racct_enable) { +			PROC_LOCK(curproc); +			racct_add_buf(curproc, bp, 0); +			PROC_UNLOCK(curproc); +		} +#endif +		curthread->td_ru.ru_inblock++; +		error = bufwait(bp); +		if (error != 0) { +			brelse(bp); +			return (error); +		} +	} +	*bpp = bp; +	return (0); +} +  /*   * Indirect blocks are now on the vnode for the file.  They are given negative   * logical block numbers.  Indirect blocks are addressed by the negative @@ -228,34 +269,9 @@ ext2_bmaparray(struct vnode *vp, daddr_t bn, daddr_t *bnp, int *runp, int *runb)  		 */  		if (bp)  			bqrelse(bp); - -		bp = getblk(vp, metalbn, bsize, 0, 0, 0); -		if ((bp->b_flags & B_CACHE) == 0) { -#ifdef INVARIANTS -			if (!daddr) -				panic("ext2_bmaparray: indirect block not in cache"); -#endif -			bp->b_blkno = blkptrtodb(ump, daddr); -			bp->b_iocmd = BIO_READ; -			bp->b_flags &= ~B_INVAL; -			bp->b_ioflags &= ~BIO_ERROR; -			vfs_busy_pages(bp, 0); -			bp->b_iooffset = dbtob(bp->b_blkno); -			bstrategy(bp); -#ifdef RACCT -			if (racct_enable) { -				PROC_LOCK(curproc); -				racct_add_buf(curproc, bp, 0); -				PROC_UNLOCK(curproc); -			} -#endif -			curthread->td_ru.ru_inblock++; -			error = bufwait(bp); -			if (error) { -				brelse(bp); -				return (error); -			} -		} +		error = readindir(vp, metalbn, daddr, &bp); +		if (error != 0) +			return (error);  		daddr = ((e2fs_daddr_t *)bp->b_data)[ap->in_off];  		if (num == 1 && daddr && runp) { @@ -296,6 +312,107 @@ ext2_bmaparray(struct vnode *vp, daddr_t bn, daddr_t *bnp, int *runp, int *runb)  	return (0);  } +static e2fs_lbn_t +lbn_count(struct ext2mount *ump, int level) + +{ +	e2fs_lbn_t blockcnt; + +	for (blockcnt = 1; level > 0; level--) +		blockcnt *= MNINDIR(ump); +	return (blockcnt); +} + +int +ext2_bmap_seekdata(struct vnode *vp, off_t *offp) +{ +	struct buf *bp; +	struct indir a[EXT2_NIADDR + 1], *ap; +	struct inode *ip; +	struct mount *mp; +	struct ext2mount *ump; +	e2fs_daddr_t bn, daddr, nextbn; +	uint64_t bsize; +	off_t numblks; +	int error, num, num1, off; + +	bp = NULL; +	error = 0; +	ip = VTOI(vp); +	mp = vp->v_mount; +	ump = VFSTOEXT2(mp); + +	if (vp->v_type != VREG || (ip->i_flags & SF_SNAPSHOT) != 0) +		return (EINVAL); +	if (*offp < 0 || *offp >= ip->i_size) +		return (ENXIO); + +	bsize = mp->mnt_stat.f_iosize; +	for (bn = *offp / bsize, numblks = howmany(ip->i_size, bsize); +	    bn < numblks; bn = nextbn) { +		if (bn < EXT2_NDADDR) { +			daddr = ip->i_db[bn]; +			if (daddr != 0) +				break; +			nextbn = bn + 1; +			continue; +		} + +		ap = a; +		error = ext2_getlbns(vp, bn, ap, &num); +		if (error != 0) +			break; +		MPASS(num >= 2); +		daddr = ip->i_ib[ap->in_off]; +		ap++, num--; +		for (nextbn = EXT2_NDADDR, num1 = num - 1; num1 > 0; num1--) +			nextbn += lbn_count(ump, num1); +		if (daddr == 0) { +			nextbn += lbn_count(ump, num); +			continue; +		} + +		for (; daddr != 0 && num > 0; ap++, num--) { +			if (bp != NULL) +				bqrelse(bp); +			error = readindir(vp, ap->in_lbn, daddr, &bp); +			if (error != 0) +				return (error); + +			/* +			 * Scan the indirect block until we find a non-zero +			 * pointer. +			 */ +			off = ap->in_off; +			do { +				daddr = ((e2fs_daddr_t *)bp->b_data)[off]; +			} while (daddr == 0 && ++off < MNINDIR(ump)); +			nextbn += off * lbn_count(ump, num - 1); + +			/* +			 * We need to recompute the LBNs of indirect +			 * blocks, so restart with the updated block offset. +			 */ +			if (off != ap->in_off) +				break; +		} +		if (num == 0) { +			/* +			 * We found a data block. +			 */ +			bn = nextbn; +			break; +		} +	} +	if (bp != NULL) +		bqrelse(bp); +	if (bn >= numblks) +		error = ENXIO; +	if (error == 0 && *offp < bn * bsize) +		*offp = bn * bsize; +	return (error); +} +  /*   * Create an array of logical block number/offset pairs which represent the   * path of indirect blocks required to access a data block.  The first "pair" | 
