Commit Graph

15 Commits (feb67bbdd202ac708cbf41f5698c4e2246ad4232)

Author SHA1 Message Date
Thiago Macieira feb67bbdd2 QStorageInfo/Unix: exclude invalid volumes from mountedVolumes()
Invalid are usually those mounted on a filesystem we can't access:

$ df /run/user/0/gvfs
df: /run/user/0/gvfs: Permission denied
$ ./qstorageinfo /run/user/0/gvfs
Could not get info on /run/user/0/gvfs

df already doesn't include it by default:

$ df | grep -c gvfs
0

But we were:

$./qstorageinfo | sed -n '1p;/gvfs/p'
Filesystem (Type)                        Size  Available BSize  Label   Mounted on
gvfsd-fuse (fuse.gvfsd-fuse)    RW          0          0     0          /run/user/0/gvfs

Note also how this is showing a total size of 0, which is usually the
type of filesystem we exclude. It's actually -1 but got rounded down to
0 when we divided by 1024.

Pick-to: 6.6
Change-Id: I8f3ce163ccc5408cac39fffd178d7e4f9dc13b24
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
2023-10-21 15:21:39 -07:00
Thiago Macieira 39843b65f4 QStorageInfo/Linux: decode the names encoded by udev in-place
This function is only called with the name of a file coming from
QFileInfo::fileName() so it's usually already detached anyway. And if
there's nothing to decode, pass the string through without even
attempting to modify it.

Pick-to: 6.6
Change-Id: I9d43e5b91eb142d6945cfffd1787651437074d35
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
2023-10-21 15:21:30 -07:00
Thiago Macieira 4107e4d8ca QStorageInfo/Linux: avoid parsing /dev/disks/by-label for every entry
Instead, create a (flat) map of the entries that we can seek on while
creating the list of mountedVolumes(). On my machine, that went down
from 14 times to 1.

Pick-to: 6.6
Change-Id: I9d43e5b91eb142d6945cfffd17875458541f28f9
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
2023-10-21 15:21:27 -07:00
Thiago Macieira 3e330a79ec QStorageInfo/Linux: rewrite the label retriever to use device IDs
Instead of the previous realpath() comparison resulting from the symlink
processing. parseMountInfo() was extracting the device number from
/proc, so this information was already readily available.

We must take care of anonymous block devices (major == 0). Certain
filesystems, such as btrfs, always use them, so we must still stat() the
device path to get the real block device.

This implementation assumes that udev only creates entries in the
/dev/disks/by-label directory that are symlinks to real devices, but
that must already be the case because they are in /dev in the first
place. An alternative implementation would be to compare the inode and
host device (st_dev) of the entry, if different /dev entries could have
different labels. I don't think that's possible. But multiple /dev
entries for the same device is definitely possible.

Pick-to: 6.6
Change-Id: I9d43e5b91eb142d6945cfffd1787552af3d09676
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2023-10-21 15:21:24 -07:00
Thiago Macieira ddc39eb3a4 QStorageInfo/Linux: fix mountedVolumes() for paths mounted over
This fixes a similar problem as the previous commit. Because Linux
allows one to mount over non-empty paths, it's possible to make
mountpoints unreachable, and yet they will still be present in the
/proc/self/mountinfo listing. Because we have the device ID from the
scan, we can confirm that the mountpoint matches the MountInfo.

 # mkdir -p /tmp/foo/bar
 # mount -t tmpfs /tmp/foo/bar
 # mount -t tmpfs -o size=1M /tmp/foo
 # mkdir /tmp/foo/bar
 $ ./tests/manual/qstorageinfo/qstorageinfo | awk 'NR==1 || /tmp\/foo/'
Filesystem (Type)                        Size  Available BSize  Label                           Mounted on
tmpfs                           RW       1024       1024  4096                                  /tmp/foo

Change-Id: I79e700614d034281bf55fffd178f8b99b10a8b69
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
2023-10-21 15:21:23 -07:00
Thiago Macieira 1cd6c6c69e 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>
2023-10-21 15:21:21 -07:00
Thiago Macieira cad7164ee2 QStorageInfo/Linux: include QDir::Hidden in the search for labels
There's nothing wrong with device labels starting with a dot. Whether
udev would encode those as \x2e is unknown, but we may as well not tempt
fate in case it has changed or changes in the future.

