btrfs-progs: read partition size from sysfs as fallback

Reading partition size using an ioctl requires the device open, but that
does not work for unprivileged users. This leads to 0 size in device
info structures filled by device_get_partition_size.

As a consequence, this also misreports such devices as missing in 'fi
us' overview:

 $ btrfs fi us /
 WARNING: cannot read detailed chunk info, per-device usage will not be shown, run as root
 Overall:
     Device size:		 411.35GiB
     Device allocated:		  53.01GiB
     Device unallocated:	 358.34GiB
     Device missing:		 411.35GiB
     Used:			  31.99GiB
     Free (estimated):		 379.16GiB	(min: 379.16GiB)
     Free (statfs, df):		 379.35GiB
     Data ratio:		      1.00
     Metadata ratio:		      1.00
     Global reserve:		 194.77MiB	(used: 0.00B)
     Multiple profiles:		        no

There should be 0 for 'Device missing'.

Add a fallback to read the device size from sysfs in case the ioctl is
not available.

Issue: #395
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
David Sterba 2021-09-10 12:45:19 +02:00
parent 56e9963474
commit 4980de3a5f
1 changed files with 39 additions and 1 deletions

View File

@ -26,6 +26,7 @@
#include <dirent.h>
#include <blkid/blkid.h>
#include <linux/limits.h>
#include <limits.h>
#include "kernel-lib/sizes.h"
#include "kernel-shared/disk-io.h"
#include "kernel-shared/zoned.h"
@ -303,13 +304,50 @@ u64 device_get_partition_size_fd(int fd)
return result;
}
u64 device_get_partition_size_sysfs(const char *dev)
{
int ret;
char path[PATH_MAX] = {};
char sysfs[PATH_MAX] = {};
char sizebuf[128] = {};
char *name = NULL;
int sysfd;
unsigned long long size = 0;
name = realpath(dev, path);
if (!name)
return 0;
name = basename(path);
ret = path_cat3_out(sysfs, "/sys/class/block", name, "size");
if (ret < 0)
return 0;
sysfd = open(sysfs, O_RDONLY);
if (sysfd < 0)
return 0;
ret = sysfs_read_file(sysfd, sizebuf, sizeof(sizebuf));
if (ret < 0) {
close(sysfd);
return 0;
}
errno = 0;
size = strtoull(sizebuf, NULL, 10);
if (size == ULLONG_MAX && errno == ERANGE) {
close(sysfd);
return 0;
}
close(sysfd);
return size;
}
u64 device_get_partition_size(const char *dev)
{
u64 result;
int fd = open(dev, O_RDONLY);
if (fd < 0)
return 0;
return device_get_partition_size_sysfs(dev);
if (ioctl(fd, BLKGETSIZE64, &result) < 0) {
close(fd);
return 0;