forked from qt-creator/qt-creator
Utils: Split off file access interface from IDevice
The file accessing functions form now a class hierarchy by themselves, the devices return a suitable point. The previous implementation was mildly confusing by the special handling of the DesktopDevice, fallbacks and remote cases in the same function leading to unnecessary boilerplate when adding new functions and codepaths that sometimes passed the FilePath API twice. Implemented are a "DesktopDeviceFileAccess" taking care of the previous !needsDevice() branches and a "UnixDeviceFileAccess" covering the current docker and RL uses. As a side-effect this unifies to a large degree the current docker and RL code paths with were occasionally deviating from each other while they shouldn't. Change-Id: I4ff59d4be2a07d13e2ca5e9ace26a84160a87c9d Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user