浏览代码

Add mode check for device

This fixes two problems:
1. docker run --device /dev/sda:rw ubuntu bash doesn't work
2. --device /dev/zero:/dev/noro:ro doesn't show clear error message,
but fail when writing to cgroup file.

Signed-off-by: Qiang Huang <h.huangqiang@huawei.com>
Qiang Huang 10 年之前
父节点
当前提交
429423624c
共有 5 个文件被更改,包括 79 次插入32 次删除
  1. 14 0
      integration-cli/docker_cli_run_test.go
  2. 29 8
      opts/opts.go
  3. 24 21
      opts/opts_test.go
  4. 5 1
      runconfig/parse.go
  5. 7 2
      runconfig/parse_test.go

+ 14 - 0
integration-cli/docker_cli_run_test.go

@@ -858,6 +858,20 @@ func (s *DockerSuite) TestRunAddingOptionalDevices(c *check.C) {
 	}
 }
 
+func (s *DockerSuite) TestRunAddingOptionalDevicesNoSrc(c *check.C) {
+	out, _ := dockerCmd(c, "run", "--device", "/dev/zero:rw", "busybox", "sh", "-c", "ls /dev/zero")
+	if actual := strings.Trim(out, "\r\n"); actual != "/dev/zero" {
+		c.Fatalf("expected output /dev/zero, received %s", actual)
+	}
+}
+
+func (s *DockerSuite) TestRunAddingOptionalDevicesInvalideMode(c *check.C) {
+	_, _, err := dockerCmdWithError("run", "--device", "/dev/zero:ro", "busybox", "sh", "-c", "ls /dev/zero")
+	if err == nil {
+		c.Fatalf("run container with device mode ro should fail")
+	}
+}
+
 func (s *DockerSuite) TestRunModeHostname(c *check.C) {
 	testRequires(c, SameHostDaemon)
 

+ 29 - 8
opts/opts.go

@@ -170,11 +170,32 @@ func ValidateLink(val string) (string, error) {
 	return val, nil
 }
 
+// ValidDeviceMode checks if the mode for device is valid or not.
+// Valid mode is a composition of r (read), w (write), and m (mknod).
+func ValidDeviceMode(mode string) bool {
+	var legalDeviceMode = map[rune]bool{
+		'r': true,
+		'w': true,
+		'm': true,
+	}
+	if mode == "" {
+		return false
+	}
+	for _, c := range mode {
+		if !legalDeviceMode[c] {
+			return false
+		}
+		legalDeviceMode[c] = false
+	}
+	return true
+}
+
 // ValidateDevice Validate a path for devices
 // It will make sure 'val' is in the form:
 //    [host-dir:]container-path[:mode]
+// It will also validate the device mode.
 func ValidateDevice(val string) (string, error) {
-	return validatePath(val, false)
+	return validatePath(val, ValidDeviceMode)
 }
 
 // ValidatePath Validate a path for volumes
@@ -182,27 +203,27 @@ func ValidateDevice(val string) (string, error) {
 //    [host-dir:]container-path[:rw|ro]
 // It will also validate the mount mode.
 func ValidatePath(val string) (string, error) {
-	return validatePath(val, true)
+	return validatePath(val, volume.ValidMountMode)
 }
 
-func validatePath(val string, validateMountMode bool) (string, error) {
+func validatePath(val string, validator func(string) bool) (string, error) {
 	var containerPath string
 	var mode string
 
 	if strings.Count(val, ":") > 2 {
-		return val, fmt.Errorf("bad format for volumes: %s", val)
+		return val, fmt.Errorf("bad format for path: %s", val)
 	}
 
 	splited := strings.SplitN(val, ":", 3)
 	if splited[0] == "" {
-		return val, fmt.Errorf("bad format for volumes: %s", val)
+		return val, fmt.Errorf("bad format for path: %s", val)
 	}
 	switch len(splited) {
 	case 1:
 		containerPath = splited[0]
 		val = path.Clean(containerPath)
 	case 2:
-		if isValid := volume.ValidMountMode(splited[1]); validateMountMode && isValid {
+		if isValid := validator(splited[1]); isValid {
 			containerPath = splited[0]
 			mode = splited[1]
 			val = fmt.Sprintf("%s:%s", path.Clean(containerPath), mode)
@@ -213,8 +234,8 @@ func validatePath(val string, validateMountMode bool) (string, error) {
 	case 3:
 		containerPath = splited[1]
 		mode = splited[2]
-		if isValid := volume.ValidMountMode(splited[2]); validateMountMode && !isValid {
-			return val, fmt.Errorf("bad mount mode specified : %s", mode)
+		if isValid := validator(splited[2]); !isValid {
+			return val, fmt.Errorf("bad mode specified: %s", mode)
 		}
 		val = fmt.Sprintf("%s:%s:%s", splited[0], containerPath, mode)
 	}

+ 24 - 21
opts/opts_test.go

@@ -289,24 +289,24 @@ func TestValidatePath(t *testing.T) {
 		"/rw:rw",
 	}
 	invalid := map[string]string{
-		"":                "bad format for volumes: ",
+		"":                "bad format for path: ",
 		"./":              "./ is not an absolute path",
 		"../":             "../ is not an absolute path",
 		"/:../":           "../ is not an absolute path",
 		"/:path":          "path is not an absolute path",
-		":":               "bad format for volumes: :",
+		":":               "bad format for path: :",
 		"/tmp:":           " is not an absolute path",
-		":test":           "bad format for volumes: :test",
-		":/test":          "bad format for volumes: :/test",
+		":test":           "bad format for path: :test",
+		":/test":          "bad format for path: :/test",
 		"tmp:":            " is not an absolute path",
-		":test:":          "bad format for volumes: :test:",
-		"::":              "bad format for volumes: ::",
-		":::":             "bad format for volumes: :::",
-		"/tmp:::":         "bad format for volumes: /tmp:::",
-		":/tmp::":         "bad format for volumes: :/tmp::",
+		":test:":          "bad format for path: :test:",
+		"::":              "bad format for path: ::",
+		":::":             "bad format for path: :::",
+		"/tmp:::":         "bad format for path: /tmp:::",
+		":/tmp::":         "bad format for path: :/tmp::",
 		"path:ro":         "path is not an absolute path",
-		"/path:/path:sw":  "bad mount mode specified : sw",
-		"/path:/path:rwz": "bad mount mode specified : rwz",
+		"/path:/path:sw":  "bad mode specified: sw",
+		"/path:/path:rwz": "bad mode specified: rwz",
 	}
 
 	for _, path := range valid {
@@ -333,27 +333,30 @@ func TestValidateDevice(t *testing.T) {
 		"/with space",
 		"/home:/with space",
 		"relative:/absolute-path",
-		"hostPath:/containerPath:ro",
+		"hostPath:/containerPath:r",
 		"/hostPath:/containerPath:rw",
 		"/hostPath:/containerPath:mrw",
 	}
 	invalid := map[string]string{
-		"":        "bad format for volumes: ",
+		"":        "bad format for path: ",
 		"./":      "./ is not an absolute path",
 		"../":     "../ is not an absolute path",
 		"/:../":   "../ is not an absolute path",
 		"/:path":  "path is not an absolute path",
-		":":       "bad format for volumes: :",
+		":":       "bad format for path: :",
 		"/tmp:":   " is not an absolute path",
-		":test":   "bad format for volumes: :test",
-		":/test":  "bad format for volumes: :/test",
+		":test":   "bad format for path: :test",
+		":/test":  "bad format for path: :/test",
 		"tmp:":    " is not an absolute path",
-		":test:":  "bad format for volumes: :test:",
-		"::":      "bad format for volumes: ::",
-		":::":     "bad format for volumes: :::",
-		"/tmp:::": "bad format for volumes: /tmp:::",
-		":/tmp::": "bad format for volumes: :/tmp::",
+		":test:":  "bad format for path: :test:",
+		"::":      "bad format for path: ::",
+		":::":     "bad format for path: :::",
+		"/tmp:::": "bad format for path: /tmp:::",
+		":/tmp::": "bad format for path: :/tmp::",
 		"path:ro": "ro is not an absolute path",
+		"path:rr": "rr is not an absolute path",
+		"a:/b:ro": "bad mode specified: ro",
+		"a:/b:rr": "bad mode specified: rr",
 	}
 
 	for _, path := range valid {

+ 5 - 1
runconfig/parse.go

@@ -474,7 +474,11 @@ func ParseDevice(device string) (DeviceMapping, error) {
 		permissions = arr[2]
 		fallthrough
 	case 2:
-		dst = arr[1]
+		if opts.ValidDeviceMode(arr[1]) {
+			permissions = arr[1]
+		} else {
+			dst = arr[1]
+		}
 		fallthrough
 	case 1:
 		src = arr[0]

+ 7 - 2
runconfig/parse_test.go

@@ -317,15 +317,20 @@ func TestParseDevice(t *testing.T) {
 			PathInContainer:   "/dev/snd",
 			CgroupPermissions: "rwm",
 		},
+		"/dev/snd:rw": {
+			PathOnHost:        "/dev/snd",
+			PathInContainer:   "/dev/snd",
+			CgroupPermissions: "rw",
+		},
 		"/dev/snd:/something": {
 			PathOnHost:        "/dev/snd",
 			PathInContainer:   "/something",
 			CgroupPermissions: "rwm",
 		},
-		"/dev/snd:/something:ro": {
+		"/dev/snd:/something:rw": {
 			PathOnHost:        "/dev/snd",
 			PathInContainer:   "/something",
-			CgroupPermissions: "ro",
+			CgroupPermissions: "rw",
 		},
 	}
 	for device, deviceMapping := range valids {