[lxc-devel] [lxd/master] dir: use bind-mount for pools outside ${LXD_DIR}

brauner on Github lxc-bot at linuxcontainers.org
Thu Aug 31 19:14:41 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 364 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170831/78a0b83e/attachment.bin>
-------------- next part --------------
From 889adbb2af36dc51c6296a4ee5df66127af39606 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 31 Aug 2017 20:54:25 +0200
Subject: [PATCH 1/2] dir: use bind-mount for pools outside ${LXD_DIR}

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/storage_dir.go | 128 ++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 117 insertions(+), 11 deletions(-)

diff --git a/lxd/storage_dir.go b/lxd/storage_dir.go
index 5aa37c1d5..b5a027880 100644
--- a/lxd/storage_dir.go
+++ b/lxd/storage_dir.go
@@ -5,6 +5,7 @@ import (
 	"os"
 	"path/filepath"
 	"strings"
+	"syscall"
 
 	"github.com/gorilla/websocket"
 
@@ -58,14 +59,11 @@ func (s *storageDir) StoragePoolCreate() error {
 		source = filepath.Join(shared.VarPath("storage-pools"), s.pool.Name)
 		s.pool.Config["source"] = source
 	} else {
-		cleanSource := filepath.Clean(source)
-		lxdDir := shared.VarPath()
-		poolMntPoint := getStoragePoolMountPoint(s.pool.Name)
-		if strings.HasPrefix(cleanSource, lxdDir) && cleanSource != poolMntPoint {
-			return fmt.Errorf("DIR storage pool requests in LXD directory \"%s\" are only valid under \"%s\"\n(e.g. source=%s)", shared.VarPath(), shared.VarPath("storage-pools"), poolMntPoint)
-		}
+		source = filepath.Clean(source)
 	}
 
+	poolMntPoint := getStoragePoolMountPoint(s.pool.Name)
+
 	revert := true
 	if !shared.PathExists(source) {
 		err := os.MkdirAll(source, 0711)
@@ -89,14 +87,17 @@ func (s *storageDir) StoragePoolCreate() error {
 		}
 	}
 
-	prefix := shared.VarPath("storage-pools")
-	if !strings.HasPrefix(source, prefix) {
-		// symlink from storage-pools to pool x
-		storagePoolSymlink := getStoragePoolMountPoint(s.pool.Name)
-		err := os.Symlink(source, storagePoolSymlink)
+	if !shared.PathExists(poolMntPoint) {
+		err := os.MkdirAll(poolMntPoint, 0711)
 		if err != nil {
 			return err
 		}
+		defer func() {
+			if !revert {
+				return
+			}
+			os.Remove(poolMntPoint)
+		}()
 	}
 
 	err := s.StoragePoolCheck()
@@ -104,6 +105,11 @@ func (s *storageDir) StoragePoolCreate() error {
 		return err
 	}
 
+	_, err = s.StoragePoolMount()
+	if err != nil {
+		return err
+	}
+
 	revert = false
 
 	logger.Infof("Created DIR storage pool \"%s\".", s.pool.Name)
@@ -118,6 +124,11 @@ func (s *storageDir) StoragePoolDelete() error {
 		return fmt.Errorf("no \"source\" property found for the storage pool")
 	}
 
+	_, err := s.StoragePoolUmount()
+	if err != nil {
+		return err
+	}
+
 	if shared.PathExists(source) {
 		err := os.RemoveAll(source)
 		if err != nil {
@@ -143,10 +154,105 @@ func (s *storageDir) StoragePoolDelete() error {
 }
 
 func (s *storageDir) StoragePoolMount() (bool, error) {
+	source := s.pool.Config["source"]
+	if source == "" {
+		return false, fmt.Errorf("no \"source\" property found for the storage pool")
+	}
+	cleanSource := filepath.Clean(source)
+	poolMntPoint := getStoragePoolMountPoint(s.pool.Name)
+	if cleanSource == poolMntPoint {
+		return true, nil
+	}
+
+	logger.Debugf("Mounting DIR storage pool \"%s\".", s.pool.Name)
+
+	poolMountLockID := getPoolMountLockID(s.pool.Name)
+	lxdStorageMapLock.Lock()
+	if waitChannel, ok := lxdStorageOngoingOperationMap[poolMountLockID]; ok {
+		lxdStorageMapLock.Unlock()
+		if _, ok := <-waitChannel; ok {
+			logger.Warnf("Received value over semaphore. This should not have happened.")
+		}
+		// Give the benefit of the doubt and assume that the other
+		// thread actually succeeded in mounting the storage pool.
+		return false, nil
+	}
+
+	lxdStorageOngoingOperationMap[poolMountLockID] = make(chan bool)
+	lxdStorageMapLock.Unlock()
+
+	removeLockFromMap := func() {
+		lxdStorageMapLock.Lock()
+		if waitChannel, ok := lxdStorageOngoingOperationMap[poolMountLockID]; ok {
+			close(waitChannel)
+			delete(lxdStorageOngoingOperationMap, poolMountLockID)
+		}
+		lxdStorageMapLock.Unlock()
+	}
+	defer removeLockFromMap()
+
+	mountSource := cleanSource
+	mountFlags := syscall.MS_BIND
+
+	err := syscall.Mount(mountSource, poolMntPoint, "", uintptr(mountFlags), "")
+	if err != nil {
+		logger.Errorf(`Failed to mount DIR storage pool "%s" onto `+
+			`"%s": %s`, mountSource, poolMntPoint, err)
+		return false, err
+	}
+
+	logger.Debugf("Mounted DIR storage pool \"%s\".", s.pool.Name)
+
 	return true, nil
 }
 
 func (s *storageDir) StoragePoolUmount() (bool, error) {
+	source := s.pool.Config["source"]
+	if source == "" {
+		return false, fmt.Errorf("no \"source\" property found for the storage pool")
+	}
+	cleanSource := filepath.Clean(source)
+	poolMntPoint := getStoragePoolMountPoint(s.pool.Name)
+	if cleanSource == poolMntPoint {
+		return true, nil
+	}
+
+	logger.Debugf("Unmounting DIR storage pool \"%s\".", s.pool.Name)
+
+	poolUmountLockID := getPoolUmountLockID(s.pool.Name)
+	lxdStorageMapLock.Lock()
+	if waitChannel, ok := lxdStorageOngoingOperationMap[poolUmountLockID]; ok {
+		lxdStorageMapLock.Unlock()
+		if _, ok := <-waitChannel; ok {
+			logger.Warnf("Received value over semaphore. This should not have happened.")
+		}
+		// Give the benefit of the doubt and assume that the other
+		// thread actually succeeded in unmounting the storage pool.
+		return false, nil
+	}
+
+	lxdStorageOngoingOperationMap[poolUmountLockID] = make(chan bool)
+	lxdStorageMapLock.Unlock()
+
+	removeLockFromMap := func() {
+		lxdStorageMapLock.Lock()
+		if waitChannel, ok := lxdStorageOngoingOperationMap[poolUmountLockID]; ok {
+			close(waitChannel)
+			delete(lxdStorageOngoingOperationMap, poolUmountLockID)
+		}
+		lxdStorageMapLock.Unlock()
+	}
+
+	defer removeLockFromMap()
+
+	if shared.IsMountPoint(poolMntPoint) {
+		err := syscall.Unmount(poolMntPoint, 0)
+		if err != nil {
+			return false, err
+		}
+	}
+
+	logger.Debugf("Unmounted DIR pool \"%s\".", s.pool.Name)
 	return true, nil
 }
 

From 36731caa66c5f9b10a6caa07cb479040d75e5004 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 31 Aug 2017 21:02:45 +0200
Subject: [PATCH 2/2] patches: make dir pool use bind-mount

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/patches.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/lxd/patches.go b/lxd/patches.go
index 29aa3df47..94540d73d 100644
--- a/lxd/patches.go
+++ b/lxd/patches.go
@@ -48,6 +48,7 @@ var patches = []patch{
 	{name: "storage_zfs_noauto", run: patchStorageZFSnoauto},
 	{name: "storage_zfs_volume_size", run: patchStorageZFSVolumeSize},
 	{name: "network_dnsmasq_hosts", run: patchNetworkDnsmasqHosts},
+	{name: "storage_api_dir_bind_mount", run: patchStorageApiDirBindMount},
 }
 
 type patch struct {
@@ -2618,3 +2619,68 @@ func patchNetworkDnsmasqHosts(name string, d *Daemon) error {
 
 	return nil
 }
+
+func patchStorageApiDirBindMount(name string, d *Daemon) error {
+	pools, err := db.StoragePools(d.db)
+	if err != nil && err == db.NoSuchObjectError {
+		// No pool was configured in the previous update. So we're on a
+		// pristine LXD instance.
+		return nil
+	} else if err != nil {
+		// Database is screwed.
+		logger.Errorf("Failed to query database: %s", err)
+		return err
+	}
+
+	for _, poolName := range pools {
+		_, pool, err := db.StoragePoolGet(d.db, poolName)
+		if err != nil {
+			logger.Errorf("Failed to query database: %s", err)
+			return err
+		}
+
+		// We only care about dir
+		if pool.Driver != "dir" {
+			continue
+		}
+
+		source := pool.Config["source"]
+		if source == "" {
+			msg := fmt.Sprintf(`No "source" property for storage `+
+				`pool "%s" found`, poolName)
+			logger.Errorf(msg)
+			return fmt.Errorf(msg)
+		}
+		cleanSource := filepath.Clean(source)
+		poolMntPoint := getStoragePoolMountPoint(poolName)
+
+		if cleanSource == poolName {
+			continue
+		}
+
+		if shared.PathExists(poolMntPoint) {
+			err := os.Remove(poolMntPoint)
+			if err != nil {
+				return err
+			}
+		}
+
+		err = os.MkdirAll(poolMntPoint, 0711)
+		if err != nil {
+			return err
+		}
+
+		mountSource := cleanSource
+		mountFlags := syscall.MS_BIND
+
+		err = syscall.Mount(mountSource, poolMntPoint, "", uintptr(mountFlags), "")
+		if err != nil {
+			logger.Errorf(`Failed to mount DIR storage pool "%s" onto `+
+					`"%s": %s`, mountSource, poolMntPoint, err)
+			return err
+		}
+
+	}
+
+	return nil
+}


More information about the lxc-devel mailing list