Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 829890d2 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull gfs2 fix from Andreas Gruenbacher:
 "Fix incorrect lseek / fiemap results"

* tag 'gfs2-v5.3-rc3.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2:
  gfs2: gfs2_walk_metadata fix
parents bfd77145 a27a0c9b
Loading
Loading
Loading
Loading
+101 −63
Original line number Original line Diff line number Diff line
@@ -390,6 +390,19 @@ static int fillup_metapath(struct gfs2_inode *ip, struct metapath *mp, int h)
	return mp->mp_aheight - x - 1;
	return mp->mp_aheight - x - 1;
}
}


static sector_t metapath_to_block(struct gfs2_sbd *sdp, struct metapath *mp)
{
	sector_t factor = 1, block = 0;
	int hgt;

	for (hgt = mp->mp_fheight - 1; hgt >= 0; hgt--) {
		if (hgt < mp->mp_aheight)
			block += mp->mp_list[hgt] * factor;
		factor *= sdp->sd_inptrs;
	}
	return block;
}

static void release_metapath(struct metapath *mp)
static void release_metapath(struct metapath *mp)
{
{
	int i;
	int i;
@@ -430,60 +443,84 @@ static inline unsigned int gfs2_extent_length(struct buffer_head *bh, __be64 *pt
	return ptr - first;
	return ptr - first;
}
}


typedef const __be64 *(*gfs2_metadata_walker)(
enum walker_status { WALK_STOP, WALK_FOLLOW, WALK_CONTINUE };
		struct metapath *mp,
		const __be64 *start, const __be64 *end,
		u64 factor, void *data);


#define WALK_STOP ((__be64 *)0)
/*
#define WALK_NEXT ((__be64 *)1)
 * gfs2_metadata_walker - walk an indirect block
 * @mp: Metapath to indirect block
 * @ptrs: Number of pointers to look at
 *
 * When returning WALK_FOLLOW, the walker must update @mp to point at the right
 * indirect block to follow.
 */
typedef enum walker_status (*gfs2_metadata_walker)(struct metapath *mp,
						   unsigned int ptrs);


static int gfs2_walk_metadata(struct inode *inode, sector_t lblock,
/*
		u64 len, struct metapath *mp, gfs2_metadata_walker walker,
 * gfs2_walk_metadata - walk a tree of indirect blocks
		void *data)
 * @inode: The inode
 * @mp: Starting point of walk
 * @max_len: Maximum number of blocks to walk
 * @walker: Called during the walk
 *
 * Returns 1 if the walk was stopped by @walker, 0 if we went past @max_len or
 * past the end of metadata, and a negative error code otherwise.
 */

static int gfs2_walk_metadata(struct inode *inode, struct metapath *mp,
		u64 max_len, gfs2_metadata_walker walker)
{
{
	struct metapath clone;
	struct gfs2_inode *ip = GFS2_I(inode);
	struct gfs2_inode *ip = GFS2_I(inode);
	struct gfs2_sbd *sdp = GFS2_SB(inode);
	struct gfs2_sbd *sdp = GFS2_SB(inode);
	const __be64 *start, *end, *ptr;
	u64 factor = 1;
	u64 factor = 1;
	unsigned int hgt;
	unsigned int hgt;
	int ret = 0;
	int ret;


	for (hgt = ip->i_height - 1; hgt >= mp->mp_aheight; hgt--)
	/*
	 * The walk starts in the lowest allocated indirect block, which may be
	 * before the position indicated by @mp.  Adjust @max_len accordingly
	 * to avoid a short walk.
	 */
	for (hgt = mp->mp_fheight - 1; hgt >= mp->mp_aheight; hgt--) {
		max_len += mp->mp_list[hgt] * factor;
		mp->mp_list[hgt] = 0;
		factor *= sdp->sd_inptrs;
		factor *= sdp->sd_inptrs;
	}


	for (;;) {
	for (;;) {
		u64 step;
		u16 start = mp->mp_list[hgt];
		enum walker_status status;
		unsigned int ptrs;
		u64 len;


		/* Walk indirect block. */
		/* Walk indirect block. */
		start = metapointer(hgt, mp);
		ptrs = (hgt >= 1 ? sdp->sd_inptrs : sdp->sd_diptrs) - start;
		end = metaend(hgt, mp);
		len = ptrs * factor;

		if (len > max_len)
		step = (end - start) * factor;
			ptrs = DIV_ROUND_UP_ULL(max_len, factor);
		if (step > len)
		status = walker(mp, ptrs);
			end = start + DIV_ROUND_UP_ULL(len, factor);
		switch (status) {

		case WALK_STOP:
		ptr = walker(mp, start, end, factor, data);
			return 1;
		if (ptr == WALK_STOP)
		case WALK_FOLLOW:
			BUG_ON(mp->mp_aheight == mp->mp_fheight);
			ptrs = mp->mp_list[hgt] - start;
			len = ptrs * factor;
			break;
			break;
		if (step >= len)
		case WALK_CONTINUE:
			break;
			break;
		len -= step;
		if (ptr != WALK_NEXT) {
			BUG_ON(!*ptr);
			mp->mp_list[hgt] += ptr - start;
			goto fill_up_metapath;
		}
		}
		if (len >= max_len)
			break;
		max_len -= len;
		if (status == WALK_FOLLOW)
			goto fill_up_metapath;


lower_metapath:
lower_metapath:
		/* Decrease height of metapath. */
		/* Decrease height of metapath. */
		if (mp != &clone) {
			clone_metapath(&clone, mp);
			mp = &clone;
		}
		brelse(mp->mp_bh[hgt]);
		brelse(mp->mp_bh[hgt]);
		mp->mp_bh[hgt] = NULL;
		mp->mp_bh[hgt] = NULL;
		mp->mp_list[hgt] = 0;
		if (!hgt)
		if (!hgt)
			break;
			break;
		hgt--;
		hgt--;
@@ -491,10 +528,7 @@ static int gfs2_walk_metadata(struct inode *inode, sector_t lblock,


		/* Advance in metadata tree. */
		/* Advance in metadata tree. */
		(mp->mp_list[hgt])++;
		(mp->mp_list[hgt])++;
		start = metapointer(hgt, mp);
		if (mp->mp_list[hgt] >= sdp->sd_inptrs) {
		end = metaend(hgt, mp);
		if (start >= end) {
			mp->mp_list[hgt] = 0;
			if (!hgt)
			if (!hgt)
				break;
				break;
			goto lower_metapath;
			goto lower_metapath;
@@ -502,44 +536,36 @@ static int gfs2_walk_metadata(struct inode *inode, sector_t lblock,


fill_up_metapath:
fill_up_metapath:
		/* Increase height of metapath. */
		/* Increase height of metapath. */
		if (mp != &clone) {
			clone_metapath(&clone, mp);
			mp = &clone;
		}
		ret = fillup_metapath(ip, mp, ip->i_height - 1);
		ret = fillup_metapath(ip, mp, ip->i_height - 1);
		if (ret < 0)
		if (ret < 0)
			break;
			return ret;
		hgt += ret;
		hgt += ret;
		for (; ret; ret--)
		for (; ret; ret--)
			do_div(factor, sdp->sd_inptrs);
			do_div(factor, sdp->sd_inptrs);
		mp->mp_aheight = hgt + 1;
		mp->mp_aheight = hgt + 1;
	}
	}
	if (mp == &clone)
	return 0;
		release_metapath(mp);
	return ret;
}
}


struct gfs2_hole_walker_args {
static enum walker_status gfs2_hole_walker(struct metapath *mp,
	u64 blocks;
					   unsigned int ptrs)
};

static const __be64 *gfs2_hole_walker(struct metapath *mp,
		const __be64 *start, const __be64 *end,
		u64 factor, void *data)
{
{
	struct gfs2_hole_walker_args *args = data;
	const __be64 *start, *ptr, *end;
	const __be64 *ptr;
	unsigned int hgt;

	hgt = mp->mp_aheight - 1;
	start = metapointer(hgt, mp);
	end = start + ptrs;


	for (ptr = start; ptr < end; ptr++) {
	for (ptr = start; ptr < end; ptr++) {
		if (*ptr) {
		if (*ptr) {
			args->blocks += (ptr - start) * factor;
			mp->mp_list[hgt] += ptr - start;
			if (mp->mp_aheight == mp->mp_fheight)
			if (mp->mp_aheight == mp->mp_fheight)
				return WALK_STOP;
				return WALK_STOP;
			return ptr;  /* increase height */
			return WALK_FOLLOW;
		}
		}
	}
	}
	args->blocks += (end - start) * factor;
	return WALK_CONTINUE;
	return WALK_NEXT;
}
}


/**
/**
@@ -557,12 +583,24 @@ static const __be64 *gfs2_hole_walker(struct metapath *mp,
static int gfs2_hole_size(struct inode *inode, sector_t lblock, u64 len,
static int gfs2_hole_size(struct inode *inode, sector_t lblock, u64 len,
			  struct metapath *mp, struct iomap *iomap)
			  struct metapath *mp, struct iomap *iomap)
{
{
	struct gfs2_hole_walker_args args = { };
	struct metapath clone;
	int ret = 0;
	u64 hole_size;
	int ret;


	ret = gfs2_walk_metadata(inode, lblock, len, mp, gfs2_hole_walker, &args);
	clone_metapath(&clone, mp);
	if (!ret)
	ret = gfs2_walk_metadata(inode, &clone, len, gfs2_hole_walker);
		iomap->length = args.blocks << inode->i_blkbits;
	if (ret < 0)
		goto out;

	if (ret == 1)
		hole_size = metapath_to_block(GFS2_SB(inode), &clone) - lblock;
	else
		hole_size = len;
	iomap->length = hole_size << inode->i_blkbits;
	ret = 0;

out:
	release_metapath(&clone);
	return ret;
	return ret;
}
}