Skip to content

Commit 9db5db1

Browse files
committed
This fixes the case where arguments are escaped twice from Dockerfiles on
Windows Signed-off-by: Darren Stahl <[email protected]>
1 parent 6c31b17 commit 9db5db1

9 files changed

Lines changed: 51 additions & 31 deletions

File tree

api/client/run.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ func (cli *DockerCli) CmdRun(args ...string) error {
104104
return nil
105105
}
106106

107+
config.ArgsEscaped = false
108+
107109
if !*flDetach {
108110
if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil {
109111
return err

builder/dockerfile/dispatchers.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,8 @@ func run(b *Builder, args []string, attributes map[string]bool, original string)
389389
b.runConfig.Cmd = config.Cmd
390390
// set build-time environment for 'run'.
391391
b.runConfig.Env = append(b.runConfig.Env, cmdBuildEnv...)
392+
// set config as already being escaped, this prevents double escaping on windows
393+
b.runConfig.ArgsEscaped = true
392394

393395
logrus.Debugf("[BUILDER] Command to be executed: %v", b.runConfig.Cmd)
394396

@@ -479,7 +481,7 @@ func entrypoint(b *Builder, args []string, attributes map[string]bool, original
479481
if runtime.GOOS != "windows" {
480482
b.runConfig.Entrypoint = stringutils.NewStrSlice("/bin/sh", "-c", parsed[0])
481483
} else {
482-
b.runConfig.Entrypoint = stringutils.NewStrSlice("cmd", "/S /C", parsed[0])
484+
b.runConfig.Entrypoint = stringutils.NewStrSlice("cmd", "/S", "/C", parsed[0])
483485
}
484486
}
485487

builder/dockerfile/internals.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowLocalD
181181
if runtime.GOOS != "windows" {
182182
b.runConfig.Cmd = stringutils.NewStrSlice("/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest))
183183
} else {
184-
b.runConfig.Cmd = stringutils.NewStrSlice("cmd", "/S /C", fmt.Sprintf("REM (nop) %s %s in %s", cmdName, srcHash, dest))
184+
b.runConfig.Cmd = stringutils.NewStrSlice("cmd", "/S", "/C", fmt.Sprintf("REM (nop) %s %s in %s", cmdName, srcHash, dest))
185185
}
186186
defer func(cmd *stringutils.StrSlice) { b.runConfig.Cmd = cmd }(cmd)
187187

daemon/container_windows.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error {
137137
LayerPaths: layerPaths,
138138
Hostname: c.Config.Hostname,
139139
Isolation: c.hostConfig.Isolation,
140+
ArgsEscaped: c.Config.ArgsEscaped,
140141
}
141142

142143
return nil

daemon/execdriver/driver_windows.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type Command struct {
4848
LayerFolder string `json:"layer_folder"` // Layer folder for a command
4949
LayerPaths []string `json:"layer_paths"` // Layer paths for a command
5050
Isolation runconfig.IsolationLevel `json:"isolation"` // Isolation level for the container
51+
ArgsEscaped bool `json:"args_escaped"` // True if args are already escaped
5152
}
5253

5354
// ExitStatus provides exit reasons for a container.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//+build windows
2+
3+
package windows
4+
5+
import (
6+
"errors"
7+
"syscall"
8+
9+
"github.com/Sirupsen/logrus"
10+
"github.com/docker/docker/daemon/execdriver"
11+
)
12+
13+
// createCommandLine creates a command line from the Entrypoint and args
14+
// of the ProcessConfig. It escapes the arguments if they are not already
15+
// escaped
16+
func createCommandLine(processConfig *execdriver.ProcessConfig, alreadyEscaped bool) (commandLine string, err error) {
17+
// While this should get caught earlier, just in case, validate that we
18+
// have something to run.
19+
if processConfig.Entrypoint == "" {
20+
return "", errors.New("No entrypoint specified")
21+
}
22+
23+
// Build the command line of the process
24+
commandLine = processConfig.Entrypoint
25+
logrus.Debugf("Entrypoint: %s", processConfig.Entrypoint)
26+
for _, arg := range processConfig.Arguments {
27+
logrus.Debugf("appending %s", arg)
28+
if !alreadyEscaped {
29+
arg = syscall.EscapeArg(arg)
30+
}
31+
commandLine += " " + arg
32+
}
33+
34+
logrus.Debugf("commandLine: %s", commandLine)
35+
return commandLine, nil
36+
}

daemon/execdriver/windows/exec.go

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
package windows
44

55
import (
6-
"errors"
76
"fmt"
87

98
"github.com/Sirupsen/logrus"
@@ -34,21 +33,11 @@ func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo
3433
// Configure the environment for the process // Note NOT c.ProcessConfig.Tty
3534
createProcessParms.Environment = setupEnvironmentVariables(processConfig.Env)
3635

37-
// While this should get caught earlier, just in case, validate that we
38-
// have something to run.
39-
if processConfig.Entrypoint == "" {
40-
err = errors.New("No entrypoint specified")
41-
logrus.Error(err)
42-
return -1, err
43-
}
36+
createProcessParms.CommandLine, err = createCommandLine(&c.ProcessConfig, c.ArgsEscaped)
4437

45-
// Build the command line of the process
46-
createProcessParms.CommandLine = processConfig.Entrypoint
47-
for _, arg := range processConfig.Arguments {
48-
logrus.Debugln("appending ", arg)
49-
createProcessParms.CommandLine += " " + arg
38+
if err != nil {
39+
return -1, err
5040
}
51-
logrus.Debugln("commandLine: ", createProcessParms.CommandLine)
5241

5342
// Start the command running in the container.
5443
pid, stdin, stdout, stderr, rc, err := hcsshim.CreateProcessInComputeSystem(c.ID, pipes.Stdin != nil, true, !processConfig.Tty, createProcessParms)

daemon/execdriver/windows/run.go

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ package windows
44

55
import (
66
"encoding/json"
7-
"errors"
87
"fmt"
98
"os"
109
"path/filepath"
1110
"strconv"
1211
"strings"
13-
"syscall"
1412

1513
"github.com/Sirupsen/logrus"
1614
"github.com/docker/docker/daemon/execdriver"
@@ -279,21 +277,11 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execd
279277
// Configure the environment for the process
280278
createProcessParms.Environment = setupEnvironmentVariables(c.ProcessConfig.Env)
281279

282-
// This should get caught earlier, but just in case - validate that we
283-
// have something to run
284-
if c.ProcessConfig.Entrypoint == "" {
285-
err = errors.New("No entrypoint specified")
286-
logrus.Error(err)
287-
return execdriver.ExitStatus{ExitCode: -1}, err
288-
}
280+
createProcessParms.CommandLine, err = createCommandLine(&c.ProcessConfig, c.ArgsEscaped)
289281

290-
// Build the command line of the process
291-
createProcessParms.CommandLine = c.ProcessConfig.Entrypoint
292-
for _, arg := range c.ProcessConfig.Arguments {
293-
logrus.Debugln("appending ", arg)
294-
createProcessParms.CommandLine += " " + syscall.EscapeArg(arg)
282+
if err != nil {
283+
return execdriver.ExitStatus{ExitCode: -1}, err
295284
}
296-
logrus.Debugf("CommandLine: %s", createProcessParms.CommandLine)
297285

298286
// Start the command running in the container.
299287
pid, stdin, stdout, stderr, _, err := hcsshim.CreateProcessInComputeSystem(c.ID, pipes.Stdin != nil, true, !c.ProcessConfig.Tty, createProcessParms)

runconfig/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type Config struct {
3030
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
3131
Env []string // List of environment variable to set in the container
3232
Cmd *stringutils.StrSlice // Command to run when starting the container
33+
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific)
3334
Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
3435
Volumes map[string]struct{} // List of volumes (mounts) used for the container
3536
WorkingDir string // Current directory (PWD) in the command will be launched

0 commit comments

Comments
 (0)