• Darrick J. Wong's avatar
    xfs: fix an agbno overflow in __xfs_getfsmap_datadev · cfa2df68
    Darrick J. Wong authored
    Dave Chinner reported that xfs/273 fails if the AG size happens to be an
    exact power of two.  I traced this to an agbno integer overflow when the
    current GETFSMAP call is a continuation of a previous GETFSMAP call, and
    the last record returned was non-shareable space at the end of an AG.
    
    __xfs_getfsmap_datadev sets up a data device query by converting the
    incoming fmr_physical into an xfs_fsblock_t and cracking it into an agno
    and agbno pair.  In the (failing) case of where fmr_blockcount of the
    low key is nonzero and the record was for a non-shareable extent, it
    will add fmr_blockcount to start_fsb and info->low.rm_startblock.
    
    If the low key was actually the last record for that AG, then this
    addition causes info->low.rm_startblock to point beyond EOAG.  When the
    rmapbt range query starts, it'll return an empty set, and fsmap moves on
    to the next AG.
    
    Or so I thought.  Remember how we added to start_fsb?
    
    If agsize < 1<<agblklog, start_fsb points to the same AG as the original
    fmr_physical from the low key.  We run the rmapbt query, which returns
    nothing, so getfsmap zeroes info->low and moves on to the next AG.
    
    If agsize == 1<<agblklog, start_fsb now points to the next AG.  We run
    the rmapbt query on the next AG with the excessively large
    rm_startblock.  If this next AG is actually the last AG, we'll set
    info->high to EOFS (which is now has a lower rm_startblock than
    info->low), and the ranged btree query code will return -EINVAL.  If
    it's not the last AG, we ignore all records for the intermediate AGs.
    
    Oops.
    
    Fix this by decoding start_fsb into agno and agbno only after making
    adjustments to start_fsb.  This means that info->low.rm_startblock will
    always be set to a valid agbno, and we always start the rmapbt iteration
    in the correct AG.
    
    While we're at it, fix the predicate for determining if an fsmap record
    represents non-shareable space to include file data on pre-reflink
    filesystems.
    Reported-by: default avatarDave Chinner <david@fromorbit.com>
    Fixes: 63ef7a35 ("xfs: fix interval filtering in multi-step fsmap queries")
    Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
    Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
    cfa2df68
xfs_fsmap.c 26.9 KB