diff options
author | Marcus Tillmanns <marcus.tillmanns@qt.io> | 2022-10-06 13:03:23 +0200 |
---|---|---|
committer | Marcus Tillmanns <marcus.tillmanns@qt.io> | 2022-10-11 12:24:29 +0000 |
commit | bcadbe666b719581814291ab28d7c490d06a85cc (patch) | |
tree | a17ee54c2440c4e0f96640c7937c2121ea96599b /src/plugins/docker/dockerdevice.cpp | |
parent | 4b42f05439417f76619c0d667cf691592d0fe388 (diff) | |
download | qt-creator-bcadbe666b719581814291ab28d7c490d06a85cc.tar.gz |
Docker: Fix mount arguments
Previously when trying to mount paths that contained spaces or colons
docker create would fail as the -v/--volume syntax does not support
these.
Switching to "--mount" and using the "type=bind" syntax allows to
escape the necessary characters.
Change-Id: If969173505dbf1b36a67dc8b398c58b78941519a
Reviewed-by: hjk <hjk@qt.io>
Diffstat (limited to 'src/plugins/docker/dockerdevice.cpp')
-rw-r--r-- | src/plugins/docker/dockerdevice.cpp | 94 |
1 files changed, 74 insertions, 20 deletions
diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 401e98d59c..87815a7b3f 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -149,6 +149,7 @@ public: CommandLine withDockerExecCmd(const CommandLine &cmd, bool interactive = false); bool prepareForBuild(const Target *target); + Tasks validateMounts() const; bool createContainer(); void startContainer(); @@ -157,6 +158,8 @@ public: bool addTemporaryMount(const FilePath &path, const FilePath &containerPath); + QStringList createMountArgs() const; + DockerDevice *const q; DockerDeviceData m_data; DockerSettings *m_settings; @@ -315,14 +318,21 @@ IDeviceWidget *DockerDevice::createWidget() Tasks DockerDevice::validate() const { + return d->validateMounts(); +} + +Tasks DockerDevicePrivate::validateMounts() const +{ Tasks result; - if (d->data().mounts.isEmpty()) { - result << Task(Task::Error, - Tr::tr("The docker device has not set up shared directories." - "This will not work for building."), - {}, - -1, - {}); + + for (const QString &mount : m_data.mounts) { + const FilePath path = FilePath::fromUserInput(mount); + if (!path.isDir()) { + const QString message = Tr::tr("Path \"%1\" is not a directory or does not exist.") + .arg(mount); + + result.append(Task(Task::Error, message, {}, -1, {})); + } } return result; } @@ -460,6 +470,60 @@ bool DockerDevicePrivate::prepareForBuild(const Target *target) && ensureReachable(target->activeBuildConfiguration()->buildDirectory()); } +QString escapeMountPathUnix(const FilePath &fp) +{ + return fp.nativePath().replace('\"', "\"\""); +} + +QString escapeMountPathWin(const FilePath &fp) +{ + QString result = fp.nativePath().replace('\"', "\"\"").replace('\\', '/'); + if (result.size() >= 2 && result[1] == ':') + result = "/" + result[0] + "/" + result.mid(3); + return result; +} + +QStringList toMountArg(const DockerDevicePrivate::TemporaryMountInfo &mi) +{ + QString escapedPath; + QString escapedContainerPath; + + if (HostOsInfo::isWindowsHost()) { + escapedPath = escapeMountPathWin(mi.path); + escapedContainerPath = escapeMountPathWin(mi.containerPath); + } else { + escapedPath = escapeMountPathUnix(mi.path); + escapedContainerPath = escapeMountPathUnix(mi.containerPath); + } + + const QString mountArg = QString(R"(type=bind,"source=%1","destination=%2")") + .arg(escapedPath) + .arg(escapedContainerPath); + + return QStringList{"--mount", mountArg}; +} + +bool isValidMountInfo(const DockerDevicePrivate::TemporaryMountInfo &mi) +{ + return !mi.path.isEmpty() && !mi.containerPath.isEmpty() && mi.path.isAbsolutePath() + && mi.containerPath.isAbsolutePath(); +} + +QStringList DockerDevicePrivate::createMountArgs() const +{ + QStringList cmds; + QList<TemporaryMountInfo> mounts = m_temporaryMounts; + for (const QString &m : m_data.mounts) + mounts.append({FilePath::fromUserInput(m), FilePath::fromUserInput(m)}); + + for (const TemporaryMountInfo &mi : mounts) { + if (isValidMountInfo(mi)) + cmds += toMountArg(mi); + } + + return cmds; +} + bool DockerDevicePrivate::createContainer() { if (!m_settings) @@ -483,28 +547,18 @@ bool DockerDevicePrivate::createContainer() if (m_data.useLocalUidGid) dockerCreate.addArgs({"-u", QString("%1:%2").arg(getuid()).arg(getgid())}); #endif - - for (QString mount : std::as_const(m_data.mounts)) { - if (mount.isEmpty()) - continue; - mount = q->mapToDevicePath(FilePath::fromUserInput(mount)); - dockerCreate.addArgs({"-v", mount + ':' + mount}); - } FilePath dumperPath = FilePath::fromString("/tmp/qtcreator/debugger"); addTemporaryMount(Core::ICore::resourcePath("debugger/"), dumperPath); q->setDebugDumperPath(dumperPath); - for (const auto &[path, containerPath] : std::as_const(m_temporaryMounts)) { - if (path.isEmpty()) - continue; - dockerCreate.addArgs({"-v", path.nativePath() + ':' + containerPath.nativePath()}); - } + dockerCreate.addArgs(createMountArgs()); + if (!m_data.keepEntryPoint) dockerCreate.addArgs({"--entrypoint", "/bin/sh"}); dockerCreate.addArg(m_data.repoAndTag()); - qCDebug(dockerDeviceLog) << "RUNNING: " << dockerCreate.toUserOutput(); + qCDebug(dockerDeviceLog).noquote() << "RUNNING: " << dockerCreate.toUserOutput(); QtcProcess createProcess; createProcess.setCommand(dockerCreate); createProcess.runBlocking(); |