[lxc-devel] [lxd/master] Add `shift` property on disk devices

stgraber on Github lxc-bot at linuxcontainers.org
Wed Jul 24 20:38:07 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 512 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190724/80f01c8a/attachment.bin>
-------------- next part --------------
From a2016b362a2058d50f0d58fbdd067d0ec4e7950d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 17 Jul 2019 23:35:06 -0400
Subject: [PATCH 1/6] api: Add container_disk_shift extension
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 doc/api-extensions.md | 3 +++
 shared/version/api.go | 1 +
 2 files changed, 4 insertions(+)

diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index 3bf3d1adad..3202ad3e1b 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -803,3 +803,6 @@ Adds support for specifying User, Group and Cwd during `POST /1.0/containers/NAM
 Adds the `security.syscalls.intercept.\*` configuration keys to control
 what system calls will be interecepted by LXD and processed with
 elevated permissions.
+
+## container\_disk\_shift
+Adds the `shift` property on `disk` devices which controls the use of the shiftfs overlay.
diff --git a/shared/version/api.go b/shared/version/api.go
index 99fef0b913..fdf8a5921f 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -160,6 +160,7 @@ var APIExtensions = []string{
 	"resources_v2",
 	"container_exec_user_group_cwd",
 	"container_syscall_intercept",
+	"container_disk_shift",
 }
 
 // APIExtensionsCount returns the number of available API extensions.

From 1df04cfd26b3d975788bba99c59af6da2222da3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 17 Jul 2019 23:35:44 -0400
Subject: [PATCH 2/6] doc: Add shift option to disk device
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 doc/containers.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/doc/containers.md b/doc/containers.md
index 4c33c2c254..c3741f416b 100644
--- a/doc/containers.md
+++ b/doc/containers.md
@@ -493,6 +493,7 @@ size            | string    | -                 | no        | Disk size in bytes
 recursive       | boolean   | false             | no        | Whether or not to recursively mount the source path
 pool            | string    | -                 | no        | The storage pool the disk device belongs to. This is only applicable for storage volumes managed by LXD.
 propagation     | string    | -                 | no        | Controls how a bind-mount is shared between the container and the host. (Can be one of `private`, the default, or `shared`, `slave`, `unbindable`,  `rshared`, `rslave`, `runbindable`,  `rprivate`. Please see the Linux Kernel [shared subtree](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt) documentation for a full explanation)
+shift           | boolean   | false             | no        | Setup a shifting overlay to translate the source uid/gid to match the container
 
 If multiple disks, backed by the same block device, have I/O limits set,
 the average of the limits will be used.

From 3f04a1e092074fdd5a62d68fa55b2612d910f438 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 17 Jul 2019 23:40:42 -0400
Subject: [PATCH 3/6] lxd/container: Don't validate liblxc version during
 config parsing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/container.go     | 5 -----
 lxd/container_lxc.go | 4 ++++
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index a690fff70c..d6e1cb8a5e 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -22,7 +22,6 @@ import (
 	"github.com/lxc/lxd/lxd/state"
 	"github.com/lxc/lxd/lxd/sys"
 	"github.com/lxc/lxd/lxd/task"
-	"github.com/lxc/lxd/lxd/util"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/idmap"
@@ -515,10 +514,6 @@ func containerValidDevices(cluster *db.Cluster, devices config.Devices, profile
 			}
 
 			if m["propagation"] != "" {
-				if !util.RuntimeLiblxcVersionAtLeast(3, 0, 0) {
-					return fmt.Errorf("liblxc 3.0 is required for mount propagation configuration")
-				}
-
 				if !shared.StringInSlice(m["propagation"], []string{"private", "shared", "slave", "unbindable", "rprivate", "rshared", "rslave", "runbindable"}) {
 					return fmt.Errorf("Invalid propagation mode '%s'", m["propagation"])
 				}
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 1b5ca2ccd0..31354ce189 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -1843,6 +1843,10 @@ func (c *containerLXC) initLXC(config bool) error {
 				}
 
 				if m["propagation"] != "" {
+					if !util.RuntimeLiblxcVersionAtLeast(3, 0, 0) {
+						return fmt.Errorf("liblxc 3.0 is required for mount propagation configuration")
+					}
+
 					options = append(options, m["propagation"])
 				}
 

From ef59832e811f83d2771764e5dc8cc037ea8c889b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 17 Jul 2019 23:59:28 -0400
Subject: [PATCH 4/6] lxd: Add shift disk option
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/container.go | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/lxd/container.go b/lxd/container.go
index d6e1cb8a5e..4d4df4b91d 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -185,6 +185,8 @@ func containerValidDeviceConfigKey(t, k string) bool {
 			return true
 		case "propagation":
 			return true
+		case "shift":
+			return true
 		default:
 			return false
 		}
@@ -518,6 +520,12 @@ func containerValidDevices(cluster *db.Cluster, devices config.Devices, profile
 					return fmt.Errorf("Invalid propagation mode '%s'", m["propagation"])
 				}
 			}
+
+			if m["shift"] != "" {
+				if m["pool"] != "" {
+					return fmt.Errorf("The \"shift\" property cannot be used with custom storage volumes")
+				}
+			}
 		} else if shared.StringInSlice(m["type"], []string{"unix-char", "unix-block"}) {
 			if m["source"] == "" && m["path"] == "" {
 				return fmt.Errorf("Unix device entry is missing the required \"source\" or \"path\" property")

From 12f16a412240f03222860bf31be7a38ca211a473 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Thu, 18 Jul 2019 01:23:03 -0400
Subject: [PATCH 5/6] lxd/containers: Add shiftfs for disk devices
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/container_lxc.go  | 44 ++++++++++++++++++++++++++++++++++---------
 lxd/main_forkmount.go | 24 ++++++++++++++++++++++-
 2 files changed, 58 insertions(+), 10 deletions(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 31354ce189..003b453e9c 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -1828,6 +1828,23 @@ func (c *containerLXC) initLXC(config bool) error {
 					}
 				}
 			} else {
+				if shared.IsTrue(m["shift"]) {
+					err = lxcSetConfigItem(cc, "lxc.hook.pre-start", fmt.Sprintf("/bin/mount -t shiftfs -o mark,passthrough=3 %s %s", sourceDevPath, sourceDevPath))
+					if err != nil {
+						return err
+					}
+
+					err = lxcSetConfigItem(cc, "lxc.hook.pre-mount", fmt.Sprintf("/bin/mount -t shiftfs -o passthrough=3 %s %s", sourceDevPath, sourceDevPath))
+					if err != nil {
+						return err
+					}
+
+					err = lxcSetConfigItem(cc, "lxc.hook.start-host", fmt.Sprintf("/bin/umount -l %s", sourceDevPath))
+					if err != nil {
+						return err
+					}
+				}
+
 				rbind := ""
 				options := []string{}
 				if isReadOnly {
@@ -5130,7 +5147,7 @@ func (c *containerLXC) Update(args db.ContainerArgs, userRequested bool) error {
 				}
 			} else if key == "security.devlxd" {
 				if value == "" || shared.IsTrue(value) {
-					err = c.insertMount(shared.VarPath("devlxd"), "/dev/lxd", "none", unix.MS_BIND)
+					err = c.insertMount(shared.VarPath("devlxd"), "/dev/lxd", "none", unix.MS_BIND, false)
 					if err != nil {
 						return err
 					}
@@ -7274,7 +7291,7 @@ func (c *containerLXC) StorageStop() (bool, error) {
 }
 
 // Mount handling
-func (c *containerLXC) insertMountLXD(source, target, fstype string, flags int, mntnsPID int) error {
+func (c *containerLXC) insertMountLXD(source, target, fstype string, flags int, mntnsPID int, shiftfs bool) error {
 	pid := mntnsPID
 	if pid <= 0 {
 		// Get the init PID
@@ -7311,11 +7328,20 @@ func (c *containerLXC) insertMountLXD(source, target, fstype string, flags int,
 	}
 	defer unix.Unmount(tmpMount, unix.MNT_DETACH)
 
+	// Setup host side shiftfs as needed
+	if shiftfs {
+		err = unix.Mount(tmpMount, tmpMount, "shiftfs", 0, "mark,passthrough=3")
+		if err != nil {
+			return fmt.Errorf("Failed to setup host side shiftfs mount: %s", err)
+		}
+		defer unix.Unmount(tmpMount, unix.MNT_DETACH)
+	}
+
 	// Move the mount inside the container
 	mntsrc := filepath.Join("/dev/.lxd-mounts", filepath.Base(tmpMount))
 	pidStr := fmt.Sprintf("%d", pid)
 
-	_, err = shared.RunCommand(c.state.OS.ExecPath, "forkmount", "lxd-mount", pidStr, mntsrc, target)
+	_, err = shared.RunCommand(c.state.OS.ExecPath, "forkmount", "lxd-mount", pidStr, mntsrc, target, fmt.Sprintf("%v", shiftfs))
 	if err != nil {
 		return err
 	}
@@ -7342,12 +7368,12 @@ func (c *containerLXC) insertMountLXC(source, target, fstype string, flags int)
 	return nil
 }
 
-func (c *containerLXC) insertMount(source, target, fstype string, flags int) error {
-	if c.state.OS.LXCFeatures["mount_injection_file"] {
+func (c *containerLXC) insertMount(source, target, fstype string, flags int, shiftfs bool) error {
+	if c.state.OS.LXCFeatures["mount_injection_file"] && !shiftfs {
 		return c.insertMountLXC(source, target, fstype, flags)
 	}
 
-	return c.insertMountLXD(source, target, fstype, flags, -1)
+	return c.insertMountLXD(source, target, fstype, flags, -1, shiftfs)
 }
 
 func (c *containerLXC) removeMount(mount string) error {
@@ -7549,7 +7575,7 @@ func (c *containerLXC) insertUnixDevice(prefix string, m config.Device, defaultM
 	tgtPath := paths[1]
 
 	// Bind-mount it into the container
-	err = c.insertMount(devPath, tgtPath, "none", unix.MS_BIND)
+	err = c.insertMount(devPath, tgtPath, "none", unix.MS_BIND, false)
 	if err != nil {
 		return fmt.Errorf("Failed to add mount for device: %s", err)
 	}
@@ -7642,7 +7668,7 @@ func (c *containerLXC) InsertSeccompUnixDevice(prefix string, m config.Device, p
 
 	// Bind-mount it into the container
 	defer os.Remove(devPath)
-	return c.insertMountLXD(devPath, tgtPath, "none", unix.MS_BIND, pid)
+	return c.insertMountLXD(devPath, tgtPath, "none", unix.MS_BIND, pid, false)
 }
 
 func (c *containerLXC) insertUnixDeviceNum(name string, m config.Device, major int, minor int, path string, defaultMode bool) error {
@@ -9334,7 +9360,7 @@ func (c *containerLXC) insertDiskDevice(name string, m config.Device) error {
 
 	// Bind-mount it into the container
 	destPath := strings.TrimSuffix(m["path"], "/")
-	err = c.insertMount(devPath, destPath, "none", flags)
+	err = c.insertMount(devPath, destPath, "none", flags, shared.IsTrue(m["shift"]))
 	if err != nil {
 		return fmt.Errorf("Failed to add mount for device: %s", err)
 	}
diff --git a/lxd/main_forkmount.go b/lxd/main_forkmount.go
index 5b0209895b..c42bfa1f2d 100644
--- a/lxd/main_forkmount.go
+++ b/lxd/main_forkmount.go
@@ -128,7 +128,7 @@ void create(char *src, char *dest)
 }
 
 void do_lxd_forkmount(pid_t pid) {
-	char *src, *dest, *opts;
+	char *src, *dest, *opts, *shiftfs;
 
 	attach_userns(pid);
 
@@ -139,6 +139,7 @@ void do_lxd_forkmount(pid_t pid) {
 
 	src = advance_arg(true);
 	dest = advance_arg(true);
+	shiftfs = advance_arg(true);
 
 	create(src, dest);
 
@@ -152,14 +153,35 @@ void do_lxd_forkmount(pid_t pid) {
 		_exit(1);
 	}
 
+	if (strcmp(shiftfs, "true") == 0) {
+		// Setup shiftfs inside the container
+		if (mount(src, src, "shiftfs", 0, "passthrough=3") < 0) {
+			fprintf(stderr, "Failed shiftfs setup for %s: %s\n", src, strerror(errno));
+			_exit(1);
+		}
+	}
+
 	// Here, we always move recursively, because we sometimes allow
 	// recursive mounts. If the mount has no kids then it doesn't matter,
 	// but if it does, we want to move those too.
 	if (mount(src, dest, "none", MS_MOVE | MS_REC, NULL) < 0) {
+		// If using shiftfs, undo the shiftfs mount
+		if (strcmp(shiftfs, "true") == 0) {
+			umount2(src, MNT_DETACH);
+		}
+
 		fprintf(stderr, "Failed mounting %s onto %s: %s\n", src, dest, strerror(errno));
 		_exit(1);
 	}
 
+	if (strcmp(shiftfs, "true") == 0) {
+		// Clear source mount as target is now in place
+		if (umount2(src, MNT_DETACH) < 0) {
+			fprintf(stderr, "Failed shiftfs source unmount for %s: %s\n", src, strerror(errno));
+			_exit(1);
+		}
+	}
+
 	_exit(0);
 }
 

From 6ff7c7c1d0f94e8293116d70c35c6ce5286ee843 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 24 Jul 2019 16:29:06 -0400
Subject: [PATCH 6/6] tests: Test the shift disk property
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 test/main.sh                          |  1 +
 test/suites/container_devices_disk.sh | 32 +++++++++++++++++++++++++++
 2 files changed, 33 insertions(+)
 create mode 100644 test/suites/container_devices_disk.sh

diff --git a/test/main.sh b/test/main.sh
index 1e1e1b0578..6e0b6369f9 100755
--- a/test/main.sh
+++ b/test/main.sh
@@ -188,6 +188,7 @@ run_test test_projects_images "images inside projects"
 run_test test_projects_images_default "images from the global default project"
 run_test test_projects_storage "projects and storage pools"
 run_test test_projects_network "projects and networks"
+run_test test_container_devices_disk "container devices - disk"
 run_test test_container_devices_nic_p2p "container devices - nic - p2p"
 run_test test_container_devices_nic_bridged "container devices - nic - bridged"
 run_test test_container_devices_nic_bridged_filtering "container devices - nic - bridged - filtering"
diff --git a/test/suites/container_devices_disk.sh b/test/suites/container_devices_disk.sh
new file mode 100644
index 0000000000..e0368b25eb
--- /dev/null
+++ b/test/suites/container_devices_disk.sh
@@ -0,0 +1,32 @@
+test_container_devices_disk() {
+  ensure_import_testimage
+  ensure_has_localhost_remote "${LXD_ADDR}"
+
+  lxc launch testimage foo
+
+  test_container_devices_disk_shift
+
+  lxc delete -f foo
+}
+
+test_container_devices_disk_shift() {
+  if ! grep -q shiftfs /proc/filesystems; then
+    return
+  fi
+
+  mkdir -p "${TEST_DIR}/shift-source"
+  touch "${TEST_DIR}/shift-source/a"
+  chown 123:456 "${TEST_DIR}/shift-source/a"
+
+  lxc config device add foo shiftfs disk source="${TEST_DIR}/shift-source" path=/mnt
+  [ "$(lxc exec foo -- stat /mnt/a -c '%u:%g')" = "65534:65534" ] || false
+  lxc config device remove foo shiftfs
+
+  lxc config device add foo shiftfs disk source="${TEST_DIR}/shift-source" path=/mnt shift=true
+  [ "$(lxc exec foo -- stat /mnt/a -c '%u:%g')" = "123:456" ] || false
+
+  lxc stop foo -f
+  lxc start foo
+  [ "$(lxc exec foo -- stat /mnt/a -c '%u:%g')" = "123:456" ] || false
+  lxc stop foo -f
+}


More information about the lxc-devel mailing list