The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/4168

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Move the functions `Unpack` and `DownloadFileSha256`, along with some dependencies, to shared.
From 37aeeaadad4599402ba68eb66aee9214453e8aa5 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <[email protected]>
Date: Fri, 12 Jan 2018 12:27:57 +0100
Subject: [PATCH 1/2] lxd,shared: move archive functions to shared

This moves the Unpack() function, as well as some of its dependent code
to shared.

Signed-off-by: Thomas Hipp <[email protected]>
---
 lxd/container_lxc.go     |   6 +--
 lxd/devices.go           |  34 -------------
 lxd/images.go            | 122 +++--------------------------------------------
 lxd/main_test.go         |   2 +-
 lxd/patches.go           |  16 +++----
 lxd/storage.go           |  66 +++++++++++--------------
 lxd/storage_btrfs.go     |   4 +-
 lxd/storage_ceph.go      |   4 +-
 lxd/storage_dir.go       |   4 +-
 lxd/storage_lvm.go       |   4 +-
 lxd/storage_lvm_utils.go |   2 +-
 lxd/storage_migration.go |   2 +-
 lxd/storage_mock.go      |   3 +-
 lxd/storage_shared.go    |   4 +-
 lxd/storage_zfs.go       |   4 +-
 shared/archive.go        | 120 ++++++++++++++++++++++++++++++++++++++++++++++
 shared/devices.go        |  42 ++++++++++++++++
 shared/storage.go        |  13 +++++
 18 files changed, 236 insertions(+), 216 deletions(-)
 create mode 100644 shared/archive.go
 create mode 100644 shared/devices.go
 create mode 100644 shared/storage.go

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index f72a1aedc..97ce25f66 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -1168,7 +1168,7 @@ func (c *containerLXC) initLXC(config bool) error {
                                        return err
                                }
 
-                               memoryTotal, err := deviceTotalMemory()
+                               memoryTotal, err := shared.DeviceTotalMemory()
                                if err != nil {
                                        return err
                                }
@@ -3846,7 +3846,7 @@ func (c *containerLXC) Update(args db.ContainerArgs, 
userRequested bool) error {
                                                return err
                                        }
 
-                                       memoryTotal, err := deviceTotalMemory()
+                                       memoryTotal, err := 
shared.DeviceTotalMemory()
                                        if err != nil {
                                                return err
                                        }
