-
Bug
-
Resolution: Won't Do
-
Minor
-
None
-
rhel-9.4.z
-
None
-
None
-
Low
-
TestCaseProvided
-
rhel-sst-filesystems
-
ssg_filesystems_storage_and_HA
-
4
-
False
-
-
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
- ./make_fragged2 -d /home -f testfile -s 100g -b 4k
- (creates a 100 GiB file at /home/testfile with (nominally) 26 million extents
2) run xfs_bmap on the file:
- xfs_bmap /home/testfile
xfs_bmap: xfsctl(XFS_IOC_GETBMAPX) iflags=0x0 ["/home/testfile"]: Cannot allocate memoryExpected 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).