summaryrefslogtreecommitdiffstats
path: root/src/corelib/io
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io')
-rw-r--r--src/corelib/io/io.pri4
-rw-r--r--src/corelib/io/qfile.cpp60
-rw-r--r--src/corelib/io/qfile.h3
-rw-r--r--src/corelib/io/qfileinfo.cpp2
-rw-r--r--src/corelib/io/qfilesystemengine_mac.mm83
-rw-r--r--src/corelib/io/qfilesystemengine_p.h3
-rw-r--r--src/corelib/io/qfilesystemengine_unix.cpp212
-rw-r--r--src/corelib/io/qfilesystemengine_win.cpp198
-rw-r--r--src/corelib/io/qiodevice.cpp2
-rw-r--r--src/corelib/io/qlockfile_unix.cpp3
-rw-r--r--src/corelib/io/qstorageinfo_unix.cpp21
-rw-r--r--src/corelib/io/qurl.cpp7
-rw-r--r--src/corelib/io/qurl.h4
13 files changed, 592 insertions, 10 deletions
diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri
index c4c6f41387..a33ffe75f2 100644
--- a/src/corelib/io/io.pri
+++ b/src/corelib/io/io.pri
@@ -183,7 +183,9 @@ win32 {
SOURCES += io/qstorageinfo_mac.cpp
qtConfig(processenvironment): \
OBJECTIVE_SOURCES += io/qprocess_darwin.mm
- OBJECTIVE_SOURCES += io/qstandardpaths_mac.mm
+ OBJECTIVE_SOURCES += \
+ io/qstandardpaths_mac.mm \
+ io/qfilesystemengine_mac.mm
osx {
LIBS += -framework DiskArbitration -framework IOKit
} else {
diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp
index 5320ae2986..0cdc5bd6d3 100644
--- a/src/corelib/io/qfile.cpp
+++ b/src/corelib/io/qfile.cpp
@@ -552,6 +552,66 @@ QFile::remove(const QString &fileName)
}
/*!
+ \since 5.15
+
+ Moves the file specified by fileName() to the trash. Returns \c true if successful,
+ and sets the fileName() to the path at which the file can be found within the trash;
+ otherwise returns \c false.
+
+ \note On systems where the system API doesn't report the location of the file in the
+ trash, fileName() will be set to the null string once the file has been moved. On
+ systems that don't have a trash can, this function always returns false.
+*/
+bool
+QFile::moveToTrash()
+{
+ Q_D(QFile);
+ if (d->fileName.isEmpty() &&
+ !static_cast<QFSFileEngine *>(d->engine())->isUnnamedFile()) {
+ qWarning("QFile::remove: Empty or null file name");
+ return false;
+ }
+ unsetError();
+ close();
+ if (error() == QFile::NoError) {
+ QFileSystemEntry fileEntry(d->fileName);
+ QFileSystemEntry trashEntry;
+ QSystemError error;
+ if (QFileSystemEngine::moveFileToTrash(fileEntry, trashEntry, error)) {
+ setFileName(trashEntry.filePath());
+ unsetError();
+ return true;
+ }
+ d->setError(QFile::RenameError, error.toString());
+ }
+ return false;
+}
+
+/*!
+ \since 5.15
+ \overload
+
+ Moves the file specified by fileName() to the trash. Returns \c true if successful,
+ and sets \a pathInTrash (if provided) to the path at which the file can be found within
+ the trash; otherwise returns \c false.
+
+ \note On systems where the system API doesn't report the path of the file in the
+ trash, \a pathInTrash will be set to the null string once the file has been moved.
+ On systems that don't have a trash can, this function always returns false.
+*/
+bool
+QFile::moveToTrash(const QString &fileName, QString *pathInTrash)
+{
+ QFile file(fileName);
+ if (file.moveToTrash()) {
+ if (pathInTrash)
+ *pathInTrash = file.fileName();
+ return true;
+ }
+ return false;
+}
+
+/*!
Renames the file currently specified by fileName() to \a newName.
Returns \c true if successful; otherwise returns \c false.
diff --git a/src/corelib/io/qfile.h b/src/corelib/io/qfile.h
index 2099b2852f..917fec4e1a 100644
--- a/src/corelib/io/qfile.h
+++ b/src/corelib/io/qfile.h
@@ -125,6 +125,9 @@ public:
bool remove();
static bool remove(const QString &fileName);
+ bool moveToTrash();
+ static bool moveToTrash(const QString &fileName, QString *pathInTrash = nullptr);
+
bool rename(const QString &newName);
static bool rename(const QString &oldName, const QString &newName);
diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp
index 3fe1aec41f..64b1557231 100644
--- a/src/corelib/io/qfileinfo.cpp
+++ b/src/corelib/io/qfileinfo.cpp
@@ -1147,6 +1147,8 @@ bool QFileInfo::isShortcut() const
/*!
+ \since 5.15
+
Returns \c true if the object points to a junction;
otherwise returns \c false.
diff --git a/src/corelib/io/qfilesystemengine_mac.mm b/src/corelib/io/qfilesystemengine_mac.mm
new file mode 100644
index 0000000000..258ae2e8e0
--- /dev/null
+++ b/src/corelib/io/qfilesystemengine_mac.mm
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qfilesystemengine_p.h"
+#include "qfile.h"
+#include "qurl.h"
+
+#include <QtCore/private/qcore_mac_p.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ This implementation does not enable the "put back" option in Finder
+ for the trashed object. The only way to get this is to use Finder automation,
+ which would query the user for permission to access Finder using a modal,
+ blocking dialog - which we definitely can't have in a console application.
+
+ Using Finder would also play the trash sound, which we don't want either in
+ such a core API; applications that want that can play the sound themselves.
+*/
+//static
+bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
+ QFileSystemEntry &newLocation, QSystemError &error)
+{
+#ifdef Q_OS_MACOS // desktop macOS has a trash can
+ QMacAutoReleasePool pool;
+
+ QFileInfo info(source.filePath());
+ NSString *filepath = info.filePath().toNSString();
+ NSURL *fileurl = [NSURL fileURLWithPath:filepath isDirectory:info.isDir()];
+ NSURL *resultingUrl = nil;
+ NSError *nserror = nil;
+ NSFileManager *fm = [NSFileManager defaultManager];
+ if ([fm trashItemAtURL:fileurl resultingItemURL:&resultingUrl error:&nserror] != YES) {
+ error = QSystemError(nserror.code, QSystemError::NativeError);
+ return false;
+ }
+ newLocation = QFileSystemEntry(QUrl::fromNSURL(resultingUrl).path());
+ return true;
+#else // watch, tv, iOS don't have a trash can
+ return false;
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qfilesystemengine_p.h b/src/corelib/io/qfilesystemengine_p.h
index ecfdc03743..555483a972 100644
--- a/src/corelib/io/qfilesystemengine_p.h
+++ b/src/corelib/io/qfilesystemengine_p.h
@@ -88,7 +88,7 @@ inline bool qIsFilenameBroken(const QFileSystemEntry &entry)
Q_RETURN_ON_INVALID_FILENAME("Broken filename passed to function", (result)); \
} while (false)
-class QFileSystemEngine
+class Q_AUTOTEST_EXPORT QFileSystemEngine
{
public:
static bool isCaseSensitive()
@@ -155,6 +155,7 @@ public:
static bool createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
static bool copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
+ static bool moveFileToTrash(const QFileSystemEntry &source, QFileSystemEntry &newLocation, QSystemError &error);
static bool renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
static bool renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
static bool removeFile(const QFileSystemEntry &entry, QSystemError &error);
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp
index 3bbebc7fe9..eaf4e2d9af 100644
--- a/src/corelib/io/qfilesystemengine_unix.cpp
+++ b/src/corelib/io/qfilesystemengine_unix.cpp
@@ -42,6 +42,8 @@
#include "qplatformdefs.h"
#include "qfilesystemengine_p.h"
#include "qfile.h"
+#include "qstorageinfo.h"
+#include "qtextstream.h"
#include <QtCore/qoperatingsystemversion.h>
#include <QtCore/private/qcore_unix_p.h>
@@ -1197,6 +1199,216 @@ bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSy
return false;
}
+#ifndef Q_OS_DARWIN
+/*
+ Implementing as per https://specifications.freedesktop.org/trash-spec/trashspec-1.0.html
+*/
+
+// bootstrapped tools don't need this, and we don't want QStorageInfo
+#ifndef QT_BOOTSTRAPPED
+static QString freeDesktopTrashLocation(const QString &sourcePath)
+{
+ auto makeTrashDir = [](const QDir &topDir, const QString &trashDir) -> QString {
+ auto ownerPerms = QFileDevice::ReadOwner
+ | QFileDevice::WriteOwner
+ | QFileDevice::ExeOwner;
+ QString targetDir = topDir.filePath(trashDir);
+ if (topDir.mkdir(trashDir))
+ QFile::setPermissions(targetDir, ownerPerms);
+ if (QFileInfo(targetDir).isDir())
+ return targetDir;
+ return QString();
+ };
+ auto isSticky = [](const QFileInfo &fileInfo) -> bool {
+ struct stat st;
+ if (stat(QFile::encodeName(fileInfo.absoluteFilePath()).constData(), &st) == 0)
+ return st.st_mode & S_ISVTX;
+
+ return false;
+ };
+
+ QString trash;
+ const QLatin1String dotTrash(".Trash");
+ const QStorageInfo sourceStorage(sourcePath);
+ const QStorageInfo homeStorage(QDir::home());
+ // We support trashing of files outside the users home partition
+ if (sourceStorage != homeStorage) {
+ QDir topDir(sourceStorage.rootPath());
+ /*
+ Method 1:
+ "An administrator can create an $topdir/.Trash directory. The permissions on this
+ directories should permit all users who can trash files at all to write in it;
+ and the “sticky bit” in the permissions must be set, if the file system supports
+ it.
+ When trashing a file from a non-home partition/device, an implementation
+ (if it supports trashing in top directories) MUST check for the presence
+ of $topdir/.Trash."
+ */
+ const QString userID = QString::number(::getuid());
+ if (topDir.cd(dotTrash)) {
+ const QFileInfo trashInfo(topDir.path());
+
+ // we MUST check that the sticky bit is set, and that it is not a symlink
+ if (trashInfo.isSymLink()) {
+ // we SHOULD report the failed check to the administrator
+ qCritical("Warning: '%s' is a symlink to '%s'",
+ trashInfo.absoluteFilePath().toLocal8Bit().constData(),
+ trashInfo.symLinkTarget().toLatin1().constData());
+ } else if (!isSticky(trashInfo)) {
+ // we SHOULD report the failed check to the administrator
+ qCritical("Warning: '%s' doesn't have sticky bit set!",
+ trashInfo.absoluteFilePath().toLocal8Bit().constData());
+ } else if (trashInfo.isDir()) {
+ /*
+ "If the directory exists and passes the checks, a subdirectory of the
+ $topdir/.Trash directory is to be used as the user's trash directory
+ for this partition/device. The name of this subdirectory is the numeric
+ identifier of the current user ($topdir/.Trash/$uid).
+ When trashing a file, if this directory does not exist for the current user,
+ the implementation MUST immediately create it, without any warnings or
+ delays for the user."
+ */
+ trash = makeTrashDir(topDir, userID);
+ }
+ }
+ /*
+ Method 2:
+ "If an $topdir/.Trash directory is absent, an $topdir/.Trash-$uid directory is to be
+ used as the user's trash directory for this device/partition. [...] When trashing a
+ file, if an $topdir/.Trash-$uid directory does not exist, the implementation MUST
+ immediately create it, without any warnings or delays for the user."
+ */
+ if (trash.isEmpty()) {
+ topDir = QDir(sourceStorage.rootPath());
+ const QString userTrashDir = dotTrash + QLatin1Char('-') + userID;
+ trash = makeTrashDir(topDir, userTrashDir);
+ }
+ }
+ /*
+ "If both (1) and (2) fail [...], the implementation MUST either trash the
+ file into the user's “home trash” or refuse to trash it."
+
+ We trash the file into the user's home trash.
+ */
+ if (trash.isEmpty()) {
+ QDir topDir = QDir::home();
+ trash = makeTrashDir(topDir, dotTrash);
+ if (!QFileInfo(trash).isDir()) {
+ qWarning("Unable to establish trash directory %s in %s",
+ dotTrash.latin1(), topDir.path().toLocal8Bit().constData());
+ }
+ }
+
+ return trash;
+}
+#endif // QT_BOOTSTRAPPED
+
+//static
+bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
+ QFileSystemEntry &newLocation, QSystemError &error)
+{
+#ifdef QT_BOOTSTRAPPED
+ Q_UNUSED(source);
+ Q_UNUSED(newLocation);
+ error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
+ return false;
+#else
+ const QFileInfo sourceInfo(source.filePath());
+ if (!sourceInfo.exists()) {
+ error = QSystemError(ENOENT, QSystemError::StandardLibraryError);
+ return false;
+ }
+ const QString sourcePath = sourceInfo.absoluteFilePath();
+
+ QDir trashDir(freeDesktopTrashLocation(sourcePath));
+ if (!trashDir.exists())
+ return false;
+ /*
+ "A trash directory contains two subdirectories, named info and files."
+ */
+ const QLatin1String filesDir("files");
+ const QLatin1String infoDir("info");
+ trashDir.mkdir(filesDir);
+ int savedErrno = errno;
+ trashDir.mkdir(infoDir);
+ if (!savedErrno)
+ savedErrno = errno;
+ if (!trashDir.exists(filesDir) || !trashDir.exists(infoDir)) {
+ error = QSystemError(savedErrno, QSystemError::StandardLibraryError);
+ return false;
+ }
+ /*
+ "The $trash/files directory contains the files and directories that were trashed.
+ The names of files in this directory are to be determined by the implementation;
+ the only limitation is that they must be unique within the directory. Even if a
+ file with the same name and location gets trashed many times, each subsequent
+ trashing must not overwrite a previous copy."
+ */
+ const QString trashedName = sourceInfo.isDir()
+ ? QDir(sourcePath).dirName()
+ : sourceInfo.fileName();
+ QString uniqueTrashedName = QLatin1Char('/') + trashedName;
+ QString infoFileName;
+ int counter = 0;
+ QFile infoFile;
+ auto makeUniqueTrashedName = [trashedName, &counter]() -> QString {
+ ++counter;
+ return QString(QLatin1String("/%1-%2"))
+ .arg(trashedName)
+ .arg(counter, 4, 10, QLatin1Char('0'));
+ };
+ do {
+ while (QFile::exists(trashDir.filePath(filesDir) + uniqueTrashedName))
+ uniqueTrashedName = makeUniqueTrashedName();
+ /*
+ "The $trash/info directory contains an "information file" for every file and directory
+ in $trash/files. This file MUST have exactly the same name as the file or directory in
+ $trash/files, plus the extension ".trashinfo"
+ [...]
+ When trashing a file or directory, the implementation MUST create the corresponding
+ file in $trash/info first. Moreover, it MUST try to do this in an atomic fashion,
+ so that if two processes try to trash files with the same filename this will result
+ in two different trash files. On Unix-like systems this is done by generating a
+ filename, and then opening with O_EXCL. If that succeeds the creation was atomic
+ (at least on the same machine), if it fails you need to pick another filename."
+ */
+ infoFileName = trashDir.filePath(infoDir)
+ + uniqueTrashedName + QLatin1String(".trashinfo");
+ infoFile.setFileName(infoFileName);
+ if (!infoFile.open(QIODevice::NewOnly | QIODevice::WriteOnly | QIODevice::Text))
+ uniqueTrashedName = makeUniqueTrashedName();
+ } while (!infoFile.isOpen());
+
+ const QString targetPath = trashDir.filePath(filesDir) + uniqueTrashedName;
+ const QFileSystemEntry target(targetPath);
+
+ /*
+ We might fail to rename if source and target are on different file systems.
+ In that case, we don't try further, i.e. copying and removing the original
+ is usually not what the user would expect to happen.
+ */
+ if (!renameFile(source, target, error)) {
+ infoFile.close();
+ infoFile.remove();
+ return false;
+ }
+
+ QTextStream out(&infoFile);
+#if QT_CONFIG(textcodec)
+ out.setCodec("UTF-8");
+#endif
+ out << "[Trash Info]" << Qt::endl;
+ out << "Path=" << sourcePath << Qt::endl;
+ out << "DeletionDate="
+ << QDateTime::currentDateTime().toString(QLatin1String("yyyy-MM-ddThh:mm:ss")) << Qt::endl;
+ infoFile.close();
+
+ newLocation = QFileSystemEntry(targetPath);
+ return true;
+#endif // QT_BOOTSTRAPPED
+}
+#endif // Q_OS_DARWIN
+
//static
bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp
index 0579872f8d..36d43e9cb7 100644
--- a/src/corelib/io/qfilesystemengine_win.cpp
+++ b/src/corelib/io/qfilesystemengine_win.cpp
@@ -41,6 +41,7 @@
#include "qoperatingsystemversion.h"
#include "qplatformdefs.h"
#include "qsysinfo.h"
+#include "qscopeguard.h"
#include "private/qabstractfileengine_p.h"
#include "private/qfsfileengine_p.h"
#include <private/qsystemlibrary_p.h>
@@ -59,6 +60,8 @@
#include <objbase.h>
#ifndef Q_OS_WINRT
# include <shlobj.h>
+# include <shobjidl.h>
+# include <shellapi.h>
# include <lm.h>
# include <accctrl.h>
#endif
@@ -422,6 +425,104 @@ static inline bool getFindData(QString path, WIN32_FIND_DATA &findData)
return false;
}
+#if defined(__IFileOperation_INTERFACE_DEFINED__)
+class FileOperationProgressSink : public IFileOperationProgressSink
+{
+public:
+ FileOperationProgressSink()
+ : ref(1)
+ {}
+ virtual ~FileOperationProgressSink() {}
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return ++ref;
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ if (--ref == 0) {
+ delete this;
+ return 0;
+ }
+ return ref;
+ }
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
+ {
+ if (!ppvObject)
+ return E_POINTER;
+
+ *ppvObject = nullptr;
+
+ if (iid == __uuidof(IUnknown)) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else if (iid == __uuidof(IFileOperationProgressSink)) {
+ *ppvObject = static_cast<IFileOperationProgressSink*>(this);
+ }
+
+ if (*ppvObject) {
+ AddRef();
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ HRESULT STDMETHODCALLTYPE StartOperations()
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE FinishOperations(HRESULT)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PreRenameItem(DWORD, IShellItem *, LPCWSTR)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PostRenameItem(DWORD, IShellItem *, LPCWSTR, HRESULT, IShellItem *)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PreMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PostMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT,
+ IShellItem *)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PreCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR )
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PostCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT,
+ IShellItem *)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PreDeleteItem(DWORD dwFlags, IShellItem *)
+ {
+ // stop the operation if the file will be deleted rather than trashed
+ return (dwFlags & TSF_DELETE_RECYCLE_IF_POSSIBLE) ? S_OK : E_FAIL;
+ }
+ HRESULT STDMETHODCALLTYPE PostDeleteItem(DWORD /* dwFlags */, IShellItem * /* psiItem */,
+ HRESULT /* hrDelete */, IShellItem *psiNewlyCreated)
+ {
+ if (psiNewlyCreated) {
+ wchar_t *pszName = nullptr;
+ psiNewlyCreated->GetDisplayName(SIGDN_FILESYSPATH, &pszName);
+ if (pszName) {
+ targetPath = QString::fromWCharArray(pszName);
+ CoTaskMemFree(pszName);
+ }
+ }
+ return S_OK;
+ }
+ HRESULT STDMETHODCALLTYPE PreNewItem(DWORD, IShellItem *, LPCWSTR)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PostNewItem(DWORD, IShellItem *, LPCWSTR, LPCWSTR, DWORD, HRESULT,
+ IShellItem *)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE UpdateProgress(UINT,UINT)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE ResetTimer()
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PauseTimer()
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE ResumeTimer()
+ { return S_OK; }
+
+ QString targetPath;
+private:
+ ULONG ref;
+};
+#endif
+
bool QFileSystemEngine::uncListSharesOnServer(const QString &server, QStringList *list)
{
DWORD res = ERROR_NOT_SUPPORTED;
@@ -1431,6 +1532,103 @@ bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &
return ret;
}
+/*
+ If possible, we use the IFileOperation implementation, which allows us to determine
+ the location of the object in the trash.
+ If not (likely on mingw), we fall back to the old API, which won't allow us to know
+ that.
+*/
+//static
+bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
+ QFileSystemEntry &newLocation, QSystemError &error)
+{
+#ifndef Q_OS_WINRT
+ // we need the "display name" of the file, so can't use nativeFilePath
+ const QString sourcePath = QDir::toNativeSeparators(source.filePath());
+
+ /*
+ Windows 7 insists on showing confirmation dialogs and ignores the respective
+ flags set on IFileOperation. Fall back to SHFileOperation, even if it doesn't
+ give us the new location of the file.
+ */
+ if (QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7) {
+# if defined(__IFileOperation_INTERFACE_DEFINED__)
+ CoInitialize(NULL);
+ IFileOperation *pfo = nullptr;
+ IShellItem *deleteItem = nullptr;
+ FileOperationProgressSink *sink = nullptr;
+ HRESULT hres = E_FAIL;
+
+ auto coUninitialize = qScopeGuard([&](){
+ if (sink)
+ sink->Release();
+ if (deleteItem)
+ deleteItem->Release();
+ if (pfo)
+ pfo->Release();
+ CoUninitialize();
+ if (!SUCCEEDED(hres))
+ error = QSystemError(hres, QSystemError::NativeError);
+ });
+
+ hres = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pfo));
+ if (!pfo)
+ return false;
+ pfo->SetOperationFlags(FOF_ALLOWUNDO | FOFX_RECYCLEONDELETE | FOF_NOCONFIRMATION
+ | FOF_SILENT | FOF_NOERRORUI);
+ hres = SHCreateItemFromParsingName(reinterpret_cast<const wchar_t*>(sourcePath.utf16()),
+ nullptr, IID_PPV_ARGS(&deleteItem));
+ if (!deleteItem)
+ return false;
+ sink = new FileOperationProgressSink;
+ hres = pfo->DeleteItem(deleteItem, static_cast<IFileOperationProgressSink*>(sink));
+ if (!SUCCEEDED(hres))
+ return false;
+ hres = pfo->PerformOperations();
+ if (!SUCCEEDED(hres))
+ return false;
+ newLocation = QFileSystemEntry(sink->targetPath);
+
+# endif // no IFileOperation in SDK (mingw, likely) - fall back to SHFileOperation
+ } else {
+ // double null termination needed, so can't use QString::utf16
+ QVarLengthArray<wchar_t, MAX_PATH + 1> winFile(sourcePath.length() + 2);
+ sourcePath.toWCharArray(winFile.data());
+ winFile[sourcePath.length()] = wchar_t{};
+ winFile[sourcePath.length() + 1] = wchar_t{};
+
+ SHFILEOPSTRUCTW operation;
+ operation.hwnd = nullptr;
+ operation.wFunc = FO_DELETE;
+ operation.pFrom = winFile.constData();
+ operation.pTo = nullptr;
+ operation.fFlags = FOF_ALLOWUNDO | FOF_NO_UI;
+ operation.fAnyOperationsAborted = FALSE;
+ operation.hNameMappings = nullptr;
+ operation.lpszProgressTitle = nullptr;
+
+ int result = SHFileOperation(&operation);
+ if (result != 0) {
+ error = QSystemError(result, QSystemError::NativeError);
+ return false;
+ }
+ /*
+ This implementation doesn't let us know where the file ended up, even if
+ we would specify FOF_WANTMAPPINGHANDLE | FOF_RENAMEONCOLLISION, as
+ FOF_RENAMEONCOLLISION has no effect unless files are moved, copied, or renamed.
+ */
+ Q_UNUSED(newLocation);
+ }
+ return true;
+
+#else // Q_OS_WINRT
+ Q_UNUSED(source);
+ Q_UNUSED(newLocation);
+ Q_UNUSED(error);
+ return false;
+#endif
+}
+
//static
bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error,
QFileSystemMetaData *data)
diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp
index f7a86c25cd..cc1d110252 100644
--- a/src/corelib/io/qiodevice.cpp
+++ b/src/corelib/io/qiodevice.cpp
@@ -714,7 +714,7 @@ void QIODevicePrivate::setReadChannelCount(int count)
/*!
\since 5.7
- Returns the the index of the current write channel.
+ Returns the index of the current write channel.
\sa setCurrentWriteChannel(), writeChannelCount()
*/
diff --git a/src/corelib/io/qlockfile_unix.cpp b/src/corelib/io/qlockfile_unix.cpp
index b2bf77c0da..ce2b7e8faa 100644
--- a/src/corelib/io/qlockfile_unix.cpp
+++ b/src/corelib/io/qlockfile_unix.cpp
@@ -58,7 +58,8 @@
#include <sys/file.h> // flock
#endif
-#if defined(Q_OS_RTEMS)
+#if defined(Q_OS_RTEMS) || defined(Q_OS_QNX)
+// flock() does not work in these OSes and produce warnings when we try to use
# undef LOCK_EX
# undef LOCK_NB
#endif
diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp
index 37b8a60c37..698c4ddf41 100644
--- a/src/corelib/io/qstorageinfo_unix.cpp
+++ b/src/corelib/io/qstorageinfo_unix.cpp
@@ -60,6 +60,7 @@
#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD)
# include <mntent.h>
# include <sys/statvfs.h>
+# include <sys/sysmacros.h>
#elif defined(Q_OS_SOLARIS)
# include <sys/mnttab.h>
# include <sys/statvfs.h>
@@ -152,7 +153,7 @@ private:
//(2) parent ID: the ID of the parent mount (or of self for the top of the mount tree).
// int parent_id;
//(3) major:minor: the value of st_dev for files on this filesystem (see stat(2)).
-// dev_t rdev;
+ dev_t rdev;
//(4) root: the pathname of the directory in the filesystem which forms the root of this mount.
char *subvolume;
//(5) mount point: the pathname of the mount point relative to the process's root directory.
@@ -503,8 +504,7 @@ inline bool QStorageIterator::next()
int rdevminor = qstrtoll(ptr + 1, const_cast<const char **>(&ptr), 10, &ok);
if (!ptr || !ok)
return false;
- Q_UNUSED(rdevmajor);
- Q_UNUSED(rdevminor);
+ mnt.rdev = makedev(rdevmajor, rdevminor);
if (*ptr != ' ')
return false;
@@ -566,6 +566,21 @@ inline QByteArray QStorageIterator::fileSystemType() const
inline QByteArray QStorageIterator::device() const
{
+ // check that the device exists
+ if (mnt.mnt_fsname[0] == '/' && access(mnt.mnt_fsname, F_OK) != 0) {
+ // It doesn't, so let's try to resolve the dev_t from /dev/block.
+ // Note how strlen("4294967295") == digits10 + 1, so we need to add 1
+ // for each number, plus the ':'.
+ char buf[sizeof("/dev/block/") + 2 * std::numeric_limits<unsigned>::digits10 + 3];
+ QByteArray dev(PATH_MAX, Qt::Uninitialized);
+ char *devdata = dev.data();
+
+ snprintf(buf, sizeof(buf), "/dev/block/%u:%u", major(mnt.rdev), minor(mnt.rdev));
+ if (realpath(buf, devdata)) {
+ dev.truncate(strlen(devdata));
+ return dev;
+ }
+ }
return QByteArray(mnt.mnt_fsname);
}
diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp
index 0a5f9256eb..1beeb7b2a2 100644
--- a/src/corelib/io/qurl.cpp
+++ b/src/corelib/io/qurl.cpp
@@ -415,7 +415,7 @@
#include "qhash.h"
#include "qdir.h" // for QDir::fromNativeSeparators
#include "qdatastream.h"
-#if QT_CONFIG(topleveldomain)
+#if QT_CONFIG(topleveldomain) // ### Qt6: Remove section
#include "qtldurl_p.h"
#endif
#include "private/qipaddress_p.h"
@@ -3149,10 +3149,13 @@ bool QUrl::hasFragment() const
return d->hasFragment();
}
+#if QT_DEPRECATED_SINCE(5, 15)
#if QT_CONFIG(topleveldomain)
/*!
\since 4.8
+ \deprecated
+
Returns the TLD (Top-Level Domain) of the URL, (e.g. .co.uk, .net).
Note that the return value is prefixed with a '.' unless the
URL does not contain a valid TLD, in which case the function returns
@@ -3185,7 +3188,7 @@ QString QUrl::topLevelDomain(ComponentFormattingOptions options) const
return tld;
}
#endif
-
+#endif // QT_DEPRECATED_SINCE(5, 15)
/*!
Returns the result of the merge of this URL with \a relative. This
URL is used as a base to convert \a relative to an absolute URL.
diff --git a/src/corelib/io/qurl.h b/src/corelib/io/qurl.h
index 94269e4369..eb7fb8087c 100644
--- a/src/corelib/io/qurl.h
+++ b/src/corelib/io/qurl.h
@@ -233,9 +233,11 @@ public:
void setHost(const QString &host, ParsingMode mode = DecodedMode);
QString host(ComponentFormattingOptions = FullyDecoded) const;
+#if QT_DEPRECATED_SINCE(5, 15)
#if QT_CONFIG(topleveldomain)
- QString topLevelDomain(ComponentFormattingOptions options = FullyDecoded) const;
+ QT_DEPRECATED QString topLevelDomain(ComponentFormattingOptions options = FullyDecoded) const;
#endif
+#endif // QT_DEPRECATED_SINCE(5, 15)
void setPort(int port);
int port(int defaultPort = -1) const;