What's New in Qt 5.4: The QStorageInfo Class

Jeff Tranter


Jeff Tranter is a Qt Consulting Manager at ICS. Jeff oversees the architectural and high-level design of software systems for clients. Jeff’s leadership organizes global teams of developers on desktop and embedded applications.

By Jeff Tranter | Wednesday, November 19, 2014

In this blog post, I will cover a new class introduced in Qt 5.4.0: QStorageInfo. This post is based on a Lightning Talk I gave at Qt Developer Days 2014 in San Francisco.

Overview

The QStorageInfo class provides information about mounted filesystems, also known as volumes. It allows retrieving information about a volume's storage space, mount point, label and filesystem name. New in the Qt 5.4.0 release, QStorageInfo is part of the Qt Core module.

The supported platforms are Windows, WinRT, Mac OS X, Linux and Android. It should also work on the various flavors of Unix such as BSD, Solaris, etc.

The original author was Ivan Komissarov, and as is usual with the Qt community, many other people contributed to the development as well.

The information herein is based on the Qt 5.4.0 beta release, and could still change slightly before its final software release.

Basic Usage

The class is defined in the header file <QStorageInfo> and is quite simple, offering about twenty methods. Note that QStorageInfo is not derived from any other class, so an instance of QStorageInfo is not a QObject.

The basic steps for using the class are much the same as any other:

  1. Create an instance
  2. Call appropriate methods on the instance

There are several ways to instantiate a QStorageInfo object:

  1. Use the default constructor. Note that it will not be invalid unless you subsequently call the setPath() method on it with a suitable path.
  2. Use the constructor that accepts a path: QStorageInfo::QStorageInfo(const QString &path).
  3. Use the constructor that accepts a directory: QStorageInfo::QStorageInfo(const QDir &dir).
  4. Use the copy constructor: QStorageInfo::QStorageInfo(const QStorageInfo &other).
  5. Call the static method that returns a list of all mounted volumes: QList<QStorageInfo> mountedVolumes().
  6. Call the static method that returns the root volume: QStorageInfo root().

Methods

Let's look briefly at the methods provided by the class. We've already covered the constructors. There is a default destructor:

QStorageInfo::~QStorageInfo()

These methods provide information about space on the volume:

qint64 bytesAvailable() - returns the space available for current user.
qint64 bytesFree() - returns the free space on the volume.
qint64 bytesTotal() - returns the total volume size.

Note that bytesAvailable() returns the total space available if the current user is running as root or sysadmin.

The following methods provide information about the volume name and type:

QByteArray device() - returns the name, the device path on Linux/Unix and the UNC path/volume GUID on Windows.
QString displayName() - returns the volume's displahable name, if available, or the root path if not.
QByteArray fileSystemType() - returns the filesystem type, e.g. NTFS or ext4.

The methods give status information about the filesystem:

bool isReadOnly() - is the volume read only or read-write?
bool isReady() - is it ready? This can be false for removable media, for example.
bool isRoot() - is it the root volume? (i.e. / on Unix, the operating system installation drive on Windows)
bool isValid() - is it valid? As explained later, the instance may become invalid due to caching.

These methods manage the name and paths and refresh cached information:

QString name() - returns the volume label.
void refresh() - updates cached information.
QString rootPath() - returns path/drive letter or root path.
void setPath(const QString &path) - sets the path, typically used to initialize an invalid QStorageInfo object.

Calling setPath() updates the instance's information about the volume is related to. Note that refresh() is not a slot, because a QStorageInfo instance is not a QObject.

Also provided are some standard methods and operators that most Qt classes provide:

void swap(QStorageInfo &other) - the standard Qt swap method.
QStorageInfo & operator=(const QStorageInfo &other) - a copy constructor.
QStorageInfo & operator=(QStorageInfo &&other) - an assignment operator.
bool operator!=(const QStorageInfo &first, const QStorageInfo &second) and
bool operator==(const QStorageInfo &first, const QStorageInfo &second) - comparison operators (not class methods).

Finally, there are two static methods provided:

QList<QStorageInfo> mountedVolumes() - returns a list of mounted volumes.
QStorageInfo QStorageInfo::root() - returns an instance for the root volume.

Gotchas

There are some things to be aware of when using the class. For efficiency, it caches data about the filesystems. As mount points can change, the information may become out of date. To force an instance to update its cached information you can call the refresh() method.

An instance may not always be valid. This can be the case, for example, if you used the default constructor or if a cached mount point was unmounted. You can test validity with the isValid() method. It is recommended to also check if a volume is ready using isReady().

The bytesAvailable() method can return different values depending on whether the user is a sysadmin/root user or not.

QStorageInfo::name() can return an empty string if volume names are not supported by the underlying operating system. That can be the case on some Linux systems.