Also including QDir::System in case udev places the actual device nodes
in /dev/disks/by-label instead of a symlink.

As a nice and intentional side-effect, QDirIterator no longer performs a
stat() in each of the entries, removing the double stat'ing that started
happening with the previous commit.

Pick-to: 6.6
Change-Id: I9d43e5b91eb142d6945cfffd1787681b4cf2bb58
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2023-10-16 08:41:21 -07:00
Thiago Macieira df8514a764 QStorageInfo/Linux: simplify the code to deal with skipped entries
In addition to what parseMountInfo() filtered, we also filter entries
with zero total bytes (other than the root filesystem). This avoids
creating yet another QStorageInfoPrivate that may not be used, but most
importantly it avoids calling root() for that check, which would call
parseMountInfo() again.

Pick-to: 6.6
Change-Id: I9d43e5b91eb142d6945cfffd1787538dd3f285d0
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
2023-10-16 08:41:21 -07:00
Thiago Macieira c82ed8b279 QStorageInfo/Linux: avoid parsing /proc/self/mountinfo N+1 times
Mine has 41 lines, of which 22 are returned by parseMountInfo with
filtering. That meant the file was parsed once to get the listing, then
22 times more to create a QStorageInfo for each entry. Now
QStorageInfo::mountedVolumes() opens the file and parses it only once.

Pick-to: 6.6
Change-Id: I9d43e5b91eb142d6945cfffd178752ef6c2122f2
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
2023-10-16 08:41:21 -07:00
Thiago Macieira a5a288feb3 QStorageInfo/Linux: remove const to enable moving from MountInfo
Amends da95ad91b3. Caught by CodeChecker:

std::move of the const expression has no effect; remove std::move()

I'll instead remove the const.

Pick-to: 6.6
Change-Id: I8f3ce163ccc5408cac39fffd178ccec9fcc38e9c
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
2023-10-11 08:39:43 +00:00
Thiago Macieira 94df3f8d6b QStorageInfo/Unix: check the mount point length before isParentOf()
Execute the cheaper test first, so we loop over the entries more
quickly.

Pick-to: 6.6
Change-Id: I9d43e5b91eb142d6945cfffd178749a12c966739
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2023-09-29 19:55:48 +00:00
Thiago Macieira da95ad91b3 QStorageInfo/Linux: move from MountInfo's contents
Copying QStrings and QByteArrays is reasonably cheap, but moving is
cheaper.

Pick-to: 6.6
Change-Id: I9d43e5b91eb142d6945cfffd1787498ead687da2
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
2023-09-29 12:55:47 -07:00
Thiago Macieira 914b3bc985 QStorageInfo/Linux: don't copy the mount info's contents until the end
All these where somewhat cheap to copy (QStrings and QByteArrays), but
why copy multiple times at all? Just copy at the end.

Pick-to: 6.6
Change-Id: I9d43e5b91eb142d6945cfffd1787497434632dd4
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2023-09-29 12:55:47 -07:00
Thiago Macieira c8d31833f0 QStorageInfo/Linux: remove unnecessary isSymlink() call
symlinkTarget() suffices, because if the candidate is not a symlink, it
will return an empty string. Plus, /dev/disks/by-label is a udev-managed
directory, so everything should be a symlink.

This doesn't change the number of statx() calls because QDirIterator
needs some information on file types to decide how to filter and,
unfortunately, that information is missing for symlinks (we know it's a
symlink, but we don't know what it points to). Moreover, due to
QDirIterator's design, we always statx() one entry past the one we
wanted.

Pick-to: 6.6
Change-Id: I9d43e5b91eb142d6945cfffd1786ce1bd3398d1b
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2023-09-29 12:55:47 -07:00
Ahmad Samir 536696196c QStorageInfo: split Linux specific code to a separate source file
Much less crowded.

Inline QStorageInfoPrivate::root() to ease the split; the Windows
specific code is one extra line.

Change-Id: Icec6822cf436e2b4aa1b3a04184fbfa40e508078
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2023-07-09 01:44:49 +03:00