@@ -5807,7 +5807,7 @@ func (c *containerLXC) StorageStart() (bool, error) {
 
        isOurOperation, err := c.StorageStartSensitive()
        // Remove this as soon as zfs is fixed
-       if c.storage.GetStorageType() == storageTypeZfs && err == syscall.EBUSY 
{
+       if c.storage.GetStorageType() == shared.StorageTypeZfs && err == 
syscall.EBUSY {
                return isOurOperation, nil
        }
 
diff --git a/lxd/devices.go b/lxd/devices.go
index f29b2df24..ec11a7de9 100644
--- a/lxd/devices.go
+++ b/lxd/devices.go
@@ -1067,40 +1067,6 @@ func deviceParseCPU(cpuAllowance string, cpuPriority 
string) (string, string, st
        return fmt.Sprintf("%d", cpuShares), cpuCfsQuota, cpuCfsPeriod, nil
 }
 
-func deviceTotalMemory() (int64, error) {
-       // Open /proc/meminfo
-       f, err := os.Open("/proc/meminfo")
-       if err != nil {
-               return -1, err
-       }
-       defer f.Close()
-
-       // Read it line by line
-       scan := bufio.NewScanner(f)
-       for scan.Scan() {
-               line := scan.Text()
-
-               // We only care about MemTotal
-               if !strings.HasPrefix(line, "MemTotal:") {
-                       continue
-               }
-
-               // Extract the before last (value) and last (unit) fields
-               fields := strings.Split(line, " ")
-               value := fields[len(fields)-2] + fields[len(fields)-1]
-
-               // Feed the result to shared.ParseByteSizeString to get an int 
value
-               valueBytes, err := shared.ParseByteSizeString(value)
-               if err != nil {
-                       return -1, err
-               }
-
-               return valueBytes, nil
-       }
-
-       return -1, fmt.Errorf("Couldn't find MemTotal")
-}
-
 func deviceGetParentBlocks(path string) ([]string, error) {
        var devices []string
        var device []string
diff --git a/lxd/images.go b/lxd/images.go
index 68fd51f6d..72d4a530b 100644
--- a/lxd/images.go
+++ b/lxd/images.go
@@ -17,7 +17,6 @@ import (
        "strconv"
        "strings"
        "sync"
-       "syscall"
        "time"
 
        "github.com/gorilla/mux"
@@ -47,117 +46,8 @@ import (
    end for whichever finishes last. */
 var imagePublishLock sync.Mutex
 
-func detectCompression(fname string) ([]string, string, error) {
-       f, err := os.Open(fname)
-       if err != nil {
-               return []string{""}, "", err
-       }
-       defer f.Close()
-
-       // read header parts to detect compression method
-       // bz2 - 2 bytes, 'BZ' signature/magic number
-       // gz - 2 bytes, 0x1f 0x8b
-       // lzma - 6 bytes, { [0x000, 0xE0], '7', 'z', 'X', 'Z', 0x00 } -
-       // xy - 6 bytes,  header format { 0xFD, '7', 'z', 'X', 'Z', 0x00 }
-       // tar - 263 bytes, trying to get ustar from 257 - 262
-       header := make([]byte, 263)
-       _, err = f.Read(header)
-       if err != nil {
-               return []string{""}, "", err
-       }
-
-       switch {
-       case bytes.Equal(header[0:2], []byte{'B', 'Z'}):
-               return []string{"-jxf"}, ".tar.bz2", nil
-       case bytes.Equal(header[0:2], []byte{0x1f, 0x8b}):
-               return []string{"-zxf"}, ".tar.gz", nil
-       case (bytes.Equal(header[1:5], []byte{'7', 'z', 'X', 'Z'}) && header[0] 
== 0xFD):
-               return []string{"-Jxf"}, ".tar.xz", nil
-       case (bytes.Equal(header[1:5], []byte{'7', 'z', 'X', 'Z'}) && header[0] 
!= 0xFD):
-               return []string{"--lzma", "-xf"}, ".tar.lzma", nil
-       case bytes.Equal(header[0:3], []byte{0x5d, 0x00, 0x00}):
-               return []string{"--lzma", "-xf"}, ".tar.lzma", nil
-       case bytes.Equal(header[257:262], []byte{'u', 's', 't', 'a', 'r'}):
-               return []string{"-xf"}, ".tar", nil
-       case bytes.Equal(header[0:4], []byte{'h', 's', 'q', 's'}):
-               return []string{""}, ".squashfs", nil
-       default:
-               return []string{""}, "", fmt.Errorf("Unsupported compression")
-       }
-
-}
-
-func unpack(file string, path string, sType storageType, runningInUserns bool) 
error {
-       extractArgs, extension, err := detectCompression(file)
-       if err != nil {
-               return err
-       }
-
-       command := ""
-       args := []string{}
-       if strings.HasPrefix(extension, ".tar") {
-               command = "tar"
-               if runningInUserns {
-                       args = append(args, "--wildcards")
-                       args = append(args, "--exclude=dev/*")
-                       args = append(args, "--exclude=./dev/*")
-                       args = append(args, "--exclude=rootfs/dev/*")
-                       args = append(args, "--exclude=rootfs/./dev/*")
-               }
-               args = append(args, "-C", path, "--numeric-owner")
-               args = append(args, extractArgs...)
-               args = append(args, file)
-       } else if strings.HasPrefix(extension, ".squashfs") {
-               command = "unsquashfs"
-               args = append(args, "-f", "-d", path, "-n")
-
-               // Limit unsquashfs chunk size to 10% of memory and up to 256MB 
(default)
-               // When running on a low memory system, also disable 
multi-processing
-               mem, err := deviceTotalMemory()
-               mem = mem / 1024 / 1024 / 10
-               if err == nil && mem < 256 {
-                       args = append(args, "-da", fmt.Sprintf("%d", mem), 
"-fr", fmt.Sprintf("%d", mem), "-p", "1")
-               }
-
-               args = append(args, file)
-       } else {
-               return fmt.Errorf("Unsupported image format: %s", extension)
-       }
-
-       output, err := shared.RunCommand(command, args...)
-       if err != nil {
-               // Check if we ran out of space
-               fs := syscall.Statfs_t{}
-
-               err1 := syscall.Statfs(path, &fs)
-               if err1 != nil {
-                       return err1
-               }
-
-               // Check if we're running out of space
-               if int64(fs.Bfree) < int64(2*fs.Bsize) {
-                       if sType == storageTypeLvm {
-                               return fmt.Errorf("Unable to unpack image, run 
out of disk space (consider increasing your pool's volume.size).")
-                       } else {
-                               return fmt.Errorf("Unable to unpack image, run 
out of disk space.")
-                       }
-               }
-
-               co := output
-               logger.Debugf("Unpacking failed")
-               logger.Debugf(co)
-
-               // Truncate the output to a single line for inclusion in the 
error
-               // message.  The first line isn't guaranteed to pinpoint the 
issue,
-               // but it's better than nothing and better than a multi-line 
message.
-               return fmt.Errorf("Unpack failed, %s.  %s", err, 
strings.SplitN(co, "\n", 2)[0])
-       }
-
-       return nil
-}
-
-func unpackImage(imagefname string, destpath string, sType storageType, 
runningInUserns bool) error {
-       err := unpack(imagefname, destpath, sType, runningInUserns)
+func unpackImage(imagefname string, destpath string, sType shared.StorageType, 
runningInUserns bool) error {
+       err := shared.Unpack(imagefname, destpath, sType, runningInUserns)
        if err != nil {
                return err
        }
@@ -169,7 +59,7 @@ func unpackImage(imagefname string, destpath string, sType 
storageType, runningI
                        return fmt.Errorf("Error creating rootfs directory")
                }
 
-               err = unpack(imagefname+".rootfs", rootfsPath, sType, 
runningInUserns)
+               err = shared.Unpack(imagefname+".rootfs", rootfsPath, sType, 
runningInUserns)
                if err != nil {
                        return err
                }
@@ -771,7 +661,7 @@ func imagesPost(d *Daemon, r *http.Request) Response {
 func getImageMetadata(fname string) (*api.ImageMetadata, error) {
        metadataName := "metadata.yaml"
 
-       compressionArgs, _, err := detectCompression(fname)
+       compressionArgs, _, err := shared.DetectCompression(fname)
 
        if err != nil {
                return nil, fmt.Errorf(
@@ -1589,7 +1479,7 @@ func imageExport(d *Daemon, r *http.Request) Response {
        imagePath := shared.VarPath("images", imgInfo.Fingerprint)
        rootfsPath := imagePath + ".rootfs"
 
-       _, ext, err := detectCompression(imagePath)
+       _, ext, err := shared.DetectCompression(imagePath)
        if err != nil {
                ext = ""
        }
@@ -1604,7 +1494,7 @@ func imageExport(d *Daemon, r *http.Request) Response {
 
                // Recompute the extension for the root filesystem, it may use 
a different
                // compression algorithm than the metadata.
-               _, ext, err = detectCompression(rootfsPath)
+               _, ext, err = shared.DetectCompression(rootfsPath)
                if err != nil {
                        ext = ""
                }
diff --git a/lxd/main_test.go b/lxd/main_test.go
index 0f05ac700..4a76412a2 100644
--- a/lxd/main_test.go
+++ b/lxd/main_test.go
@@ -68,7 +68,7 @@ func (suite *lxdTestSuite) SetupTest() {
        // the next function.
        poolConfig := map[string]string{}
 
-       mockStorage, _ := storageTypeToString(storageTypeMock)
+       mockStorage, _ := storageTypeToString(shared.StorageTypeMock)
        // Create the database entry for the storage pool.
        poolDescription := fmt.Sprintf("%s storage pool", 
lxdTestSuiteDefaultStoragePool)
        _, err = dbStoragePoolCreateAndUpdateCache(suite.d.db, 
lxdTestSuiteDefaultStoragePool, poolDescription, mockStorage, poolConfig)
diff --git a/lxd/patches.go b/lxd/patches.go
index 79367d5a3..60ad7773f 100644
--- a/lxd/patches.go
+++ b/lxd/patches.go
@@ -166,16 +166,16 @@ func patchStorageApi(name string, d *Daemon) error {
        lvmVgName := daemonConfig["storage.lvm_vg_name"].Get()
        zfsPoolName := daemonConfig["storage.zfs_pool_name"].Get()
        defaultPoolName := "default"
-       preStorageApiStorageType := storageTypeDir
+       preStorageApiStorageType := shared.StorageTypeDir
 
        if lvmVgName != "" {
-               preStorageApiStorageType = storageTypeLvm
+               preStorageApiStorageType = shared.StorageTypeLvm
                defaultPoolName = lvmVgName
        } else if zfsPoolName != "" {
-               preStorageApiStorageType = storageTypeZfs
+               preStorageApiStorageType = shared.StorageTypeZfs
                defaultPoolName = zfsPoolName
        } else if d.os.BackingFS == "btrfs" {
-               preStorageApiStorageType = storageTypeBtrfs
+               preStorageApiStorageType = shared.StorageTypeBtrfs
        } else {
                // Dir storage pool.
        }
@@ -229,13 +229,13 @@ func patchStorageApi(name string, d *Daemon) error {
        // If any of these are actually called, there's no way back.
        poolName := defaultPoolName
        switch preStorageApiStorageType {
-       case storageTypeBtrfs:
+       case shared.StorageTypeBtrfs:
                err = upgradeFromStorageTypeBtrfs(name, d, defaultPoolName, 
defaultStorageTypeName, cRegular, cSnapshots, imgPublic, imgPrivate)
-       case storageTypeDir:
+       case shared.StorageTypeDir:
                err = upgradeFromStorageTypeDir(name, d, defaultPoolName, 
defaultStorageTypeName, cRegular, cSnapshots, imgPublic, imgPrivate)
-       case storageTypeLvm:
+       case shared.StorageTypeLvm:
                err = upgradeFromStorageTypeLvm(name, d, defaultPoolName, 
defaultStorageTypeName, cRegular, cSnapshots, imgPublic, imgPrivate)
-       case storageTypeZfs:
+       case shared.StorageTypeZfs:
                // The user is using a zfs dataset. This case needs to be
                // handled with care:
 
diff --git a/lxd/storage.go b/lxd/storage.go
index 4a651d70e..99dacae79 100644
--- a/lxd/storage.go
+++ b/lxd/storage.go
@@ -75,53 +75,41 @@ func readStoragePoolDriversCache() map[string]string {
        return drivers.(map[string]string)
 }
 
-// storageType defines the type of a storage
-type storageType int
-
-const (
-       storageTypeBtrfs storageType = iota
-       storageTypeCeph
-       storageTypeDir
-       storageTypeLvm
-       storageTypeMock
-       storageTypeZfs
-)
-
 var supportedStoragePoolDrivers = []string{"btrfs", "ceph", "dir", "lvm", 
"zfs"}
 
-func storageTypeToString(sType storageType) (string, error) {
+func storageTypeToString(sType shared.StorageType) (string, error) {
        switch sType {
-       case storageTypeBtrfs:
+       case shared.StorageTypeBtrfs:
                return "btrfs", nil
-       case storageTypeCeph:
+       case shared.StorageTypeCeph:
                return "ceph", nil
-       case storageTypeDir:
+       case shared.StorageTypeDir:
                return "dir", nil
-       case storageTypeLvm:
+       case shared.StorageTypeLvm:
                return "lvm", nil
-       case storageTypeMock:
+       case shared.StorageTypeMock:
                return "mock", nil
-       case storageTypeZfs:
+       case shared.StorageTypeZfs:
                return "zfs", nil
        }
 
        return "", fmt.Errorf("invalid storage type")
 }
 
-func storageStringToType(sName string) (storageType, error) {
+func storageStringToType(sName string) (shared.StorageType, error) {
        switch sName {
        case "btrfs":
-               return storageTypeBtrfs, nil
+               return shared.StorageTypeBtrfs, nil
        case "ceph":
-               return storageTypeCeph, nil
+               return shared.StorageTypeCeph, nil
        case "dir":
-               return storageTypeDir, nil
+               return shared.StorageTypeDir, nil
        case "lvm":
-               return storageTypeLvm, nil
+               return shared.StorageTypeLvm, nil
        case "mock":
-               return storageTypeMock, nil
+               return shared.StorageTypeMock, nil
        case "zfs":
-               return storageTypeZfs, nil
+               return shared.StorageTypeZfs, nil
        }
 
        return -1, fmt.Errorf("invalid storage type name")
@@ -132,7 +120,7 @@ func storageStringToType(sName string) (storageType, error) 
{
 type storage interface {
        // Functions dealing with basic driver properties only.
        StorageCoreInit() error
-       GetStorageType() storageType
+       GetStorageType() shared.StorageType
        GetStorageTypeName() string
        GetStorageTypeVersion() string
 
@@ -234,42 +222,42 @@ func storageCoreInit(driver string) (storage, error) {
        }
 
        switch sType {
-       case storageTypeBtrfs:
+       case shared.StorageTypeBtrfs:
                btrfs := storageBtrfs{}
                err = btrfs.StorageCoreInit()
                if err != nil {
                        return nil, err
                }
                return &btrfs, nil
-       case storageTypeDir:
+       case shared.StorageTypeDir:
                dir := storageDir{}
                err = dir.StorageCoreInit()
                if err != nil {
                        return nil, err
                }
                return &dir, nil
-       case storageTypeCeph:
+       case shared.StorageTypeCeph:
                ceph := storageCeph{}
                err = ceph.StorageCoreInit()
                if err != nil {
                        return nil, err
                }
                return &ceph, nil
-       case storageTypeLvm:
+       case shared.StorageTypeLvm:
                lvm := storageLvm{}
                err = lvm.StorageCoreInit()
                if err != nil {
                        return nil, err
                }
                return &lvm, nil
-       case storageTypeMock:
+       case shared.StorageTypeMock:
                mock := storageMock{}
                err = mock.StorageCoreInit()
                if err != nil {
                        return nil, err
                }
                return &mock, nil
-       case storageTypeZfs:
+       case shared.StorageTypeZfs:
                zfs := storageZfs{}
                err = zfs.StorageCoreInit()
                if err != nil {
@@ -310,7 +298,7 @@ func storageInit(s *state.State, poolName string, 
volumeName string, volumeType
        }
 
        switch sType {
-       case storageTypeBtrfs:
+       case shared.StorageTypeBtrfs:
                btrfs := storageBtrfs{}
                btrfs.poolID = poolID
                btrfs.pool = pool
@@ -322,7 +310,7 @@ func storageInit(s *state.State, poolName string, 
volumeName string, volumeType
                        return nil, err
                }
                return &btrfs, nil
-       case storageTypeDir:
+       case shared.StorageTypeDir:
                dir := storageDir{}
                dir.poolID = poolID
                dir.pool = pool
@@ -334,7 +322,7 @@ func storageInit(s *state.State, poolName string, 
volumeName string, volumeType
                        return nil, err
                }
                return &dir, nil
-       case storageTypeCeph:
+       case shared.StorageTypeCeph:
                ceph := storageCeph{}
                ceph.poolID = poolID
                ceph.pool = pool
@@ -346,7 +334,7 @@ func storageInit(s *state.State, poolName string, 
volumeName string, volumeType
                        return nil, err
                }
                return &ceph, nil
-       case storageTypeLvm:
+       case shared.StorageTypeLvm:
                lvm := storageLvm{}
                lvm.poolID = poolID
                lvm.pool = pool
@@ -358,7 +346,7 @@ func storageInit(s *state.State, poolName string, 
volumeName string, volumeType
                        return nil, err
                }
                return &lvm, nil
-       case storageTypeMock:
+       case shared.StorageTypeMock:
                mock := storageMock{}
                mock.poolID = poolID
                mock.pool = pool
@@ -370,7 +358,7 @@ func storageInit(s *state.State, poolName string, 
volumeName string, volumeType
                        return nil, err
                }
                return &mock, nil
-       case storageTypeZfs:
+       case shared.StorageTypeZfs:
                zfs := storageZfs{}
                zfs.poolID = poolID
                zfs.pool = pool
diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go
index 1218ee271..6cbe83532 100644
--- a/lxd/storage_btrfs.go
+++ b/lxd/storage_btrfs.go
@@ -61,7 +61,7 @@ func (s *storageBtrfs) getCustomSubvolumePath(poolName 
string) string {
 }
 
 func (s *storageBtrfs) StorageCoreInit() error {
-       s.sType = storageTypeBtrfs
+       s.sType = shared.StorageTypeBtrfs
        typeName, err := storageTypeToString(s.sType)
        if err != nil {
                return err
@@ -1433,7 +1433,7 @@ func (s *storageBtrfs) ImageCreate(fingerprint string) 
error {
 
        // Unpack the image in imageMntPoint.
        imagePath := shared.VarPath("images", fingerprint)
-       err = unpackImage(imagePath, tmpImageSubvolumeName, storageTypeBtrfs, 
s.s.OS.RunningInUserNS)
+       err = unpackImage(imagePath, tmpImageSubvolumeName, 
shared.StorageTypeBtrfs, s.s.OS.RunningInUserNS)
        if err != nil {
                return err
        }
diff --git a/lxd/storage_ceph.go b/lxd/storage_ceph.go
index 1af3cc410..5bc742f85 100644
--- a/lxd/storage_ceph.go
+++ b/lxd/storage_ceph.go
@@ -21,7 +21,7 @@ type storageCeph struct {
 }
 
 func (s *storageCeph) StorageCoreInit() error {
-       s.sType = storageTypeCeph
+       s.sType = shared.StorageTypeCeph
        typeName, err := storageTypeToString(s.sType)
        if err != nil {
                return err
@@ -2368,7 +2368,7 @@ func (s *storageCeph) ImageCreate(fingerprint string) 
error {
 
                // rsync contents into image
                imagePath := shared.VarPath("images", fingerprint)
-               err = unpackImage(imagePath, imageMntPoint, storageTypeCeph, 
s.s.OS.RunningInUserNS)
+               err = unpackImage(imagePath, imageMntPoint, 
shared.StorageTypeCeph, s.s.OS.RunningInUserNS)
                if err != nil {
                        logger.Errorf(`Failed to unpack image for RBD storage `+
                                `volume for image "%s" on storage pool "%s": 
%s`,
diff --git a/lxd/storage_dir.go b/lxd/storage_dir.go
index 78e562fa7..afbb324a4 100644
--- a/lxd/storage_dir.go
+++ b/lxd/storage_dir.go
@@ -21,7 +21,7 @@ type storageDir struct {
 
 // Only initialize the minimal information we need about a given storage type.
 func (s *storageDir) StorageCoreInit() error {
-       s.sType = storageTypeDir
+       s.sType = shared.StorageTypeDir
        typeName, err := storageTypeToString(s.sType)
        if err != nil {
                return err
@@ -509,7 +509,7 @@ func (s *storageDir) ContainerCreateFromImage(container 
container, imageFingerpr
        }()
 
        imagePath := shared.VarPath("images", imageFingerprint)
-       err = unpackImage(imagePath, containerMntPoint, storageTypeDir, 
s.s.OS.RunningInUserNS)
+       err = unpackImage(imagePath, containerMntPoint, shared.StorageTypeDir, 
s.s.OS.RunningInUserNS)
        if err != nil {
                return err
        }
diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go
index 95c596b1c..8866b0d17 100644
--- a/lxd/storage_lvm.go
+++ b/lxd/storage_lvm.go
@@ -25,7 +25,7 @@ type storageLvm struct {
 
 // Only initialize the minimal information we need about a given storage type.
 func (s *storageLvm) StorageCoreInit() error {
-       s.sType = storageTypeLvm
+       s.sType = shared.StorageTypeLvm
        typeName, err := storageTypeToString(s.sType)
        if err != nil {
                return err
@@ -1627,7 +1627,7 @@ func (s *storageLvm) ImageCreate(fingerprint string) 
error {
                }
 
                imagePath := shared.VarPath("images", fingerprint)
-               err = unpackImage(imagePath, imageMntPoint, storageTypeLvm, 
s.s.OS.RunningInUserNS)
+               err = unpackImage(imagePath, imageMntPoint, 
shared.StorageTypeLvm, s.s.OS.RunningInUserNS)
                if err != nil {
                        return err
                }
diff --git a/lxd/storage_lvm_utils.go b/lxd/storage_lvm_utils.go
index f9f3f1340..b71dcb1e7 100644
--- a/lxd/storage_lvm_utils.go
+++ b/lxd/storage_lvm_utils.go
@@ -461,7 +461,7 @@ func (s *storageLvm) containerCreateFromImageLv(c 
container, fp string) error {
 
        imagePath := shared.VarPath("images", fp)
        containerMntPoint := getContainerMountPoint(s.pool.Name, containerName)
-       err = unpackImage(imagePath, containerMntPoint, storageTypeLvm, 
s.s.OS.RunningInUserNS)
+       err = unpackImage(imagePath, containerMntPoint, shared.StorageTypeLvm, 
s.s.OS.RunningInUserNS)
        if err != nil {
                logger.Errorf(`Failed to unpack image "%s" into non-thinpool `+
                        `LVM storage volume "%s" for container "%s" on `+
diff --git a/lxd/storage_migration.go b/lxd/storage_migration.go
index 76308e96d..57f122bb7 100644
--- a/lxd/storage_migration.go
+++ b/lxd/storage_migration.go
@@ -149,7 +149,7 @@ func rsyncMigrationSink(live bool, container container, 
snapshots []*Snapshot, c
                return fmt.Errorf("the container's root device is missing the 
pool property")
        }
 
-       isDirBackend := container.Storage().GetStorageType() == storageTypeDir
+       isDirBackend := container.Storage().GetStorageType() == 
shared.StorageTypeDir
        if isDirBackend {
                if !containerOnly {
                        for _, snap := range snapshots {
diff --git a/lxd/storage_mock.go b/lxd/storage_mock.go
index aacf7cdc0..bef1f3dac 100644
--- a/lxd/storage_mock.go
+++ b/lxd/storage_mock.go
@@ -5,6 +5,7 @@ import (
 
        "github.com/gorilla/websocket"
 
+       "github.com/lxc/lxd/shared"
        "github.com/lxc/lxd/shared/api"
        "github.com/lxc/lxd/shared/idmap"
        "github.com/lxc/lxd/shared/logger"
@@ -15,7 +16,7 @@ type storageMock struct {
 }
 
 func (s *storageMock) StorageCoreInit() error {
-       s.sType = storageTypeMock
+       s.sType = shared.StorageTypeMock
        typeName, err := storageTypeToString(s.sType)
        if err != nil {
                return err
diff --git a/lxd/storage_shared.go b/lxd/storage_shared.go
index f2e1b692d..e4ab8c288 100644
--- a/lxd/storage_shared.go
+++ b/lxd/storage_shared.go
@@ -11,7 +11,7 @@ import (
 )
 
 type storageShared struct {
-       sType        storageType
+       sType        shared.StorageType
        sTypeName    string
        sTypeVersion string
 
@@ -24,7 +24,7 @@ type storageShared struct {
        volume *api.StorageVolume
 }
 
-func (s *storageShared) GetStorageType() storageType {
+func (s *storageShared) GetStorageType() shared.StorageType {
        return s.sType
 }
 
diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go
index 30a616b93..137621ad6 100644
--- a/lxd/storage_zfs.go
+++ b/lxd/storage_zfs.go
@@ -40,7 +40,7 @@ func (s *storageZfs) getOnDiskPoolName() string {
 
 // Only initialize the minimal information we need about a given storage type.
 func (s *storageZfs) StorageCoreInit() error {
-       s.sType = storageTypeZfs
+       s.sType = shared.StorageTypeZfs
        typeName, err := storageTypeToString(s.sType)
        if err != nil {
                return err
@@ -1907,7 +1907,7 @@ func (s *storageZfs) ImageCreate(fingerprint string) 
error {
        }
 
        // Unpack the image into the temporary mountpoint.
-       err = unpackImage(imagePath, tmpImageDir, storageTypeZfs, 
s.s.OS.RunningInUserNS)
+       err = unpackImage(imagePath, tmpImageDir, shared.StorageTypeZfs, 
s.s.OS.RunningInUserNS)
        if err != nil {
                return err
        }
diff --git a/shared/archive.go b/shared/archive.go
new file mode 100644
index 000000000..cc576a727
--- /dev/null
+++ b/shared/archive.go
@@ -0,0 +1,120 @@
+package shared
+
+import (
+       "bytes"
+       "fmt"
+       "os"
+       "strings"
+       "syscall"
+
+       "github.com/lxc/lxd/shared/logger"
+)
+
+func DetectCompression(fname string) ([]string, string, error) {
+       f, err := os.Open(fname)
+       if err != nil {
+               return []string{""}, "", err
+       }
+       defer f.Close()
+
+       // read header parts to detect compression method
+       // bz2 - 2 bytes, 'BZ' signature/magic number
+       // gz - 2 bytes, 0x1f 0x8b
+       // lzma - 6 bytes, { [0x000, 0xE0], '7', 'z', 'X', 'Z', 0x00 } -
+       // xy - 6 bytes,  header format { 0xFD, '7', 'z', 'X', 'Z', 0x00 }
+       // tar - 263 bytes, trying to get ustar from 257 - 262
+       header := make([]byte, 263)
+       _, err = f.Read(header)
+       if err != nil {
+               return []string{""}, "", err
+       }
+
+       switch {
+       case bytes.Equal(header[0:2], []byte{'B', 'Z'}):
+               return []string{"-jxf"}, ".tar.bz2", nil
+       case bytes.Equal(header[0:2], []byte{0x1f, 0x8b}):
+               return []string{"-zxf"}, ".tar.gz", nil
+       case (bytes.Equal(header[1:5], []byte{'7', 'z', 'X', 'Z'}) && header[0] 
== 0xFD):
+               return []string{"-Jxf"}, ".tar.xz", nil
+       case (bytes.Equal(header[1:5], []byte{'7', 'z', 'X', 'Z'}) && header[0] 
!= 0xFD):
+               return []string{"--lzma", "-xf"}, ".tar.lzma", nil
+       case bytes.Equal(header[0:3], []byte{0x5d, 0x00, 0x00}):
+               return []string{"--lzma", "-xf"}, ".tar.lzma", nil
+       case bytes.Equal(header[257:262], []byte{'u', 's', 't', 'a', 'r'}):
+               return []string{"-xf"}, ".tar", nil
+       case bytes.Equal(header[0:4], []byte{'h', 's', 'q', 's'}):
+               return []string{""}, ".squashfs", nil
+       default:
+               return []string{""}, "", fmt.Errorf("Unsupported compression")
+       }
+
+}
+
+func Unpack(file string, path string, sType StorageType, runningInUserns bool) 
error {
+       extractArgs, extension, err := DetectCompression(file)
+       if err != nil {
+               return err
+       }
+
+       command := ""
+       args := []string{}
+       if strings.HasPrefix(extension, ".tar") {
+               command = "tar"
+               if runningInUserns {
+                       args = append(args, "--wildcards")
+                       args = append(args, "--exclude=dev/*")
+                       args = append(args, "--exclude=./dev/*")
+                       args = append(args, "--exclude=rootfs/dev/*")
+                       args = append(args, "--exclude=rootfs/./dev/*")
+               }
+               args = append(args, "-C", path, "--numeric-owner")
+               args = append(args, extractArgs...)
+               args = append(args, file)
+       } else if strings.HasPrefix(extension, ".squashfs") {
+               command = "unsquashfs"
+               args = append(args, "-f", "-d", path, "-n")
+
+               // Limit unsquashfs chunk size to 10% of memory and up to 256MB 
(default)
+               // When running on a low memory system, also disable 
multi-processing
+               mem, err := DeviceTotalMemory()
+               mem = mem / 1024 / 1024 / 10
+               if err == nil && mem < 256 {
+                       args = append(args, "-da", fmt.Sprintf("%d", mem), 
"-fr", fmt.Sprintf("%d", mem), "-p", "1")
+               }
+
+               args = append(args, file)
+       } else {
+               return fmt.Errorf("Unsupported image format: %s", extension)
+       }
+
+       output, err := RunCommand(command, args...)
+       if err != nil {
+               // Check if we ran out of space
+               fs := syscall.Statfs_t{}
+
+               err1 := syscall.Statfs(path, &fs)
+               if err1 != nil {
+                       return err1
+               }
+
+               // Check if we're running out of space
+               if int64(fs.Bfree) < int64(2*fs.Bsize) {
+                       if sType == StorageTypeLvm {
+                               return fmt.Errorf("Unable to unpack image, run 
out of disk space (consider increasing your pool's volume.size).")
+                       } else {
+                               return fmt.Errorf("Unable to unpack image, run 
out of disk space.")
+                       }
+               }
+
+               co := output
+               logger.Debugf("Unpacking failed")
+               logger.Debugf(co)
+
+               // Truncate the output to a single line for inclusion in the 
error
+               // message.  The first line isn't guaranteed to pinpoint the 
issue,
+               // but it's better than nothing and better than a multi-line 
message.
+               return fmt.Errorf("Unpack failed, %s.  %s", err, 
strings.SplitN(co, "\n", 2)[0])
+       }
+
+       return nil
+}
diff --git a/shared/devices.go b/shared/devices.go
new file mode 100644
index 000000000..df281970a
--- /dev/null
+++ b/shared/devices.go
@@ -0,0 +1,42 @@
+package shared
+
+import (
+       "bufio"
+       "fmt"
+       "os"
+       "strings"
+)
+
+func DeviceTotalMemory() (int64, error) {
+       // Open /proc/meminfo
+       f, err := os.Open("/proc/meminfo")
+       if err != nil {
+               return -1, err
+       }
+       defer f.Close()
+
+       // Read it line by line
+       scan := bufio.NewScanner(f)
+       for scan.Scan() {
+               line := scan.Text()
+
+               // We only care about MemTotal
+               if !strings.HasPrefix(line, "MemTotal:") {
+                       continue
+               }
+
+               // Extract the before last (value) and last (unit) fields
+               fields := strings.Split(line, " ")
+               value := fields[len(fields)-2] + fields[len(fields)-1]
+
+               // Feed the result to shared.ParseByteSizeString to get an int 
value
+               valueBytes, err := ParseByteSizeString(value)
+               if err != nil {
+                       return -1, err
+               }
+
+               return valueBytes, nil
+       }
+
+       return -1, fmt.Errorf("Couldn't find MemTotal")
+}
diff --git a/shared/storage.go b/shared/storage.go
new file mode 100644
index 000000000..d13209fd5
--- /dev/null
+++ b/shared/storage.go
@@ -0,0 +1,13 @@
+package shared
+
+// StorageType defines the type of a storage
+type StorageType int
+
+const (
+       StorageTypeBtrfs StorageType = iota
+       StorageTypeCeph
+       StorageTypeDir
+       StorageTypeLvm
+       StorageTypeMock
+       StorageTypeZfs
+)

From 1c8d4cc81b18c8d37dd14393851716c6a163e0c7 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <[email protected]>
Date: Fri, 12 Jan 2018 14:37:51 +0100
Subject: [PATCH 2/2] *: move download function to shared

This moves the DownloadFileSha256 function and its dependent code to
shared.

Signed-off-by: Thomas Hipp <[email protected]>
---
 client/interfaces.go           | 20 ++------------
 client/lxd_images.go           |  4 +--
 client/simplestreams_images.go |  4 +--
 client/util.go                 | 62 ------------------------------------------
 lxc/utils.go                   |  3 +-
 lxd/daemon_images.go           |  4 +--
 shared/util.go                 | 62 ++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 73 insertions(+), 86 deletions(-)

diff --git a/client/interfaces.go b/client/interfaces.go
index b88fbef2d..6bfc245f0 100644
--- a/client/interfaces.go
+++ b/client/interfaces.go
@@ -6,6 +6,7 @@ import (
 
        "github.com/gorilla/websocket"
 
+       "github.com/lxc/lxd/shared"
        "github.com/lxc/lxd/shared/api"
        "github.com/lxc/lxd/shared/cancel"
 )
@@ -172,21 +173,6 @@ type ConnectionInfo struct {
        Protocol    string
 }
 
-// The ProgressData struct represents new progress information on an operation
-type ProgressData struct {
-       // Preferred string repreentation of progress (always set)
-       Text string
-
-       // Progress in percent
-       Percentage int
-
-       // Number of bytes transferred (for files)
-       TransferredBytes int64
-
-       // Total number of bytes (for files)
-       TotalBytes int64
-}
-
 // The ImageCreateArgs struct is used for direct image upload
 type ImageCreateArgs struct {
        // Reader for the meta file
@@ -202,7 +188,7 @@ type ImageCreateArgs struct {
        RootfsName string
 
        // Progress handler (called with upload progress)
-       ProgressHandler func(progress ProgressData)
+       ProgressHandler func(progress shared.ProgressData)
 }
 
 // The ImageFileRequest struct is used for an image download request
@@ -214,7 +200,7 @@ type ImageFileRequest struct {
        RootfsFile io.WriteSeeker
 
        // Progress handler (called whenever some progress is made)
-       ProgressHandler func(progress ProgressData)
+       ProgressHandler func(progress shared.ProgressData)
 
        // A canceler that can be used to interrupt some part of the image 
download request
        Canceler *cancel.Canceler
diff --git a/client/lxd_images.go b/client/lxd_images.go
index 8d0889640..385ad04de 100644
--- a/client/lxd_images.go
+++ b/client/lxd_images.go
@@ -145,7 +145,7 @@ func (r *ProtocolLXD) GetPrivateImageFile(fingerprint 
string, secret string, req
                        Tracker: &ioprogress.ProgressTracker{
                                Length: response.ContentLength,
                                Handler: func(percent int64, speed int64) {
-                                       req.ProgressHandler(ProgressData{Text: 
fmt.Sprintf("%d%% (%s/s)", percent, shared.GetByteSizeString(speed, 2))})
+                                       
req.ProgressHandler(shared.ProgressData{Text: fmt.Sprintf("%d%% (%s/s)", 
percent, shared.GetByteSizeString(speed, 2))})
                                },
                        },
                }
@@ -363,7 +363,7 @@ func (r *ProtocolLXD) CreateImage(image api.ImagesPost, 
args *ImageCreateArgs) (
                        Tracker: &ioprogress.ProgressTracker{
                                Length: size,
                                Handler: func(percent int64, speed int64) {
-                                       args.ProgressHandler(ProgressData{Text: 
fmt.Sprintf("%d%% (%s/s)", percent, shared.GetByteSizeString(speed, 2))})
+                                       
args.ProgressHandler(shared.ProgressData{Text: fmt.Sprintf("%d%% (%s/s)", 
percent, shared.GetByteSizeString(speed, 2))})
                                },
                        },
                }
diff --git a/client/simplestreams_images.go b/client/simplestreams_images.go
index 39a1f5e87..065bedabe 100644
--- a/client/simplestreams_images.go
+++ b/client/simplestreams_images.go
@@ -67,11 +67,11 @@ func (r *ProtocolSimpleStreams) GetImageFile(fingerprint 
string, req ImageFileRe
                // Try over http
                url := fmt.Sprintf("http://%s/%s";, 
strings.TrimPrefix(r.httpHost, "https://";), path)
 
-               size, err := downloadFileSha256(r.http, r.httpUserAgent, 
req.ProgressHandler, req.Canceler, filename, url, sha256, target)
+               size, err := shared.DownloadFileSha256(r.http, r.httpUserAgent, 
req.ProgressHandler, req.Canceler, filename, url, sha256, target)
                if err != nil {
                        // Try over https
                        url = fmt.Sprintf("%s/%s", r.httpHost, path)
-                       size, err = downloadFileSha256(r.http, r.httpUserAgent, 
req.ProgressHandler, req.Canceler, filename, url, sha256, target)
+                       size, err = shared.DownloadFileSha256(r.http, 
r.httpUserAgent, req.ProgressHandler, req.Canceler, filename, url, sha256, 
target)
                        if err != nil {
                                return -1, err
                        }
diff --git a/client/util.go b/client/util.go
index e041fd979..256a184f8 100644
--- a/client/util.go
+++ b/client/util.go
@@ -1,16 +1,12 @@
 package lxd
 
 import (
-       "crypto/sha256"
-       "fmt"
        "io"
        "net"
        "net/http"
        "net/url"
 
        "github.com/lxc/lxd/shared"
-       "github.com/lxc/lxd/shared/cancel"
-       "github.com/lxc/lxd/shared/ioprogress"
 )
 
 func tlsHTTPClient(client *http.Client, tlsClientCert string, tlsClientKey 
string, tlsCA string, tlsServerCert string, insecureSkipVerify bool, proxy 
func(req *http.Request) (*url.URL, error)) (*http.Client, error) {
@@ -84,64 +80,6 @@ func unixHTTPClient(client *http.Client, path string) 
(*http.Client, error) {
        return client, nil
 }
 
-func downloadFileSha256(httpClient *http.Client, useragent string, progress 
func(progress ProgressData), canceler *cancel.Canceler, filename string, url 
string, hash string, target io.WriteSeeker) (int64, error) {
-       // Always seek to the beginning
-       target.Seek(0, 0)
-
-       // Prepare the download request
-       req, err := http.NewRequest("GET", url, nil)
-       if err != nil {
-               return -1, err
-       }
-
-       if useragent != "" {
-               req.Header.Set("User-Agent", useragent)
-       }
-
-       // Perform the request
-       r, doneCh, err := cancel.CancelableDownload(canceler, httpClient, req)
-       if err != nil {
-               return -1, err
-       }
-       defer r.Body.Close()
-       defer close(doneCh)
-
-       if r.StatusCode != http.StatusOK {
-               return -1, fmt.Errorf("Unable to fetch %s: %s", url, r.Status)
-       }
-
-       // Handle the data
-       body := r.Body
-       if progress != nil {
-               body = &ioprogress.ProgressReader{
-                       ReadCloser: r.Body,
-                       Tracker: &ioprogress.ProgressTracker{
-                               Length: r.ContentLength,
-                               Handler: func(percent int64, speed int64) {
-                                       if filename != "" {
-                                               progress(ProgressData{Text: 
fmt.Sprintf("%s: %d%% (%s/s)", filename, percent, 
shared.GetByteSizeString(speed, 2))})
-                                       } else {
-                                               progress(ProgressData{Text: 
fmt.Sprintf("%d%% (%s/s)", percent, shared.GetByteSizeString(speed, 2))})
-                                       }
-                               },
-                       },
-               }
-       }
-
-       sha256 := sha256.New()
-       size, err := io.Copy(io.MultiWriter(target, sha256), body)
-       if err != nil {
-               return -1, err
-       }
-
-       result := fmt.Sprintf("%x", sha256.Sum(nil))
-       if result != hash {
-               return -1, fmt.Errorf("Hash mismatch for %s: %s != %s", url, 
result, hash)
-       }
-
-       return size, nil
-}
-
 type nullReadWriteCloser int
 
 func (nullReadWriteCloser) Close() error                { return nil }
diff --git a/lxc/utils.go b/lxc/utils.go
index 0215bd394..358269841 100644
--- a/lxc/utils.go
+++ b/lxc/utils.go
@@ -13,6 +13,7 @@ import (
        "time"
 
        "github.com/lxc/lxd/client"
+       "github.com/lxc/lxd/shared"
        "github.com/lxc/lxd/shared/api"
        "github.com/lxc/lxd/shared/i18n"
 )
@@ -119,7 +120,7 @@ func (p *ProgressRenderer) Warn(status string, timeout 
time.Duration) {
        fmt.Print(msg)
 }
 
-func (p *ProgressRenderer) UpdateProgress(progress lxd.ProgressData) {
+func (p *ProgressRenderer) UpdateProgress(progress shared.ProgressData) {
        p.Update(progress.Text)
 }
 
diff --git a/lxd/daemon_images.go b/lxd/daemon_images.go
index abc81a965..ca5d49f16 100644
--- a/lxd/daemon_images.go
+++ b/lxd/daemon_images.go
@@ -343,7 +343,7 @@ func (d *Daemon) ImageDownload(op *operation, server 
string, protocol string, ce
        defer cleanup()
 
        // Setup a progress handler
-       progress := func(progress lxd.ProgressData) {
+       progress := func(progress shared.ProgressData) {
                if op == nil {
                        return
                }
@@ -462,7 +462,7 @@ func (d *Daemon) ImageDownload(op *operation, server 
string, protocol string, ce
                        Tracker: &ioprogress.ProgressTracker{
                                Length: raw.ContentLength,
                                Handler: func(percent int64, speed int64) {
-                                       progress(lxd.ProgressData{Text: 
fmt.Sprintf("%d%% (%s/s)", percent, shared.GetByteSizeString(speed, 2))})
+                                       progress(shared.ProgressData{Text: 
fmt.Sprintf("%d%% (%s/s)", percent, shared.GetByteSizeString(speed, 2))})
                                },
                        },
                }
diff --git a/shared/util.go b/shared/util.go
index e039905f0..f262c3aca 100644
--- a/shared/util.go
+++ b/shared/util.go
@@ -4,6 +4,7 @@ import (
        "bufio"
        "bytes"
        "crypto/rand"
+       "crypto/sha256"
        "encoding/gob"
        "encoding/hex"
        "encoding/json"
@@ -24,6 +25,9 @@ import (
        "strings"
        "time"
        "unicode"
+
+       "github.com/lxc/lxd/shared/cancel"
+       "github.com/lxc/lxd/shared/ioprogress"
 )
 
 const SnapshotDelimiter = "/"
@@ -902,3 +906,61 @@ func EscapePathFstab(path string) string {
                "\\", "\\\\")
        return r.Replace(path)
 }
+
+func DownloadFileSha256(httpClient *http.Client, useragent string, progress 
func(progress ProgressData), canceler *cancel.Canceler, filename string, url 
string, hash string, target io.WriteSeeker) (int64, error) {
+       // Always seek to the beginning
+       target.Seek(0, 0)
+
+       // Prepare the download request
+       req, err := http.NewRequest("GET", url, nil)
+       if err != nil {
+               return -1, err
+       }
+
+       if useragent != "" {
+               req.Header.Set("User-Agent", useragent)
+       }
+
+       // Perform the request
+       r, doneCh, err := cancel.CancelableDownload(canceler, httpClient, req)
+       if err != nil {
+               return -1, err
+       }
+       defer r.Body.Close()
+       defer close(doneCh)
+
+       if r.StatusCode != http.StatusOK {
+               return -1, fmt.Errorf("Unable to fetch %s: %s", url, r.Status)
+       }
+
+       // Handle the data
+       body := r.Body
+       if progress != nil {
+               body = &ioprogress.ProgressReader{
+                       ReadCloser: r.Body,
+                       Tracker: &ioprogress.ProgressTracker{
+                               Length: r.ContentLength,
+                               Handler: func(percent int64, speed int64) {
+                                       if filename != "" {
+                                               progress(ProgressData{Text: 
fmt.Sprintf("%s: %d%% (%s/s)", filename, percent, GetByteSizeString(speed, 2))})
+                                       } else {
+                                               progress(ProgressData{Text: 
fmt.Sprintf("%d%% (%s/s)", percent, GetByteSizeString(speed, 2))})
+                                       }
+                               },
+                       },
+               }
+       }
+
+       sha256 := sha256.New()
+       size, err := io.Copy(io.MultiWriter(target, sha256), body)
+       if err != nil {
+               return -1, err
+       }
+
+       result := fmt.Sprintf("%x", sha256.Sum(nil))
+       if result != hash {
+               return -1, fmt.Errorf("Hash mismatch for %s: %s != %s", url, 
result, hash)
+       }
+
+       return size, nil
+}
_______________________________________________
lxc-devel mailing list
[email protected]
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to