diff mbox series

btrfs-progs: mkfs: do not enlarge the target block device

Message ID 8ce8ead459b46a5b6849077ee50cf526418263da.1697099461.git.wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs-progs: mkfs: do not enlarge the target block device | expand

Commit Message

Qu Wenruo Oct. 12, 2023, 8:31 a.m. UTC
[BUG]
When running mkfs.btrfs with --rootdir on a block device, and the source
directory contains a sparse file, whose size is larger than the block
size, then mkfs.btrfs would fail:

  # lsblk  /dev/test/test
  NAME      MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
  test-test 253:0    0  10G  0 lvm
  # mkdir -p /tmp/output
  # truncate -s 20G /tmp/output/file
  # mkfs.btrfs -f --rootdir /tmp/output /dev/test/test
  # sudo mkfs.btrfs  -f /dev/test/scratch1  --rootdir /tmp/output/
  btrfs-progs v6.3.3
  See https://btrfs.readthedocs.io for more information.

  ERROR: unable to zero the output file

[CAUSE]
Mkfs.btrfs would try to zero out the target file according to the total
size of the directory.

However the directory size is calculated using the file size, not the
real bytes taken by the file, thus for such sparse file with holes only,
it would still take 20G.

Then we would use that 20G size to zero out the target file, but if the
target file is a block device, we would fail as we can not enlarge a block
device.

[FIX]
When zeroing the file, we only enlarge it if the target is a regular
file.
Otherwise we warn about the size and continue.

Please note that, since "mkfs.btrfs --rootdir" doesn't handle sparse
file any differently from regular file, above case would still fail due
to ENOSPC, as will write zeros into the target file inside the fs.

Proper handling for sparse files would need a new series of patch to
address.

Issue: #653
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 mkfs/main.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

Comments

David Sterba Oct. 12, 2023, 4:36 p.m. UTC | #1
On Thu, Oct 12, 2023 at 07:01:04PM +1030, Qu Wenruo wrote:
> [BUG]
> When running mkfs.btrfs with --rootdir on a block device, and the source
> directory contains a sparse file, whose size is larger than the block
> size, then mkfs.btrfs would fail:
> 
>   # lsblk  /dev/test/test
>   NAME      MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
>   test-test 253:0    0  10G  0 lvm
>   # mkdir -p /tmp/output
>   # truncate -s 20G /tmp/output/file
>   # mkfs.btrfs -f --rootdir /tmp/output /dev/test/test
>   # sudo mkfs.btrfs  -f /dev/test/scratch1  --rootdir /tmp/output/
>   btrfs-progs v6.3.3
>   See https://btrfs.readthedocs.io for more information.
> 
>   ERROR: unable to zero the output file
> 
> [CAUSE]
> Mkfs.btrfs would try to zero out the target file according to the total
> size of the directory.
> 
> However the directory size is calculated using the file size, not the
> real bytes taken by the file, thus for such sparse file with holes only,
> it would still take 20G.
> 
> Then we would use that 20G size to zero out the target file, but if the
> target file is a block device, we would fail as we can not enlarge a block
> device.
> 
> [FIX]
> When zeroing the file, we only enlarge it if the target is a regular
> file.
> Otherwise we warn about the size and continue.
> 
> Please note that, since "mkfs.btrfs --rootdir" doesn't handle sparse
> file any differently from regular file, above case would still fail due
> to ENOSPC, as will write zeros into the target file inside the fs.
> 
> Proper handling for sparse files would need a new series of patch to
> address.
> 
> Issue: #653
> Signed-off-by: Qu Wenruo <wqu@suse.com>

Added to devel, thanks.
> ---
>  mkfs/main.c | 8 ++++++--
>  1 file changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/mkfs/main.c b/mkfs/main.c
> index 5abf7605326c..7d0ffac309e8 100644
> --- a/mkfs/main.c
> +++ b/mkfs/main.c
> @@ -1567,8 +1567,12 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
>  			block_count = device_get_partition_size_fd_stat(fd, &statbuf);
>  		source_dir_size = btrfs_mkfs_size_dir(source_dir, sectorsize,
>  				min_dev_size, metadata_profile, data_profile);
> -		if (block_count < source_dir_size)
> -			block_count = source_dir_size;
> +		if (block_count < source_dir_size) {
> +			if (S_ISREG(statbuf.st_mode))
> +				block_count = source_dir_size;
> +			else
> +				warning("the target device is smaller than the source directory, mkfs may fail");

I've updated the message to also say the numbers:

WARNING: the target device 122683392 (117.00MiB) is smaller than the
calculated source directory size 114294784 (209.00MiB) , mkfs may fail
diff mbox series

Patch

diff --git a/mkfs/main.c b/mkfs/main.c
index 5abf7605326c..7d0ffac309e8 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -1567,8 +1567,12 @@  int BOX_MAIN(mkfs)(int argc, char **argv)
 			block_count = device_get_partition_size_fd_stat(fd, &statbuf);
 		source_dir_size = btrfs_mkfs_size_dir(source_dir, sectorsize,
 				min_dev_size, metadata_profile, data_profile);
-		if (block_count < source_dir_size)
-			block_count = source_dir_size;
+		if (block_count < source_dir_size) {
+			if (S_ISREG(statbuf.st_mode))
+				block_count = source_dir_size;
+			else
+				warning("the target device is smaller than the source directory, mkfs may fail");
+		}
 		ret = zero_output_file(fd, block_count);
 		if (ret) {
 			error("unable to zero the output file");