Explorar o código

Merge pull request #20684 from yongtang/13840-follow-symlink

Follow symlink for --device argument.
Brian Goff %!s(int64=9) %!d(string=hai) anos
pai
achega
d883002fac

+ 13 - 4
daemon/container_operations_unix.go

@@ -1107,7 +1107,16 @@ func killProcessDirectly(container *container.Container) error {
 }
 
 func getDevicesFromPath(deviceMapping containertypes.DeviceMapping) (devs []*configs.Device, err error) {
-	device, err := devices.DeviceFromPath(deviceMapping.PathOnHost, deviceMapping.CgroupPermissions)
+	resolvedPathOnHost := deviceMapping.PathOnHost
+
+	// check if it is a symbolic link
+	if src, e := os.Lstat(deviceMapping.PathOnHost); e == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink {
+		if linkedPathOnHost, e := os.Readlink(deviceMapping.PathOnHost); e == nil {
+			resolvedPathOnHost = linkedPathOnHost
+		}
+	}
+
+	device, err := devices.DeviceFromPath(resolvedPathOnHost, deviceMapping.CgroupPermissions)
 	// if there was no error, return the device
 	if err == nil {
 		device.Path = deviceMapping.PathInContainer
@@ -1119,10 +1128,10 @@ func getDevicesFromPath(deviceMapping containertypes.DeviceMapping) (devs []*con
 	if err == devices.ErrNotADevice {
 
 		// check if it is a directory
-		if src, e := os.Stat(deviceMapping.PathOnHost); e == nil && src.IsDir() {
+		if src, e := os.Stat(resolvedPathOnHost); e == nil && src.IsDir() {
 
 			// mount the internal devices recursively
-			filepath.Walk(deviceMapping.PathOnHost, func(dpath string, f os.FileInfo, e error) error {
+			filepath.Walk(resolvedPathOnHost, func(dpath string, f os.FileInfo, e error) error {
 				childDevice, e := devices.DeviceFromPath(dpath, deviceMapping.CgroupPermissions)
 				if e != nil {
 					// ignore the device
@@ -1130,7 +1139,7 @@ func getDevicesFromPath(deviceMapping containertypes.DeviceMapping) (devs []*con
 				}
 
 				// add the device to userSpecified devices
-				childDevice.Path = strings.Replace(dpath, deviceMapping.PathOnHost, deviceMapping.PathInContainer, 1)
+				childDevice.Path = strings.Replace(dpath, resolvedPathOnHost, deviceMapping.PathInContainer, 1)
 				devs = append(devs, childDevice)
 
 				return nil

+ 37 - 0
integration-cli/docker_cli_run_unix_test.go

@@ -919,3 +919,40 @@ func (s *DockerSuite) TestRunSeccompWithDefaultProfile(c *check.C) {
 	c.Assert(err, checker.NotNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, "unshare: unshare failed: Operation not permitted")
 }
+
+// TestRunDeviceSymlink checks run with device that follows symlink (#13840)
+func (s *DockerSuite) TestRunDeviceSymlink(c *check.C) {
+	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm, SameHostDaemon)
+	if _, err := os.Stat("/dev/zero"); err != nil {
+		c.Skip("Host does not have /dev/zero")
+	}
+
+	// Create a temporary directory to create symlink
+	tmpDir, err := ioutil.TempDir("", "docker_device_follow_symlink_tests")
+	c.Assert(err, checker.IsNil)
+
+	defer os.RemoveAll(tmpDir)
+
+	// Create a symbolic link to /dev/zero
+	symZero := filepath.Join(tmpDir, "zero")
+	err = os.Symlink("/dev/zero", symZero)
+	c.Assert(err, checker.IsNil)
+
+	// Create a temporary file "temp" inside tmpDir, write some data to "tmpDir/temp",
+	// then create a symlink "tmpDir/file" to the temporary file "tmpDir/temp".
+	tmpFile := filepath.Join(tmpDir, "temp")
+	err = ioutil.WriteFile(tmpFile, []byte("temp"), 0666)
+	c.Assert(err, checker.IsNil)
+	symFile := filepath.Join(tmpDir, "file")
+	err = os.Symlink(tmpFile, symFile)
+	c.Assert(err, checker.IsNil)
+
+	// md5sum of 'dd if=/dev/zero bs=4K count=8' is bb7df04e1b0a2570657527a7e108ae23
+	out, _ := dockerCmd(c, "run", "--device", symZero+":/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum")
+	c.Assert(strings.Trim(out, "\r\n"), checker.Contains, "bb7df04e1b0a2570657527a7e108ae23", check.Commentf("expected output bb7df04e1b0a2570657527a7e108ae23"))
+
+	// symlink "tmpDir/file" to a file "tmpDir/temp" will result in an error as it is not a device.
+	out, _, err = dockerCmdWithError("run", "--device", symFile+":/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum")
+	c.Assert(err, check.NotNil)
+	c.Assert(strings.Trim(out, "\r\n"), checker.Contains, "not a device node", check.Commentf("expected output 'not a device node'"))
+}