QStorageInfo/Linux: fix getting information on unmounted btrfs subvols

Amends 1cd6c6c69e9813c791f8bebb6c0c9214ce765060.

Btrfs can have subvolumes and each one of them is assigned a device ID
when the filesystem is loaded into the kernel. But subvolumes don't all
have to be a mountpoint of their own: if we insist on matching device
IDs, as initRootPath() was doing, we'd fail at finding the mount point.

Fixes: QTBUG-121140
Change-Id: I76ffba14ece04f24b43efffd17ab39f503000dd7
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
(cherry picked from commit 987abb92538f8657d861611b1ced4e7eaa660adf)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Thiago Macieira 2024-01-17 11:43:28 -08:00 committed by Qt Cherry-pick Bot
parent 90a723b7cd
commit f3782300d2

View File

@ -10,6 +10,8 @@
#include <private/qcore_unix_p.h>
#include <private/qtools_p.h>
#include <q20memory.h>
#include <linux/mount.h>
#include <sys/statfs.h>
@ -199,15 +201,31 @@ quint64 QStorageInfoPrivate::initRootPath()
// # mount | tail -2
// tmpfs on /tmp/foo/bar type tmpfs (rw,relatime,inode64)
// tmpfs on /tmp/foo type tmpfs (rw,relatime,inode64)
// But just in case there's a mount --move, we ensure the device ID does
// match.
//
// We try to match the device ID in case there's a mount --move.
// We can't *rely* on it because some filesystems like btrfs will assign
// device IDs to subvolumes that aren't listed in /proc/self/mountinfo.
const QString oldRootPath = std::exchange(rootPath, QString());
const dev_t rootPathDevId = deviceIdForPath(oldRootPath);
MountInfo *best = nullptr;
for (auto it = infos.rbegin(); it != infos.rend(); ++it) {
if (rootPathDevId != it->stDev || !isParentOf(it->mountPoint, oldRootPath))
if (!isParentOf(it->mountPoint, oldRootPath))
continue;
auto stDev = it->stDev;
setFromMountInfo(std::move(*it));
if (rootPathDevId == it->stDev) {
// device ID matches; this is definitely the best option
best = q20::to_address(it);
break;
}
if (!best) {
// if we can't find a device ID match, this parent path is probably
// the correct one
best = q20::to_address(it);
}
}
if (best) {
auto stDev = best->stDev;
setFromMountInfo(std::move(*best));
return stDev;
}
return 0;