[lxc-devel] [lxd/master] Add recursive bind mount support

tych0 on Github lxc-bot at linuxcontainers.org
Fri Mar 11 22:59:29 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160311/e54c9502/attachment.bin>
-------------- next part --------------
From a9357506f4038d69fbee138d2dbbf7bea1e474dd Mon Sep 17 00:00:00 2001
From: Tycho Andersen <tycho.andersen at canonical.com>
Date: Fri, 11 Mar 2016 10:51:08 -0700
Subject: [PATCH 1/4] devices: allow recursive bind mounts

Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
---
 lxd/container.go       |  6 ++++++
 lxd/container_lxc.go   | 20 +++++++++++++++++---
 lxd/devices.go         |  5 ++++-
 lxd/nsexec.go          |  2 +-
 specs/configuration.md |  1 +
 5 files changed, 29 insertions(+), 5 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 57fa9ba..73b16d2 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -185,6 +185,8 @@ func containerValidDeviceConfigKey(t, k string) bool {
 			return true
 		case "source":
 			return true
+		case "recursive":
+			return true
 		default:
 			return false
 		}
@@ -262,6 +264,10 @@ func containerValidDevices(devices shared.Devices, profile bool, expanded bool)
 			if m["size"] != "" && m["path"] != "/" {
 				return fmt.Errorf("Only the root disk may have a size quota.")
 			}
+
+			if m["path"] == "/" && m["recursive"] != "" {
+				return fmt.Errorf("recursive only makes sense on non-rootfs entries")
+			}
 		} else if shared.StringInSlice(m["type"], []string{"unix-char", "unix-block"}) {
 			if m["path"] == "" {
 				return fmt.Errorf("Unix device entry is missing the required \"path\" property.")
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 017127d..ced4c26 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -758,6 +758,7 @@ func (c *containerLXC) initLXC() error {
 			// Various option checks
 			isOptional := m["optional"] == "1" || m["optional"] == "true"
 			isReadOnly := m["readonly"] == "1" || m["readonly"] == "true"
+			isRecursive := m["recursive"] == "1" || m["recursive"] == "true"
 			isFile := !shared.IsDir(srcPath) && !deviceIsBlockdev(srcPath)
 
 			// Deal with a rootfs
@@ -776,6 +777,7 @@ func (c *containerLXC) initLXC() error {
 					}
 				}
 			} else {
+				rbind := ""
 				options := []string{}
 				if isReadOnly {
 					options = append(options, "ro")
@@ -785,13 +787,17 @@ func (c *containerLXC) initLXC() error {
 					options = append(options, "optional")
 				}
 
+				if isRecursive {
+					rbind = "r"
+				}
+
 				if isFile {
 					options = append(options, "create=file")
 				} else {
 					options = append(options, "create=dir")
 				}
 
-				err = lxcSetConfigItem(cc, "lxc.mount.entry", fmt.Sprintf("%s %s none bind,%s", devPath, tgtPath, strings.Join(options, ",")))
+				err = lxcSetConfigItem(cc, "lxc.mount.entry", fmt.Sprintf("%s %s none %sbind,%s", devPath, tgtPath, rbind, strings.Join(options, ",")))
 				if err != nil {
 					return err
 				}
@@ -3638,6 +3644,7 @@ func (c *containerLXC) createDiskDevice(name string, m shared.Device) (string, e
 	// Check if read-only
 	isOptional := m["optional"] == "1" || m["optional"] == "true"
 	isReadOnly := m["readonly"] == "1" || m["readonly"] == "true"
+	isRecursive := m["recursive"] == "1" || m["recursive"] == "true"
 	isFile := !shared.IsDir(srcPath) && !deviceIsBlockdev(srcPath)
 
 	// Check if the source exists
@@ -3680,7 +3687,7 @@ func (c *containerLXC) createDiskDevice(name string, m shared.Device) (string, e
 	}
 
 	// Mount the fs
-	err := deviceMountDisk(srcPath, devPath, isReadOnly)
+	err := deviceMountDisk(srcPath, devPath, isReadOnly, isRecursive)
 	if err != nil {
 		return "", err
 	}
@@ -3694,15 +3701,22 @@ func (c *containerLXC) insertDiskDevice(name string, m shared.Device) error {
 		return fmt.Errorf("Can't insert device into stopped container")
 	}
 
+	isRecursive := m["recursive"] == "1" || m["recursive"] == "true"
+
 	// Create the device on the host
 	devPath, err := c.createDiskDevice(name, m)
 	if err != nil {
 		return fmt.Errorf("Failed to setup device: %s", err)
 	}
 
+	flags := syscall.MS_BIND
+	if isRecursive {
+		flags |= syscall.MS_REC
+	}
+
 	// Bind-mount it into the container
 	tgtPath := strings.TrimSuffix(m["path"], "/")
-	err = c.insertMount(devPath, tgtPath, "none", syscall.MS_BIND)
+	err = c.insertMount(devPath, tgtPath, "none", flags)
 	if err != nil {
 		return fmt.Errorf("Failed to add mount for device: %s", err)
 	}
diff --git a/lxd/devices.go b/lxd/devices.go
index 6b12c30..f22bf89 100644
--- a/lxd/devices.go
+++ b/lxd/devices.go
@@ -516,7 +516,7 @@ func deviceRemoveInterface(nic string) error {
 	return exec.Command("ip", "link", "del", nic).Run()
 }
 
-func deviceMountDisk(srcPath string, dstPath string, readonly bool) error {
+func deviceMountDisk(srcPath string, dstPath string, readonly bool, recursive bool) error {
 	var err error
 
 	// Prepare the mount flags
@@ -534,6 +534,9 @@ func deviceMountDisk(srcPath string, dstPath string, readonly bool) error {
 		}
 	} else {
 		flags |= syscall.MS_BIND
+		if recursive {
+			flags |= syscall.MS_REC
+		}
 	}
 
 	// Mount the filesystem
diff --git a/lxd/nsexec.go b/lxd/nsexec.go
index 03d6a55..b321191 100644
--- a/lxd/nsexec.go
+++ b/lxd/nsexec.go
@@ -283,7 +283,7 @@ void forkmount(char *buf, char *cur, ssize_t size) {
 		_exit(1);
 	}
 
-	if (mount(src, dest, "none", MS_MOVE, NULL) < 0) {
+	if (mount(src, dest, "none", MS_MOVE | MS_REC, NULL) < 0) {
 		fprintf(stderr, "Failed mounting %s onto %s: %s\n", src, dest, strerror(errno));
 		_exit(1);
 	}
diff --git a/specs/configuration.md b/specs/configuration.md
index b32ffce..1edfa7d 100644
--- a/specs/configuration.md
+++ b/specs/configuration.md
@@ -210,6 +210,7 @@ source          | string    | -                 | yes       | Path on the host,
 optional        | boolean   | false             | no        | Controls whether to fail if the source doesn't exist
 readonly        | boolean   | false             | no        | Controls whether to make the mount read-only
 size            | string    | -                 | no        | Disk size in bytes (supports kB, MB, GB, TB, PB and EB suffixes). This is only supported for the rootfs (/).
+recursive       | boolean   | false             | no        | Whether or not to make a bind mount recursive.
 
 If multiple disks, backed by the same block device, have I/O limits set,
 the average of the limits will be used.

From 5955b09adf542e5240dbe49357acc6edffcf8be8 Mon Sep 17 00:00:00 2001
From: Tycho Andersen <tycho.andersen at canonical.com>
Date: Fri, 11 Mar 2016 12:04:55 -0700
Subject: [PATCH 2/4] log the actual error as well as semantic error

Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
---
 lxd/container_lxc.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index ced4c26..31dd652 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -1417,13 +1417,13 @@ func (c *containerLXC) OnStop(target string) error {
 		// Clean all the unix devices
 		err = c.removeUnixDevices()
 		if err != nil {
-			shared.Log.Error("Unable to remove unix devices")
+			shared.Log.Error("Unable to remove unix devices", "err", err)
 		}
 
 		// Clean all the disk devices
 		err = c.removeDiskDevices()
 		if err != nil {
-			shared.Log.Error("Unable to remove disk devices")
+			shared.Log.Error("Unable to remove disk devices", "err", err)
 		}
 
 		// Reboot the container

From f4184e3f3e1302399e63208ae524ce76047c8243 Mon Sep 17 00:00:00 2001
From: Tycho Andersen <tycho.andersen at canonical.com>
Date: Fri, 11 Mar 2016 15:18:14 -0700
Subject: [PATCH 3/4] disk devices: remount the device as MS_SLAVE

It's annoying that these are mounted MS_SHARED for development (thanks
systemd), and in the case where we are doing recursive mounts, our
MNT_DETACH unmounts the mounts on the host. So let's remount them as
slaves.

Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
---
 lxd/devices.go | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/lxd/devices.go b/lxd/devices.go
index f22bf89..db018ca 100644
--- a/lxd/devices.go
+++ b/lxd/devices.go
@@ -544,6 +544,11 @@ func deviceMountDisk(srcPath string, dstPath string, readonly bool, recursive bo
 		return fmt.Errorf("Unable to mount %s at %s: %s", srcPath, dstPath, err)
 	}
 
+	flags = syscall.MS_REC | syscall.MS_SLAVE
+	if err = syscall.Mount("", dstPath, "", uintptr(flags), ""); err != nil {
+		return fmt.Errorf("unable to make mount %s private: %s", dstPath, err)
+	}
+
 	return nil
 }
 

From 0d84af5445071dfd83a1c306e9c3eedf9dae74bc Mon Sep 17 00:00:00 2001
From: Tycho Andersen <tycho.andersen at canonical.com>
Date: Fri, 11 Mar 2016 15:46:30 -0700
Subject: [PATCH 4/4] fix copy pasted error message

Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
---
 lxd/container_lxc.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 31dd652..938add6 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -3109,7 +3109,7 @@ func (c *containerLXC) removeMount(mount string) error {
 	pid := c.InitPID()
 	if pid == -1 {
 		// Container isn't running
-		return fmt.Errorf("Can't insert mount into stopped container")
+		return fmt.Errorf("Can't remove mount from stopped container")
 	}
 
 	// Remove the mount from the container


More information about the lxc-devel mailing list