[lxc-devel] [lxd/master] idmap: shift ro btrfs subvolumes

brauner on Github lxc-bot at linuxcontainers.org
Wed Jun 5 12:26:56 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 655 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190605/d7ffc6b7/attachment.bin>
-------------- next part --------------
From 86acb92d7eb82c71402d6313800b871baef2257a Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 5 Jun 2019 14:06:30 +0200
Subject: [PATCH] idmap: shift ro btrfs subvolumes

This lets LXD shift ro btrfs subvolumes.

When users create subvolumes in btrfs backed containers they can make
them ro. In this case LXD will fail to shift the rootfs. To handle this
case we temporarily make these subvolumes rw, shift them, and make them
ro again.

Closes #5818.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/container_lxc.go | 47 ++++++++++++++++++++++++++++++++++++++++++++
 lxd/storage_btrfs.go | 19 ++++++++++++++++++
 test/suites/idmap.sh |  6 ++++++
 3 files changed, 72 insertions(+)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 46f0f85a36..323c4bf946 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -2065,6 +2065,43 @@ func (c *containerLXC) setupUnixDevice(prefix string, dev types.Device, major in
 	return lxcSetConfigItem(c.c, "lxc.mount.entry", val)
 }
 
+func shiftBtrfsRootfs(path string, diskIdmap *idmap.IdmapSet, shift bool) error {
+	var err error
+	roSubvols := []string{}
+	subvols, _ := btrfsSubVolumesGet(path)
+	sort.Sort(sort.StringSlice(subvols))
+	for _, subvol := range subvols {
+		subvol = filepath.Join(path, subvol)
+
+		if !btrfsSubVolumeIsRo(subvol) {
+			continue
+		}
+
+		roSubvols = append(roSubvols, subvol)
+		btrfsSubVolumeMakeRw(subvol)
+	}
+
+	if shift {
+		err = diskIdmap.ShiftRootfs(path, nil)
+	} else {
+		err = diskIdmap.UnshiftRootfs(path, nil)
+	}
+
+	for _, subvol := range roSubvols {
+		btrfsSubVolumeMakeRo(subvol)
+	}
+
+	return err
+}
+
+func ShiftBtrfsRootfs(path string, diskIdmap *idmap.IdmapSet) error {
+	return shiftBtrfsRootfs(path, diskIdmap, true)
+}
+
+func UnshiftBtrfsRootfs(path string, diskIdmap *idmap.IdmapSet) error {
+	return shiftBtrfsRootfs(path, diskIdmap, false)
+}
+
 // Start functions
 func (c *containerLXC) startCommon() (string, error) {
 	// Load the go-lxc struct
@@ -2179,6 +2216,8 @@ func (c *containerLXC) startCommon() (string, error) {
 		if diskIdmap != nil {
 			if c.Storage().GetStorageType() == storageTypeZfs {
 				err = diskIdmap.UnshiftRootfs(c.RootfsPath(), zfsIdmapSetSkipper)
+			} else if c.Storage().GetStorageType() == storageTypeBtrfs {
+				err = UnshiftBtrfsRootfs(c.RootfsPath(), diskIdmap)
 			} else {
 				err = diskIdmap.UnshiftRootfs(c.RootfsPath(), nil)
 			}
@@ -2193,6 +2232,8 @@ func (c *containerLXC) startCommon() (string, error) {
 		if nextIdmap != nil && !c.state.OS.Shiftfs {
 			if c.Storage().GetStorageType() == storageTypeZfs {
 				err = nextIdmap.ShiftRootfs(c.RootfsPath(), zfsIdmapSetSkipper)
+			} else if c.Storage().GetStorageType() == storageTypeBtrfs {
+				err = ShiftBtrfsRootfs(c.RootfsPath(), diskIdmap)
 			} else {
 				err = nextIdmap.ShiftRootfs(c.RootfsPath(), nil)
 			}
@@ -5376,6 +5417,8 @@ func (c *containerLXC) Export(w io.Writer, properties map[string]string) error {
 
 		if c.Storage().GetStorageType() == storageTypeZfs {
 			err = idmap.UnshiftRootfs(c.RootfsPath(), zfsIdmapSetSkipper)
+		} else if c.Storage().GetStorageType() == storageTypeBtrfs {
+			err = UnshiftBtrfsRootfs(c.RootfsPath(), idmap)
 		} else {
 			err = idmap.UnshiftRootfs(c.RootfsPath(), nil)
 		}
@@ -5386,6 +5429,8 @@ func (c *containerLXC) Export(w io.Writer, properties map[string]string) error {
 
 		if c.Storage().GetStorageType() == storageTypeZfs {
 			defer idmap.ShiftRootfs(c.RootfsPath(), zfsIdmapSetSkipper)
+		} else if c.Storage().GetStorageType() == storageTypeBtrfs {
+			defer ShiftBtrfsRootfs(c.RootfsPath(), idmap)
 		} else {
 			defer idmap.ShiftRootfs(c.RootfsPath(), nil)
 		}
@@ -5701,6 +5746,8 @@ func (c *containerLXC) Migrate(args *CriuMigrationArgs) error {
 
 			if c.Storage().GetStorageType() == storageTypeZfs {
 				err = idmapset.ShiftRootfs(args.stateDir, zfsIdmapSetSkipper)
+			} else if c.Storage().GetStorageType() == storageTypeBtrfs {
+				err = ShiftBtrfsRootfs(args.stateDir, idmapset)
 			} else {
 				err = idmapset.ShiftRootfs(args.stateDir, nil)
 			}
diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go
index c186549892..bc07a8e895 100644
--- a/lxd/storage_btrfs.go
+++ b/lxd/storage_btrfs.go
@@ -2391,6 +2391,25 @@ func isOnBtrfs(path string) bool {
 	return true
 }
 
+func btrfsSubVolumeIsRo(path string) bool {
+	output, err := shared.RunCommand("btrfs", "property", "get", "-ts", path)
+	if err != nil {
+		return false
+	}
+
+	return strings.HasPrefix(string(output), "ro=true")
+}
+
+func btrfsSubVolumeMakeRo(path string) error {
+	_, err := shared.RunCommand("btrfs", "property", "set", "-ts", path, "ro", "true")
+	return err
+}
+
+func btrfsSubVolumeMakeRw(path string) error {
+	_, err := shared.RunCommand("btrfs", "property", "set", "-ts", path, "ro", "false")
+	return err
+}
+
 func btrfsSubVolumesGet(path string) ([]string, error) {
 	result := []string{}
 
diff --git a/test/suites/idmap.sh b/test/suites/idmap.sh
index 2d8be7d8f9..18f01f95a4 100644
--- a/test/suites/idmap.sh
+++ b/test/suites/idmap.sh
@@ -46,6 +46,12 @@ test_idmap() {
 
   # Check a normal, non-isolated container (full LXD id range)
   lxc launch testimage idmap
+
+  lxd_backend=$(storage_backend "$LXD_DIR")
+  if [ "$lxd_backend" = "btrfs" ]; then
+    lxc exec idmap -- btrfs subvolume create -r /aaa || true
+  fi
+
   [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $2}')" = "${UID_BASE}" ]
   [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $2}')" = "${GID_BASE}" ]
   [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $3}')" = "${UIDs}" ]


More information about the lxc-devel mailing list