aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/libs/utils/CMakeLists.txt1
-rw-r--r--src/libs/utils/devicefileaccess.cpp862
-rw-r--r--src/libs/utils/devicefileaccess.h194
-rw-r--r--src/libs/utils/filepath.cpp370
-rw-r--r--src/libs/utils/filepath.h50
-rw-r--r--src/libs/utils/fileutils.cpp154
-rw-r--r--src/libs/utils/fileutils.h7
-rw-r--r--src/libs/utils/utils.qbs2
-rw-r--r--src/plugins/docker/dockerdevice.cpp261
-rw-r--r--src/plugins/docker/dockerdevice.h31
-rw-r--r--src/plugins/projectexplorer/devicesupport/desktopdevice.cpp143
-rw-r--r--src/plugins/projectexplorer/devicesupport/desktopdevice.h29
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicemanager.cpp186
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.cpp234
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.h49
-rw-r--r--src/plugins/projectexplorer/gcctoolchain.cpp3
-rw-r--r--src/plugins/remotelinux/linuxdevice.cpp248
-rw-r--r--src/plugins/remotelinux/linuxdevice.h31
-rw-r--r--src/tools/sdktool/CMakeLists.txt1
-rw-r--r--src/tools/sdktool/sdktoollib.qbs1
-rw-r--r--tests/auto/utils/fsengine/tst_fsengine.cpp115
21 files changed, 1234 insertions, 1738 deletions
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt
index 1d7099bc3a9..a4135401651 100644
--- a/src/libs/utils/CMakeLists.txt
+++ b/src/libs/utils/CMakeLists.txt
@@ -28,6 +28,7 @@ add_qtc_library(Utils
delegates.cpp delegates.h
detailsbutton.cpp detailsbutton.h
detailswidget.cpp detailswidget.h
+ devicefileaccess.cpp devicefileaccess.h
deviceshell.cpp deviceshell.h
differ.cpp differ.h
displayname.cpp displayname.h
diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp
new file mode 100644
index 00000000000..6b07edaf62b
--- /dev/null
+++ b/src/libs/utils/devicefileaccess.cpp
@@ -0,0 +1,862 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "devicefileaccess.h"
+
+#include "algorithm.h"
+#include "qtcassert.h"
+#include "hostosinfo.h"
+
+#include <QCoreApplication>
+#include <QRegularExpression>
+#include <QStorageInfo>
+
+namespace Utils {
+
+// DeviceFileAccess
+
+DeviceFileAccess::~DeviceFileAccess() = default;
+
+QString DeviceFileAccess::mapToDevicePath(const FilePath &filePath) const
+{
+ return filePath.path();
+}
+
+bool DeviceFileAccess::isExecutableFile(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::isReadableFile(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::isWritableFile(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::isReadableDirectory(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::isWritableDirectory(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::isFile(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::isDirectory(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const
+{
+ if (isWritableDirectory(filePath))
+ return true;
+ return createDirectory(filePath);
+}
+
+bool DeviceFileAccess::ensureExistingFile(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::createDirectory(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::exists(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::removeFile(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const
+{
+ Q_UNUSED(filePath)
+ Q_UNUSED(error)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const
+{
+ Q_UNUSED(filePath)
+ Q_UNUSED(target)
+ QTC_CHECK(false);
+ return false;
+}
+
+bool DeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
+{
+ Q_UNUSED(filePath)
+ Q_UNUSED(target)
+ QTC_CHECK(false);
+ return false;
+}
+
+OsType DeviceFileAccess::osType(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ return OsTypeOther;
+}
+
+FilePath DeviceFileAccess::symLinkTarget(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return {};
+}
+
+void DeviceFileAccess::iterateDirectory(
+ const FilePath &filePath,
+ const FilePath::IterateDirCallback &callBack,
+ const FileFilter &filter) const
+{
+ Q_UNUSED(filePath)
+ Q_UNUSED(callBack)
+ Q_UNUSED(filter)
+ QTC_CHECK(false);
+}
+
+std::optional<QByteArray> DeviceFileAccess::fileContents(
+ const FilePath &filePath,
+ qint64 limit,
+ qint64 offset) const
+{
+ Q_UNUSED(filePath)
+ Q_UNUSED(limit)
+ Q_UNUSED(offset)
+ QTC_CHECK(false);
+ return {};
+}
+
+bool DeviceFileAccess::writeFileContents(
+ const FilePath &filePath,
+ const QByteArray &data,
+ qint64 offset) const
+{
+ Q_UNUSED(filePath)
+ Q_UNUSED(data)
+ Q_UNUSED(offset)
+ QTC_CHECK(false);
+ return false;
+}
+
+FilePathInfo DeviceFileAccess::filePathInfo(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return {};
+}
+
+QDateTime DeviceFileAccess::lastModified(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return {};
+}
+
+QFile::Permissions DeviceFileAccess::permissions(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return {};
+}
+
+bool DeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permissions) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+qint64 DeviceFileAccess::fileSize(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
+qint64 DeviceFileAccess::bytesAvailable(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return -1;
+}
+
+void DeviceFileAccess::asyncFileContents(
+ const FilePath &filePath,
+ const Continuation<std::optional<QByteArray>> &cont,
+ qint64 limit,
+ qint64 offset) const
+{
+ cont(fileContents(filePath, limit, offset));
+}
+
+void DeviceFileAccess::asyncWriteFileContents(
+ const FilePath &filePath,
+ const Continuation<bool> &cont,
+ const QByteArray &data,
+ qint64 offset) const
+{
+ cont(writeFileContents(filePath, data, offset));
+}
+
+void DeviceFileAccess::asyncCopyFile(
+ const FilePath &filePath,
+ const Continuation<bool> &cont,
+ const FilePath &target) const
+{
+ cont(copyFile(filePath, target));
+}
+
+
+// DesktopDeviceFileAccess
+
+DesktopDeviceFileAccess::~DesktopDeviceFileAccess() = default;
+
+DesktopDeviceFileAccess *DesktopDeviceFileAccess::instance()
+{
+ static DesktopDeviceFileAccess theInstance;
+ return &theInstance;
+}
+
+bool DesktopDeviceFileAccess::isExecutableFile(const FilePath &filePath) const
+{
+ const QFileInfo fi(filePath.path());
+ return fi.isExecutable() && !fi.isDir();
+}
+
+bool DesktopDeviceFileAccess::isReadableFile(const FilePath &filePath) const
+{
+ const QFileInfo fi(filePath.path());
+ return fi.exists() && fi.isFile() && fi.isReadable();
+}
+
+bool DesktopDeviceFileAccess::isWritableFile(const FilePath &filePath) const
+{
+ const QFileInfo fi(filePath.path());
+ return fi.exists() && fi.isFile() && fi.isWritable();
+}
+
+bool DesktopDeviceFileAccess::isReadableDirectory(const FilePath &filePath) const
+{
+ const QFileInfo fi(filePath.path());
+ return fi.exists() && fi.isDir() && fi.isReadable();
+}
+
+bool DesktopDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const
+{
+ const QFileInfo fi(filePath.path());
+ return fi.exists() && fi.isDir() && fi.isWritable();
+}
+
+bool DesktopDeviceFileAccess::isFile(const FilePath &filePath) const
+{
+ const QFileInfo fi(filePath.path());
+ return fi.isFile();
+}
+
+bool DesktopDeviceFileAccess::isDirectory(const FilePath &filePath) const
+{
+ const QFileInfo fi(filePath.path());
+ return fi.isDir();
+}
+
+bool DesktopDeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const
+{
+ const QFileInfo fi(filePath.path());
+ if (fi.isDir() && fi.isWritable())
+ return true;
+ return QDir().mkpath(filePath.path());
+}
+
+bool DesktopDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const
+{
+ QFile f(filePath.path());
+ if (f.exists())
+ return true;
+ f.open(QFile::WriteOnly);
+ f.close();
+ return f.exists();
+}
+
+bool DesktopDeviceFileAccess::createDirectory(const FilePath &filePath) const
+{
+ QDir dir(filePath.path());
+ return dir.mkpath(dir.absolutePath());
+}
+
+bool DesktopDeviceFileAccess::exists(const FilePath &filePath) const
+{
+ return !filePath.isEmpty() && QFileInfo::exists(filePath.path());
+}
+
+bool DesktopDeviceFileAccess::removeFile(const FilePath &filePath) const
+{
+ return QFile::remove(filePath.path());
+}
+
+bool DesktopDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const
+{
+ QTC_ASSERT(!filePath.needsDevice(), return false);
+ QFileInfo fileInfo = filePath.toFileInfo();
+ if (!fileInfo.exists() && !fileInfo.isSymLink())
+ return true;
+
+ QFile::setPermissions(fileInfo.absoluteFilePath(), fileInfo.permissions() | QFile::WriteUser);
+
+ if (fileInfo.isDir()) {
+ QDir dir(fileInfo.absoluteFilePath());
+ dir.setPath(dir.canonicalPath());
+ if (dir.isRoot()) {
+ if (error) {
+ *error = QCoreApplication::translate("Utils::FileUtils",
+ "Refusing to remove root directory.");
+ }
+ return false;
+ }
+ if (dir.path() == QDir::home().canonicalPath()) {
+ if (error) {
+ *error = QCoreApplication::translate("Utils::FileUtils",
+ "Refusing to remove your home directory.");
+ }
+ return false;
+ }
+
+ const QStringList fileNames = dir.entryList(
+ QDir::Files | QDir::Hidden | QDir::System | QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QString &fileName : fileNames) {
+ if (!removeRecursively(filePath / fileName, error))
+ return false;
+ }
+ if (!QDir::root().rmdir(dir.path())) {
+ if (error) {
+ *error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove directory \"%1\".")
+ .arg(filePath.toUserOutput());
+ }
+ return false;
+ }
+ } else {
+ if (!QFile::remove(filePath.toString())) {
+ if (error) {
+ *error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove file \"%1\".")
+ .arg(filePath.toUserOutput());
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DesktopDeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const
+{
+ return QFile::copy(filePath.path(), target.path());
+}
+
+bool DesktopDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
+{
+ return QFile::rename(filePath.path(), target.path());
+}
+
+FilePathInfo DesktopDeviceFileAccess::filePathInfo(const FilePath &filePath) const
+{
+ FilePathInfo result;
+
+ QFileInfo fi(filePath.path());
+ result.fileSize = fi.size();
+ result.lastModified = fi.lastModified();
+ result.fileFlags = (FilePathInfo::FileFlag) int(fi.permissions());
+
+ if (fi.isDir())
+ result.fileFlags |= FilePathInfo::DirectoryType;
+ if (fi.isFile())
+ result.fileFlags |= FilePathInfo::FileType;
+ if (fi.exists())
+ result.fileFlags |= FilePathInfo::ExistsFlag;
+ if (fi.isSymbolicLink())
+ result.fileFlags |= FilePathInfo::LinkType;
+ if (fi.isBundle())
+ result.fileFlags |= FilePathInfo::BundleType;
+ if (fi.isHidden())
+ result.fileFlags |= FilePathInfo::HiddenFlag;
+ if (fi.isRoot())
+ result.fileFlags |= FilePathInfo::RootFlag;
+
+ return result;
+}
+
+FilePath DesktopDeviceFileAccess::symLinkTarget(const FilePath &filePath) const
+{
+ const QFileInfo info(filePath.path());
+ if (!info.isSymLink())
+ return {};
+ return FilePath::fromString(info.symLinkTarget());
+}
+
+void DesktopDeviceFileAccess::iterateDirectory(
+ const FilePath &filePath,
+ const FilePath::IterateDirCallback &callBack,
+ const FileFilter &filter) const
+{
+ QDirIterator it(filePath.path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags);
+ while (it.hasNext()) {
+ const FilePath path = FilePath::fromString(it.next());
+ bool res = false;
+ if (callBack.index() == 0)
+ res = std::get<0>(callBack)(path);
+ else
+ res = std::get<1>(callBack)(path, path.filePathInfo());
+ if (!res)
+ return;
+ }
+}
+
+std::optional<QByteArray> DesktopDeviceFileAccess::fileContents(
+ const FilePath &filePath,
+ qint64 limit,
+ qint64 offset) const
+{
+ const QString path = filePath.path();
+ QFile f(path);
+ if (!f.exists())
+ return {};
+
+ if (!f.open(QFile::ReadOnly))
+ return {};
+
+ if (offset != 0)
+ f.seek(offset);
+
+ if (limit != -1)
+ return f.read(limit);
+
+ return f.readAll();
+}
+
+bool DesktopDeviceFileAccess::writeFileContents(
+ const FilePath &filePath,
+ const QByteArray &data,
+ qint64 offset) const
+{
+ QFile file(filePath.path());
+ QTC_ASSERT(file.open(QFile::WriteOnly | QFile::Truncate), return false);
+ if (offset != 0)
+ file.seek(offset);
+ qint64 res = file.write(data);
+ return res == data.size();
+}
+
+QDateTime DesktopDeviceFileAccess::lastModified(const FilePath &filePath) const
+{
+ return QFileInfo(filePath.path()).lastModified();
+}
+
+QFile::Permissions DesktopDeviceFileAccess::permissions(const FilePath &filePath) const
+{
+ return QFileInfo(filePath.path()).permissions();
+}
+
+bool DesktopDeviceFileAccess::setPermissions(const FilePath &filePath,
+ QFile::Permissions permissions) const
+{
+ return QFile(filePath.path()).setPermissions(permissions);
+}
+
+qint64 DesktopDeviceFileAccess::fileSize(const FilePath &filePath) const
+{
+ return QFileInfo(filePath.path()).size();
+}
+
+qint64 DesktopDeviceFileAccess::bytesAvailable(const FilePath &filePath) const
+{
+ return QStorageInfo(filePath.path()).bytesAvailable();
+}
+
+OsType DesktopDeviceFileAccess::osType(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath);
+ return HostOsInfo::hostOs();
+}
+
+
+// UnixDeviceAccess
+
+UnixDeviceFileAccess::~UnixDeviceFileAccess() = default;
+
+bool UnixDeviceFileAccess::runInShellSuccess(
+ const QString &executable,
+ const QStringList &args,
+ const QByteArray &stdInData) const
+{
+ return runInShell(executable, args, stdInData).exitCode == 0;
+}
+
+bool UnixDeviceFileAccess::isExecutableFile(const FilePath &filePath) const
+{
+ const QString path = filePath.path();
+ return runInShellSuccess("test", {"-x", path});
+}
+
+bool UnixDeviceFileAccess::isReadableFile(const FilePath &filePath) const
+{
+ const QString path = filePath.path();
+ return runInShellSuccess("test", {"-r", path, "-a", "-f", path});
+}
+
+bool UnixDeviceFileAccess::isWritableFile(const FilePath &filePath) const
+{
+ const QString path = filePath.path();
+ return runInShellSuccess("test", {"-w", path, "-a", "-f", path});
+}
+
+bool UnixDeviceFileAccess::isReadableDirectory(const FilePath &filePath) const
+{
+ const QString path = filePath.path();
+ return runInShellSuccess("test", {"-r", path, "-a", "-d", path});
+}
+
+bool UnixDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const
+{
+ const QString path = filePath.path();
+ return runInShellSuccess("test", {"-w", path, "-a", "-d", path});
+}
+
+bool UnixDeviceFileAccess::isFile(const FilePath &filePath) const
+{
+ const QString path = filePath.path();
+ return runInShellSuccess("test", {"-f", path});
+}
+
+bool UnixDeviceFileAccess::isDirectory(const FilePath &filePath) const
+{
+ const QString path = filePath.path();
+ return runInShellSuccess("test", {"-d", path});
+}
+
+bool UnixDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const
+{
+ const QString path = filePath.path();
+ return runInShellSuccess("touch", {path});
+}
+
+bool UnixDeviceFileAccess::createDirectory(const FilePath &filePath) const
+{
+ const QString path = filePath.path();
+ return runInShellSuccess("mkdir", {"-p", path});
+}
+
+bool UnixDeviceFileAccess::exists(const FilePath &filePath) const
+{
+ const QString path = filePath.path();
+ return runInShellSuccess("test", {"-e", path});
+}
+
+bool UnixDeviceFileAccess::removeFile(const FilePath &filePath) const
+{
+ return runInShellSuccess("rm", {filePath.path()});
+}
+
+bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const
+{
+ QTC_ASSERT(filePath.path().startsWith('/'), return false);
+
+ const QString path = filePath.cleanPath().path();
+ // We are expecting this only to be called in a context of build directories or similar.
+ // Chicken out in some cases that _might_ be user code errors.
+ QTC_ASSERT(path.startsWith('/'), return false);
+ int levelsNeeded = path.startsWith("/home/") ? 4 : 3;
+ if (path.startsWith("/tmp/"))
+ levelsNeeded = 2;
+ QTC_ASSERT(path.count('/') >= levelsNeeded, return false);
+
+ RunResult result = runInShell("rm", {"-rf", "--", path});
+ if (error)
+ *error = QString::fromUtf8(result.stdErr);
+ return result.exitCode == 0;
+}
+
+bool UnixDeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const
+{
+ return runInShellSuccess("cp", {filePath.path(), target.path()});
+}
+
+bool UnixDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
+{
+ return runInShellSuccess("mv", {filePath.path(), target.path()});
+}
+
+FilePath UnixDeviceFileAccess::symLinkTarget(const FilePath &filePath) const
+{
+ const RunResult result = runInShell("readlink", {"-n", "-e", filePath.path()});
+ const QString out = QString::fromUtf8(result.stdOut);
+ return out.isEmpty() ? FilePath() : filePath.withNewPath(out);
+}
+
+std::optional<QByteArray> UnixDeviceFileAccess::fileContents(
+ const FilePath &filePath,
+ qint64 limit,
+ qint64 offset) const
+{
+ QStringList args = {"if=" + filePath.path(), "status=none"};
+ if (limit > 0 || offset > 0) {
+ const qint64 gcd = std::gcd(limit, offset);
+ args += QString("bs=%1").arg(gcd);
+ args += QString("count=%1").arg(limit / gcd);
+ args += QString("seek=%1").arg(offset / gcd);
+ }
+
+ const RunResult r = runInShell("dd", args);
+
+ if (r.exitCode != 0)
+ return {};
+
+ return r.stdOut;
+}
+
+bool UnixDeviceFileAccess::writeFileContents(
+ const FilePath &filePath,
+ const QByteArray &data,
+ qint64 offset) const
+{
+ QStringList args = {"of=" + filePath.path()};
+ if (offset != 0) {
+ args.append("bs=1");
+ args.append(QString("seek=%1").arg(offset));
+ }
+ return runInShellSuccess("dd", args, data);
+}
+
+OsType UnixDeviceFileAccess::osType(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ return OsTypeLinux;
+}
+
+QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const
+{
+ const RunResult result = runInShell("stat", {"-L", "-c", "%Y", filePath.path()});
+ qint64 secs = result.stdOut.toLongLong();
+ const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC);
+ return dt;
+}
+
+QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) const
+{
+ const RunResult result = runInShell("stat", {"-L", "-c", "%a", filePath.path()});
+ const uint bits = result.stdOut.toUInt(nullptr, 8);
+ QFileDevice::Permissions perm = {};
+#define BIT(n, p) if (bits & (1<<n)) perm |= QFileDevice::p
+ BIT(0, ExeOther);
+ BIT(1, WriteOther);
+ BIT(2, ReadOther);
+ BIT(3, ExeGroup);
+ BIT(4, WriteGroup);
+ BIT(5, ReadGroup);
+ BIT(6, ExeUser);
+ BIT(7, WriteUser);
+ BIT(8, ReadUser);
+#undef BIT
+ return perm;
+}
+
+bool UnixDeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permissions perms) const
+{
+ const int flags = int(perms);
+ return runInShellSuccess("chmod", {QString::number(flags, 16), filePath.path()});
+}
+
+qint64 UnixDeviceFileAccess::fileSize(const FilePath &filePath) const
+{
+ const RunResult result = runInShell("stat", {"-L", "-c", "%s", filePath.path()});
+ return result.stdOut.toLongLong();
+}
+
+qint64 UnixDeviceFileAccess::bytesAvailable(const FilePath &filePath) const
+{
+ const RunResult result = runInShell("df", {"-k", filePath.path()});
+ return FileUtils::bytesAvailableFromDFOutput(result.stdOut);
+}
+
+FilePathInfo UnixDeviceFileAccess::filePathInfo(const FilePath &filePath) const
+{
+ const RunResult stat = runInShell("stat", {"-L", "-c", "%f %Y %s", filePath.path()});
+ return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut));
+}
+
+// returns whether 'find' could be used.
+bool UnixDeviceFileAccess::iterateWithFind(
+ const FilePath &filePath,
+ const FileFilter &filter,
+ const FilePath::IterateDirCallback &callBack) const
+{
+ QTC_CHECK(filePath.isAbsolutePath());
+
+ QStringList arguments = filter.asFindArguments(filePath.path());
+
+ // TODO: Using stat -L will always return the link target, not the link itself.
+ // We may wan't to add the information that it is a link at some point.
+ if (callBack.index() == 1)
+ arguments.append(R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)");
+
+ const RunResult result = runInShell("find", arguments);
+ const QString out = QString::fromUtf8(result.stdOut);
+ if (result.exitCode != 0) {
+ // Find returns non-zero exit code for any error it encounters, even if it finds some files.
+
+ if (!out.startsWith('"' + filePath.path())) {
+ if (!filePath.exists()) // File does not exist, so no files to find.
+ return true;
+
+ // If the output does not start with the path we are searching in, find has failed.
+ // Possibly due to unknown options.
+ return false;
+ }
+ }
+
+ QStringList entries = out.split("\n", Qt::SkipEmptyParts);
+ if (entries.isEmpty())
+ return true;
+
+ const auto toFilePath = [&filePath, &callBack](const QString &entry) -> bool {
+ if (callBack.index() == 0)
+ return std::get<0>(callBack)(filePath.withNewPath(entry));
+
+ const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1);
+ const QString infos = entry.mid(fileName.length() + 3);
+
+ const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos);
+ if (!fi.fileFlags)
+ return true;
+
+ const FilePath fp = filePath.withNewPath(fileName);
+ return std::get<1>(callBack)(fp, fi);
+ };
+
+ // Remove the first line, this can be the directory we are searching in.
+ // as long as we do not specify "mindepth > 0"
+ if (entries.front() == filePath.path())
+ entries.pop_front();
+
+ for (const QString &entry : entries) {
+ if (!toFilePath(entry))
+ break;
+ }
+
+ return true;
+}
+
+void UnixDeviceFileAccess::findUsingLs(
+ const QString &current,
+ const FileFilter &filter,
+ QStringList *found) const
+{
+ const RunResult result = runInShell("ls", {"-1", "-p", "--", current});
+ const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts);
+ for (QString entry : entries) {
+ const QChar last = entry.back();
+ if (last == '/') {
+ entry.chop(1);
+ if (filter.iteratorFlags.testFlag(QDirIterator::Subdirectories))
+ findUsingLs(current + '/' + entry, filter, found);
+ }
+ found->append(entry);
+ }
+}
+
+// Used on 'ls' output on unix-like systems.
+static void iterateLsOutput(const FilePath &base,
+ const QStringList &entries,
+ const FileFilter &filter,
+ const FilePath::IterateDirCallback &callBack)
+{
+ const QList<QRegularExpression> nameRegexps =
+ transform(filter.nameFilters, [](const QString &filter) {
+ QRegularExpression re;
+ re.setPattern(QRegularExpression::wildcardToRegularExpression(filter));
+ QTC_CHECK(re.isValid());
+ return re;
+ });
+
+ const auto nameMatches = [&nameRegexps](const QString &fileName) {
+ for (const QRegularExpression &re : nameRegexps) {
+ const QRegularExpressionMatch match = re.match(fileName);
+ if (match.hasMatch())
+ return true;
+ }
+ return nameRegexps.isEmpty();
+ };
+
+ // FIXME: Handle filters. For now bark on unsupported options.
+ QTC_CHECK(filter.fileFilters == QDir::NoFilter);
+
+ for (const QString &entry : entries) {
+ if (!nameMatches(entry))
+ continue;
+ const FilePath current = base.pathAppended(entry);
+ bool res = false;
+ if (callBack.index() == 0)
+ res = std::get<0>(callBack)(current);
+ else
+ res = std::get<1>(callBack)(current, current.filePathInfo());
+ if (!res)
+ break;
+ }
+}
+
+void UnixDeviceFileAccess::iterateDirectory(
+ const FilePath &filePath,
+ const FilePath::IterateDirCallback &callBack,
+ const FileFilter &filter) const
+{
+ // We try to use 'find' first, because that can filter better directly.
+ // Unfortunately, it's not installed on all devices by default.
+ if (m_tryUseFind) {
+ if (iterateWithFind(filePath, filter, callBack))
+ return;
+ m_tryUseFind = false; // remember the failure for the next time and use the 'ls' fallback below.
+ }
+
+ // if we do not have find - use ls as fallback
+ QStringList entries;
+ findUsingLs(filePath.path(), filter, &entries);
+ iterateLsOutput(filePath, entries, filter, callBack);
+}
+
+} // Utils
diff --git a/src/libs/utils/devicefileaccess.h b/src/libs/utils/devicefileaccess.h
new file mode 100644
index 00000000000..e1ef5db1ddb
--- /dev/null
+++ b/src/libs/utils/devicefileaccess.h
@@ -0,0 +1,194 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "utils_global.h"
+
+#include "fileutils.h"
+
+namespace Utils {
+
+// Base class including dummy implementation usable as fallback.
+class QTCREATOR_UTILS_EXPORT DeviceFileAccess
+{
+public:
+ virtual ~DeviceFileAccess();
+
+protected:
+ friend class FilePath;
+
+ virtual QString mapToDevicePath(const FilePath &filePath) const;
+
+ virtual bool isExecutableFile(const FilePath &filePath) const;
+ virtual bool isReadableFile(const FilePath &filePath) const;
+ virtual bool isWritableFile(const FilePath &filePath) const;
+ virtual bool isReadableDirectory(const FilePath &filePath) const;
+ virtual bool isWritableDirectory(const FilePath &filePath) const;
+ virtual bool isFile(const FilePath &filePath) const;
+ virtual bool isDirectory(const FilePath &filePath) const;
+ virtual bool ensureWritableDirectory(const FilePath &filePath) const;
+ virtual bool ensureExistingFile(const FilePath &filePath) const;
+ virtual bool createDirectory(const FilePath &filePath) const;
+ virtual bool exists(const FilePath &filePath) const;
+ virtual bool removeFile(const FilePath &filePath) const;
+ virtual bool removeRecursively(const FilePath &filePath, QString *error) const;
+ virtual bool copyFile(const FilePath &filePath, const FilePath &target) const;
+ virtual bool renameFile(const FilePath &filePath, const FilePath &target) const;
+
+ virtual OsType osType(const FilePath &filePath) const;
+ virtual FilePath symLinkTarget(const FilePath &filePath) const;
+ virtual FilePathInfo filePathInfo(const FilePath &filePath) const;
+ virtual QDateTime lastModified(const FilePath &filePath) const;
+ virtual QFile::Permissions permissions(const FilePath &filePath) const;
+ virtual bool setPermissions(const FilePath &filePath, QFile::Permissions) const;
+ virtual qint64 fileSize(const FilePath &filePath) const;
+ virtual qint64 bytesAvailable(const FilePath &filePath) const;
+
+ virtual void iterateDirectory(
+ const FilePath &filePath,
+ const FilePath::IterateDirCallback &callBack,
+ const FileFilter &filter) const;
+
+ virtual std::optional<QByteArray> fileContents(
+ const FilePath &filePath,
+ qint64 limit,
+ qint64 offset) const;
+ virtual bool writeFileContents(
+ const FilePath &filePath,
+ const QByteArray &data,
+ qint64 offset) const;
+
+ virtual void asyncFileContents(
+ const FilePath &filePath,
+ const Continuation<std::optional<QByteArray>> &cont,
+ qint64 limit,
+ qint64 offset) const;
+
+ virtual void asyncWriteFileContents(
+ const FilePath &filePath,
+ const Continuation<bool> &cont,
+ const QByteArray &data,
+ qint64 offset) const;
+
+ virtual void asyncCopyFile(
+ const FilePath &filePath,
+ const Continuation<bool> &cont,
+ const FilePath &target) const;
+};
+
+class QTCREATOR_UTILS_EXPORT DesktopDeviceFileAccess : public DeviceFileAccess
+{
+public:
+ ~DesktopDeviceFileAccess() override;
+
+ static DesktopDeviceFileAccess *instance();
+
+protected:
+ bool isExecutableFile(const FilePath &filePath) const override;
+ bool isReadableFile(const FilePath &filePath) const override;
+ bool isWritableFile(const FilePath &filePath) const override;
+ bool isReadableDirectory(const FilePath &filePath) const override;
+ bool isWritableDirectory(const FilePath &filePath) const override;
+ bool isFile(const FilePath &filePath) const override;
+ bool isDirectory(const FilePath &filePath) const override;
+ bool ensureWritableDirectory(const FilePath &filePath) const override;
+ bool ensureExistingFile(const FilePath &filePath) const override;
+ bool createDirectory(const FilePath &filePath) const override;
+ bool exists(const FilePath &filePath) const override;
+ bool removeFile(const FilePath &filePath) const override;
+ bool removeRecursively(const FilePath &filePath, QString *error) const override;
+ bool copyFile(const FilePath &filePath, const FilePath &target) const override;
+ bool renameFile(const FilePath &filePath, const FilePath &target) const override;
+
+ OsType osType(const FilePath &filePath) const override;
+ FilePath symLinkTarget(const FilePath &filePath) const override;
+ FilePathInfo filePathInfo(const FilePath &filePath) const override;
+ QDateTime lastModified(const FilePath &filePath) const override;
+ QFile::Permissions permissions(const FilePath &filePath) const override;
+ bool setPermissions(const FilePath &filePath, QFile::Permissions) const override;
+ qint64 fileSize(const FilePath &filePath) const override;
+ qint64 bytesAvailable(const FilePath &filePath) const override;
+
+ void iterateDirectory(
+ const FilePath &filePath,
+ const FilePath::IterateDirCallback &callBack,
+ const FileFilter &filter) const override;
+
+ std::optional<QByteArray> fileContents(
+ const FilePath &filePath,
+ qint64 limit,
+ qint64 offset) const override;
+ bool writeFileContents(
+ const FilePath &filePath,
+ const QByteArray &data,
+ qint64 offset) const override;
+};
+
+class QTCREATOR_UTILS_EXPORT UnixDeviceFileAccess : public DeviceFileAccess
+{
+public:
+ ~UnixDeviceFileAccess() override;
+
+protected:
+ virtual RunResult runInShell(
+ const QString &executable,
+ const QStringList &args,
+ const QByteArray &inputData = {}) const = 0;
+ bool runInShellSuccess(
+ const QString &executable,
+ const QStringList &args,
+ const QByteArray &stdInData = {}) const;
+
+ bool isExecutableFile(const FilePath &filePath) const override;
+ bool isReadableFile(const FilePath &filePath) const override;
+ bool isWritableFile(const FilePath &filePath) const override;
+ bool isReadableDirectory(const FilePath &filePath) const override;
+ bool isWritableDirectory(const FilePath &filePath) const override;
+ bool isFile(const FilePath &filePath) const override;
+ bool isDirectory(const FilePath &filePath) const override;
+ bool ensureExistingFile(const FilePath &filePath) const override;
+ bool createDirectory(const FilePath &filePath) const override;
+ bool exists(const FilePath &filePath) const override;
+ bool removeFile(const FilePath &filePath) const override;
+ bool removeRecursively(const FilePath &filePath, QString *error) const override;
+ bool copyFile(const FilePath &filePath, const FilePath &target) const override;
+ bool renameFile(const FilePath &filePath, const FilePath &target) const override;
+
+ FilePathInfo filePathInfo(const FilePath &filePath) const override;
+ OsType osType(const FilePath &filePath) const override;
+ FilePath symLinkTarget(const FilePath &filePath) const override;
+ QDateTime lastModified(const FilePath &filePath) const override;
+ QFile::Permissions permissions(const FilePath &filePath) const override;
+ bool setPermissions(const FilePath &filePath, QFile::Permissions) const override;
+ qint64 fileSize(const FilePath &filePath) const override;
+ qint64 bytesAvailable(const FilePath &filePath) const override;
+
+ void iterateDirectory(
+ const FilePath &filePath,
+ const FilePath::IterateDirCallback &callBack,
+ const FileFilter &filter) const override;
+
+ std::optional<QByteArray> fileContents(
+ const FilePath &filePath,
+ qint64 limit,
+ qint64 offset) const override;
+ bool writeFileContents(
+ const FilePath &filePath,
+ const QByteArray &data,
+ qint64 offset) const override;
+
+private:
+ bool iterateWithFind(
+ const FilePath &filePath,
+ const FileFilter &filter,
+ const FilePath::IterateDirCallback &callBack) const;
+ void findUsingLs(
+ const QString &current,
+ const FileFilter &filter,
+ QStringList *found) const;
+
+ mutable bool m_tryUseFind = true;
+};
+
+} // Utils
diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp
index 91796a6850f..645ded358e5 100644
--- a/src/libs/utils/filepath.cpp
+++ b/src/libs/utils/filepath.cpp
@@ -4,6 +4,7 @@
#include "filepath.h"
#include "algorithm.h"
+#include "devicefileaccess.h"
#include "environment.h"
#include "fileutils.h"
#include "hostosinfo.h"
@@ -15,7 +16,6 @@
#include <QDirIterator>
#include <QFileInfo>
#include <QRegularExpression>
-#include <QStorageInfo>
#include <QUrl>
#include <QStringView>
@@ -30,7 +30,6 @@
namespace Utils {
static DeviceFileHooks s_deviceHooks;
-static bool removeRecursivelyLocal(const FilePath &filePath, QString *error);
inline bool isWindowsDriveLetter(QChar ch);
@@ -359,136 +358,68 @@ void FilePath::setParts(const QStringView scheme, const QStringView host, const
/// FilePath exists.
bool FilePath::exists() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.exists, return false);
- return s_deviceHooks.exists(*this);
- }
- return !isEmpty() && QFileInfo::exists(path());
+ return fileAccess()->exists(*this);
}
/// \returns a bool indicating whether a path is writable.
bool FilePath::isWritableDir() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.isWritableDir, return false);
- return s_deviceHooks.isWritableDir(*this);
- }
- const QFileInfo fi{path()};
- return exists() && fi.isDir() && fi.isWritable();
+ return fileAccess()->isWritableDirectory(*this);
}
bool FilePath::isWritableFile() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.isWritableFile, return false);
- return s_deviceHooks.isWritableFile(*this);
- }
- const QFileInfo fi{path()};
- return fi.isWritable() && !fi.isDir();
+ return fileAccess()->isWritableFile(*this);
}
bool FilePath::ensureWritableDir() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.ensureWritableDir, return false);
- return s_deviceHooks.ensureWritableDir(*this);
- }
- const QFileInfo fi{path()};
- if (fi.isDir() && fi.isWritable())
- return true;
- return QDir().mkpath(path());
+ return fileAccess()->ensureWritableDirectory(*this);
}
bool FilePath::ensureExistingFile() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.ensureExistingFile, return false);
- return s_deviceHooks.ensureExistingFile(*this);
- }
- QFile f(path());
- if (f.exists())
- return true;
- f.open(QFile::WriteOnly);
- f.close();
- return f.exists();
+ return fileAccess()->ensureExistingFile(*this);
}
bool FilePath::isExecutableFile() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.isExecutableFile, return false);
- return s_deviceHooks.isExecutableFile(*this);
- }
- const QFileInfo fi{path()};
- return fi.isExecutable() && !fi.isDir();
+ return fileAccess()->isExecutableFile(*this);
}
bool FilePath::isReadableFile() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.isReadableFile, return false);
- return s_deviceHooks.isReadableFile(*this);
- }
- const QFileInfo fi{path()};
- return fi.isReadable() && !fi.isDir();
+ return fileAccess()->isReadableFile(*this);
}
bool FilePath::isReadableDir() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.isReadableDir, return false);
- return s_deviceHooks.isReadableDir(*this);
- }
- const QFileInfo fi{path()};
- return fi.isReadable() && fi.isDir();
+ return fileAccess()->isReadableDirectory(*this);
}
bool FilePath::isFile() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.isFile, return false);
- return s_deviceHooks.isFile(*this);
- }
- const QFileInfo fi{path()};
- return fi.isFile();
+ return fileAccess()->isFile(*this);
}
bool FilePath::isDir() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.isDir, return false);
- return s_deviceHooks.isDir(*this);
- }
- const QFileInfo fi{path()};
- return fi.isDir();
+ return fileAccess()->isDirectory(*this);
}
bool FilePath::createDir() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.createDir, return false);
- return s_deviceHooks.createDir(*this);
- }
- QDir dir(path());
- return dir.mkpath(dir.absolutePath());
+ return fileAccess()->createDirectory(*this);
}
FilePaths FilePath::dirEntries(const FileFilter &filter, QDir::SortFlags sort) const
{
FilePaths result;
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.iterateDirectory, return {});
- const auto callBack = [&result](const FilePath &path) { result.append(path); return true; };
- s_deviceHooks.iterateDirectory(*this, callBack, filter);
- } else {
- QDirIterator dit(path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags);
- while (dit.hasNext())
- result.append(FilePath::fromString(dit.next()));
- }
+ const auto callBack = [&result](const FilePath &path) { result.append(path); return true; };
+ iterateDirectory(callBack, filter);
// FIXME: Not all flags supported here.
-
const QDir::SortFlags sortBy = (sort & QDir::SortByMask);
if (sortBy == QDir::Name) {
Utils::sort(result);
@@ -515,23 +446,7 @@ FilePaths FilePath::dirEntries(QDir::Filters filters) const
void FilePath::iterateDirectory(const IterateDirCallback &callBack, const FileFilter &filter) const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.iterateDirectory, return);
- s_deviceHooks.iterateDirectory(*this, callBack, filter);
- return;
- }
-
- QDirIterator it(path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags);
- while (it.hasNext()) {
- const FilePath path = FilePath::fromString(it.next());
- bool res = false;
- if (callBack.index() == 0)
- res = std::get<0>(callBack)(path);
- else
- res = std::get<1>(callBack)(path, path.filePathInfo());
- if (!res)
- return;
- }
+ fileAccess()->iterateDirectory(*this, callBack, filter);
}
void FilePath::iterateDirectories(const FilePaths &dirs,
@@ -544,26 +459,7 @@ void FilePath::iterateDirectories(const FilePaths &dirs,
std::optional<QByteArray> FilePath::fileContents(qint64 maxSize, qint64 offset) const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.fileContents, return {});
- return s_deviceHooks.fileContents(*this, maxSize, offset);
- }
-
- const QString path = toString();
- QFile f(path);
- if (!f.exists())
- return {};
-
- if (!f.open(QFile::ReadOnly))
- return {};
-
- if (offset != 0)
- f.seek(offset);
-
- if (maxSize != -1)
- return f.read(maxSize);
-
- return f.readAll();
+ return fileAccess()->fileContents(*this, maxSize, offset);
}
bool FilePath::ensureReachable(const FilePath &other) const
@@ -577,78 +473,30 @@ bool FilePath::ensureReachable(const FilePath &other) const
return false;
}
-
-void FilePath::asyncFileContents(const Continuation<const std::optional<QByteArray> &> &cont,
- qint64 maxSize,
- qint64 offset) const
+void FilePath::asyncFileContents(
+ const Continuation<const std::optional<QByteArray> &> &cont,
+ qint64 maxSize,
+ qint64 offset) const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.asyncFileContents, return);
- s_deviceHooks.asyncFileContents(cont, *this, maxSize, offset);
- return;
- }
-
- cont(fileContents(maxSize, offset));
+ return fileAccess()->asyncFileContents(*this, cont, maxSize, offset);
}
bool FilePath::writeFileContents(const QByteArray &data, qint64 offset) const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.writeFileContents, return {});
- return s_deviceHooks.writeFileContents(*this, data, offset);
- }
-
- QFile file(path());
- QTC_ASSERT(file.open(QFile::WriteOnly | QFile::Truncate), return false);
- if (offset != 0)
- file.seek(offset);
- qint64 res = file.write(data);
- return res == data.size();
+ return fileAccess()->writeFileContents(*this, data, offset);
}
FilePathInfo FilePath::filePathInfo() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.filePathInfo, return {});
- return s_deviceHooks.filePathInfo(*this);
- }
-
- FilePathInfo result;
-
- QFileInfo fi(path());
- result.fileSize = fi.size();
- result.lastModified = fi.lastModified();
- result.fileFlags = (FilePathInfo::FileFlag) int(fi.permissions());
-
- if (fi.isDir())
- result.fileFlags |= FilePathInfo::DirectoryType;
- if (fi.isFile())
- result.fileFlags |= FilePathInfo::FileType;
- if (fi.exists())
- result.fileFlags |= FilePathInfo::ExistsFlag;
- if (fi.isSymbolicLink())
- result.fileFlags |= FilePathInfo::LinkType;
- if (fi.isBundle())
- result.fileFlags |= FilePathInfo::BundleType;
- if (fi.isHidden())
- result.fileFlags |= FilePathInfo::HiddenFlag;
- if (fi.isRoot())
- result.fileFlags |= FilePathInfo::RootFlag;
-
- return result;
+ return fileAccess()->filePathInfo(*this);
}
-void FilePath::asyncWriteFileContents(const Continuation<bool> &cont,
- const QByteArray &data,
- qint64 offset) const
+void FilePath::asyncWriteFileContents(
+ const Continuation<bool> &cont,
+ const QByteArray &data,
+ qint64 offset) const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.asyncWriteFileContents, return);
- s_deviceHooks.asyncWriteFileContents(cont, *this, data, offset);
- return;
- }
-
- cont(writeFileContents(data, offset));
+ return fileAccess()->asyncWriteFileContents(*this, cont, data, offset);
}
bool FilePath::needsDevice() const
@@ -670,23 +518,12 @@ bool FilePath::isSameDevice(const FilePath &other) const
/// \returns an empty FilePath if this is not a symbolic linl
FilePath FilePath::symLinkTarget() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.symLinkTarget, return {});
- return s_deviceHooks.symLinkTarget(*this);
- }
- const QFileInfo info(path());
- if (!info.isSymLink())
- return {};
- return FilePath::fromString(info.symLinkTarget());
+ return fileAccess()->symLinkTarget(*this);
}
QString FilePath::mapToDevicePath() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.mapToDevicePath, return path());
- return s_deviceHooks.mapToDevicePath(*this);
- }
- return path();
+ return fileAccess()->mapToDevicePath(*this);
}
FilePath FilePath::withExecutableSuffix() const
@@ -937,6 +774,23 @@ void FilePath::setFromString(const QString &unnormalizedFileName)
setParts({}, {}, fileName);
}
+DeviceFileAccess *FilePath::fileAccess() const
+{
+ if (!needsDevice())
+ return DesktopDeviceFileAccess::instance();
+
+ if (!s_deviceHooks.fileAccess) {
+ // Happens during startup and in tst_fsengine
+ QTC_CHECK(false);
+ return DesktopDeviceFileAccess::instance();
+ }
+
+ static DeviceFileAccess dummy;
+ DeviceFileAccess *access = s_deviceHooks.fileAccess(*this);
+ QTC_ASSERT(access, return &dummy);
+ return access;
+}
+
/// Constructs a FilePath from \a filePath. The \a defaultExtension is appended
/// to \a filename if that does not have an extension already.
/// \a filePath is not checked for validity.
@@ -1241,10 +1095,10 @@ FilePath FilePath::withNewPath(const QString &newPath) const
*/
FilePath FilePath::searchInDirectories(const FilePaths &dirs) const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.searchInPath, return {});
- return s_deviceHooks.searchInPath(*this, dirs);
- }
+ if (isAbsolutePath())
+ return *this;
+ // FIXME: Ramp down use.
+ QTC_ASSERT(!needsDevice(), return {});
return Environment::systemEnvironment().searchInDirectories(path(), dirs);
}
@@ -1252,6 +1106,7 @@ FilePath FilePath::searchInPath(const FilePaths &additionalDirs, PathAmending am
{
if (isAbsolutePath())
return *this;
+ // FIXME: Ramp down use.
FilePaths directories = deviceEnvironment().path();
if (!additionalDirs.isEmpty()) {
if (amending == AppendToPath)
@@ -1259,7 +1114,8 @@ FilePath FilePath::searchInPath(const FilePaths &additionalDirs, PathAmending am
else
directories = additionalDirs + directories;
}
- return searchInDirectories(directories);
+ QTC_ASSERT(!needsDevice(), return {});
+ return Environment::systemEnvironment().searchInDirectories(path(), directories);
}
Environment FilePath::deviceEnvironment() const
@@ -1338,47 +1194,27 @@ size_t FilePath::hash(uint seed) const
QDateTime FilePath::lastModified() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.lastModified, return {});
- return s_deviceHooks.lastModified(*this);
- }
- return toFileInfo().lastModified();
+ return fileAccess()->lastModified(*this);
}
QFile::Permissions FilePath::permissions() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.permissions, return {});
- return s_deviceHooks.permissions(*this);
- }
- return toFileInfo().permissions();
+ return fileAccess()->permissions(*this);
}
bool FilePath::setPermissions(QFile::Permissions permissions) const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.setPermissions, return false);
- return s_deviceHooks.setPermissions(*this, permissions);
- }
- return QFile(path()).setPermissions(permissions);
+ return fileAccess()->setPermissions(*this, permissions);
}
OsType FilePath::osType() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.osType, return OsType::OsTypeLinux);
- return s_deviceHooks.osType(*this);
- }
- return HostOsInfo::hostOs();
+ return fileAccess()->osType(*this);
}
bool FilePath::removeFile() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.removeFile, return false);
- return s_deviceHooks.removeFile(*this);
- }
- return QFile::remove(path());
+ return fileAccess()->removeFile(*this);
}
/*!
@@ -1390,11 +1226,7 @@ bool FilePath::removeFile() const
*/
bool FilePath::removeRecursively(QString *error) const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.removeRecursively, return false);
- return s_deviceHooks.removeRecursively(*this);
- }
- return removeRecursivelyLocal(*this, error);
+ return fileAccess()->removeRecursively(*this, error);
}
bool FilePath::copyFile(const FilePath &target) const
@@ -1406,11 +1238,7 @@ bool FilePath::copyFile(const FilePath &target) const
return false;
return target.writeFileContents(*ba);
}
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.copyFile, return false);
- return s_deviceHooks.copyFile(*this, target);
- }
- return QFile::copy(path(), target.path());
+ return fileAccess()->copyFile(*this, target);
}
void FilePath::asyncCopyFile(const std::function<void(bool)> &cont, const FilePath &target) const
@@ -1420,90 +1248,24 @@ void FilePath::asyncCopyFile(const std::function<void(bool)> &cont, const FilePa
if (ba)
target.asyncWriteFileContents(cont, *ba);
});
- } else if (needsDevice()) {
- s_deviceHooks.asyncCopyFile(cont, *this, target);
- } else {
- cont(copyFile(target));
+ return;
}
+ return fileAccess()->asyncCopyFile(*this, cont, target);
}
bool FilePath::renameFile(const FilePath &target) const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.renameFile, return false);
- return s_deviceHooks.renameFile(*this, target);
- }
- return QFile::rename(path(), target.path());
+ return fileAccess()->renameFile(*this, target);
}
qint64 FilePath::fileSize() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.fileSize, return false);
- return s_deviceHooks.fileSize(*this);
- }
- return QFileInfo(path()).size();
+ return fileAccess()->fileSize(*this);
}
qint64 FilePath::bytesAvailable() const
{
- if (needsDevice()) {
- QTC_ASSERT(s_deviceHooks.bytesAvailable, return false);
- return s_deviceHooks.bytesAvailable(*this);
- }
- return QStorageInfo(path()).bytesAvailable();
-}
-
-static bool removeRecursivelyLocal(const FilePath &filePath, QString *error)
-{
- QTC_ASSERT(!filePath.needsDevice(), return false);
- QFileInfo fileInfo = filePath.toFileInfo();
- if (!fileInfo.exists() && !fileInfo.isSymLink())
- return true;
-
- QFile::setPermissions(fileInfo.absoluteFilePath(), fileInfo.permissions() | QFile::WriteUser);
-
- if (fileInfo.isDir()) {
- QDir dir(fileInfo.absoluteFilePath());
- dir.setPath(dir.canonicalPath());
- if (dir.isRoot()) {
- if (error) {
- *error = QCoreApplication::translate("Utils::FileUtils",
- "Refusing to remove root directory.");
- }
- return false;
- }
- if (dir.path() == QDir::home().canonicalPath()) {
- if (error) {
- *error = QCoreApplication::translate("Utils::FileUtils",
- "Refusing to remove your home directory.");
- }
- return false;
- }
-
- const QStringList fileNames = dir.entryList(
- QDir::Files | QDir::Hidden | QDir::System | QDir::Dirs | QDir::NoDotAndDotDot);
- for (const QString &fileName : fileNames) {
- if (!removeRecursivelyLocal(filePath / fileName, error))
- return false;
- }
- if (!QDir::root().rmdir(dir.path())) {
- if (error) {
- *error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove directory \"%1\".")
- .arg(filePath.toUserOutput());
- }
- return false;
- }
- } else {
- if (!QFile::remove(filePath.toString())) {
- if (error) {
- *error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove file \"%1\".")
- .arg(filePath.toUserOutput());
- }
- return false;
- }
- }
- return true;
+ return fileAccess()->bytesAvailable(*this);
}
/*!
diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h
index 01100dc1320..0cc9455eef7 100644
--- a/src/libs/utils/filepath.h
+++ b/src/libs/utils/filepath.h
@@ -28,9 +28,12 @@ class tst_fileutils; // This becomes a friend of Utils::FilePath for testing pri
namespace Utils {
+class DeviceFileAccess;
class Environment;
class EnvironmentChange;
+template <class ...Args> using Continuation = std::function<void(Args...)>;
+
class QTCREATOR_UTILS_EXPORT FileFilter
{
public:
@@ -188,7 +191,6 @@ public:
static void sort(FilePaths &files);
// Asynchronous interface
- template <class ...Args> using Continuation = std::function<void(Args...)>;
void asyncCopyFile(const Continuation<bool> &cont, const FilePath &target) const;
void asyncFileContents(const Continuation<const std::optional<QByteArray> &> &cont,
qint64 maxSize = -1,
@@ -232,6 +234,7 @@ private:
static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath);
void setPath(QStringView path);
void setFromString(const QString &filepath);
+ DeviceFileAccess *fileAccess() const;
[[nodiscard]] QString mapToDevicePath() const;
[[nodiscard]] QString encodedHost() const;
@@ -252,50 +255,11 @@ class QTCREATOR_UTILS_EXPORT DeviceFileHooks
public:
static DeviceFileHooks &instance();
- std::function<bool(const FilePath &)> isExecutableFile;
- std::function<bool(const FilePath &)> isReadableFile;
- std::function<bool(const FilePath &)> isReadableDir;
- std::function<bool(const FilePath &)> isWritableDir;
- std::function<bool(const FilePath &)> isWritableFile;
- std::function<bool(const FilePath &)> isFile;
- std::function<bool(const FilePath &)> isDir;
- std::function<bool(const FilePath &)> ensureWritableDir;
- std::function<bool(const FilePath &)> ensureExistingFile;
- std::function<bool(const FilePath &)> createDir;
- std::function<bool(const FilePath &)> exists;
- std::function<bool(const FilePath &)> removeFile;
- std::function<bool(const FilePath &)> removeRecursively;
- std::function<bool(const FilePath &, const FilePath &)> copyFile;
- std::function<bool(const FilePath &, const FilePath &)> renameFile;
- std::function<FilePath(const FilePath &, const FilePaths &)> searchInPath;
- std::function<FilePath(const FilePath &)> symLinkTarget;
- std::function<QString(const FilePath &)> mapToDevicePath;
- std::function<void(const FilePath &,
- const FilePath::IterateDirCallback &, // Abort on 'false' return.
- const FileFilter &)>
- iterateDirectory;
- std::function<std::optional<QByteArray>(const FilePath &, qint64, qint64)> fileContents;
- std::function<bool(const FilePath &, const QByteArray &, qint64)> writeFileContents;
- std::function<QDateTime(const FilePath &)> lastModified;
- std::function<QFile::Permissions(const FilePath &)> permissions;
- std::function<bool(const FilePath &, QFile::Permissions)> setPermissions;
- std::function<OsType(const FilePath &)> osType;
- std::function<Environment(const FilePath &)> environment;
- std::function<qint64(const FilePath &)> fileSize;
- std::function<qint64(const FilePath &)> bytesAvailable;
+ std::function<DeviceFileAccess *(const FilePath &)> fileAccess;
std::function<QString(const FilePath &)> deviceDisplayName;
- std::function<bool(const FilePath &, const FilePath &)> isSameDevice;
- std::function<FilePathInfo(const FilePath &)> filePathInfo;
-
-
- template <class ...Args> using Continuation = std::function<void(Args...)>;
- std::function<void(const Continuation<bool> &, const FilePath &, const FilePath &)> asyncCopyFile;
- std::function<void(
- const Continuation<const std::optional<QByteArray> &> &, const FilePath &, qint64, qint64)>
- asyncFileContents;
- std::function<void(const Continuation<bool> &, const FilePath &, const QByteArray &, qint64)>
- asyncWriteFileContents;
std::function<bool(const FilePath &, const FilePath &)> ensureReachable;
+ std::function<Environment(const FilePath &)> environment;
+ std::function<bool(const FilePath &left, const FilePath &right)> isSameDevice;
};
} // namespace Utils
diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp
index 42d8f553894..989f6a3fcc9 100644
--- a/src/libs/utils/fileutils.cpp
+++ b/src/libs/utils/fileutils.cpp
@@ -646,46 +646,6 @@ FilePaths FileUtils::getOpenFilePaths(QWidget *parent,
#endif // QT_WIDGETS_LIB
-// Used on 'ls' output on unix-like systems.
-static void iterateLsOutput(const FilePath &base,
- const QStringList &entries,
- const FileFilter &filter,
- const FilePath::IterateDirCallback &callBack)
-{
- const QList<QRegularExpression> nameRegexps =
- transform(filter.nameFilters, [](const QString &filter) {
- QRegularExpression re;
- re.setPattern(QRegularExpression::wildcardToRegularExpression(filter));
- QTC_CHECK(re.isValid());
- return re;
- });
-
- const auto nameMatches = [&nameRegexps](const QString &fileName) {
- for (const QRegularExpression &re : nameRegexps) {
- const QRegularExpressionMatch match = re.match(fileName);
- if (match.hasMatch())
- return true;
- }
- return nameRegexps.isEmpty();
- };
-
- // FIXME: Handle filters. For now bark on unsupported options.
- QTC_CHECK(filter.fileFilters == QDir::NoFilter);
-
- for (const QString &entry : entries) {
- if (!nameMatches(entry))
- continue;
- const FilePath current = base.pathAppended(entry);
- bool res = false;
- if (callBack.index() == 0)
- res = std::get<0>(callBack)(current);
- else
- res = std::get<1>(callBack)(current, current.filePathInfo());
- if (!res)
- break;
- }
-}
-
FilePathInfo::FileFlags fileInfoFlagsfromStatRawModeHex(const QString &hexString)
{
bool ok = false;
@@ -741,120 +701,6 @@ FilePathInfo FileUtils::filePathInfoFromTriple(const QString &infos)
return {size, flags, dt};
}
-static bool iterateWithFindHelper(
- const FilePath &filePath,
- const FileFilter &filter,
- const std::function<RunResult(const CommandLine &)> &runInShell,
- const std::function<bool(const QString &)> callBack,
- const QString &extraArguments)
-{
- QTC_CHECK(filePath.isAbsolutePath());
- const QStringList arguments = filter.asFindArguments(filePath.path());
-
- CommandLine cmdLine{"find", arguments};
- if (!extraArguments.isEmpty())
- cmdLine.addArgs(extraArguments, CommandLine::Raw);
-
- const RunResult result = runInShell(cmdLine);
- const QString out = QString::fromUtf8(result.stdOut);
- if (result.exitCode != 0) {
- // Find returns non-zero exit code for any error it encounters, even if it finds some files.
-
- if (!out.startsWith('"' + filePath.path())) {
- if (!filePath.exists()) // File does not exist, so no files to find.
- return true;
-
- // If the output does not start with the path we are searching in, find has failed.
- // Possibly due to unknown options.
- return false;
- }
- }
-
- QStringList entries = out.split("\n", Qt::SkipEmptyParts);
- if (entries.isEmpty())
- return true;
-
- // Remove the first line, it is always the directory we are searching in.
- // as long as we do not specify "mindepth > 0"
- if (entries.size() > 0)
- entries.pop_front();
- for (const QString &entry : entries) {
- if (!callBack(entry))
- break;
- }
-
- return true;
-}
-
-// returns whether 'find' could be used.
-static bool iterateWithFind(
- const FilePath &filePath,
- const FileFilter &filter,
- const std::function<RunResult(const CommandLine &)> &runInShell,
- const FilePath::IterateDirCallback &callBack)
-{
- const auto toFilePath = [&filePath, &callBack](const QString &entry) {
- if (callBack.index() == 0)
- return std::get<0>(callBack)(filePath.withNewPath(entry));
-
- const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1);
- const QString infos = entry.mid(fileName.length() + 3);
-
- const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos);
- if (!fi.fileFlags)
- return true;
-
- const FilePath fp = filePath.withNewPath(fileName);
- return std::get<1>(callBack)(fp, fi);
- };
-
- // TODO: Using stat -L will always return the link target, not the link itself.
- // We may wan't to add the information that it is a link at some point.
- QString infoArgs;
- if (callBack.index() == 1)
- infoArgs = R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)";
-
- return iterateWithFindHelper(filePath, filter, runInShell, toFilePath, infoArgs);
-}
-
-static void findUsingLs(const QString &current,
- const FileFilter &filter,
- const std::function<RunResult(const CommandLine &)> &runInShell,
- QStringList *found)
-{
- const RunResult result = runInShell({"ls", {"-1", "-p", "--", current}});
- const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts);
- for (QString entry : entries) {
- const QChar last = entry.back();
- if (last == '/') {
- entry.chop(1);
- if (filter.iteratorFlags.testFlag(QDirIterator::Subdirectories))
- findUsingLs(current + '/' + entry, filter, runInShell, found);
- }
- found->append(entry);
- }
-}
-
-void FileUtils::iterateUnixDirectory(const FilePath &filePath,
- const FileFilter &filter,
- bool *useFind,
- const std::function<RunResult (const CommandLine &)> &runInShell,
- const FilePath::IterateDirCallback &callBack)
-{
- // We try to use 'find' first, because that can filter better directly.
- // Unfortunately, it's not installed on all devices by default.
- if (useFind && *useFind) {
- if (iterateWithFind(filePath, filter, runInShell, callBack))
- return;
- *useFind = false; // remember the failure for the next time and use the 'ls' fallback below.
- }
-
- // if we do not have find - use ls as fallback
- QStringList entries;
- findUsingLs(filePath.path(), filter, runInShell, &entries);
- iterateLsOutput(filePath, entries, filter, callBack);
-}
-
/*!
Copies the directory specified by \a srcFilePath recursively to \a tgtFilePath. \a tgtFilePath will contain
the target directory, which will be created. Example usage:
diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h
index 9949a82d858..76ad91efbc8 100644
--- a/src/libs/utils/fileutils.h
+++ b/src/libs/utils/fileutils.h
@@ -86,13 +86,6 @@ public:
static FilePaths toFilePathList(const QStringList &paths);
- static void iterateUnixDirectory(
- const FilePath &base,
- const FileFilter &filter,
- bool *useFind,
- const std::function<RunResult(const CommandLine &)> &runInShell,
- const FilePath::IterateDirCallback &callBack);
-
static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput);
static FilePathInfo filePathInfoFromTriple(const QString &infos);
diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs
index b5da80bbc5f..4cc71591c35 100644
--- a/src/libs/utils/utils.qbs
+++ b/src/libs/utils/utils.qbs
@@ -83,6 +83,8 @@ Project {
"detailsbutton.h",
"detailswidget.cpp",
"detailswidget.h",
+ "devicefileaccess.cpp",
+ "devicefileaccess.h",
"deviceshell.cpp",
"deviceshell.h",
"differ.cpp",
diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp
index bcab94bcb8b..be7124c995e 100644
--- a/src/plugins/docker/dockerdevice.cpp
+++ b/src/plugins/docker/dockerdevice.cpp
@@ -32,6 +32,7 @@
#include <utils/algorithm.h>
#include <utils/basetreeview.h>
+#include <utils/devicefileaccess.h>
#include <utils/deviceshell.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
@@ -113,6 +114,25 @@ private:
FilePath m_devicePath;
};
+class DockerDeviceFileAccess : public UnixDeviceFileAccess
+{
+public:
+ DockerDeviceFileAccess(DockerDevicePrivate *dev)
+ : m_dev(dev)
+ {}
+
+ RunResult runInShell(const QString &executable,
+ const QStringList &arguments,
+ const QByteArray &stdInData) const override;
+
+ std::optional<QByteArray> fileContents(
+ const FilePath &filePath,
+ qint64 limit,
+ qint64 offset) const override;
+
+ DockerDevicePrivate *m_dev = nullptr;
+};
+
class DockerDevicePrivate : public QObject
{
public:
@@ -129,8 +149,6 @@ public:
return runInShell(cmd, stdInData).exitCode == 0;
}
- std::optional<QByteArray> fileContents(const FilePath &filePath, qint64 limit, qint64 offset);
-
void updateContainerAccess();
void changeMounts(QStringList newMounts);
bool ensureReachable(const FilePath &other);
@@ -177,8 +195,8 @@ public:
QString m_container;
Environment m_cachedEnviroment;
- bool m_useFind = true; // prefer find over ls and hacks, but be able to use ls as fallback
bool m_isShutdown = false;
+ DockerDeviceFileAccess m_fileAccess{this};
};
class DockerProcessImpl : public Utils::ProcessInterface
@@ -337,9 +355,28 @@ Tasks DockerDevicePrivate::validateMounts() const
return result;
}
+RunResult DockerDeviceFileAccess::runInShell(
+ const QString &executable,
+ const QStringList &arguments,
+ const QByteArray &stdInData) const
+{
+ QTC_ASSERT(m_dev, return {});
+ return m_dev->runInShell({FilePath::fromString(executable), arguments}, stdInData);
+}
+
+std::optional<QByteArray> DockerDeviceFileAccess::fileContents(
+ const FilePath &filePath,
+ qint64 limit,
+ qint64 offset) const
+{
+ m_dev->updateContainerAccess();
+ return UnixDeviceFileAccess::fileContents(filePath, limit, offset);
+}
+
DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &data)
: d(new DockerDevicePrivate(this, settings, data))
{
+ setFileAccess(&d->m_fileAccess);
setDisplayType(Tr::tr("Docker"));
setOsType(OsTypeOtherUnix);
setDefaultDisplayName(Tr::tr("Docker Image"));
@@ -778,165 +815,6 @@ bool DockerDevice::handlesFile(const FilePath &filePath) const
return false;
}
-bool DockerDevice::isExecutableFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-x", path}});
-}
-
-bool DockerDevice::isReadableFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-r", path, "-a", "-f", path}});
-}
-
-bool DockerDevice::isWritableFile(const Utils::FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-w", path, "-a", "-f", path}});
-}
-
-bool DockerDevice::isReadableDirectory(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-r", path, "-a", "-d", path}});
-}
-
-bool DockerDevice::isWritableDirectory(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-w", path, "-a", "-d", path}});
-}
-
-bool DockerDevice::isFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-f", path}});
-}
-
-bool DockerDevice::isDirectory(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-d", path}});
-}
-
-bool DockerDevice::createDirectory(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"mkdir", {"-p", path}});
-}
-
-bool DockerDevice::exists(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-e", path}});
-}
-
-bool DockerDevice::ensureExistingFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"touch", {path}});
-}
-
-bool DockerDevice::removeFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return d->runInShellSuccess({"rm", {filePath.path()}});
-}
-
-bool DockerDevice::removeRecursively(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- QTC_ASSERT(filePath.path().startsWith('/'), return false);
-
- const QString path = filePath.cleanPath().path();
- // We are expecting this only to be called in a context of build directories or similar.
- // Chicken out in some cases that _might_ be user code errors.
- QTC_ASSERT(path.startsWith('/'), return false);
- const int levelsNeeded = path.startsWith("/home/") ? 4 : 3;
- QTC_ASSERT(path.count('/') >= levelsNeeded, return false);
-
- return d->runInShellSuccess({"rm", {"-rf", "--", path}});
-}
-
-bool DockerDevice::copyFile(const FilePath &filePath, const FilePath &target) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- QTC_ASSERT(handlesFile(target), return false);
- return d->runInShellSuccess({"cp", {filePath.path(), target.path()}});
-}
-
-bool DockerDevice::renameFile(const FilePath &filePath, const FilePath &target) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- QTC_ASSERT(handlesFile(target), return false);
- return d->runInShellSuccess({"mv", {filePath.path(), target.path()}});
-}
-
-QDateTime DockerDevice::lastModified(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- const RunResult result = d->runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}});
- qint64 secs = result.stdOut.toLongLong();
- const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC);
- return dt;
-}
-
-FilePath DockerDevice::symLinkTarget(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- const RunResult result = d->runInShell({"readlink", {"-n", "-e", filePath.path()}});
- const QString out = QString::fromUtf8(result.stdOut);
- return out.isEmpty() ? FilePath() : filePath.withNewPath(out);
-}
-
-qint64 DockerDevice::fileSize(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return -1);
- const RunResult result = d->runInShell({"stat", {"-L", "-c", "%s", filePath.path()}});
- return result.stdOut.toLongLong();
-}
-
-QFileDevice::Permissions DockerDevice::permissions(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
-
- const RunResult result = d->runInShell({"stat", {"-L", "-c", "%a", filePath.path()}});
- const uint bits = result.stdOut.toUInt(nullptr, 8);
- QFileDevice::Permissions perm = {};
-#define BIT(n, p) if (bits & (1<<n)) perm |= QFileDevice::p
- BIT(0, ExeOther);
- BIT(1, WriteOther);
- BIT(2, ReadOther);
- BIT(3, ExeGroup);
- BIT(4, WriteGroup);
- BIT(5, ReadGroup);
- BIT(6, ExeUser);
- BIT(7, WriteUser);
- BIT(8, ReadUser);
-#undef BIT
- return perm;
-}
-
-bool DockerDevice::setPermissions(const FilePath &filePath,
- QFileDevice::Permissions permissions) const
-{
- Q_UNUSED(permissions)
- QTC_ASSERT(handlesFile(filePath), return {});
- QTC_CHECK(false); // FIXME: Implement.
- return false;
-}
-
bool DockerDevice::ensureReachable(const FilePath &other) const
{
if (other.needsDevice())
@@ -947,43 +825,6 @@ bool DockerDevice::ensureReachable(const FilePath &other) const
return d->ensureReachable(other.parentDir());
}
-void DockerDevice::iterateDirectory(const FilePath &filePath,
- const FilePath::IterateDirCallback &callBack,
- const FileFilter &filter) const
-{
- QTC_ASSERT(handlesFile(filePath), return);
- auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); };
- FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack);
-}
-
-std::optional<QByteArray> DockerDevice::fileContents(const FilePath &filePath,
- qint64 limit,
- qint64 offset) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- return d->fileContents(filePath, limit, offset);
-}
-
-bool DockerDevice::writeFileContents(const FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- CommandLine cmd({"dd", {"of=" + filePath.path()}});
- if (offset != 0) {
- cmd.addArg("bs=1");
- cmd.addArg(QString("seek=%1").arg(offset));
- }
- return d->runInShellSuccess(cmd, data);
-}
-
-FilePathInfo DockerDevice::filePathInfo(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- const RunResult stat = d->runInShell({"stat", {"-L", "-c", "%f %Y %s", filePath.path()}});
- return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut));
-}
-
Environment DockerDevice::systemEnvironment() const
{
return d->environment();
@@ -995,28 +836,6 @@ void DockerDevice::aboutToBeRemoved() const
detector.undoAutoDetect(id().toString());
}
-std::optional<QByteArray> DockerDevicePrivate::fileContents(const FilePath &filePath,
- qint64 limit,
- qint64 offset)
-{
- updateContainerAccess();
-
- QStringList args = {"if=" + filePath.path(), "status=none"};
- if (limit > 0 || offset > 0) {
- const qint64 gcd = std::gcd(limit, offset);
- args += {QString("bs=%1").arg(gcd),
- QString("count=%1").arg(limit / gcd),
- QString("seek=%1").arg(offset / gcd)};
- }
-
- const RunResult r = m_shell->runInShell({"dd", args});
-
- if (r.exitCode != 0)
- return {};
-
- return r.stdOut;
-}
-
void DockerDevicePrivate::fetchSystemEnviroment()
{
updateContainerAccess();
diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h
index 1382dc7d7b6..625b45b46ff 100644
--- a/src/plugins/docker/dockerdevice.h
+++ b/src/plugins/docker/dockerdevice.h
@@ -84,37 +84,6 @@ public:
Utils::FilePath rootPath() const override;
bool handlesFile(const Utils::FilePath &filePath) const override;
- bool isExecutableFile(const Utils::FilePath &filePath) const override;
- bool isReadableFile(const Utils::FilePath &filePath) const override;
- bool isWritableFile(const Utils::FilePath &filePath) const override;
- bool isReadableDirectory(const Utils::FilePath &filePath) const override;
- bool isWritableDirectory(const Utils::FilePath &filePath) const override;
- bool isFile(const Utils::FilePath &filePath) const override;
- bool isDirectory(const Utils::FilePath &filePath) const override;
- bool createDirectory(const Utils::FilePath &filePath) const override;
- bool exists(const Utils::FilePath &filePath) const override;
- bool ensureExistingFile(const Utils::FilePath &filePath) const override;
- bool removeFile(const Utils::FilePath &filePath) const override;
- bool removeRecursively(const Utils::FilePath &filePath) const override;
- bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override;
- bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override;
- Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override;
- void iterateDirectory(
- const Utils::FilePath &filePath,
- const Utils::FilePath::IterateDirCallback &callBack,
- const Utils::FileFilter &filter) const override;
- std::optional<QByteArray> fileContents(const Utils::FilePath &filePath,
- qint64 limit,
- qint64 offset) const override;
- bool writeFileContents(const Utils::FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const override;
- Utils::FilePathInfo filePathInfo(const Utils::FilePath &filePath) const override;
- QDateTime lastModified(const Utils::FilePath &filePath) const override;
- qint64 fileSize(const Utils::FilePath &filePath) const override;
- QFileDevice::Permissions permissions(const Utils::FilePath &filePath) const override;
- bool setPermissions(const Utils::FilePath &filePath,
- QFileDevice::Permissions permissions) const override;
bool ensureReachable(const Utils::FilePath &other) const override;
Utils::Environment systemEnvironment() const override;
diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
index ee5dae12e09..e65e9892a18 100644
--- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
@@ -11,6 +11,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runcontrol.h>
+#include <utils/devicefileaccess.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/portlist.h>
@@ -27,6 +28,8 @@ namespace ProjectExplorer {
DesktopDevice::DesktopDevice()
{
+ setFileAccess(DesktopDeviceFileAccess::instance());
+
setupId(IDevice::AutoDetected, DESKTOP_DEVICE_ID);
setType(DESKTOP_DEVICE_TYPE);
setDefaultDisplayName(tr("Local PC"));
@@ -136,33 +139,6 @@ bool DesktopDevice::handlesFile(const FilePath &filePath) const
return !filePath.needsDevice();
}
-void DesktopDevice::iterateDirectory(
- const FilePath &filePath,
- const FilePath::IterateDirCallback &callBack,
- const FileFilter &filter) const
-{
- QTC_CHECK(!filePath.needsDevice());
- filePath.iterateDirectory(callBack, filter);
-}
-
-qint64 DesktopDevice::fileSize(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return -1);
- return filePath.fileSize();
-}
-
-QFile::Permissions DesktopDevice::permissions(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- return filePath.permissions();
-}
-
-bool DesktopDevice::setPermissions(const FilePath &filePath, QFile::Permissions permissions) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- return filePath.setPermissions(permissions);
-}
-
FilePath DesktopDevice::mapToGlobalPath(const Utils::FilePath &pathOnDevice) const
{
QTC_CHECK(!pathOnDevice.needsDevice());
@@ -174,117 +150,4 @@ Environment DesktopDevice::systemEnvironment() const
return Environment::systemEnvironment();
}
-bool DesktopDevice::isExecutableFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.isExecutableFile();
-}
-
-bool DesktopDevice::isReadableFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.isReadableFile();
-}
-
-bool DesktopDevice::isWritableFile(const Utils::FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.isWritableFile();
-}
-
-bool DesktopDevice::isReadableDirectory(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.isReadableDir();
-}
-
-bool DesktopDevice::isWritableDirectory(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.isWritableDir();
-}
-
-bool DesktopDevice::isFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.isFile();
-}
-
-bool DesktopDevice::isDirectory(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.isDir();
-}
-
-bool DesktopDevice::createDirectory(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.createDir();
-}
-
-bool DesktopDevice::exists(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.exists();
-}
-
-bool DesktopDevice::ensureExistingFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.ensureExistingFile();
-}
-
-bool DesktopDevice::removeFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.removeFile();
-}
-
-bool DesktopDevice::removeRecursively(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.removeRecursively();
-}
-
-bool DesktopDevice::copyFile(const FilePath &filePath, const FilePath &target) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return filePath.copyFile(target);
-}
-
-bool DesktopDevice::renameFile(const FilePath &filePath, const FilePath &target) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- QTC_ASSERT(handlesFile(target), return false);
- return filePath.renameFile(target);
-}
-
-QDateTime DesktopDevice::lastModified(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- return filePath.lastModified();
-}
-
-FilePath DesktopDevice::symLinkTarget(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- return filePath.symLinkTarget();
-}
-
-std::optional<QByteArray> DesktopDevice::fileContents(const FilePath &filePath,
- qint64 limit,
- qint64 offset) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- return filePath.fileContents(limit, offset);
-}
-
-bool DesktopDevice::writeFileContents(const FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- return filePath.writeFileContents(data, offset);
-}
-
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h
index d0d89f7458c..7144eba8eea 100644
--- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h
+++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h
@@ -34,35 +34,6 @@ public:
bool handlesFile(const Utils::FilePath &filePath) const override;
Utils::Environment systemEnvironment() const override;
- bool isExecutableFile(const Utils::FilePath &filePath) const override;
- bool isReadableFile(const Utils::FilePath &filePath) const override;
- bool isWritableFile(const Utils::FilePath &filePath) const override;
- bool isReadableDirectory(const Utils::FilePath &filePath) const override;
- bool isWritableDirectory(const Utils::FilePath &filePath) const override;
- bool isFile(const Utils::FilePath &filePath) const override;
- bool isDirectory(const Utils::FilePath &filePath) const override;
- bool ensureExistingFile(const Utils::FilePath &filePath) const override;
- bool createDirectory(const Utils::FilePath &filePath) const override;
- bool exists(const Utils::FilePath &filePath) const override;
- bool removeFile(const Utils::FilePath &filePath) const override;
- bool removeRecursively(const Utils::FilePath &filePath) const override;
- bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override;
- bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override;
- QDateTime lastModified(const Utils::FilePath &filePath) const override;
- Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override;
- void iterateDirectory(
- const Utils::FilePath &filePath,
- const Utils::FilePath::IterateDirCallback &callBack,
- const Utils::FileFilter &filter) const override;
- std::optional<QByteArray> fileContents(const Utils::FilePath &filePath,
- qint64 limit,
- qint64 offset) const override;
- bool writeFileContents(const Utils::FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const override;
- qint64 fileSize(const Utils::FilePath &filePath) const override;
- QFile::Permissions permissions(const Utils::FilePath &filePath) const override;
- bool setPermissions(const Utils::FilePath &filePath, QFile::Permissions) const override;
Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const override;
protected:
diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
index 8a98a11b292..2d7d060427b 100644
--- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
@@ -9,7 +9,9 @@
#include <coreplugin/messagemanager.h>
#include <projectexplorer/projectexplorerconstants.h>
+
#include <utils/algorithm.h>
+#include <utils/devicefileaccess.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/fsengine/fsengine.h>
@@ -405,30 +407,6 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
DeviceFileHooks &deviceHooks = DeviceFileHooks::instance();
- deviceHooks.isExecutableFile = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->isExecutableFile(filePath);
- };
-
- deviceHooks.isReadableFile = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->isReadableFile(filePath);
- };
-
- deviceHooks.isReadableDir = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->isReadableDirectory(filePath);
- };
-
- deviceHooks.isWritableDir = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->isWritableDirectory(filePath);
- };
-
deviceHooks.isSameDevice = [](const FilePath &left, const FilePath &right) {
auto leftDevice = DeviceManager::deviceForPath(left);
auto rightDevice = DeviceManager::deviceForPath(right);
@@ -436,150 +414,12 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
return leftDevice == rightDevice;
};
- deviceHooks.isWritableFile = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->isWritableFile(filePath);
- };
-
- deviceHooks.isFile = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->isFile(filePath);
- };
-
- deviceHooks.isDir = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->isDirectory(filePath);
- };
-
- deviceHooks.ensureWritableDir = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->ensureWritableDirectory(filePath);
- };
-
- deviceHooks.ensureExistingFile = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->ensureExistingFile(filePath);
- };
-
- deviceHooks.createDir = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->createDirectory(filePath);
- };
-
- deviceHooks.exists = [](const FilePath &filePath) {
+ deviceHooks.fileAccess = [](const FilePath &filePath) -> DeviceFileAccess * {
+ if (!filePath.needsDevice())
+ return DesktopDeviceFileAccess::instance();
auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->exists(filePath);
- };
-
- deviceHooks.removeFile = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->removeFile(filePath);
- };
-
- deviceHooks.removeRecursively = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->removeRecursively(filePath);
- };
-
- deviceHooks.copyFile = [](const FilePath &filePath, const FilePath &target) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->copyFile(filePath, target);
- };
-
- deviceHooks.renameFile = [](const FilePath &filePath, const FilePath &target) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->renameFile(filePath, target);
- };
-
- deviceHooks.searchInPath = [](const FilePath &filePath, const FilePaths &dirs) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return FilePath{});
- return device->searchExecutable(filePath.path(), dirs);
- };
-
- deviceHooks.symLinkTarget = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return FilePath{});
- return device->symLinkTarget(filePath);
- };
-
- deviceHooks.mapToDevicePath = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return QString{});
- return device->mapToDevicePath(filePath);
- };
-
- deviceHooks.iterateDirectory = [](const FilePath &filePath,
- const FilePath::IterateDirCallback &callBack,
- const FileFilter &filter) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return );
- device->iterateDirectory(filePath, callBack, filter);
- };
-
- deviceHooks.fileContents =
- [](const FilePath &filePath, qint64 maxSize, qint64 offset) -> std::optional<QByteArray> {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return {});
- return device->fileContents(filePath, maxSize, offset);
- };
-
- deviceHooks.asyncFileContents = [](const Continuation<std::optional<QByteArray>> &cont,
- const FilePath &filePath,
- qint64 maxSize,
- qint64 offset) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return);
- device->asyncFileContents(cont, filePath, maxSize, offset);
- };
-
- deviceHooks.writeFileContents = [](const FilePath &filePath,
- const QByteArray &data,
- qint64 offset) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->writeFileContents(filePath, data, offset);
- };
-
- deviceHooks.filePathInfo = [](const FilePath &filePath) -> FilePathInfo {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return {});
- return device->filePathInfo(filePath);
- };
-
- deviceHooks.lastModified = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return QDateTime());
- return device->lastModified(filePath);
- };
-
- deviceHooks.permissions = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return QFile::Permissions());
- return device->permissions(filePath);
- };
-
- deviceHooks.setPermissions = [](const FilePath &filePath, QFile::Permissions permissions) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return false);
- return device->setPermissions(filePath, permissions);
- };
-
- deviceHooks.osType = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return OsTypeOther);
- return device->osType();
+ QTC_ASSERT(device, return nullptr);
+ return device->fileAccess();
};
deviceHooks.environment = [](const FilePath &filePath) {
@@ -588,18 +428,6 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
return device->systemEnvironment();
};
- deviceHooks.fileSize = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return qint64(-1));
- return device->fileSize(filePath);
- };
-
- deviceHooks.bytesAvailable = [](const FilePath &filePath) {
- auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return qint64(-1));
- return device->bytesAvailable(filePath);
- };
-
deviceHooks.deviceDisplayName = [](const FilePath &filePath) {
auto device = DeviceManager::deviceForPath(filePath);
QTC_ASSERT(device, return QString());
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp
index ed6dcc73824..110f3f93fd0 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp
@@ -15,6 +15,7 @@
#include <projectexplorer/target.h>
+#include <utils/devicefileaccess.h>
#include <utils/displayname.h>
#include <utils/icon.h>
#include <utils/portlist.h>
@@ -130,6 +131,7 @@ public:
IDevice::DeviceState deviceState = IDevice::DeviceStateUnknown;
IDevice::MachineType machineType = IDevice::Hardware;
OsType osType = OsTypeOther;
+ DeviceFileAccess *fileAccess = nullptr;
int version = 0; // This is used by devices that have been added by the SDK.
QReadWriteLock lock; // Currently used to protect sshParameters only
@@ -153,6 +155,8 @@ IDevice::IDevice() : d(new Internal::IDevicePrivate)
{
}
+IDevice::~IDevice() = default;
+
void IDevice::setOpenTerminal(const IDevice::OpenTerminal &openTerminal)
{
d->openTerminal = openTerminal;
@@ -191,6 +195,11 @@ bool IDevice::isAnyUnixDevice() const
return d->osType == OsTypeLinux || d->osType == OsTypeMac || d->osType == OsTypeOtherUnix;
}
+DeviceFileAccess *IDevice::fileAccess() const
+{
+ return d->fileAccess;
+}
+
FilePath IDevice::mapToGlobalPath(const FilePath &pathOnDevice) const
{
if (pathOnDevice.needsDevice()) {
@@ -219,113 +228,6 @@ bool IDevice::handlesFile(const FilePath &filePath) const
return false;
}
-bool IDevice::isExecutableFile(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::isReadableFile(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::isWritableFile(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::isReadableDirectory(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::isWritableDirectory(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::isFile(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::isDirectory(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::ensureWritableDirectory(const FilePath &filePath) const
-{
- if (isWritableDirectory(filePath))
- return true;
- return createDirectory(filePath);
-}
-
-bool IDevice::ensureExistingFile(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::createDirectory(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::exists(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::removeFile(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::removeRecursively(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::copyFile(const FilePath &filePath, const FilePath &target) const
-{
- Q_UNUSED(filePath);
- Q_UNUSED(target);
- QTC_CHECK(false);
- return false;
-}
-
-bool IDevice::renameFile(const FilePath &filePath, const FilePath &target) const
-{
- Q_UNUSED(filePath);
- Q_UNUSED(target);
- QTC_CHECK(false);
- return false;
-}
-
FilePath IDevice::searchExecutableInPath(const QString &fileName) const
{
FilePaths paths;
@@ -341,108 +243,13 @@ FilePath IDevice::searchExecutable(const QString &fileName, const FilePaths &dir
dir = mapToGlobalPath(dir);
QTC_CHECK(handlesFile(dir));
const FilePath candidate = dir / fileName;
- if (isExecutableFile(candidate))
+ if (candidate.isExecutableFile())
return candidate;
}
return {};
}
-FilePath IDevice::symLinkTarget(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return {};
-}
-
-void IDevice::iterateDirectory(const FilePath &filePath,
- const FilePath::IterateDirCallback &callBack,
- const FileFilter &filter) const
-{
- Q_UNUSED(filePath);
- Q_UNUSED(callBack);
- Q_UNUSED(filter);
- QTC_CHECK(false);
-}
-
-std::optional<QByteArray> IDevice::fileContents(const FilePath &filePath,
- qint64 limit,
- qint64 offset) const
-{
- Q_UNUSED(filePath);
- Q_UNUSED(limit);
- Q_UNUSED(offset);
- QTC_CHECK(false);
- return {};
-}
-
-void IDevice::asyncFileContents(const Continuation<std::optional<QByteArray>> &cont,
- const FilePath &filePath,
- qint64 limit,
- qint64 offset) const
-{
- cont(fileContents(filePath, limit, offset));
-}
-
-bool IDevice::writeFileContents(const FilePath &filePath, const QByteArray &data, qint64 offset) const
-{
- Q_UNUSED(filePath);
- Q_UNUSED(data);
- Q_UNUSED(offset);
- QTC_CHECK(false);
- return {};
-}
-
-FilePathInfo IDevice::filePathInfo(const Utils::FilePath &filePath) const
-{
- bool exists = filePath.exists();
- if (!exists)
- return {};
-
- FilePathInfo result {
- filePath.fileSize(),
- {FilePathInfo::ExistsFlag},
- filePath.lastModified(),
- };
-
- if (filePath.isDir())
- result.fileFlags |= FilePathInfo::DirectoryType;
- if (filePath.isFile())
- result.fileFlags |= FilePathInfo::FileType;
- if (filePath.isRootPath())
- result.fileFlags |= FilePathInfo::RootFlag;
-
- return result;
-}
-
-void IDevice::asyncWriteFileContents(const Continuation<bool> &cont,
- const FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const
-{
- cont(writeFileContents(filePath, data, offset));
-}
-
-QDateTime IDevice::lastModified(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- return {};
-}
-
-QFileDevice::Permissions IDevice::permissions(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return {};
-}
-
-bool IDevice::setPermissions(const FilePath &filePath, QFile::Permissions) const
-{
- Q_UNUSED(filePath);
- QTC_CHECK(false);
- return false;
-}
-
ProcessInterface *IDevice::createProcessInterface() const
{
QTC_CHECK(false);
@@ -463,22 +270,6 @@ Environment IDevice::systemEnvironment() const
return Environment::systemEnvironment();
}
-qint64 IDevice::fileSize(const FilePath &filePath) const
-{
- Q_UNUSED(filePath)
- QTC_CHECK(false);
- return -1;
-}
-
-qint64 IDevice::bytesAvailable(const Utils::FilePath &filePath) const
-{
- Q_UNUSED(filePath)
- QTC_CHECK(false);
- return -1;
-}
-
-IDevice::~IDevice() = default;
-
/*!
Specifies a free-text name for the device to be displayed in GUI elements.
*/
@@ -513,6 +304,11 @@ void IDevice::setOsType(OsType osType)
d->osType = osType;
}
+void IDevice::setFileAccess(DeviceFileAccess *fileAccess)
+{
+ d->fileAccess = fileAccess;
+}
+
IDevice::DeviceInfo IDevice::deviceInformation() const
{
const QString key = QCoreApplication::translate("ProjectExplorer::IDevice", "Device");
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h
index ac46ff77f47..0f5649afd6c 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.h
+++ b/src/plugins/projectexplorer/devicesupport/idevice.h
@@ -27,6 +27,7 @@ QT_END_NAMESPACE
namespace Utils {
class CommandLine;
+class DeviceFileAccess;
class Environment;
class Icon;
class PortList;
@@ -214,62 +215,23 @@ public:
bool isMacDevice() const { return osType() == Utils::OsTypeMac; }
bool isAnyUnixDevice() const;
+ Utils::DeviceFileAccess *fileAccess() const;
+ virtual bool handlesFile(const Utils::FilePath &filePath) const;
+
virtual Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const;
virtual QString mapToDevicePath(const Utils::FilePath &globalPath) const;
- virtual bool handlesFile(const Utils::FilePath &filePath) const;
- virtual bool isExecutableFile(const Utils::FilePath &filePath) const;
- virtual bool isReadableFile(const Utils::FilePath &filePath) const;
- virtual bool isWritableFile(const Utils::FilePath &filePath) const;
- virtual bool isReadableDirectory(const Utils::FilePath &filePath) const;
- virtual bool isWritableDirectory(const Utils::FilePath &filePath) const;
- virtual bool isFile(const Utils::FilePath &filePath) const;
- virtual bool isDirectory(const Utils::FilePath &filePath) const;
- virtual bool ensureWritableDirectory(const Utils::FilePath &filePath) const;
- virtual bool ensureExistingFile(const Utils::FilePath &filePath) const;
- virtual bool createDirectory(const Utils::FilePath &filePath) const;
- virtual bool exists(const Utils::FilePath &filePath) const;
- virtual bool removeFile(const Utils::FilePath &filePath) const;
- virtual bool removeRecursively(const Utils::FilePath &filePath) const;
- virtual bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const;
- virtual bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const;
virtual Utils::FilePath searchExecutableInPath(const QString &fileName) const;
virtual Utils::FilePath searchExecutable(const QString &fileName,
const Utils::FilePaths &dirs) const;
- virtual Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const;
- virtual void iterateDirectory(
- const Utils::FilePath &filePath,
- const Utils::FilePath::IterateDirCallback &callBack,
- const Utils::FileFilter &filter) const;
-
- virtual std::optional<QByteArray> fileContents(const Utils::FilePath &filePath,
- qint64 limit,
- qint64 offset) const;
- virtual bool writeFileContents(const Utils::FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const;
- virtual Utils::FilePathInfo filePathInfo(const Utils::FilePath &filePath) const;
- virtual QDateTime lastModified(const Utils::FilePath &filePath) const;
- virtual QFile::Permissions permissions(const Utils::FilePath &filePath) const;
- virtual bool setPermissions(const Utils::FilePath &filePath, QFile::Permissions) const;
+
virtual Utils::ProcessInterface *createProcessInterface() const;
virtual FileTransferInterface *createFileTransferInterface(
const FileTransferSetupData &setup) const;
virtual Utils::Environment systemEnvironment() const;
- virtual qint64 fileSize(const Utils::FilePath &filePath) const;
- virtual qint64 bytesAvailable(const Utils::FilePath &filePath) const;
virtual void aboutToBeRemoved() const {}
- virtual void asyncFileContents(const Continuation<std::optional<QByteArray>> &cont,
- const Utils::FilePath &filePath,
- qint64 limit,
- qint64 offset) const;
- virtual void asyncWriteFileContents(const Continuation<bool> &cont,
- const Utils::FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const;
-
virtual bool ensureReachable(const Utils::FilePath &other) const;
virtual bool prepareForBuild(const Target *target);
@@ -284,6 +246,7 @@ protected:
void setOpenTerminal(const OpenTerminal &openTerminal);
void setDisplayType(const QString &type);
void setOsType(Utils::OsType osType);
+ void setFileAccess(Utils::DeviceFileAccess *fileAccess);
private:
IDevice(const IDevice &) = delete;
diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp
index dae5d277fd2..884412f5b5f 100644
--- a/src/plugins/projectexplorer/gcctoolchain.cpp
+++ b/src/plugins/projectexplorer/gcctoolchain.cpp
@@ -1118,8 +1118,7 @@ static FilePaths findCompilerCandidates(const ToolchainDetector &detector,
return true;
};
const FilePath globalDir = device->mapToGlobalPath(deviceDir);
- device->iterateDirectory(globalDir, callBack,
- {nameFilters, QDir::Files | QDir::Executable});
+ globalDir.iterateDirectory(callBack, {nameFilters, QDir::Files | QDir::Executable});
}
} else {
// The normal, local host case.
diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp
index f9018e04e78..b5b9205377d 100644
--- a/src/plugins/remotelinux/linuxdevice.cpp
+++ b/src/plugins/remotelinux/linuxdevice.cpp
@@ -25,6 +25,7 @@
#include <projectexplorer/runcontrol.h>
#include <utils/algorithm.h>
+#include <utils/devicefileaccess.h>
#include <utils/deviceshell.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
@@ -349,6 +350,21 @@ private:
// LinuxDevicePrivate
class ShellThreadHandler;
+class LinuxDevicePrivate;
+
+class LinuxDeviceFileAccess : public UnixDeviceFileAccess
+{
+public:
+ LinuxDeviceFileAccess(LinuxDevicePrivate *dev)
+ : m_dev(dev)
+ {}
+
+ RunResult runInShell(const QString &executable,
+ const QStringList &arguments,
+ const QByteArray &stdInData) const override;
+
+ LinuxDevicePrivate *m_dev;
+};
class LinuxDevicePrivate
{
@@ -358,9 +374,6 @@ public:
bool setupShell();
RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {});
- bool runInShellSuccess(const CommandLine &cmd, const QByteArray &stdInData = {}) {
- return runInShell(cmd, stdInData).exitCode == 0;
- }
void attachToSharedConnection(SshConnectionHandle *connectionHandle,
const SshParameters &sshParameters);
@@ -370,9 +383,16 @@ public:
ShellThreadHandler *m_handler = nullptr;
mutable QMutex m_shellMutex;
QList<QtcProcess *> m_terminals;
- bool m_useFind = true;
+ LinuxDeviceFileAccess m_fileAccess{this};
};
+RunResult LinuxDeviceFileAccess::runInShell(const QString &executable,
+ const QStringList &arguments,
+ const QByteArray &stdInData) const
+{
+ return m_dev->runInShell({FilePath::fromString(executable), arguments}, stdInData);
+}
+
// SshProcessImpl
class SshProcessInterfacePrivate : public QObject
@@ -465,7 +485,7 @@ qint64 SshProcessInterface::processId() const
bool SshProcessInterface::runInShell(const CommandLine &command, const QByteArray &data)
{
- return d->m_devicePrivate->runInShellSuccess(command, data);
+ return d->m_devicePrivate->runInShell(command, data).exitCode == 0;
}
void SshProcessInterface::start()
@@ -904,6 +924,7 @@ private:
LinuxDevice::LinuxDevice()
: d(new LinuxDevicePrivate(this))
{
+ setFileAccess(&d->m_fileAccess);
setDisplayType(Tr::tr("Remote Linux"));
setDefaultDisplayName(Tr::tr("Remote Linux Device"));
setOsType(OsTypeLinux);
@@ -1121,223 +1142,6 @@ void LinuxDevicePrivate::attachToSharedConnection(SshConnectionHandle *connectio
emit connectionHandle->connected(socketFilePath);
}
-bool LinuxDevice::isExecutableFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-x", path}});
-}
-
-bool LinuxDevice::isReadableFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-r", path, "-a", "-f", path}});
-}
-
-bool LinuxDevice::isWritableFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-w", path, "-a", "-f", path}});
-}
-
-bool LinuxDevice::isReadableDirectory(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-r", path, "-a", "-d", path}});
-}
-
-bool LinuxDevice::isWritableDirectory(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-w", path, "-a", "-d", path}});
-}
-
-bool LinuxDevice::isFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-f", path}});
-}
-
-bool LinuxDevice::isDirectory(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-d", path}});
-}
-
-bool LinuxDevice::createDirectory(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"mkdir", {"-p", path}});
-}
-
-bool LinuxDevice::exists(const FilePath &filePath) const
-{
- DEBUG("filepath " << filePath.path());
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"test", {"-e", path}});
-}
-
-bool LinuxDevice::ensureExistingFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const QString path = filePath.path();
- return d->runInShellSuccess({"touch", {path}});
-}
-
-bool LinuxDevice::removeFile(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- return d->runInShellSuccess({"rm", {filePath.path()}});
-}
-
-bool LinuxDevice::removeRecursively(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- QTC_ASSERT(filePath.path().startsWith('/'), return false);
-
- const QString path = filePath.cleanPath().path();
- // We are expecting this only to be called in a context of build directories or similar.
- // Chicken out in some cases that _might_ be user code errors.
- QTC_ASSERT(path.startsWith('/'), return false);
- const int levelsNeeded = path.startsWith("/home/") ? 3 : 2;
- QTC_ASSERT(path.count('/') >= levelsNeeded, return false);
-
- return d->runInShellSuccess({"rm", {"-rf", "--", path}});
-}
-
-bool LinuxDevice::copyFile(const FilePath &filePath, const FilePath &target) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- QTC_ASSERT(handlesFile(target), return false);
- return d->runInShellSuccess({"cp", {filePath.path(), target.path()}});
-}
-
-bool LinuxDevice::renameFile(const FilePath &filePath, const FilePath &target) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- QTC_ASSERT(handlesFile(target), return false);
- return d->runInShellSuccess({"mv", {filePath.path(), target.path()}});
-}
-
-QDateTime LinuxDevice::lastModified(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- const RunResult result = d->runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}});
- const qint64 secs = result.stdOut.toLongLong();
- const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC);
- return dt;
-}
-
-FilePath LinuxDevice::symLinkTarget(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- const RunResult result = d->runInShell({"readlink", {"-n", "-e", filePath.path()}});
- return result.stdOut.isEmpty() ? FilePath()
- : filePath.withNewPath(QString::fromUtf8(result.stdOut));
-}
-
-qint64 LinuxDevice::fileSize(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return -1);
- const RunResult result = d->runInShell({"stat", {"-L", "-c", "%s", filePath.path()}});
- return result.stdOut.toLongLong();
-}
-
-qint64 LinuxDevice::bytesAvailable(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return -1);
- CommandLine cmd("df", {"-k"});
- cmd.addArg(filePath.path());
- const RunResult result = d->runInShell(cmd);
- return FileUtils::bytesAvailableFromDFOutput(result.stdOut);
-}
-
-QFileDevice::Permissions LinuxDevice::permissions(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- const RunResult result = d->runInShell({"stat", {"-L", "-c", "%a", filePath.path()}});
- const uint bits = result.stdOut.toUInt(nullptr, 8);
- QFileDevice::Permissions perm = {};
-#define BIT(n, p) if (bits & (1<<n)) perm |= QFileDevice::p
- BIT(0, ExeOther);
- BIT(1, WriteOther);
- BIT(2, ReadOther);
- BIT(3, ExeGroup);
- BIT(4, WriteGroup);
- BIT(5, ReadGroup);
- BIT(6, ExeUser);
- BIT(7, WriteUser);
- BIT(8, ReadUser);
-#undef BIT
- return perm;
-}
-
-bool LinuxDevice::setPermissions(const FilePath &filePath, QFileDevice::Permissions permissions) const
-{
- QTC_ASSERT(handlesFile(filePath), return false);
- const int flags = int(permissions);
- return d->runInShellSuccess({"chmod", {QString::number(flags, 16), filePath.path()}});
-}
-
-void LinuxDevice::iterateDirectory(const FilePath &filePath,
- const FilePath::IterateDirCallback &callBack,
- const FileFilter &filter) const
-{
- QTC_ASSERT(handlesFile(filePath), return);
- auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); };
- FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack);
-}
-
-FilePathInfo LinuxDevice::filePathInfo(const FilePath &filePath) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- const RunResult stat = d->runInShell({"stat", {"-L", "-c", "%f %Y %s", filePath.path()}});
- return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut));
-}
-
-std::optional<QByteArray> LinuxDevice::fileContents(const FilePath &filePath,
- qint64 limit,
- qint64 offset) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- QString args = "if=" + filePath.path() + " status=none";
- if (limit > 0 || offset > 0) {
- const qint64 gcd = std::gcd(limit, offset);
- args += QString(" bs=%1 count=%2 seek=%3").arg(gcd).arg(limit / gcd).arg(offset / gcd);
- }
- CommandLine cmd(FilePath::fromString("dd"), args, CommandLine::Raw);
-
- const RunResult result = d->runInShell(cmd);
- if (result.exitCode != 0) {
- DEBUG("fileContents failed");
- return {};
- }
-
- DEBUG(result.stdOut << QByteArray::fromHex(result.stdOut));
- return result.stdOut;
-}
-
-bool LinuxDevice::writeFileContents(const FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const
-{
- QTC_ASSERT(handlesFile(filePath), return {});
- CommandLine cmd({"dd", {"of=" + filePath.path()}});
- if (offset != 0) {
- cmd.addArg("bs=1");
- cmd.addArg(QString("seek=%1").arg(offset));
- }
- return d->runInShellSuccess(cmd, data);
-}
-
static FilePaths dirsToCreate(const FilesToTransfer &files)
{
FilePaths dirs;
diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h
index b1e992c024a..51517ed8622 100644
--- a/src/plugins/remotelinux/linuxdevice.h
+++ b/src/plugins/remotelinux/linuxdevice.h
@@ -37,40 +37,11 @@ public:
Utils::FilePath rootPath() const override;
bool handlesFile(const Utils::FilePath &filePath) const override;
- bool isExecutableFile(const Utils::FilePath &filePath) const override;
- bool isReadableFile(const Utils::FilePath &filePath) const override;
- bool isWritableFile(const Utils::FilePath &filePath) const override;
- bool isReadableDirectory(const Utils::FilePath &filePath) const override;
- bool isWritableDirectory(const Utils::FilePath &filePath) const override;
- bool isFile(const Utils::FilePath &filePath) const override;
- bool isDirectory(const Utils::FilePath &filePath) const override;
- bool createDirectory(const Utils::FilePath &filePath) const override;
- bool exists(const Utils::FilePath &filePath) const override;
- bool ensureExistingFile(const Utils::FilePath &filePath) const override;
- bool removeFile(const Utils::FilePath &filePath) const override;
- bool removeRecursively(const Utils::FilePath &filePath) const override;
- bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override;
- bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override;
- Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override;
- void iterateDirectory(const Utils::FilePath &filePath,
- const Utils::FilePath::IterateDirCallback &callBack,
- const Utils::FileFilter &filter) const override;
- std::optional<QByteArray> fileContents(const Utils::FilePath &filePath,
- qint64 limit,
- qint64 offset) const override;
- bool writeFileContents(const Utils::FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const override;
- Utils::FilePathInfo filePathInfo(const Utils::FilePath &filePath) const override;
- QDateTime lastModified(const Utils::FilePath &filePath) const override;
+
Utils::ProcessInterface *createProcessInterface() const override;
ProjectExplorer::FileTransferInterface *createFileTransferInterface(
const ProjectExplorer::FileTransferSetupData &setup) const override;
Utils::Environment systemEnvironment() const override;
- qint64 fileSize(const Utils::FilePath &filePath) const override;
- qint64 bytesAvailable(const Utils::FilePath &filePath) const override;
- QFileDevice::Permissions permissions(const Utils::FilePath &filePath) const override;
- bool setPermissions(const Utils::FilePath &filePath, QFileDevice::Permissions permissions) const override;
protected:
LinuxDevice();
diff --git a/src/tools/sdktool/CMakeLists.txt b/src/tools/sdktool/CMakeLists.txt
index 050940aca96..ffd578dbcac 100644
--- a/src/tools/sdktool/CMakeLists.txt
+++ b/src/tools/sdktool/CMakeLists.txt
@@ -69,6 +69,7 @@ extend_qtc_library(sdktoolLib
PUBLIC_DEFINES UTILS_STATIC_LIBRARY
SOURCES
commandline.cpp commandline.h
+ devicefileaccess.cpp devicefileaccess.h
environment.cpp environment.h
filepath.cpp filepath.h
fileutils.cpp fileutils.h
diff --git a/src/tools/sdktool/sdktoollib.qbs b/src/tools/sdktool/sdktoollib.qbs
index 4352c1f6c06..3c27d823023 100644
--- a/src/tools/sdktool/sdktoollib.qbs
+++ b/src/tools/sdktool/sdktoollib.qbs
@@ -92,6 +92,7 @@ QtcLibrary {
prefix: libsDir + "/utils/"
files: [
"commandline.cpp", "commandline.h",
+ "devicefileaccess.cpp", "devicefileaccess.h",
"environment.cpp", "environment.h",
"filepath.cpp", "filepath.h",
"fileutils.cpp", "fileutils.h",
diff --git a/tests/auto/utils/fsengine/tst_fsengine.cpp b/tests/auto/utils/fsengine/tst_fsengine.cpp
index 997fe26fd97..e9a21353f99 100644
--- a/tests/auto/utils/fsengine/tst_fsengine.cpp
+++ b/tests/auto/utils/fsengine/tst_fsengine.cpp
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include <utils/filepath.h>
-#include <utils/fileutils.h>
#include <utils/fsengine/fsengine.h>
+#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <QDebug>
@@ -58,119 +58,6 @@ void tst_fsengine::initTestCase()
if (HostOsInfo::isWindowsHost())
QSKIP("The fsengine tests are not supported on Windows.");
- DeviceFileHooks &deviceHooks = DeviceFileHooks::instance();
-
- deviceHooks.fileContents =
- [](const FilePath &path, qint64 maxSize, qint64 offset) -> std::optional<QByteArray> {
- return FilePath::fromString(path.path()).fileContents(maxSize, offset);
- };
-
- deviceHooks.isExecutableFile = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).isExecutableFile();
- };
- deviceHooks.isReadableFile = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).isReadableFile();
- };
- deviceHooks.isReadableDir = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).isReadableDir();
- };
- deviceHooks.isWritableDir = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).isWritableDir();
- };
- deviceHooks.isWritableFile = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).isWritableFile();
- };
- deviceHooks.isFile = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).isFile();
- };
- deviceHooks.isDir = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).isDir();
- };
- deviceHooks.ensureWritableDir = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).ensureWritableDir();
- };
- deviceHooks.ensureExistingFile = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).ensureExistingFile();
- };
- deviceHooks.createDir = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).createDir();
- };
- deviceHooks.exists = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).exists();
- };
- deviceHooks.removeFile = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).removeFile();
- };
- deviceHooks.removeRecursively = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).removeRecursively();
- };
- deviceHooks.copyFile = [](const FilePath &filePath, const FilePath &target) {
- return FilePath::fromString(filePath.path()).copyFile(target);
- };
- deviceHooks.renameFile = [](const FilePath &filePath, const FilePath &target) {
- return FilePath::fromString(filePath.path()).renameFile(target);
- };
- deviceHooks.searchInPath = [](const FilePath &filePath, const FilePaths &dirs) {
- return FilePath::fromString(filePath.path()).searchInPath(dirs);
- };
- deviceHooks.symLinkTarget = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).symLinkTarget();
- };
- deviceHooks.iterateDirectory = [](const FilePath &filePath,
- const FilePath::IterateDirCallback &callBack,
- const FileFilter &filter) {
- const FilePath fp = FilePath::fromString(filePath.path());
- if (callBack.index() == 0) {
- fp.iterateDirectory(
- [&filePath, cb = std::get<0>(callBack)](const FilePath &path) {
- return cb(path.onDevice(filePath));
- },
- filter);
- } else {
- fp.iterateDirectory(
- [&filePath, cb = std::get<1>(callBack)](const FilePath &path, const FilePathInfo &fpi) {
- return cb(path.onDevice(filePath), fpi);
- },
- filter);
- }
- };
- deviceHooks.asyncFileContents = [](const Continuation<const std::optional<QByteArray> &> &cont,
- const FilePath &filePath,
- qint64 maxSize,
- qint64 offset) {
- return FilePath::fromString(filePath.path()).asyncFileContents(cont, maxSize, offset);
- };
- deviceHooks.writeFileContents = [](const FilePath &filePath,
- const QByteArray &data,
- qint64 offset) {
- return FilePath::fromString(filePath.path()).writeFileContents(data, offset);
- };
- deviceHooks.lastModified = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).lastModified();
- };
- deviceHooks.permissions = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).permissions();
- };
- deviceHooks.setPermissions = [](const FilePath &filePath, QFile::Permissions permissions) {
- return FilePath::fromString(filePath.path()).setPermissions(permissions);
- };
- deviceHooks.osType = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).osType();
- };
- // deviceHooks.environment = [](const FilePath &filePath) -> Environment {return {};};
- deviceHooks.fileSize = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).fileSize();
- };
- deviceHooks.bytesAvailable = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).bytesAvailable();
- };
- deviceHooks.filePathInfo = [](const FilePath &filePath) {
- return FilePath::fromString(filePath.path()).filePathInfo();
- };
-
-
- deviceHooks.mapToDevicePath = [](const FilePath &filePath) { return filePath.path(); };
-
FSEngine::addDevice(FilePath::fromString("device://test"));
tempFolder = QDir::tempPath();