Example 1 (Non-GUI)

Here is the complete source code for a simple non-graphical example of using the QStorageInfo class.

#include <QDebug>
#include <QStorageInfo>

int main()
{
    for (auto volume : QStorageInfo::mountedVolumes()) {
        qDebug() << "Name:" << volume.name();
        qDebug() << "Display name:" << volume.displayName();
        qDebug() << "Device:" << volume.device();
        qDebug() << "Root path:" << volume.rootPath();
        qDebug() << "File system type:" << volume.fileSystemType();
        qDebug() << "Is valid?" << (volume.isValid() ? "yes" : "no");
        qDebug() << "Is root?" << (volume.isRoot() ? "yes" : "no");
        qDebug() << "Is ready?" << (volume.isReady() ? "yes" : "no");
        qDebug() << "Is read only?" << (volume.isReadOnly() ? "yes" : "no");
        qDebug() << "Bytes available:" << volume.bytesAvailable();
        qDebug() << "Bytes free:" << volume.bytesFree();
        qDebug() << "Bytes total:" << volume.bytesTotal() << endl;
    }
}

Note that no QApplication or QCoreApplication is needed, as the class does not require an event loop.

For this example I used a couple of C++11 features: type inference and range-based for. For this to work you need to add to your qmake project file the following line, "CONFIG += c++11" and use a suitable compiler.

Here is some sample output taken from a Linux desktop system:

Name: ""
Display name: "/"
Device: "/dev/sda4"
Root path: "/"
File system type: "ext4"
Is valid? yes
Is root? yes
Is ready? yes
Is read only? no
Bytes available: 246936653824
Bytes free: 265178152960
Bytes total: 358640545792 

Name: "OS"
Display name: "OS"
Device: "/dev/sda3"
Root path: "/windows"
File system type: "fuseblk"
Is valid? yes
Is root? no
Is ready? yes
Is read only? no
Bytes available: 93807919104
Bytes free: 93807919104
Bytes total: 128538992640 

Name: "Lexar"
Display name: "Lexar"
Device: "/dev/sdb1"
Root path: "/media/tranter/Lexar"
File system type: "vfat"
Is valid? yes
Is root? no
Is ready? yes
Is read only? no
Bytes available: 15314558976
Bytes free: 15314558976
Bytes total: 15688114176

Here is the output on a Windows machine:

Name: ""
Display name: "C:/"
Device: "\\?\Volume{9b687c31-66e9-11e0-af36-806e6f6e6963}\"
Root path: "C:/"
File system type: "NTFS"
Is valid? yes
Is root? yes
Is ready? yes
Is read only? no
Bytes available: 116315054080
Bytes free: 116315054080
Bytes total: 154288517120 

Name: "RECOVERY"
Display name: "RECOVERY"
Device: "\\?\Volume{9b687c32-66e9-11e0-af36-806e6f6e6963}\"
Root path: "D:/"
File system type: "NTFS"
Is valid? yes
Is root? no
Is ready? yes
Is read only? no
Bytes available: 11417243648
Bytes free: 11417243648

Example 2 (GUI)

I also wrote a simple graphical example that displays some of the information that the class can return in a dialog box. The user can select the volume by name from a combobox. I won't go over the code in detail, as it is straightforward. You can download the full source code from here.

Below is a screen shot of the application running on Linux. I was able to run it on Windows, Linux and Android platforms (using the emulator in the Android SDK). I also ran it on a real Android device.

More Details

The source code for the QStorageInfo class implementation can be found in the Qt source under qtbase/src/corelib/io in the following files:

qstorageinfo.cpp
qstorageinfo.h
qstorageinfo_mac.cpp
qstorageinfo_p.h
qstorageinfo_stub.cpp
qstorageinfo_unix.cpp
qstorageinfo_win.cpp

The code follows the usual Qt convention of using a private class and a d-pointer.

Related Classes

The Qt classes QStandardPaths and QDesktopServices return similar information to QStorageInfo. Note that some methods in QDesktopServices are now deprecated and have replacements in the newer QStandardPaths class.

Summary

QStorageInfo is a simple and straightforward class that allows obtaining information about mounted filesystems in a portable manner. It is a C++-only class that is not exposed to QML. The class is new in the Qt 5.4.0 release.

Full documentation on the class can be found here.

The source code for the two code examples can be downloaded from here.


Tags:  Qt Qt 5

Comments

Comment: 

Many thanks for this great article.
I have a question related to the detection of mounted/unmounted drives. Do you know if QStorageInfo has something like a "logicalDriveChanged" signal sent when a new drive is mounted, like in QtMobility/QSystemStorageInfo?
I guess the answer is no because it doesn't inherit QObject, but I am wondering if this feature will be back in the future or available in another component...

Comment: