From 4980de3a5fdc91838817694655329780ede657fc Mon Sep 17 00:00:00 2001 From: David Sterba Date: Fri, 10 Sep 2021 12:45:19 +0200 Subject: [PATCH] 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 --- common/device-utils.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/common/device-utils.c b/common/device-utils.c index f3987244..1c853545 100644 --- a/common/device-utils.c +++ b/common/device-utils.c @@ -26,6 +26,7 @@ #include #include #include +#include #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;