Uploaded image for project: 'RHEL'
  1. RHEL
  2. RHEL-24904

xfs_bmap can't list the block mapping for files with >22 million extents

    • Icon: Bug Bug
    • Resolution: Won't Do
    • Icon: Minor Minor
    • None
    • rhel-9.4.z
    • xfsprogs
    • None
    • None
    • Low
    • TestCaseProvided
    • rhel-sst-filesystems
    • ssg_filesystems_storage_and_HA
    • 4
    • False
    • Hide

      None

      Show
      None
    • None
    • Red Hat Enterprise Linux
    • None
    • None
    • None
    • All
    • None

      What were you trying to do that didn't work?

      Running xfs_bmap on a file with more than about 22 million extents results in ENOMEM.

      Please provide the package NVR for which bug is seen:

      xfsprogs-6.3.0-1.el9.x86_64

      kernel-5.14.0-418.el9.x86_64

      How reproducible: 

      easy, see below

      Steps to reproduce

      1) create a badly-fragmented file with a large number of extents;

          https://github.com/fsorenson/misc/blob/master/make_frag/make_fragged2.c

      1. ./make_fragged2 -d /home -f testfile -s 100g -b 4k
      2. (creates a 100 GiB file at /home/testfile with (nominally) 26 million extents

      2) run xfs_bmap on the file:

      1. xfs_bmap /home/testfile
        xfs_bmap: xfsctl(XFS_IOC_GETBMAPX) iflags=0x0 ["/home/testfile"]: Cannot allocate memory

        Expected results

      no error while running?

      Actual results

      xfs_bmap: xfsctl(XFS_IOC_GETBMAPX) iflags=0x0 ["/home/testfile"]: Cannot allocate memory

       

      strace shows that xfs_bmap performs ioctl XFS_IOC_GETBMAPX with 32 entries, then FS_IOC_FSGETXATTR to get the number of extents.  It then allocates space for twice that number of extents and repeats XFS_IOC_GETBMAPX with twice the number of extents:

                       if (2 * fsx.fsx_nextents > map_size) {
                              map_size = 2 * fsx.fsx_nextents + 1;
                              map = realloc(map, map_size*sizeof(*map));
      

      inside the kernel, the ioctl checks the number of entries, and returns ENOMEM if the count is greater than INT_MAX / recsize (which is 48 bytes):

               if (bmx.bmv_count >= INT_MAX / recsize)
                      return -ENOMEM;
      

       

      If xfs_bmap is called with the '-n <num_extents>' flag, this number is used for the XFS_IOC_GETBMAPX (without doubling)

       

      Not doubling the number of extents would at least allow twice as many extents to be printed, though there would still be a limit.

      Always setting/limiting num_extents to INT_MAX/recsize would allow the kernel to return just below the limit without error, though the output may be truncated at that limit.

      Probably the only way around a huge number of extents would be to iterate through the list, rather than requesting the entire list. (the current method also requires a great deal of memory, both in userspace in the kernel).

              aalbersh@redhat.com Andrey Albershteyn
              rhn-support-fsorenso Frank Sorenson
              Bill O'Donnell Bill O'Donnell
              Zirong Lang Zirong Lang
              Votes:
              0 Vote for this issue
              Watchers:
              8 Start watching this issue

                Created:
                Updated:
                Resolved: