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();
|
||||
|
||||
Reference in New Issue
Block a user