summaryrefslogtreecommitdiffstats
path: root/src/corelib/io
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2023-10-19 08:30:46 -0700
committerThiago Macieira <thiago.macieira@intel.com>2023-10-21 15:21:21 -0700
commit1cd6c6c69e9813c791f8bebb6c0c9214ce765060 (patch)
treee84c2f8d4a425df16177e41f487e3adbb65dae4d /src/corelib/io
parent9df2b7ffa4fd99c39e73263c0ee07c203053096b (diff)
QStorageInfo/Linux: fix setPath() for paths mounted over
Linux allows admins to mount new filesystems over non-empty paths (though I managed to do so on FreeBSD and macOS too), so we must find the most recent mount that applies to this path instead of the longest matching mountpoint. We do that by scanning the /proc/self/mountinfo list backwards and thus any matching isParentOf() must be the correct one. # mkdir -p /tmp/foo/bar # mount -t tmpfs tmpfs /tmp/foo/bar # mount -t tmpfs -o size=1M tmpfs /tmp/foo $ ./tests/manual/qstorageinfo/qstorageinfo /tmp/foo/bar Filesystem (Type) Size Available BSize Label Mounted on tmpfs RW 1024 1024 4096 /tmp/foo But we must guard against an earlier mount still being (somehow) accessible. We've seen this in the CI, where /run is earlier than / but still somehow accessible -- I guess this is one or a pair of mount --move. An additional benefit is that don't even attempt to compare to the virtual filesystems mounted by the system early after boot, if what we're looking for isn't the root. See next commit for a fix for QStorageInfo::mountedVolumes(). Change-Id: I79e700614d034281bf55fffd178f8befc5e80edb Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Diffstat (limited to 'src/corelib/io')
-rw-r--r--src/corelib/io/qstorageinfo_linux.cpp45
1 files changed, 27 insertions, 18 deletions
diff --git a/src/corelib/io/qstorageinfo_linux.cpp b/src/corelib/io/qstorageinfo_linux.cpp
index 99143a5975..fab5a44fad 100644
--- a/src/corelib/io/qstorageinfo_linux.cpp
+++ b/src/corelib/io/qstorageinfo_linux.cpp
@@ -59,6 +59,14 @@ static QString decodeFsEncString(const QString &str)
return decoded;
}
+static inline dev_t deviceIdForPath(const QString &device)
+{
+ QT_STATBUF st;
+ if (QT_STAT(QFile::encodeName(device), &st) < 0)
+ return 0;
+ return st.st_dev;
+}
+
static inline QString retrieveLabel(const QByteArray &device)
{
static const char pathDiskByLabel[] = "/dev/disk/by-label";
@@ -136,24 +144,25 @@ void QStorageInfoPrivate::initRootPath()
return;
}
- qsizetype maxLength = 0;
- const QString oldRootPath = rootPath;
- rootPath.clear();
-
- MountInfo *bestInfo = nullptr;
- for (MountInfo &info : infos) {
- // we try to find most suitable entry
- qsizetype mpSize = info.mountPoint.size();
- if (maxLength < mpSize && isParentOf(info.mountPoint, oldRootPath)) {
- bestInfo = &info;
- maxLength = mpSize;
- }
- }
- if (bestInfo) {
- rootPath = std::move(bestInfo->mountPoint);
- device = std::move(bestInfo->device);
- fileSystemType = std::move(bestInfo->fsType);
- subvolume = std::move(bestInfo->fsRoot);
+ // We iterate over the /proc/self/mountinfo list backwards because then any
+ // matching isParentOf must be the actual mount point because it's the most
+ // recent mount on that path. Linux does allow mounting over non-empty
+ // directories, such as in:
+ // # 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.
+ const QString oldRootPath = std::exchange(rootPath, QString());
+ const dev_t rootPathDevId = deviceIdForPath(oldRootPath);
+ for (auto it = infos.rbegin(); it != infos.rend(); ++it) {
+ if (rootPathDevId != it->stDev || !isParentOf(it->mountPoint, oldRootPath))
+ continue;
+ rootPath = std::move(it->mountPoint);
+ device = std::move(it->device);
+ fileSystemType = std::move(it->fsType);
+ subvolume = std::move(it->fsRoot);
+ return;
}
}