mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-11 23:23:52 +08:00
Fix incorrect lseek / fiemap results
-----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJdTsiNAAoJENW/n+sDE2U6NAQP/3sffDXhuA3smLw41OjPs/wu kFbf0GdDrxouPfCEzpqdOaETcdsb9LVnJWmWKexDNjgzj4+5NdvwjD8UdEHUUsfp FBl2ALYobAmyEyKlzgcJ+CA40DMrsWWe8cqwcDqukz5nuyK/XwKJAtUa36TXhZx5 1takFgd2FSpxCbBkyS/4SboG3xuXFyuKMJ3j3iSHiZFFlasf1WijLZEE4WnLeKTU BJYhf3SWPHKjwJb+vHJrXEwibluK4yfcYUPJUx+unZLsIoAw1DRj4uU6DrQTGc0t vJov4B56TQcwWIt3ZO24GrO0bZ7/xYruucgrYfhx8C9enWsN/bDcrcePeNJJaAd6 fv2dmZAP/x5MQZWIz5wB2Kj3MwZ51gOru4pRJylJsqi2GwBt3TXyj/fXcOf3gs/6 JjuxDxorg3lRrv67SFYzhmPxHYKhAEG+0pj+hci+BXoBnfY71Rs2qnzmynf3z6Yg /n5lZwfGRv0QqSaaDtQsXcyhjMAhewJhSabWwgFIc80xgARyA3KpsbIXRI8vrnFo bs07P8eROaPVaL4lkVJCil9nJhh2K+XZsal5QpUGHDKaM5NIT0JG+Iho5D34vrF/ B62+GlAoaJwiOU4A86U7lY99zk7HYFuz5chkjNpFnrki3Y6umM+i4U2faO92kAIw /SCEBYH7oACnN4uwKxYZ =nHlX -----END PGP SIGNATURE----- Merge tag 'gfs2-v5.3-rc3.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2 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
This commit is contained in:
commit
829890d266
164
fs/gfs2/bmap.c
164
fs/gfs2/bmap.c
@ -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 @@ lower_metapath:
|
|||||||
|
|
||||||
/* 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 @@ lower_metapath:
|
|||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user