Fix the dependency issue (#231)
This commit is contained in:
557
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/aufs/aufs.go
generated
vendored
557
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/aufs/aufs.go
generated
vendored
@@ -1,557 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
|
||||
aufs driver directory structure
|
||||
|
||||
.
|
||||
├── layers // Metadata of layers
|
||||
│ ├── 1
|
||||
│ ├── 2
|
||||
│ └── 3
|
||||
├── diff // Content of the layer
|
||||
│ ├── 1 // Contains layers that need to be mounted for the id
|
||||
│ ├── 2
|
||||
│ └── 3
|
||||
└── mnt // Mount points for the rw layers to be mounted
|
||||
├── 1
|
||||
├── 2
|
||||
└── 3
|
||||
|
||||
*/
|
||||
|
||||
package aufs
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver"
|
||||
"github.com/hyperhq/hypercli/pkg/archive"
|
||||
"github.com/hyperhq/hypercli/pkg/chrootarchive"
|
||||
"github.com/hyperhq/hypercli/pkg/directory"
|
||||
"github.com/hyperhq/hypercli/pkg/idtools"
|
||||
mountpk "github.com/hyperhq/hypercli/pkg/mount"
|
||||
"github.com/hyperhq/hypercli/pkg/stringid"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrAufsNotSupported is returned if aufs is not supported by the host.
|
||||
ErrAufsNotSupported = fmt.Errorf("AUFS was not found in /proc/filesystems")
|
||||
incompatibleFsMagic = []graphdriver.FsMagic{
|
||||
graphdriver.FsMagicBtrfs,
|
||||
graphdriver.FsMagicAufs,
|
||||
}
|
||||
backingFs = "<unknown>"
|
||||
|
||||
enableDirpermLock sync.Once
|
||||
enableDirperm bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
graphdriver.Register("aufs", Init)
|
||||
}
|
||||
|
||||
type data struct {
|
||||
referenceCount int
|
||||
path string
|
||||
}
|
||||
|
||||
// Driver contains information about the filesystem mounted.
|
||||
// root of the filesystem
|
||||
// sync.Mutex to protect against concurrent modifications
|
||||
// active maps mount id to the count
|
||||
type Driver struct {
|
||||
root string
|
||||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
sync.Mutex // Protects concurrent modification to active
|
||||
active map[string]*data
|
||||
}
|
||||
|
||||
// Init returns a new AUFS driver.
|
||||
// An error is returned if AUFS is not supported.
|
||||
func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
|
||||
// Try to load the aufs kernel module
|
||||
if err := supportsAufs(); err != nil {
|
||||
return nil, graphdriver.ErrNotSupported
|
||||
}
|
||||
|
||||
fsMagic, err := graphdriver.GetFSMagic(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fsName, ok := graphdriver.FsNames[fsMagic]; ok {
|
||||
backingFs = fsName
|
||||
}
|
||||
|
||||
for _, magic := range incompatibleFsMagic {
|
||||
if fsMagic == magic {
|
||||
return nil, graphdriver.ErrIncompatibleFS
|
||||
}
|
||||
}
|
||||
|
||||
paths := []string{
|
||||
"mnt",
|
||||
"diff",
|
||||
"layers",
|
||||
}
|
||||
|
||||
a := &Driver{
|
||||
root: root,
|
||||
active: make(map[string]*data),
|
||||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
}
|
||||
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Create the root aufs driver dir and return
|
||||
// if it already exists
|
||||
// If not populate the dir structure
|
||||
if err := idtools.MkdirAllAs(root, 0700, rootUID, rootGID); err != nil {
|
||||
if os.IsExist(err) {
|
||||
return a, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := mountpk.MakePrivate(root); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Populate the dir structure
|
||||
for _, p := range paths {
|
||||
if err := idtools.MkdirAllAs(path.Join(root, p), 0700, rootUID, rootGID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Return a nil error if the kernel supports aufs
|
||||
// We cannot modprobe because inside dind modprobe fails
|
||||
// to run
|
||||
func supportsAufs() error {
|
||||
// We can try to modprobe aufs first before looking at
|
||||
// proc/filesystems for when aufs is supported
|
||||
exec.Command("modprobe", "aufs").Run()
|
||||
|
||||
f, err := os.Open("/proc/filesystems")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
if strings.Contains(s.Text(), "aufs") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrAufsNotSupported
|
||||
}
|
||||
|
||||
func (a *Driver) rootPath() string {
|
||||
return a.root
|
||||
}
|
||||
|
||||
func (*Driver) String() string {
|
||||
return "aufs"
|
||||
}
|
||||
|
||||
// Status returns current information about the filesystem such as root directory, number of directories mounted, etc.
|
||||
func (a *Driver) Status() [][2]string {
|
||||
ids, _ := loadIds(path.Join(a.rootPath(), "layers"))
|
||||
return [][2]string{
|
||||
{"Root Dir", a.rootPath()},
|
||||
{"Backing Filesystem", backingFs},
|
||||
{"Dirs", fmt.Sprintf("%d", len(ids))},
|
||||
{"Dirperm1 Supported", fmt.Sprintf("%v", useDirperm())},
|
||||
}
|
||||
}
|
||||
|
||||
// GetMetadata not implemented
|
||||
func (a *Driver) GetMetadata(id string) (map[string]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Exists returns true if the given id is registered with
|
||||
// this driver
|
||||
func (a *Driver) Exists(id string) bool {
|
||||
if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create three folders for each id
|
||||
// mnt, layers, and diff
|
||||
func (a *Driver) Create(id, parent, mountLabel string) error {
|
||||
if err := a.createDirsFor(id); err != nil {
|
||||
return err
|
||||
}
|
||||
// Write the layers metadata
|
||||
f, err := os.Create(path.Join(a.rootPath(), "layers", id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if parent != "" {
|
||||
ids, err := getParentIds(a.rootPath(), parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprintln(f, parent); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, i := range ids {
|
||||
if _, err := fmt.Fprintln(f, i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
a.active[id] = &data{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Driver) createDirsFor(id string) error {
|
||||
paths := []string{
|
||||
"mnt",
|
||||
"diff",
|
||||
}
|
||||
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(a.uidMaps, a.gidMaps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range paths {
|
||||
if err := idtools.MkdirAllAs(path.Join(a.rootPath(), p, id), 0755, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove will unmount and remove the given id.
|
||||
func (a *Driver) Remove(id string) error {
|
||||
// Protect the a.active from concurrent access
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
m := a.active[id]
|
||||
if m != nil {
|
||||
if m.referenceCount > 0 {
|
||||
return nil
|
||||
}
|
||||
// Make sure the dir is umounted first
|
||||
if err := a.unmount(m); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
tmpDirs := []string{
|
||||
"mnt",
|
||||
"diff",
|
||||
}
|
||||
|
||||
// Atomically remove each directory in turn by first moving it out of the
|
||||
// way (so that docker doesn't find it anymore) before doing removal of
|
||||
// the whole tree.
|
||||
for _, p := range tmpDirs {
|
||||
realPath := path.Join(a.rootPath(), p, id)
|
||||
tmpPath := path.Join(a.rootPath(), p, fmt.Sprintf("%s-removing", id))
|
||||
if err := os.Rename(realPath, tmpPath); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpPath)
|
||||
}
|
||||
// Remove the layers file for the id
|
||||
if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the rootfs path for the id.
|
||||
// This will mount the dir at it's given path
|
||||
func (a *Driver) Get(id, mountLabel string) (string, error) {
|
||||
ids, err := getParentIds(a.rootPath(), id)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
ids = []string{}
|
||||
}
|
||||
|
||||
// Protect the a.active from concurrent access
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
m := a.active[id]
|
||||
if m == nil {
|
||||
m = &data{}
|
||||
a.active[id] = m
|
||||
}
|
||||
|
||||
// If a dir does not have a parent ( no layers )do not try to mount
|
||||
// just return the diff path to the data
|
||||
m.path = path.Join(a.rootPath(), "diff", id)
|
||||
if len(ids) > 0 {
|
||||
m.path = path.Join(a.rootPath(), "mnt", id)
|
||||
if m.referenceCount == 0 {
|
||||
if err := a.mount(id, m, mountLabel); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
m.referenceCount++
|
||||
return m.path, nil
|
||||
}
|
||||
|
||||
// Put unmounts and updates list of active mounts.
|
||||
func (a *Driver) Put(id string) error {
|
||||
// Protect the a.active from concurrent access
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
m := a.active[id]
|
||||
if m == nil {
|
||||
// but it might be still here
|
||||
if a.Exists(id) {
|
||||
path := path.Join(a.rootPath(), "mnt", id)
|
||||
err := Unmount(path)
|
||||
if err != nil {
|
||||
logrus.Debugf("Failed to unmount %s aufs: %v", id, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if count := m.referenceCount; count > 1 {
|
||||
m.referenceCount = count - 1
|
||||
} else {
|
||||
ids, _ := getParentIds(a.rootPath(), id)
|
||||
// We only mounted if there are any parents
|
||||
if ids != nil && len(ids) > 0 {
|
||||
a.unmount(m)
|
||||
}
|
||||
delete(a.active, id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Diff produces an archive of the changes between the specified
|
||||
// layer and its parent layer which may be "".
|
||||
func (a *Driver) Diff(id, parent string) (archive.Archive, error) {
|
||||
// AUFS doesn't need the parent layer to produce a diff.
|
||||
return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
|
||||
Compression: archive.Uncompressed,
|
||||
ExcludePatterns: []string{archive.WhiteoutMetaPrefix + "*", "!" + archive.WhiteoutOpaqueDir},
|
||||
UIDMaps: a.uidMaps,
|
||||
GIDMaps: a.gidMaps,
|
||||
})
|
||||
}
|
||||
|
||||
// DiffPath returns path to the directory that contains files for the layer
|
||||
// differences. Used for direct access for tar-split.
|
||||
func (a *Driver) DiffPath(id string) (string, func() error, error) {
|
||||
return path.Join(a.rootPath(), "diff", id), func() error { return nil }, nil
|
||||
}
|
||||
|
||||
func (a *Driver) applyDiff(id string, diff archive.Reader) error {
|
||||
return chrootarchive.UntarUncompressed(diff, path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
|
||||
UIDMaps: a.uidMaps,
|
||||
GIDMaps: a.gidMaps,
|
||||
})
|
||||
}
|
||||
|
||||
// DiffSize calculates the changes between the specified id
|
||||
// and its parent and returns the size in bytes of the changes
|
||||
// relative to its base filesystem directory.
|
||||
func (a *Driver) DiffSize(id, parent string) (size int64, err error) {
|
||||
// AUFS doesn't need the parent layer to calculate the diff size.
|
||||
return directory.Size(path.Join(a.rootPath(), "diff", id))
|
||||
}
|
||||
|
||||
// ApplyDiff extracts the changeset from the given diff into the
|
||||
// layer with the specified id and parent, returning the size of the
|
||||
// new layer in bytes.
|
||||
func (a *Driver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) {
|
||||
// AUFS doesn't need the parent id to apply the diff.
|
||||
if err = a.applyDiff(id, diff); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return a.DiffSize(id, parent)
|
||||
}
|
||||
|
||||
// Changes produces a list of changes between the specified layer
|
||||
// and its parent layer. If parent is "", then all changes will be ADD changes.
|
||||
func (a *Driver) Changes(id, parent string) ([]archive.Change, error) {
|
||||
// AUFS doesn't have snapshots, so we need to get changes from all parent
|
||||
// layers.
|
||||
layers, err := a.getParentLayerPaths(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return archive.Changes(layers, path.Join(a.rootPath(), "diff", id))
|
||||
}
|
||||
|
||||
func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
|
||||
parentIds, err := getParentIds(a.rootPath(), id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
layers := make([]string, len(parentIds))
|
||||
|
||||
// Get the diff paths for all the parent ids
|
||||
for i, p := range parentIds {
|
||||
layers[i] = path.Join(a.rootPath(), "diff", p)
|
||||
}
|
||||
return layers, nil
|
||||
}
|
||||
|
||||
func (a *Driver) mount(id string, m *data, mountLabel string) error {
|
||||
// If the id is mounted or we get an error return
|
||||
if mounted, err := a.mounted(m); err != nil || mounted {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
target = m.path
|
||||
rw = path.Join(a.rootPath(), "diff", id)
|
||||
)
|
||||
|
||||
layers, err := a.getParentLayerPaths(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := a.aufsMount(layers, rw, target, mountLabel); err != nil {
|
||||
return fmt.Errorf("error creating aufs mount to %s: %v", target, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Driver) unmount(m *data) error {
|
||||
if mounted, err := a.mounted(m); err != nil || !mounted {
|
||||
return err
|
||||
}
|
||||
return Unmount(m.path)
|
||||
}
|
||||
|
||||
func (a *Driver) mounted(m *data) (bool, error) {
|
||||
return mountpk.Mounted(m.path)
|
||||
}
|
||||
|
||||
// Cleanup aufs and unmount all mountpoints
|
||||
func (a *Driver) Cleanup() error {
|
||||
for id, m := range a.active {
|
||||
if err := a.unmount(m); err != nil {
|
||||
logrus.Errorf("Unmounting %s: %s", stringid.TruncateID(id), err)
|
||||
}
|
||||
}
|
||||
return mountpk.Unmount(a.root)
|
||||
}
|
||||
|
||||
func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
Unmount(target)
|
||||
}
|
||||
}()
|
||||
|
||||
// Mount options are clipped to page size(4096 bytes). If there are more
|
||||
// layers then these are remounted individually using append.
|
||||
|
||||
offset := 54
|
||||
if useDirperm() {
|
||||
offset += len("dirperm1")
|
||||
}
|
||||
b := make([]byte, syscall.Getpagesize()-len(mountLabel)-offset) // room for xino & mountLabel
|
||||
bp := copy(b, fmt.Sprintf("br:%s=rw", rw))
|
||||
|
||||
firstMount := true
|
||||
i := 0
|
||||
|
||||
for {
|
||||
for ; i < len(ro); i++ {
|
||||
layer := fmt.Sprintf(":%s=ro+wh", ro[i])
|
||||
|
||||
if firstMount {
|
||||
if bp+len(layer) > len(b) {
|
||||
break
|
||||
}
|
||||
bp += copy(b[bp:], layer)
|
||||
} else {
|
||||
data := label.FormatMountLabel(fmt.Sprintf("append%s", layer), mountLabel)
|
||||
if err = mount("none", target, "aufs", syscall.MS_REMOUNT, data); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if firstMount {
|
||||
opts := "dio,xino=/dev/shm/aufs.xino"
|
||||
if useDirperm() {
|
||||
opts += ",dirperm1"
|
||||
}
|
||||
data := label.FormatMountLabel(fmt.Sprintf("%s,%s", string(b[:bp]), opts), mountLabel)
|
||||
if err = mount("none", target, "aufs", 0, data); err != nil {
|
||||
return
|
||||
}
|
||||
firstMount = false
|
||||
}
|
||||
|
||||
if i == len(ro) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// useDirperm checks dirperm1 mount option can be used with the current
|
||||
// version of aufs.
|
||||
func useDirperm() bool {
|
||||
enableDirpermLock.Do(func() {
|
||||
base, err := ioutil.TempDir("", "docker-aufs-base")
|
||||
if err != nil {
|
||||
logrus.Errorf("error checking dirperm1: %v", err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(base)
|
||||
|
||||
union, err := ioutil.TempDir("", "docker-aufs-union")
|
||||
if err != nil {
|
||||
logrus.Errorf("error checking dirperm1: %v", err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(union)
|
||||
|
||||
opts := fmt.Sprintf("br:%s,dirperm1,xino=/dev/shm/aufs.xino", base)
|
||||
if err := mount("none", union, "aufs", 0, opts); err != nil {
|
||||
return
|
||||
}
|
||||
enableDirperm = true
|
||||
if err := Unmount(union); err != nil {
|
||||
logrus.Errorf("error checking dirperm1: failed to unmount %v", err)
|
||||
}
|
||||
})
|
||||
return enableDirperm
|
||||
}
|
||||
734
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/aufs/aufs_test.go
generated
vendored
734
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/aufs/aufs_test.go
generated
vendored
@@ -1,734 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package aufs
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver"
|
||||
"github.com/hyperhq/hypercli/pkg/archive"
|
||||
"github.com/hyperhq/hypercli/pkg/reexec"
|
||||
)
|
||||
|
||||
var (
|
||||
tmpOuter = path.Join(os.TempDir(), "aufs-tests")
|
||||
tmp = path.Join(tmpOuter, "aufs")
|
||||
)
|
||||
|
||||
func init() {
|
||||
reexec.Init()
|
||||
}
|
||||
|
||||
func testInit(dir string, t *testing.T) graphdriver.Driver {
|
||||
d, err := Init(dir, nil, nil, nil)
|
||||
if err != nil {
|
||||
if err == graphdriver.ErrNotSupported {
|
||||
t.Skip(err)
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func newDriver(t *testing.T) *Driver {
|
||||
if err := os.MkdirAll(tmp, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := testInit(tmp, t)
|
||||
return d.(*Driver)
|
||||
}
|
||||
|
||||
func TestNewDriver(t *testing.T) {
|
||||
if err := os.MkdirAll(tmp, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := testInit(tmp, t)
|
||||
defer os.RemoveAll(tmp)
|
||||
if d == nil {
|
||||
t.Fatalf("Driver should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAufsString(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if d.String() != "aufs" {
|
||||
t.Fatalf("Expected aufs got %s", d.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDirStructure(t *testing.T) {
|
||||
newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
paths := []string{
|
||||
"mnt",
|
||||
"layers",
|
||||
"diff",
|
||||
}
|
||||
|
||||
for _, p := range paths {
|
||||
if _, err := os.Stat(path.Join(tmp, p)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We should be able to create two drivers with the same dir structure
|
||||
func TestNewDriverFromExistingDir(t *testing.T) {
|
||||
if err := os.MkdirAll(tmp, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testInit(tmp, t)
|
||||
testInit(tmp, t)
|
||||
os.RemoveAll(tmp)
|
||||
}
|
||||
|
||||
func TestCreateNewDir(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateNewDirStructure(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
paths := []string{
|
||||
"mnt",
|
||||
"diff",
|
||||
"layers",
|
||||
}
|
||||
|
||||
for _, p := range paths {
|
||||
if _, err := os.Stat(path.Join(tmp, p, "1")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveImage(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := d.Remove("1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
paths := []string{
|
||||
"mnt",
|
||||
"diff",
|
||||
"layers",
|
||||
}
|
||||
|
||||
for _, p := range paths {
|
||||
if _, err := os.Stat(path.Join(tmp, p, "1")); err == nil {
|
||||
t.Fatalf("Error should not be nil because dirs with id 1 should be delted: %s", p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetWithoutParent(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diffPath, err := d.Get("1", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := path.Join(tmp, "diff", "1")
|
||||
if diffPath != expected {
|
||||
t.Fatalf("Expected path %s got %s", expected, diffPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanupWithNoDirs(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Cleanup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanupWithDir(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := d.Cleanup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountedFalseResponse(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
response, err := d.mounted(d.active["1"])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if response != false {
|
||||
t.Fatalf("Response if dir id 1 is mounted should be false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountedTrueReponse(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
defer d.Cleanup()
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := d.Create("2", "1", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err := d.Get("2", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
response, err := d.mounted(d.active["2"])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if response != true {
|
||||
t.Fatalf("Response if dir id 2 is mounted should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountWithParent(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := d.Create("2", "1", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := d.Cleanup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
mntPath, err := d.Get("2", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if mntPath == "" {
|
||||
t.Fatal("mntPath should not be empty string")
|
||||
}
|
||||
|
||||
expected := path.Join(tmp, "mnt", "2")
|
||||
if mntPath != expected {
|
||||
t.Fatalf("Expected %s got %s", expected, mntPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveMountedDir(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := d.Create("2", "1", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := d.Cleanup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
mntPath, err := d.Get("2", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if mntPath == "" {
|
||||
t.Fatal("mntPath should not be empty string")
|
||||
}
|
||||
|
||||
mounted, err := d.mounted(d.active["2"])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !mounted {
|
||||
t.Fatalf("Dir id 2 should be mounted")
|
||||
}
|
||||
|
||||
if err := d.Remove("2"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateWithInvalidParent(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Create("1", "docker", ""); err == nil {
|
||||
t.Fatalf("Error should not be nil with parent does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDiff(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diffPath, err := d.Get("1", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Add a file to the diff path with a fixed size
|
||||
size := int64(1024)
|
||||
|
||||
f, err := os.Create(path.Join(diffPath, "test_file"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Truncate(size); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
a, err := d.Diff("1", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if a == nil {
|
||||
t.Fatalf("Archive should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChanges(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := d.Create("2", "1", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := d.Cleanup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
mntPoint, err := d.Get("2", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create a file to save in the mountpoint
|
||||
f, err := os.Create(path.Join(mntPoint, "test.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := f.WriteString("testline"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
changes, err := d.Changes("2", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(changes) != 1 {
|
||||
t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
|
||||
}
|
||||
change := changes[0]
|
||||
|
||||
expectedPath := "/test.txt"
|
||||
if change.Path != expectedPath {
|
||||
t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
|
||||
}
|
||||
|
||||
if change.Kind != archive.ChangeAdd {
|
||||
t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
|
||||
}
|
||||
|
||||
if err := d.Create("3", "2", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mntPoint, err = d.Get("3", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create a file to save in the mountpoint
|
||||
f, err = os.Create(path.Join(mntPoint, "test2.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := f.WriteString("testline"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
changes, err = d.Changes("3", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(changes) != 1 {
|
||||
t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
|
||||
}
|
||||
change = changes[0]
|
||||
|
||||
expectedPath = "/test2.txt"
|
||||
if change.Path != expectedPath {
|
||||
t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
|
||||
}
|
||||
|
||||
if change.Kind != archive.ChangeAdd {
|
||||
t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiffSize(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diffPath, err := d.Get("1", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Add a file to the diff path with a fixed size
|
||||
size := int64(1024)
|
||||
|
||||
f, err := os.Create(path.Join(diffPath, "test_file"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Truncate(size); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, err := f.Stat()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
size = s.Size()
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diffSize, err := d.DiffSize("1", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if diffSize != size {
|
||||
t.Fatalf("Expected size to be %d got %d", size, diffSize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildDiffSize(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
defer d.Cleanup()
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diffPath, err := d.Get("1", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Add a file to the diff path with a fixed size
|
||||
size := int64(1024)
|
||||
|
||||
f, err := os.Create(path.Join(diffPath, "test_file"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Truncate(size); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, err := f.Stat()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
size = s.Size()
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diffSize, err := d.DiffSize("1", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if diffSize != size {
|
||||
t.Fatalf("Expected size to be %d got %d", size, diffSize)
|
||||
}
|
||||
|
||||
if err := d.Create("2", "1", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diffSize, err = d.DiffSize("2", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// The diff size for the child should be zero
|
||||
if diffSize != 0 {
|
||||
t.Fatalf("Expected size to be %d got %d", 0, diffSize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExists(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
defer d.Cleanup()
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if d.Exists("none") {
|
||||
t.Fatal("id name should not exist in the driver")
|
||||
}
|
||||
|
||||
if !d.Exists("1") {
|
||||
t.Fatal("id 1 should exist in the driver")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
defer d.Cleanup()
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
status := d.Status()
|
||||
if status == nil || len(status) == 0 {
|
||||
t.Fatal("Status should not be nil or empty")
|
||||
}
|
||||
rootDir := status[0]
|
||||
dirs := status[2]
|
||||
if rootDir[0] != "Root Dir" {
|
||||
t.Fatalf("Expected Root Dir got %s", rootDir[0])
|
||||
}
|
||||
if rootDir[1] != d.rootPath() {
|
||||
t.Fatalf("Expected %s got %s", d.rootPath(), rootDir[1])
|
||||
}
|
||||
if dirs[0] != "Dirs" {
|
||||
t.Fatalf("Expected Dirs got %s", dirs[0])
|
||||
}
|
||||
if dirs[1] != "1" {
|
||||
t.Fatalf("Expected 1 got %s", dirs[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyDiff(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
defer d.Cleanup()
|
||||
|
||||
if err := d.Create("1", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diffPath, err := d.Get("1", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Add a file to the diff path with a fixed size
|
||||
size := int64(1024)
|
||||
|
||||
f, err := os.Create(path.Join(diffPath, "test_file"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Truncate(size); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
diff, err := d.Diff("1", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := d.Create("2", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := d.Create("3", "2", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := d.applyDiff("3", diff); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Ensure that the file is in the mount point for id 3
|
||||
|
||||
mountPoint, err := d.Get("3", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := os.Stat(path.Join(mountPoint, "test_file")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func hash(c string) string {
|
||||
h := sha256.New()
|
||||
fmt.Fprint(h, c)
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func testMountMoreThan42Layers(t *testing.T, mountPath string) {
|
||||
if err := os.MkdirAll(mountPath, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer os.RemoveAll(mountPath)
|
||||
d := testInit(mountPath, t).(*Driver)
|
||||
defer d.Cleanup()
|
||||
var last string
|
||||
var expected int
|
||||
|
||||
for i := 1; i < 127; i++ {
|
||||
expected++
|
||||
var (
|
||||
parent = fmt.Sprintf("%d", i-1)
|
||||
current = fmt.Sprintf("%d", i)
|
||||
)
|
||||
|
||||
if parent == "0" {
|
||||
parent = ""
|
||||
} else {
|
||||
parent = hash(parent)
|
||||
}
|
||||
current = hash(current)
|
||||
|
||||
if err := d.Create(current, parent, ""); err != nil {
|
||||
t.Logf("Current layer %d", i)
|
||||
t.Error(err)
|
||||
}
|
||||
point, err := d.Get(current, "")
|
||||
if err != nil {
|
||||
t.Logf("Current layer %d", i)
|
||||
t.Error(err)
|
||||
}
|
||||
f, err := os.Create(path.Join(point, current))
|
||||
if err != nil {
|
||||
t.Logf("Current layer %d", i)
|
||||
t.Error(err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
if i%10 == 0 {
|
||||
if err := os.Remove(path.Join(point, parent)); err != nil {
|
||||
t.Logf("Current layer %d", i)
|
||||
t.Error(err)
|
||||
}
|
||||
expected--
|
||||
}
|
||||
last = current
|
||||
}
|
||||
|
||||
// Perform the actual mount for the top most image
|
||||
point, err := d.Get(last, "")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
files, err := ioutil.ReadDir(point)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(files) != expected {
|
||||
t.Errorf("Expected %d got %d", expected, len(files))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountMoreThan42Layers(t *testing.T) {
|
||||
os.RemoveAll(tmpOuter)
|
||||
testMountMoreThan42Layers(t, tmp)
|
||||
}
|
||||
|
||||
func TestMountMoreThan42LayersMatchingPathLength(t *testing.T) {
|
||||
defer os.RemoveAll(tmpOuter)
|
||||
zeroes := "0"
|
||||
for {
|
||||
// This finds a mount path so that when combined into aufs mount options
|
||||
// 4096 byte boundary would be in between the paths or in permission
|
||||
// section. For '/tmp' it will use '/tmp/aufs-tests/00000000/aufs'
|
||||
mountPath := path.Join(tmpOuter, zeroes, "aufs")
|
||||
pathLength := 77 + len(mountPath)
|
||||
|
||||
if mod := 4095 % pathLength; mod == 0 || mod > pathLength-2 {
|
||||
t.Logf("Using path: %s", mountPath)
|
||||
testMountMoreThan42Layers(t, mountPath)
|
||||
return
|
||||
}
|
||||
zeroes += "0"
|
||||
}
|
||||
}
|
||||
48
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/aufs/dirs.go
generated
vendored
48
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/aufs/dirs.go
generated
vendored
@@ -1,48 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package aufs
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// Return all the directories
|
||||
func loadIds(root string) ([]string, error) {
|
||||
dirs, err := ioutil.ReadDir(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := []string{}
|
||||
for _, d := range dirs {
|
||||
if !d.IsDir() {
|
||||
out = append(out, d.Name())
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Read the layers file for the current id and return all the
|
||||
// layers represented by new lines in the file
|
||||
//
|
||||
// If there are no lines in the file then the id has no parent
|
||||
// and an empty slice is returned.
|
||||
func getParentIds(root, id string) ([]string, error) {
|
||||
f, err := os.Open(path.Join(root, "layers", id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
out := []string{}
|
||||
s := bufio.NewScanner(f)
|
||||
|
||||
for s.Scan() {
|
||||
if t := s.Text(); t != "" {
|
||||
out = append(out, s.Text())
|
||||
}
|
||||
}
|
||||
return out, s.Err()
|
||||
}
|
||||
21
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/aufs/mount.go
generated
vendored
21
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/aufs/mount.go
generated
vendored
@@ -1,21 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package aufs
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Unmount the target specified.
|
||||
func Unmount(target string) error {
|
||||
if err := exec.Command("auplink", target, "flush").Run(); err != nil {
|
||||
logrus.Errorf("Couldn't run auplink before unmount: %s", err)
|
||||
}
|
||||
if err := syscall.Unmount(target, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
7
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/aufs/mount_linux.go
generated
vendored
7
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/aufs/mount_linux.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
package aufs
|
||||
|
||||
import "syscall"
|
||||
|
||||
func mount(source string, target string, fstype string, flags uintptr, data string) error {
|
||||
return syscall.Mount(source, target, fstype, flags, data)
|
||||
}
|
||||
12
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/aufs/mount_unsupported.go
generated
vendored
12
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/aufs/mount_unsupported.go
generated
vendored
@@ -1,12 +0,0 @@
|
||||
// +build !linux
|
||||
|
||||
package aufs
|
||||
|
||||
import "errors"
|
||||
|
||||
// MsRemount declared to specify a non-linux system mount.
|
||||
const MsRemount = 0
|
||||
|
||||
func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
|
||||
return errors.New("mount is not implemented on this platform")
|
||||
}
|
||||
321
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/btrfs/btrfs.go
generated
vendored
321
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/btrfs/btrfs.go
generated
vendored
@@ -1,321 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package btrfs
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <btrfs/ioctl.h>
|
||||
#include <btrfs/ctree.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver"
|
||||
"github.com/hyperhq/hypercli/pkg/idtools"
|
||||
"github.com/hyperhq/hypercli/pkg/mount"
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
)
|
||||
|
||||
func init() {
|
||||
graphdriver.Register("btrfs", Init)
|
||||
}
|
||||
|
||||
// Init returns a new BTRFS driver.
|
||||
// An error is returned if BTRFS is not supported.
|
||||
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
|
||||
fsMagic, err := graphdriver.GetFSMagic(home)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fsMagic != graphdriver.FsMagicBtrfs {
|
||||
return nil, graphdriver.ErrPrerequisites
|
||||
}
|
||||
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := mount.MakePrivate(home); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
driver := &Driver{
|
||||
home: home,
|
||||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
}
|
||||
|
||||
return graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), nil
|
||||
}
|
||||
|
||||
// Driver contains information about the filesystem mounted.
|
||||
type Driver struct {
|
||||
//root of the file system
|
||||
home string
|
||||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
}
|
||||
|
||||
// String prints the name of the driver (btrfs).
|
||||
func (d *Driver) String() string {
|
||||
return "btrfs"
|
||||
}
|
||||
|
||||
// Status returns current driver information in a two dimensional string array.
|
||||
// Output contains "Build Version" and "Library Version" of the btrfs libraries used.
|
||||
// Version information can be used to check compatibility with your kernel.
|
||||
func (d *Driver) Status() [][2]string {
|
||||
status := [][2]string{}
|
||||
if bv := btrfsBuildVersion(); bv != "-" {
|
||||
status = append(status, [2]string{"Build Version", bv})
|
||||
}
|
||||
if lv := btrfsLibVersion(); lv != -1 {
|
||||
status = append(status, [2]string{"Library Version", fmt.Sprintf("%d", lv)})
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
// GetMetadata returns empty metadata for this driver.
|
||||
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Cleanup unmounts the home directory.
|
||||
func (d *Driver) Cleanup() error {
|
||||
return mount.Unmount(d.home)
|
||||
}
|
||||
|
||||
func free(p *C.char) {
|
||||
C.free(unsafe.Pointer(p))
|
||||
}
|
||||
|
||||
func openDir(path string) (*C.DIR, error) {
|
||||
Cpath := C.CString(path)
|
||||
defer free(Cpath)
|
||||
|
||||
dir := C.opendir(Cpath)
|
||||
if dir == nil {
|
||||
return nil, fmt.Errorf("Can't open dir")
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func closeDir(dir *C.DIR) {
|
||||
if dir != nil {
|
||||
C.closedir(dir)
|
||||
}
|
||||
}
|
||||
|
||||
func getDirFd(dir *C.DIR) uintptr {
|
||||
return uintptr(C.dirfd(dir))
|
||||
}
|
||||
|
||||
func subvolCreate(path, name string) error {
|
||||
dir, err := openDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeDir(dir)
|
||||
|
||||
var args C.struct_btrfs_ioctl_vol_args
|
||||
for i, c := range []byte(name) {
|
||||
args.name[i] = C.char(c)
|
||||
}
|
||||
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SUBVOL_CREATE,
|
||||
uintptr(unsafe.Pointer(&args)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("Failed to create btrfs subvolume: %v", errno.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func subvolSnapshot(src, dest, name string) error {
|
||||
srcDir, err := openDir(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeDir(srcDir)
|
||||
|
||||
destDir, err := openDir(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeDir(destDir)
|
||||
|
||||
var args C.struct_btrfs_ioctl_vol_args_v2
|
||||
args.fd = C.__s64(getDirFd(srcDir))
|
||||
for i, c := range []byte(name) {
|
||||
args.name[i] = C.char(c)
|
||||
}
|
||||
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(destDir), C.BTRFS_IOC_SNAP_CREATE_V2,
|
||||
uintptr(unsafe.Pointer(&args)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("Failed to create btrfs snapshot: %v", errno.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isSubvolume(p string) (bool, error) {
|
||||
var bufStat syscall.Stat_t
|
||||
if err := syscall.Lstat(p, &bufStat); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// return true if it is a btrfs subvolume
|
||||
return bufStat.Ino == C.BTRFS_FIRST_FREE_OBJECTID, nil
|
||||
}
|
||||
|
||||
func subvolDelete(dirpath, name string) error {
|
||||
dir, err := openDir(dirpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeDir(dir)
|
||||
fullPath := path.Join(dirpath, name)
|
||||
|
||||
var args C.struct_btrfs_ioctl_vol_args
|
||||
|
||||
// walk the btrfs subvolumes
|
||||
walkSubvolumes := func(p string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) && p != fullPath {
|
||||
// missing most likely because the path was a subvolume that got removed in the previous iteration
|
||||
// since it's gone anyway, we don't care
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("error walking subvolumes: %v", err)
|
||||
}
|
||||
// we want to check children only so skip itself
|
||||
// it will be removed after the filepath walk anyways
|
||||
if f.IsDir() && p != fullPath {
|
||||
sv, err := isSubvolume(p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err)
|
||||
}
|
||||
if sv {
|
||||
if err := subvolDelete(path.Dir(p), f.Name()); err != nil {
|
||||
return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := filepath.Walk(path.Join(dirpath, name), walkSubvolumes); err != nil {
|
||||
return fmt.Errorf("Recursively walking subvolumes for %s failed: %v", dirpath, err)
|
||||
}
|
||||
|
||||
// all subvolumes have been removed
|
||||
// now remove the one originally passed in
|
||||
for i, c := range []byte(name) {
|
||||
args.name[i] = C.char(c)
|
||||
}
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY,
|
||||
uintptr(unsafe.Pointer(&args)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("Failed to destroy btrfs snapshot %s for %s: %v", dirpath, name, errno.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) subvolumesDir() string {
|
||||
return path.Join(d.home, "subvolumes")
|
||||
}
|
||||
|
||||
func (d *Driver) subvolumesDirID(id string) string {
|
||||
return path.Join(d.subvolumesDir(), id)
|
||||
}
|
||||
|
||||
// Create the filesystem with given id.
|
||||
func (d *Driver) Create(id, parent, mountLabel string) error {
|
||||
subvolumes := path.Join(d.home, "subvolumes")
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := idtools.MkdirAllAs(subvolumes, 0700, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
if parent == "" {
|
||||
if err := subvolCreate(subvolumes, id); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
parentDir, err := d.Get(parent, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := subvolSnapshot(parentDir, subvolumes, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a remapped root (user namespaces enabled), change the created snapshot
|
||||
// dir ownership to match
|
||||
if rootUID != 0 || rootGID != 0 {
|
||||
if err := os.Chown(path.Join(subvolumes, id), rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return label.Relabel(path.Join(subvolumes, id), mountLabel, false)
|
||||
}
|
||||
|
||||
// Remove the filesystem with given id.
|
||||
func (d *Driver) Remove(id string) error {
|
||||
dir := d.subvolumesDirID(id)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := subvolDelete(d.subvolumesDir(), id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the requested filesystem id.
|
||||
func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
dir := d.subvolumesDirID(id)
|
||||
st, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !st.IsDir() {
|
||||
return "", fmt.Errorf("%s: not a directory", dir)
|
||||
}
|
||||
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// Put is not implemented for BTRFS as there is no cleanup required for the id.
|
||||
func (d *Driver) Put(id string) error {
|
||||
// Get() creates no runtime resources (like e.g. mounts)
|
||||
// so this doesn't need to do anything.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exists checks if the id exists in the filesystem.
|
||||
func (d *Driver) Exists(id string) bool {
|
||||
dir := d.subvolumesDirID(id)
|
||||
_, err := os.Stat(dir)
|
||||
return err == nil
|
||||
}
|
||||
63
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/btrfs/btrfs_test.go
generated
vendored
63
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/btrfs/btrfs_test.go
generated
vendored
@@ -1,63 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package btrfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver/graphtest"
|
||||
)
|
||||
|
||||
// This avoids creating a new driver for each test if all tests are run
|
||||
// Make sure to put new tests between TestBtrfsSetup and TestBtrfsTeardown
|
||||
func TestBtrfsSetup(t *testing.T) {
|
||||
graphtest.GetDriver(t, "btrfs")
|
||||
}
|
||||
|
||||
func TestBtrfsCreateEmpty(t *testing.T) {
|
||||
graphtest.DriverTestCreateEmpty(t, "btrfs")
|
||||
}
|
||||
|
||||
func TestBtrfsCreateBase(t *testing.T) {
|
||||
graphtest.DriverTestCreateBase(t, "btrfs")
|
||||
}
|
||||
|
||||
func TestBtrfsCreateSnap(t *testing.T) {
|
||||
graphtest.DriverTestCreateSnap(t, "btrfs")
|
||||
}
|
||||
|
||||
func TestBtrfsSubvolDelete(t *testing.T) {
|
||||
d := graphtest.GetDriver(t, "btrfs")
|
||||
if err := d.Create("test", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer graphtest.PutDriver(t)
|
||||
|
||||
dir, err := d.Get("test", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer d.Put("test")
|
||||
|
||||
if err := subvolCreate(dir, "subvoltest"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path.Join(dir, "subvoltest")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := d.Remove("test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path.Join(dir, "subvoltest")); !os.IsNotExist(err) {
|
||||
t.Fatalf("expected not exist error on nested subvol, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBtrfsTeardown(t *testing.T) {
|
||||
graphtest.PutDriver(t)
|
||||
}
|
||||
3
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/btrfs/dummy_unsupported.go
generated
vendored
3
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/btrfs/dummy_unsupported.go
generated
vendored
@@ -1,3 +0,0 @@
|
||||
// +build !linux !cgo
|
||||
|
||||
package btrfs
|
||||
26
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/btrfs/version.go
generated
vendored
26
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/btrfs/version.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
// +build linux,!btrfs_noversion
|
||||
|
||||
package btrfs
|
||||
|
||||
/*
|
||||
#include <btrfs/version.h>
|
||||
|
||||
// around version 3.16, they did not define lib version yet
|
||||
#ifndef BTRFS_LIB_VERSION
|
||||
#define BTRFS_LIB_VERSION -1
|
||||
#endif
|
||||
|
||||
// upstream had removed it, but now it will be coming back
|
||||
#ifndef BTRFS_BUILD_VERSION
|
||||
#define BTRFS_BUILD_VERSION "-"
|
||||
#endif
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func btrfsBuildVersion() string {
|
||||
return string(C.BTRFS_BUILD_VERSION)
|
||||
}
|
||||
|
||||
func btrfsLibVersion() int {
|
||||
return int(C.BTRFS_LIB_VERSION)
|
||||
}
|
||||
14
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/btrfs/version_none.go
generated
vendored
14
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/btrfs/version_none.go
generated
vendored
@@ -1,14 +0,0 @@
|
||||
// +build linux,btrfs_noversion
|
||||
|
||||
package btrfs
|
||||
|
||||
// TODO(vbatts) remove this work-around once supported linux distros are on
|
||||
// btrfs utilities of >= 3.16.1
|
||||
|
||||
func btrfsBuildVersion() string {
|
||||
return "-"
|
||||
}
|
||||
|
||||
func btrfsLibVersion() int {
|
||||
return -1
|
||||
}
|
||||
13
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/btrfs/version_test.go
generated
vendored
13
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/btrfs/version_test.go
generated
vendored
@@ -1,13 +0,0 @@
|
||||
// +build linux,!btrfs_noversion
|
||||
|
||||
package btrfs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLibVersion(t *testing.T) {
|
||||
if btrfsLibVersion() <= 0 {
|
||||
t.Errorf("expected output from btrfs lib version > 0")
|
||||
}
|
||||
}
|
||||
97
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/devmapper/README.md
generated
vendored
97
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/devmapper/README.md
generated
vendored
@@ -1,97 +0,0 @@
|
||||
## devicemapper - a storage backend based on Device Mapper
|
||||
|
||||
### Theory of operation
|
||||
|
||||
The device mapper graphdriver uses the device mapper thin provisioning
|
||||
module (dm-thinp) to implement CoW snapshots. The preferred model is
|
||||
to have a thin pool reserved outside of Docker and passed to the
|
||||
daemon via the `--storage-opt dm.thinpooldev` option.
|
||||
|
||||
As a fallback if no thin pool is provided, loopback files will be
|
||||
created. Loopback is very slow, but can be used without any
|
||||
pre-configuration of storage. It is strongly recommended that you do
|
||||
not use loopback in production. Ensure your Docker daemon has a
|
||||
`--storage-opt dm.thinpooldev` argument provided.
|
||||
|
||||
In loopback, a thin pool is created at `/var/lib/docker/devicemapper`
|
||||
(devicemapper graph location) based on two block devices, one for
|
||||
data and one for metadata. By default these block devices are created
|
||||
automatically by using loopback mounts of automatically created sparse
|
||||
files.
|
||||
|
||||
The default loopback files used are
|
||||
`/var/lib/docker/devicemapper/devicemapper/data` and
|
||||
`/var/lib/docker/devicemapper/devicemapper/metadata`. Additional metadata
|
||||
required to map from docker entities to the corresponding devicemapper
|
||||
volumes is stored in the `/var/lib/docker/devicemapper/devicemapper/json`
|
||||
file (encoded as Json).
|
||||
|
||||
In order to support multiple devicemapper graphs on a system, the thin
|
||||
pool will be named something like: `docker-0:33-19478248-pool`, where
|
||||
the `0:33` part is the minor/major device nr and `19478248` is the
|
||||
inode number of the `/var/lib/docker/devicemapper` directory.
|
||||
|
||||
On the thin pool, docker automatically creates a base thin device,
|
||||
called something like `docker-0:33-19478248-base` of a fixed
|
||||
size. This is automatically formatted with an empty filesystem on
|
||||
creation. This device is the base of all docker images and
|
||||
containers. All base images are snapshots of this device and those
|
||||
images are then in turn used as snapshots for other images and
|
||||
eventually containers.
|
||||
|
||||
### Information on `docker info`
|
||||
|
||||
As of docker-1.4.1, `docker info` when using the `devicemapper` storage driver
|
||||
will display something like:
|
||||
|
||||
$ sudo docker info
|
||||
[...]
|
||||
Storage Driver: devicemapper
|
||||
Pool Name: docker-253:1-17538953-pool
|
||||
Pool Blocksize: 65.54 kB
|
||||
Base Device Size: 107.4 GB
|
||||
Data file: /dev/loop4
|
||||
Metadata file: /dev/loop4
|
||||
Data Space Used: 2.536 GB
|
||||
Data Space Total: 107.4 GB
|
||||
Data Space Available: 104.8 GB
|
||||
Metadata Space Used: 7.93 MB
|
||||
Metadata Space Total: 2.147 GB
|
||||
Metadata Space Available: 2.14 GB
|
||||
Udev Sync Supported: true
|
||||
Data loop file: /home/docker/devicemapper/devicemapper/data
|
||||
Metadata loop file: /home/docker/devicemapper/devicemapper/metadata
|
||||
Library Version: 1.02.82-git (2013-10-04)
|
||||
[...]
|
||||
|
||||
#### status items
|
||||
|
||||
Each item in the indented section under `Storage Driver: devicemapper` are
|
||||
status information about the driver.
|
||||
* `Pool Name` name of the devicemapper pool for this driver.
|
||||
* `Pool Blocksize` tells the blocksize the thin pool was initialized with. This only changes on creation.
|
||||
* `Base Device Size` tells the maximum size of a container and image
|
||||
* `Data file` blockdevice file used for the devicemapper data
|
||||
* `Metadata file` blockdevice file used for the devicemapper metadata
|
||||
* `Data Space Used` tells how much of `Data file` is currently used
|
||||
* `Data Space Total` tells max size the `Data file`
|
||||
* `Data Space Available` tells how much free space there is in the `Data file`. If you are using a loop device this will report the actual space available to the loop device on the underlying filesystem.
|
||||
* `Metadata Space Used` tells how much of `Metadata file` is currently used
|
||||
* `Metadata Space Total` tells max size the `Metadata file`
|
||||
* `Metadata Space Available` tells how much free space there is in the `Metadata file`. If you are using a loop device this will report the actual space available to the loop device on the underlying filesystem.
|
||||
* `Udev Sync Supported` tells whether devicemapper is able to sync with Udev. Should be `true`.
|
||||
* `Data loop file` file attached to `Data file`, if loopback device is used
|
||||
* `Metadata loop file` file attached to `Metadata file`, if loopback device is used
|
||||
* `Library Version` from the libdevmapper used
|
||||
|
||||
### About the devicemapper options
|
||||
|
||||
The devicemapper backend supports some options that you can specify
|
||||
when starting the docker daemon using the `--storage-opt` flags.
|
||||
This uses the `dm` prefix and would be used something like `docker daemon --storage-opt dm.foo=bar`.
|
||||
|
||||
These options are currently documented both in [the man
|
||||
page](../../../man/docker.1.md) and in [the online
|
||||
documentation](https://docs.docker.com/reference/commandline/daemon/#docker-
|
||||
execdriver-option). If you add an options, update both the `man` page and the
|
||||
documentation.
|
||||
2530
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/devmapper/deviceset.go
generated
vendored
2530
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/devmapper/deviceset.go
generated
vendored
File diff suppressed because it is too large
Load Diff
106
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/devmapper/devmapper_doc.go
generated
vendored
106
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/devmapper/devmapper_doc.go
generated
vendored
@@ -1,106 +0,0 @@
|
||||
package devmapper
|
||||
|
||||
// Definition of struct dm_task and sub structures (from lvm2)
|
||||
//
|
||||
// struct dm_ioctl {
|
||||
// /*
|
||||
// * The version number is made up of three parts:
|
||||
// * major - no backward or forward compatibility,
|
||||
// * minor - only backwards compatible,
|
||||
// * patch - both backwards and forwards compatible.
|
||||
// *
|
||||
// * All clients of the ioctl interface should fill in the
|
||||
// * version number of the interface that they were
|
||||
// * compiled with.
|
||||
// *
|
||||
// * All recognized ioctl commands (ie. those that don't
|
||||
// * return -ENOTTY) fill out this field, even if the
|
||||
// * command failed.
|
||||
// */
|
||||
// uint32_t version[3]; /* in/out */
|
||||
// uint32_t data_size; /* total size of data passed in
|
||||
// * including this struct */
|
||||
|
||||
// uint32_t data_start; /* offset to start of data
|
||||
// * relative to start of this struct */
|
||||
|
||||
// uint32_t target_count; /* in/out */
|
||||
// int32_t open_count; /* out */
|
||||
// uint32_t flags; /* in/out */
|
||||
|
||||
// /*
|
||||
// * event_nr holds either the event number (input and output) or the
|
||||
// * udev cookie value (input only).
|
||||
// * The DM_DEV_WAIT ioctl takes an event number as input.
|
||||
// * The DM_SUSPEND, DM_DEV_REMOVE and DM_DEV_RENAME ioctls
|
||||
// * use the field as a cookie to return in the DM_COOKIE
|
||||
// * variable with the uevents they issue.
|
||||
// * For output, the ioctls return the event number, not the cookie.
|
||||
// */
|
||||
// uint32_t event_nr; /* in/out */
|
||||
// uint32_t padding;
|
||||
|
||||
// uint64_t dev; /* in/out */
|
||||
|
||||
// char name[DM_NAME_LEN]; /* device name */
|
||||
// char uuid[DM_UUID_LEN]; /* unique identifier for
|
||||
// * the block device */
|
||||
// char data[7]; /* padding or data */
|
||||
// };
|
||||
|
||||
// struct target {
|
||||
// uint64_t start;
|
||||
// uint64_t length;
|
||||
// char *type;
|
||||
// char *params;
|
||||
|
||||
// struct target *next;
|
||||
// };
|
||||
|
||||
// typedef enum {
|
||||
// DM_ADD_NODE_ON_RESUME, /* add /dev/mapper node with dmsetup resume */
|
||||
// DM_ADD_NODE_ON_CREATE /* add /dev/mapper node with dmsetup create */
|
||||
// } dm_add_node_t;
|
||||
|
||||
// struct dm_task {
|
||||
// int type;
|
||||
// char *dev_name;
|
||||
// char *mangled_dev_name;
|
||||
|
||||
// struct target *head, *tail;
|
||||
|
||||
// int read_only;
|
||||
// uint32_t event_nr;
|
||||
// int major;
|
||||
// int minor;
|
||||
// int allow_default_major_fallback;
|
||||
// uid_t uid;
|
||||
// gid_t gid;
|
||||
// mode_t mode;
|
||||
// uint32_t read_ahead;
|
||||
// uint32_t read_ahead_flags;
|
||||
// union {
|
||||
// struct dm_ioctl *v4;
|
||||
// } dmi;
|
||||
// char *newname;
|
||||
// char *message;
|
||||
// char *geometry;
|
||||
// uint64_t sector;
|
||||
// int no_flush;
|
||||
// int no_open_count;
|
||||
// int skip_lockfs;
|
||||
// int query_inactive_table;
|
||||
// int suppress_identical_reload;
|
||||
// dm_add_node_t add_node;
|
||||
// uint64_t existing_table_size;
|
||||
// int cookie_set;
|
||||
// int new_uuid;
|
||||
// int secure_data;
|
||||
// int retry_remove;
|
||||
// int enable_checks;
|
||||
// int expected_errno;
|
||||
|
||||
// char *uuid;
|
||||
// char *mangled_uuid;
|
||||
// };
|
||||
//
|
||||
110
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/devmapper/devmapper_test.go
generated
vendored
110
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/devmapper/devmapper_test.go
generated
vendored
@@ -1,110 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package devmapper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver"
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver/graphtest"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Reduce the size the the base fs and loopback for the tests
|
||||
defaultDataLoopbackSize = 300 * 1024 * 1024
|
||||
defaultMetaDataLoopbackSize = 200 * 1024 * 1024
|
||||
defaultBaseFsSize = 300 * 1024 * 1024
|
||||
defaultUdevSyncOverride = true
|
||||
if err := graphtest.InitLoopbacks(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// This avoids creating a new driver for each test if all tests are run
|
||||
// Make sure to put new tests between TestDevmapperSetup and TestDevmapperTeardown
|
||||
func TestDevmapperSetup(t *testing.T) {
|
||||
graphtest.GetDriver(t, "devicemapper")
|
||||
}
|
||||
|
||||
func TestDevmapperCreateEmpty(t *testing.T) {
|
||||
graphtest.DriverTestCreateEmpty(t, "devicemapper")
|
||||
}
|
||||
|
||||
func TestDevmapperCreateBase(t *testing.T) {
|
||||
graphtest.DriverTestCreateBase(t, "devicemapper")
|
||||
}
|
||||
|
||||
func TestDevmapperCreateSnap(t *testing.T) {
|
||||
graphtest.DriverTestCreateSnap(t, "devicemapper")
|
||||
}
|
||||
|
||||
func TestDevmapperTeardown(t *testing.T) {
|
||||
graphtest.PutDriver(t)
|
||||
}
|
||||
|
||||
func TestDevmapperReduceLoopBackSize(t *testing.T) {
|
||||
tenMB := int64(10 * 1024 * 1024)
|
||||
testChangeLoopBackSize(t, -tenMB, defaultDataLoopbackSize, defaultMetaDataLoopbackSize)
|
||||
}
|
||||
|
||||
func TestDevmapperIncreaseLoopBackSize(t *testing.T) {
|
||||
tenMB := int64(10 * 1024 * 1024)
|
||||
testChangeLoopBackSize(t, tenMB, defaultDataLoopbackSize+tenMB, defaultMetaDataLoopbackSize+tenMB)
|
||||
}
|
||||
|
||||
func testChangeLoopBackSize(t *testing.T, delta, expectDataSize, expectMetaDataSize int64) {
|
||||
driver := graphtest.GetDriver(t, "devicemapper").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver)
|
||||
defer graphtest.PutDriver(t)
|
||||
// make sure data or metadata loopback size are the default size
|
||||
if s := driver.DeviceSet.Status(); s.Data.Total != uint64(defaultDataLoopbackSize) || s.Metadata.Total != uint64(defaultMetaDataLoopbackSize) {
|
||||
t.Fatalf("data or metadata loop back size is incorrect")
|
||||
}
|
||||
if err := driver.Cleanup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
//Reload
|
||||
d, err := Init(driver.home, []string{
|
||||
fmt.Sprintf("dm.loopdatasize=%d", defaultDataLoopbackSize+delta),
|
||||
fmt.Sprintf("dm.loopmetadatasize=%d", defaultMetaDataLoopbackSize+delta),
|
||||
}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating devicemapper driver: %v", err)
|
||||
}
|
||||
driver = d.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver)
|
||||
if s := driver.DeviceSet.Status(); s.Data.Total != uint64(expectDataSize) || s.Metadata.Total != uint64(expectMetaDataSize) {
|
||||
t.Fatalf("data or metadata loop back size is incorrect")
|
||||
}
|
||||
if err := driver.Cleanup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure devices.Lock() has been release upon return from cleanupDeletedDevices() function
|
||||
func TestDevmapperLockReleasedDeviceDeletion(t *testing.T) {
|
||||
driver := graphtest.GetDriver(t, "devicemapper").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver)
|
||||
defer graphtest.PutDriver(t)
|
||||
|
||||
// Call cleanupDeletedDevices() and after the call take and release
|
||||
// DeviceSet Lock. If lock has not been released, this will hang.
|
||||
driver.DeviceSet.cleanupDeletedDevices()
|
||||
|
||||
doneChan := make(chan bool)
|
||||
|
||||
go func() {
|
||||
driver.DeviceSet.Lock()
|
||||
defer driver.DeviceSet.Unlock()
|
||||
doneChan <- true
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(time.Second * 5):
|
||||
// Timer expired. That means lock was not released upon
|
||||
// function return and we are deadlocked. Release lock
|
||||
// here so that cleanup could succeed and fail the test.
|
||||
driver.DeviceSet.Unlock()
|
||||
t.Fatalf("Could not acquire devices lock after call to cleanupDeletedDevices()")
|
||||
case <-doneChan:
|
||||
}
|
||||
}
|
||||
204
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/devmapper/driver.go
generated
vendored
204
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/devmapper/driver.go
generated
vendored
@@ -1,204 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package devmapper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
|
||||
"github.com/docker/go-units"
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver"
|
||||
"github.com/hyperhq/hypercli/pkg/devicemapper"
|
||||
"github.com/hyperhq/hypercli/pkg/idtools"
|
||||
"github.com/hyperhq/hypercli/pkg/mount"
|
||||
)
|
||||
|
||||
func init() {
|
||||
graphdriver.Register("devicemapper", Init)
|
||||
}
|
||||
|
||||
// Driver contains the device set mounted and the home directory
|
||||
type Driver struct {
|
||||
*DeviceSet
|
||||
home string
|
||||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
}
|
||||
|
||||
// Init creates a driver with the given home and the set of options.
|
||||
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
deviceSet, err := NewDeviceSet(home, true, options, uidMaps, gidMaps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := mount.MakePrivate(home); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Driver{
|
||||
DeviceSet: deviceSet,
|
||||
home: home,
|
||||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
}
|
||||
|
||||
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
|
||||
}
|
||||
|
||||
func (d *Driver) String() string {
|
||||
return "devicemapper"
|
||||
}
|
||||
|
||||
// Status returns the status about the driver in a printable format.
|
||||
// Information returned contains Pool Name, Data File, Metadata file, disk usage by
|
||||
// the data and metadata, etc.
|
||||
func (d *Driver) Status() [][2]string {
|
||||
s := d.DeviceSet.Status()
|
||||
|
||||
status := [][2]string{
|
||||
{"Pool Name", s.PoolName},
|
||||
{"Pool Blocksize", fmt.Sprintf("%s", units.HumanSize(float64(s.SectorSize)))},
|
||||
{"Base Device Size", fmt.Sprintf("%s", units.HumanSize(float64(s.BaseDeviceSize)))},
|
||||
{"Backing Filesystem", s.BaseDeviceFS},
|
||||
{"Data file", s.DataFile},
|
||||
{"Metadata file", s.MetadataFile},
|
||||
{"Data Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Used)))},
|
||||
{"Data Space Total", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Total)))},
|
||||
{"Data Space Available", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Available)))},
|
||||
{"Metadata Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Used)))},
|
||||
{"Metadata Space Total", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Total)))},
|
||||
{"Metadata Space Available", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Available)))},
|
||||
{"Udev Sync Supported", fmt.Sprintf("%v", s.UdevSyncSupported)},
|
||||
{"Deferred Removal Enabled", fmt.Sprintf("%v", s.DeferredRemoveEnabled)},
|
||||
{"Deferred Deletion Enabled", fmt.Sprintf("%v", s.DeferredDeleteEnabled)},
|
||||
{"Deferred Deleted Device Count", fmt.Sprintf("%v", s.DeferredDeletedDeviceCount)},
|
||||
}
|
||||
if len(s.DataLoopback) > 0 {
|
||||
status = append(status, [2]string{"Data loop file", s.DataLoopback})
|
||||
}
|
||||
if len(s.MetadataLoopback) > 0 {
|
||||
status = append(status, [2]string{"Metadata loop file", s.MetadataLoopback})
|
||||
}
|
||||
if vStr, err := devicemapper.GetLibraryVersion(); err == nil {
|
||||
status = append(status, [2]string{"Library Version", vStr})
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
// GetMetadata returns a map of information about the device.
|
||||
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
|
||||
m, err := d.DeviceSet.exportDeviceMetadata(id)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metadata := make(map[string]string)
|
||||
metadata["DeviceId"] = strconv.Itoa(m.deviceID)
|
||||
metadata["DeviceSize"] = strconv.FormatUint(m.deviceSize, 10)
|
||||
metadata["DeviceName"] = m.deviceName
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
// Cleanup unmounts a device.
|
||||
func (d *Driver) Cleanup() error {
|
||||
err := d.DeviceSet.Shutdown()
|
||||
|
||||
if err2 := mount.Unmount(d.home); err == nil {
|
||||
err = err2
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Create adds a device with a given id and the parent.
|
||||
func (d *Driver) Create(id, parent, mountLabel string) error {
|
||||
if err := d.DeviceSet.AddDevice(id, parent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes a device with a given id, unmounts the filesystem.
|
||||
func (d *Driver) Remove(id string) error {
|
||||
if !d.DeviceSet.HasDevice(id) {
|
||||
// Consider removing a non-existing device a no-op
|
||||
// This is useful to be able to progress on container removal
|
||||
// if the underlying device has gone away due to earlier errors
|
||||
return nil
|
||||
}
|
||||
|
||||
// This assumes the device has been properly Get/Put:ed and thus is unmounted
|
||||
if err := d.DeviceSet.DeleteDevice(id, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mp := path.Join(d.home, "mnt", id)
|
||||
if err := os.RemoveAll(mp); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get mounts a device with given id into the root filesystem
|
||||
func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
mp := path.Join(d.home, "mnt", id)
|
||||
|
||||
uid, gid, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Create the target directories if they don't exist
|
||||
if err := idtools.MkdirAllAs(path.Join(d.home, "mnt"), 0755, uid, gid); err != nil && !os.IsExist(err) {
|
||||
return "", err
|
||||
}
|
||||
if err := idtools.MkdirAs(mp, 0755, uid, gid); err != nil && !os.IsExist(err) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Mount the device
|
||||
if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rootFs := path.Join(mp, "rootfs")
|
||||
if err := idtools.MkdirAllAs(rootFs, 0755, uid, gid); err != nil && !os.IsExist(err) {
|
||||
d.DeviceSet.UnmountDevice(id, mp)
|
||||
return "", err
|
||||
}
|
||||
|
||||
idFile := path.Join(mp, "id")
|
||||
if _, err := os.Stat(idFile); err != nil && os.IsNotExist(err) {
|
||||
// Create an "id" file with the container/image id in it to help reconstruct this in case
|
||||
// of later problems
|
||||
if err := ioutil.WriteFile(idFile, []byte(id), 0600); err != nil {
|
||||
d.DeviceSet.UnmountDevice(id, mp)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return rootFs, nil
|
||||
}
|
||||
|
||||
// Put unmounts a device and removes it.
|
||||
func (d *Driver) Put(id string) error {
|
||||
mp := path.Join(d.home, "mnt", id)
|
||||
err := d.DeviceSet.UnmountDevice(id, mp)
|
||||
if err != nil {
|
||||
logrus.Errorf("devmapper: Error unmounting device %s: %s", id, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Exists checks to see if the device exists.
|
||||
func (d *Driver) Exists(id string) bool {
|
||||
return d.DeviceSet.HasDevice(id)
|
||||
}
|
||||
89
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/devmapper/mount.go
generated
vendored
89
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/devmapper/mount.go
generated
vendored
@@ -1,89 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package devmapper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// FIXME: this is copy-pasted from the aufs driver.
|
||||
// It should be moved into the core.
|
||||
|
||||
// Mounted returns true if a mount point exists.
|
||||
func Mounted(mountpoint string) (bool, error) {
|
||||
mntpoint, err := os.Stat(mountpoint)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
parent, err := os.Stat(filepath.Join(mountpoint, ".."))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
mntpointSt := mntpoint.Sys().(*syscall.Stat_t)
|
||||
parentSt := parent.Sys().(*syscall.Stat_t)
|
||||
return mntpointSt.Dev != parentSt.Dev, nil
|
||||
}
|
||||
|
||||
type probeData struct {
|
||||
fsName string
|
||||
magic string
|
||||
offset uint64
|
||||
}
|
||||
|
||||
// ProbeFsType returns the filesystem name for the given device id.
|
||||
func ProbeFsType(device string) (string, error) {
|
||||
probes := []probeData{
|
||||
{"btrfs", "_BHRfS_M", 0x10040},
|
||||
{"ext4", "\123\357", 0x438},
|
||||
{"xfs", "XFSB", 0},
|
||||
}
|
||||
|
||||
maxLen := uint64(0)
|
||||
for _, p := range probes {
|
||||
l := p.offset + uint64(len(p.magic))
|
||||
if l > maxLen {
|
||||
maxLen = l
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Open(device)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buffer := make([]byte, maxLen)
|
||||
l, err := file.Read(buffer)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if uint64(l) != maxLen {
|
||||
return "", fmt.Errorf("devmapper: unable to detect filesystem type of %s, short read", device)
|
||||
}
|
||||
|
||||
for _, p := range probes {
|
||||
if bytes.Equal([]byte(p.magic), buffer[p.offset:p.offset+uint64(len(p.magic))]) {
|
||||
return p.fsName, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("devmapper: Unknown filesystem type on %s", device)
|
||||
}
|
||||
|
||||
func joinMountOptions(a, b string) string {
|
||||
if a == "" {
|
||||
return b
|
||||
}
|
||||
if b == "" {
|
||||
return a
|
||||
}
|
||||
return a + "," + b
|
||||
}
|
||||
299
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/graphtest/graphtest_unix.go
generated
vendored
299
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/graphtest/graphtest_unix.go
generated
vendored
@@ -1,299 +0,0 @@
|
||||
// +build linux freebsd
|
||||
|
||||
package graphtest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver"
|
||||
)
|
||||
|
||||
var (
|
||||
drv *Driver
|
||||
)
|
||||
|
||||
// Driver conforms to graphdriver.Driver interface and
|
||||
// contains information such as root and reference count of the number of clients using it.
|
||||
// This helps in testing drivers added into the framework.
|
||||
type Driver struct {
|
||||
graphdriver.Driver
|
||||
root string
|
||||
refCount int
|
||||
}
|
||||
|
||||
// InitLoopbacks ensures that the loopback devices are properly created within
|
||||
// the system running the device mapper tests.
|
||||
func InitLoopbacks() error {
|
||||
statT, err := getBaseLoopStats()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// create at least 8 loopback files, ya, that is a good number
|
||||
for i := 0; i < 8; i++ {
|
||||
loopPath := fmt.Sprintf("/dev/loop%d", i)
|
||||
// only create new loopback files if they don't exist
|
||||
if _, err := os.Stat(loopPath); err != nil {
|
||||
if mkerr := syscall.Mknod(loopPath,
|
||||
uint32(statT.Mode|syscall.S_IFBLK), int((7<<8)|(i&0xff)|((i&0xfff00)<<12))); mkerr != nil {
|
||||
return mkerr
|
||||
}
|
||||
os.Chown(loopPath, int(statT.Uid), int(statT.Gid))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getBaseLoopStats inspects /dev/loop0 to collect uid,gid, and mode for the
|
||||
// loop0 device on the system. If it does not exist we assume 0,0,0660 for the
|
||||
// stat data
|
||||
func getBaseLoopStats() (*syscall.Stat_t, error) {
|
||||
loop0, err := os.Stat("/dev/loop0")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return &syscall.Stat_t{
|
||||
Uid: 0,
|
||||
Gid: 0,
|
||||
Mode: 0660,
|
||||
}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return loop0.Sys().(*syscall.Stat_t), nil
|
||||
}
|
||||
|
||||
func newDriver(t *testing.T, name string) *Driver {
|
||||
root, err := ioutil.TempDir("/var/tmp", "docker-graphtest-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(root, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d, err := graphdriver.GetDriver(name, root, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Logf("graphdriver: %v\n", err)
|
||||
if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites || err == graphdriver.ErrIncompatibleFS {
|
||||
t.Skipf("Driver %s not supported", name)
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &Driver{d, root, 1}
|
||||
}
|
||||
|
||||
func cleanup(t *testing.T, d *Driver) {
|
||||
if err := drv.Cleanup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
os.RemoveAll(d.root)
|
||||
}
|
||||
|
||||
// GetDriver create a new driver with given name or return a existing driver with the name updating the reference count.
|
||||
func GetDriver(t *testing.T, name string) graphdriver.Driver {
|
||||
if drv == nil {
|
||||
drv = newDriver(t, name)
|
||||
} else {
|
||||
drv.refCount++
|
||||
}
|
||||
return drv
|
||||
}
|
||||
|
||||
// PutDriver removes the driver if it is no longer used and updates the reference count.
|
||||
func PutDriver(t *testing.T) {
|
||||
if drv == nil {
|
||||
t.Skip("No driver to put!")
|
||||
}
|
||||
drv.refCount--
|
||||
if drv.refCount == 0 {
|
||||
cleanup(t, drv)
|
||||
drv = nil
|
||||
}
|
||||
}
|
||||
|
||||
func verifyFile(t *testing.T, path string, mode os.FileMode, uid, gid uint32) {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if fi.Mode()&os.ModeType != mode&os.ModeType {
|
||||
t.Fatalf("Expected %s type 0x%x, got 0x%x", path, mode&os.ModeType, fi.Mode()&os.ModeType)
|
||||
}
|
||||
|
||||
if fi.Mode()&os.ModePerm != mode&os.ModePerm {
|
||||
t.Fatalf("Expected %s mode %o, got %o", path, mode&os.ModePerm, fi.Mode()&os.ModePerm)
|
||||
}
|
||||
|
||||
if fi.Mode()&os.ModeSticky != mode&os.ModeSticky {
|
||||
t.Fatalf("Expected %s sticky 0x%x, got 0x%x", path, mode&os.ModeSticky, fi.Mode()&os.ModeSticky)
|
||||
}
|
||||
|
||||
if fi.Mode()&os.ModeSetuid != mode&os.ModeSetuid {
|
||||
t.Fatalf("Expected %s setuid 0x%x, got 0x%x", path, mode&os.ModeSetuid, fi.Mode()&os.ModeSetuid)
|
||||
}
|
||||
|
||||
if fi.Mode()&os.ModeSetgid != mode&os.ModeSetgid {
|
||||
t.Fatalf("Expected %s setgid 0x%x, got 0x%x", path, mode&os.ModeSetgid, fi.Mode()&os.ModeSetgid)
|
||||
}
|
||||
|
||||
if stat, ok := fi.Sys().(*syscall.Stat_t); ok {
|
||||
if stat.Uid != uid {
|
||||
t.Fatalf("%s no owned by uid %d", path, uid)
|
||||
}
|
||||
if stat.Gid != gid {
|
||||
t.Fatalf("%s not owned by gid %d", path, gid)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// readDir reads a directory just like ioutil.ReadDir()
|
||||
// then hides specific files (currently "lost+found")
|
||||
// so the tests don't "see" it
|
||||
func readDir(dir string) ([]os.FileInfo, error) {
|
||||
a, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := a[:0]
|
||||
for _, x := range a {
|
||||
if x.Name() != "lost+found" { // ext4 always have this dir
|
||||
b = append(b, x)
|
||||
}
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// DriverTestCreateEmpty creates an new image and verifies it is empty and the right metadata
|
||||
func DriverTestCreateEmpty(t *testing.T, drivername string) {
|
||||
driver := GetDriver(t, drivername)
|
||||
defer PutDriver(t)
|
||||
|
||||
if err := driver.Create("empty", "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !driver.Exists("empty") {
|
||||
t.Fatal("Newly created image doesn't exist")
|
||||
}
|
||||
|
||||
dir, err := driver.Get("empty", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
verifyFile(t, dir, 0755|os.ModeDir, 0, 0)
|
||||
|
||||
// Verify that the directory is empty
|
||||
fis, err := readDir(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(fis) != 0 {
|
||||
t.Fatal("New directory not empty")
|
||||
}
|
||||
|
||||
driver.Put("empty")
|
||||
|
||||
if err := driver.Remove("empty"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func createBase(t *testing.T, driver graphdriver.Driver, name string) {
|
||||
// We need to be able to set any perms
|
||||
oldmask := syscall.Umask(0)
|
||||
defer syscall.Umask(oldmask)
|
||||
|
||||
if err := driver.Create(name, "", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dir, err := driver.Get(name, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer driver.Put(name)
|
||||
|
||||
subdir := path.Join(dir, "a subdir")
|
||||
if err := os.Mkdir(subdir, 0705|os.ModeSticky); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Chown(subdir, 1, 2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
file := path.Join(dir, "a file")
|
||||
if err := ioutil.WriteFile(file, []byte("Some data"), 0222|os.ModeSetuid); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyBase(t *testing.T, driver graphdriver.Driver, name string) {
|
||||
dir, err := driver.Get(name, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer driver.Put(name)
|
||||
|
||||
subdir := path.Join(dir, "a subdir")
|
||||
verifyFile(t, subdir, 0705|os.ModeDir|os.ModeSticky, 1, 2)
|
||||
|
||||
file := path.Join(dir, "a file")
|
||||
verifyFile(t, file, 0222|os.ModeSetuid, 0, 0)
|
||||
|
||||
fis, err := readDir(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(fis) != 2 {
|
||||
t.Fatal("Unexpected files in base image")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// DriverTestCreateBase create a base driver and verify.
|
||||
func DriverTestCreateBase(t *testing.T, drivername string) {
|
||||
driver := GetDriver(t, drivername)
|
||||
defer PutDriver(t)
|
||||
|
||||
createBase(t, driver, "Base")
|
||||
verifyBase(t, driver, "Base")
|
||||
|
||||
if err := driver.Remove("Base"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// DriverTestCreateSnap Create a driver and snap and verify.
|
||||
func DriverTestCreateSnap(t *testing.T, drivername string) {
|
||||
driver := GetDriver(t, drivername)
|
||||
defer PutDriver(t)
|
||||
|
||||
createBase(t, driver, "Base")
|
||||
|
||||
if err := driver.Create("Snap", "Base", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
verifyBase(t, driver, "Snap")
|
||||
|
||||
if err := driver.Remove("Snap"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := driver.Remove("Base"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
1
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/graphtest/graphtest_windows.go
generated
vendored
1
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/graphtest/graphtest_windows.go
generated
vendored
@@ -1 +0,0 @@
|
||||
package graphtest
|
||||
169
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/overlay/copy.go
generated
vendored
169
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/overlay/copy.go
generated
vendored
@@ -1,169 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/hyperhq/hypercli/pkg/pools"
|
||||
"github.com/hyperhq/hypercli/pkg/system"
|
||||
)
|
||||
|
||||
type copyFlags int
|
||||
|
||||
const (
|
||||
copyHardlink copyFlags = 1 << iota
|
||||
)
|
||||
|
||||
func copyRegular(srcPath, dstPath string, mode os.FileMode) error {
|
||||
srcFile, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
_, err = pools.Copy(dstFile, srcFile)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func copyXattr(srcPath, dstPath, attr string) error {
|
||||
data, err := system.Lgetxattr(srcPath, attr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if data != nil {
|
||||
if err := system.Lsetxattr(dstPath, attr, data, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyDir(srcDir, dstDir string, flags copyFlags) error {
|
||||
err := filepath.Walk(srcDir, func(srcPath string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Rebase path
|
||||
relPath, err := filepath.Rel(srcDir, srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dstPath := filepath.Join(dstDir, relPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stat, ok := f.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
return fmt.Errorf("Unable to get raw syscall.Stat_t data for %s", srcPath)
|
||||
}
|
||||
|
||||
isHardlink := false
|
||||
|
||||
switch f.Mode() & os.ModeType {
|
||||
case 0: // Regular file
|
||||
if flags©Hardlink != 0 {
|
||||
isHardlink = true
|
||||
if err := os.Link(srcPath, dstPath); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := copyRegular(srcPath, dstPath, f.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case os.ModeDir:
|
||||
if err := os.Mkdir(dstPath, f.Mode()); err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
case os.ModeSymlink:
|
||||
link, err := os.Readlink(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Symlink(link, dstPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case os.ModeNamedPipe:
|
||||
fallthrough
|
||||
case os.ModeSocket:
|
||||
if err := syscall.Mkfifo(dstPath, stat.Mode); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case os.ModeDevice:
|
||||
if err := syscall.Mknod(dstPath, stat.Mode, int(stat.Rdev)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("Unknown file type for %s\n", srcPath)
|
||||
}
|
||||
|
||||
// Everything below is copying metadata from src to dst. All this metadata
|
||||
// already shares an inode for hardlinks.
|
||||
if isHardlink {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := os.Lchown(dstPath, int(stat.Uid), int(stat.Gid)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := copyXattr(srcPath, dstPath, "security.capability"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We need to copy this attribute if it appears in an overlay upper layer, as
|
||||
// this function is used to copy those. It is set by overlay if a directory
|
||||
// is removed and then re-created and should not inherit anything from the
|
||||
// same dir in the lower dir.
|
||||
if err := copyXattr(srcPath, dstPath, "trusted.overlay.opaque"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isSymlink := f.Mode()&os.ModeSymlink != 0
|
||||
|
||||
// There is no LChmod, so ignore mode for symlink. Also, this
|
||||
// must happen after chown, as that can modify the file mode
|
||||
if !isSymlink {
|
||||
if err := os.Chmod(dstPath, f.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// system.Chtimes doesn't support a NOFOLLOW flag atm
|
||||
if !isSymlink {
|
||||
aTime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec))
|
||||
mTime := time.Unix(int64(stat.Mtim.Sec), int64(stat.Mtim.Nsec))
|
||||
if err := system.Chtimes(dstPath, aTime, mTime); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
ts := []syscall.Timespec{stat.Atim, stat.Mtim}
|
||||
if err := system.LUtimesNano(dstPath, ts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
476
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/overlay/overlay.go
generated
vendored
476
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/overlay/overlay.go
generated
vendored
@@ -1,476 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver"
|
||||
"github.com/hyperhq/hypercli/pkg/archive"
|
||||
"github.com/hyperhq/hypercli/pkg/chrootarchive"
|
||||
"github.com/hyperhq/hypercli/pkg/idtools"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
)
|
||||
|
||||
// This is a small wrapper over the NaiveDiffWriter that lets us have a custom
|
||||
// implementation of ApplyDiff()
|
||||
|
||||
var (
|
||||
// ErrApplyDiffFallback is returned to indicate that a normal ApplyDiff is applied as a fallback from Naive diff writer.
|
||||
ErrApplyDiffFallback = fmt.Errorf("Fall back to normal ApplyDiff")
|
||||
)
|
||||
|
||||
// ApplyDiffProtoDriver wraps the ProtoDriver by extending the interface with ApplyDiff method.
|
||||
type ApplyDiffProtoDriver interface {
|
||||
graphdriver.ProtoDriver
|
||||
// ApplyDiff writes the diff to the archive for the given id and parent id.
|
||||
// It returns the size in bytes written if successful, an error ErrApplyDiffFallback is returned otherwise.
|
||||
ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error)
|
||||
}
|
||||
|
||||
type naiveDiffDriverWithApply struct {
|
||||
graphdriver.Driver
|
||||
applyDiff ApplyDiffProtoDriver
|
||||
}
|
||||
|
||||
// NaiveDiffDriverWithApply returns a NaiveDiff driver with custom ApplyDiff.
|
||||
func NaiveDiffDriverWithApply(driver ApplyDiffProtoDriver, uidMaps, gidMaps []idtools.IDMap) graphdriver.Driver {
|
||||
return &naiveDiffDriverWithApply{
|
||||
Driver: graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps),
|
||||
applyDiff: driver,
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyDiff creates a diff layer with either the NaiveDiffDriver or with a fallback.
|
||||
func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff archive.Reader) (int64, error) {
|
||||
b, err := d.applyDiff.ApplyDiff(id, parent, diff)
|
||||
if err == ErrApplyDiffFallback {
|
||||
return d.Driver.ApplyDiff(id, parent, diff)
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
|
||||
// This backend uses the overlay union filesystem for containers
|
||||
// plus hard link file sharing for images.
|
||||
|
||||
// Each container/image can have a "root" subdirectory which is a plain
|
||||
// filesystem hierarchy, or they can use overlay.
|
||||
|
||||
// If they use overlay there is a "upper" directory and a "lower-id"
|
||||
// file, as well as "merged" and "work" directories. The "upper"
|
||||
// directory has the upper layer of the overlay, and "lower-id" contains
|
||||
// the id of the parent whose "root" directory shall be used as the lower
|
||||
// layer in the overlay. The overlay itself is mounted in the "merged"
|
||||
// directory, and the "work" dir is needed for overlay to work.
|
||||
|
||||
// When a overlay layer is created there are two cases, either the
|
||||
// parent has a "root" dir, then we start out with a empty "upper"
|
||||
// directory overlaid on the parents root. This is typically the
|
||||
// case with the init layer of a container which is based on an image.
|
||||
// If there is no "root" in the parent, we inherit the lower-id from
|
||||
// the parent and start by making a copy in the parent's "upper" dir.
|
||||
// This is typically the case for a container layer which copies
|
||||
// its parent -init upper layer.
|
||||
|
||||
// Additionally we also have a custom implementation of ApplyLayer
|
||||
// which makes a recursive copy of the parent "root" layer using
|
||||
// hardlinks to share file data, and then applies the layer on top
|
||||
// of that. This means all child images share file (but not directory)
|
||||
// data with the parent.
|
||||
|
||||
// ActiveMount contains information about the count, path and whether is mounted or not.
|
||||
// This information is part of the Driver, that contains list of active mounts that are part of this overlay.
|
||||
type ActiveMount struct {
|
||||
count int
|
||||
path string
|
||||
mounted bool
|
||||
}
|
||||
|
||||
// Driver contains information about the home directory and the list of active mounts that are created using this driver.
|
||||
type Driver struct {
|
||||
home string
|
||||
sync.Mutex // Protects concurrent modification to active
|
||||
active map[string]*ActiveMount
|
||||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
}
|
||||
|
||||
var backingFs = "<unknown>"
|
||||
|
||||
func init() {
|
||||
graphdriver.Register("overlay", Init)
|
||||
}
|
||||
|
||||
// Init returns the NaiveDiffDriver, a native diff driver for overlay filesystem.
|
||||
// If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error.
|
||||
// If a overlay filesystem is not supported over a existing filesystem then error graphdriver.ErrIncompatibleFS is returned.
|
||||
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
|
||||
if err := supportsOverlay(); err != nil {
|
||||
return nil, graphdriver.ErrNotSupported
|
||||
}
|
||||
|
||||
fsMagic, err := graphdriver.GetFSMagic(home)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fsName, ok := graphdriver.FsNames[fsMagic]; ok {
|
||||
backingFs = fsName
|
||||
}
|
||||
|
||||
// check if they are running over btrfs or aufs
|
||||
switch fsMagic {
|
||||
case graphdriver.FsMagicBtrfs:
|
||||
logrus.Error("'overlay' is not supported over btrfs.")
|
||||
return nil, graphdriver.ErrIncompatibleFS
|
||||
case graphdriver.FsMagicAufs:
|
||||
logrus.Error("'overlay' is not supported over aufs.")
|
||||
return nil, graphdriver.ErrIncompatibleFS
|
||||
case graphdriver.FsMagicZfs:
|
||||
logrus.Error("'overlay' is not supported over zfs.")
|
||||
return nil, graphdriver.ErrIncompatibleFS
|
||||
}
|
||||
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Create the driver home dir
|
||||
if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Driver{
|
||||
home: home,
|
||||
active: make(map[string]*ActiveMount),
|
||||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
}
|
||||
|
||||
return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil
|
||||
}
|
||||
|
||||
func supportsOverlay() error {
|
||||
// We can try to modprobe overlay first before looking at
|
||||
// proc/filesystems for when overlay is supported
|
||||
exec.Command("modprobe", "overlay").Run()
|
||||
|
||||
f, err := os.Open("/proc/filesystems")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
if s.Text() == "nodev\toverlay" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.")
|
||||
return graphdriver.ErrNotSupported
|
||||
}
|
||||
|
||||
func (d *Driver) String() string {
|
||||
return "overlay"
|
||||
}
|
||||
|
||||
// Status returns current driver information in a two dimensional string array.
|
||||
// Output contains "Backing Filesystem" used in this implementation.
|
||||
func (d *Driver) Status() [][2]string {
|
||||
return [][2]string{
|
||||
{"Backing Filesystem", backingFs},
|
||||
}
|
||||
}
|
||||
|
||||
// GetMetadata returns meta data about the overlay driver such as root, LowerDir, UpperDir, WorkDir and MergeDir used to store data.
|
||||
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
|
||||
dir := d.dir(id)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metadata := make(map[string]string)
|
||||
|
||||
// If id has a root, it is an image
|
||||
rootDir := path.Join(dir, "root")
|
||||
if _, err := os.Stat(rootDir); err == nil {
|
||||
metadata["RootDir"] = rootDir
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metadata["LowerDir"] = path.Join(d.dir(string(lowerID)), "root")
|
||||
metadata["UpperDir"] = path.Join(dir, "upper")
|
||||
metadata["WorkDir"] = path.Join(dir, "work")
|
||||
metadata["MergedDir"] = path.Join(dir, "merged")
|
||||
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
// Cleanup simply returns nil and do not change the existing filesystem.
|
||||
// This is required to satisfy the graphdriver.Driver interface.
|
||||
func (d *Driver) Cleanup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create is used to create the upper, lower, and merge directories required for overlay fs for a given id.
|
||||
// The parent filesystem is used to configure these directories for the overlay.
|
||||
func (d *Driver) Create(id, parent, mountLabel string) (retErr error) {
|
||||
dir := d.dir(id)
|
||||
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Clean up on failure
|
||||
if retErr != nil {
|
||||
os.RemoveAll(dir)
|
||||
}
|
||||
}()
|
||||
|
||||
// Toplevel images are just a "root" dir
|
||||
if parent == "" {
|
||||
if err := idtools.MkdirAs(path.Join(dir, "root"), 0755, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
parentDir := d.dir(parent)
|
||||
|
||||
// Ensure parent exists
|
||||
if _, err := os.Lstat(parentDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If parent has a root, just do a overlay to it
|
||||
parentRoot := path.Join(parentDir, "root")
|
||||
|
||||
if s, err := os.Lstat(parentRoot); err == nil {
|
||||
if err := idtools.MkdirAs(path.Join(dir, "upper"), s.Mode(), rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Otherwise, copy the upper and the lower-id from the parent
|
||||
|
||||
lowerID, err := ioutil.ReadFile(path.Join(parentDir, "lower-id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parentUpperDir := path.Join(parentDir, "upper")
|
||||
s, err := os.Lstat(parentUpperDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upperDir := path.Join(dir, "upper")
|
||||
if err := idtools.MkdirAs(upperDir, s.Mode(), rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return copyDir(parentUpperDir, upperDir, 0)
|
||||
}
|
||||
|
||||
func (d *Driver) dir(id string) string {
|
||||
return path.Join(d.home, id)
|
||||
}
|
||||
|
||||
// Remove cleans the directories that are created for this id.
|
||||
func (d *Driver) Remove(id string) error {
|
||||
if err := os.RemoveAll(d.dir(id)); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get creates and mounts the required file system for the given id and returns the mount path.
|
||||
func (d *Driver) Get(id string, mountLabel string) (string, error) {
|
||||
// Protect the d.active from concurrent access
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
mount := d.active[id]
|
||||
if mount != nil {
|
||||
mount.count++
|
||||
return mount.path, nil
|
||||
}
|
||||
|
||||
mount = &ActiveMount{count: 1}
|
||||
|
||||
dir := d.dir(id)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If id has a root, just return it
|
||||
rootDir := path.Join(dir, "root")
|
||||
if _, err := os.Stat(rootDir); err == nil {
|
||||
mount.path = rootDir
|
||||
d.active[id] = mount
|
||||
return mount.path, nil
|
||||
}
|
||||
|
||||
lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
lowerDir := path.Join(d.dir(string(lowerID)), "root")
|
||||
upperDir := path.Join(dir, "upper")
|
||||
workDir := path.Join(dir, "work")
|
||||
mergedDir := path.Join(dir, "merged")
|
||||
|
||||
opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir)
|
||||
if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil {
|
||||
return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
|
||||
}
|
||||
// chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
|
||||
// user namespace requires this to move a directory from lower to upper.
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
|
||||
if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil {
|
||||
return "", err
|
||||
}
|
||||
mount.path = mergedDir
|
||||
mount.mounted = true
|
||||
d.active[id] = mount
|
||||
|
||||
return mount.path, nil
|
||||
}
|
||||
|
||||
// Put unmounts the mount path created for the give id.
|
||||
func (d *Driver) Put(id string) error {
|
||||
// Protect the d.active from concurrent access
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
mount := d.active[id]
|
||||
if mount == nil {
|
||||
logrus.Debugf("Put on a non-mounted device %s", id)
|
||||
// but it might be still here
|
||||
if d.Exists(id) {
|
||||
mergedDir := path.Join(d.dir(id), "merged")
|
||||
err := syscall.Unmount(mergedDir, 0)
|
||||
if err != nil {
|
||||
logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
mount.count--
|
||||
if mount.count > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer delete(d.active, id)
|
||||
if mount.mounted {
|
||||
err := syscall.Unmount(mount.path, 0)
|
||||
if err != nil {
|
||||
logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyDiff applies the new layer on top of the root, if parent does not exist with will return a ErrApplyDiffFallback error.
|
||||
func (d *Driver) ApplyDiff(id string, parent string, diff archive.Reader) (size int64, err error) {
|
||||
dir := d.dir(id)
|
||||
|
||||
if parent == "" {
|
||||
return 0, ErrApplyDiffFallback
|
||||
}
|
||||
|
||||
parentRootDir := path.Join(d.dir(parent), "root")
|
||||
if _, err := os.Stat(parentRootDir); err != nil {
|
||||
return 0, ErrApplyDiffFallback
|
||||
}
|
||||
|
||||
// We now know there is a parent, and it has a "root" directory containing
|
||||
// the full root filesystem. We can just hardlink it and apply the
|
||||
// layer. This relies on two things:
|
||||
// 1) ApplyDiff is only run once on a clean (no writes to upper layer) container
|
||||
// 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks)
|
||||
// These are all currently true and are not expected to break
|
||||
|
||||
tmpRootDir, err := ioutil.TempDir(dir, "tmproot")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
os.RemoveAll(tmpRootDir)
|
||||
} else {
|
||||
os.RemoveAll(path.Join(dir, "upper"))
|
||||
os.RemoveAll(path.Join(dir, "work"))
|
||||
os.RemoveAll(path.Join(dir, "merged"))
|
||||
os.RemoveAll(path.Join(dir, "lower-id"))
|
||||
}
|
||||
}()
|
||||
|
||||
if err = copyDir(parentRootDir, tmpRootDir, copyHardlink); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
options := &archive.TarOptions{UIDMaps: d.uidMaps, GIDMaps: d.gidMaps}
|
||||
if size, err = chrootarchive.ApplyUncompressedLayer(tmpRootDir, diff, options); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
rootDir := path.Join(dir, "root")
|
||||
if err := os.Rename(tmpRootDir, rootDir); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Exists checks to see if the id is already mounted.
|
||||
func (d *Driver) Exists(id string) bool {
|
||||
_, err := os.Stat(d.dir(id))
|
||||
return err == nil
|
||||
}
|
||||
31
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/overlay/overlay_test.go
generated
vendored
31
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/overlay/overlay_test.go
generated
vendored
@@ -1,31 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver/graphtest"
|
||||
)
|
||||
|
||||
// This avoids creating a new driver for each test if all tests are run
|
||||
// Make sure to put new tests between TestOverlaySetup and TestOverlayTeardown
|
||||
func TestOverlaySetup(t *testing.T) {
|
||||
graphtest.GetDriver(t, "overlay")
|
||||
}
|
||||
|
||||
func TestOverlayCreateEmpty(t *testing.T) {
|
||||
graphtest.DriverTestCreateEmpty(t, "overlay")
|
||||
}
|
||||
|
||||
func TestOverlayCreateBase(t *testing.T) {
|
||||
graphtest.DriverTestCreateBase(t, "overlay")
|
||||
}
|
||||
|
||||
func TestOverlayCreateSnap(t *testing.T) {
|
||||
graphtest.DriverTestCreateSnap(t, "overlay")
|
||||
}
|
||||
|
||||
func TestOverlayTeardown(t *testing.T) {
|
||||
graphtest.PutDriver(t)
|
||||
}
|
||||
3
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/overlay/overlay_unsupported.go
generated
vendored
3
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/overlay/overlay_unsupported.go
generated
vendored
@@ -1,3 +0,0 @@
|
||||
// +build !linux
|
||||
|
||||
package overlay
|
||||
8
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/register/register_aufs.go
generated
vendored
8
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/register/register_aufs.go
generated
vendored
@@ -1,8 +0,0 @@
|
||||
// +build !exclude_graphdriver_aufs,linux
|
||||
|
||||
package register
|
||||
|
||||
import (
|
||||
// register the aufs graphdriver
|
||||
_ "github.com/hyperhq/hypercli/daemon/graphdriver/aufs"
|
||||
)
|
||||
8
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/register/register_btrfs.go
generated
vendored
8
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/register/register_btrfs.go
generated
vendored
@@ -1,8 +0,0 @@
|
||||
// +build !exclude_graphdriver_btrfs,linux
|
||||
|
||||
package register
|
||||
|
||||
import (
|
||||
// register the btrfs graphdriver
|
||||
_ "github.com/hyperhq/hypercli/daemon/graphdriver/btrfs"
|
||||
)
|
||||
@@ -1,8 +0,0 @@
|
||||
// +build !exclude_graphdriver_devicemapper,linux
|
||||
|
||||
package register
|
||||
|
||||
import (
|
||||
// register the devmapper graphdriver
|
||||
_ "github.com/hyperhq/hypercli/daemon/graphdriver/devmapper"
|
||||
)
|
||||
8
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/register/register_overlay.go
generated
vendored
8
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/register/register_overlay.go
generated
vendored
@@ -1,8 +0,0 @@
|
||||
// +build !exclude_graphdriver_overlay,linux
|
||||
|
||||
package register
|
||||
|
||||
import (
|
||||
// register the overlay graphdriver
|
||||
_ "github.com/hyperhq/hypercli/daemon/graphdriver/overlay"
|
||||
)
|
||||
6
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/register/register_vfs.go
generated
vendored
6
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/register/register_vfs.go
generated
vendored
@@ -1,6 +0,0 @@
|
||||
package register
|
||||
|
||||
import (
|
||||
// register vfs
|
||||
_ "github.com/hyperhq/hypercli/daemon/graphdriver/vfs"
|
||||
)
|
||||
6
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/register/register_windows.go
generated
vendored
6
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/register/register_windows.go
generated
vendored
@@ -1,6 +0,0 @@
|
||||
package register
|
||||
|
||||
import (
|
||||
// register the windows graph driver
|
||||
_ "github.com/hyperhq/hypercli/daemon/graphdriver/windows"
|
||||
)
|
||||
8
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/register/register_zfs.go
generated
vendored
8
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/register/register_zfs.go
generated
vendored
@@ -1,8 +0,0 @@
|
||||
// +build !exclude_graphdriver_zfs,linux !exclude_graphdriver_zfs,freebsd
|
||||
|
||||
package register
|
||||
|
||||
import (
|
||||
// register the zfs driver
|
||||
_ "github.com/hyperhq/hypercli/daemon/graphdriver/zfs"
|
||||
)
|
||||
135
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/vfs/driver.go
generated
vendored
135
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/vfs/driver.go
generated
vendored
@@ -1,135 +0,0 @@
|
||||
package vfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver"
|
||||
"github.com/hyperhq/hypercli/pkg/chrootarchive"
|
||||
"github.com/hyperhq/hypercli/pkg/idtools"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
)
|
||||
|
||||
var (
|
||||
// CopyWithTar defines the copy method to use.
|
||||
CopyWithTar = chrootarchive.CopyWithTar
|
||||
)
|
||||
|
||||
func init() {
|
||||
graphdriver.Register("vfs", Init)
|
||||
}
|
||||
|
||||
// Init returns a new VFS driver.
|
||||
// This sets the home directory for the driver and returns NaiveDiffDriver.
|
||||
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
d := &Driver{
|
||||
home: home,
|
||||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
}
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
|
||||
}
|
||||
|
||||
// Driver holds information about the driver, home directory of the driver.
|
||||
// Driver implements graphdriver.ProtoDriver. It uses only basic vfs operations.
|
||||
// In order to support layering, files are copied from the parent layer into the new layer. There is no copy-on-write support.
|
||||
// Driver must be wrapped in NaiveDiffDriver to be used as a graphdriver.Driver
|
||||
type Driver struct {
|
||||
home string
|
||||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
}
|
||||
|
||||
func (d *Driver) String() string {
|
||||
return "vfs"
|
||||
}
|
||||
|
||||
// Status is used for implementing the graphdriver.ProtoDriver interface. VFS does not currently have any status information.
|
||||
func (d *Driver) Status() [][2]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMetadata is used for implementing the graphdriver.ProtoDriver interface. VFS does not currently have any meta data.
|
||||
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Cleanup is used to implement graphdriver.ProtoDriver. There is no cleanup required for this driver.
|
||||
func (d *Driver) Cleanup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create prepares the filesystem for the VFS driver and copies the directory for the given id under the parent.
|
||||
func (d *Driver) Create(id, parent, mountLabel string) error {
|
||||
dir := d.dir(id)
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := idtools.MkdirAllAs(filepath.Dir(dir), 0700, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := idtools.MkdirAs(dir, 0755, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
opts := []string{"level:s0"}
|
||||
if _, mountLabel, err := label.InitLabels(opts); err == nil {
|
||||
label.SetFileLabel(dir, mountLabel)
|
||||
}
|
||||
if parent == "" {
|
||||
return nil
|
||||
}
|
||||
parentDir, err := d.Get(parent, "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %s", parent, err)
|
||||
}
|
||||
if err := CopyWithTar(parentDir, dir); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) dir(id string) string {
|
||||
return filepath.Join(d.home, "dir", filepath.Base(id))
|
||||
}
|
||||
|
||||
// Remove deletes the content from the directory for a given id.
|
||||
func (d *Driver) Remove(id string) error {
|
||||
if err := os.RemoveAll(d.dir(id)); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the directory for the given id.
|
||||
func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
dir := d.dir(id)
|
||||
if st, err := os.Stat(dir); err != nil {
|
||||
return "", err
|
||||
} else if !st.IsDir() {
|
||||
return "", fmt.Errorf("%s: not a directory", dir)
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// Put is a noop for vfs that return nil for the error, since this driver has no runtime resources to clean up.
|
||||
func (d *Driver) Put(id string) error {
|
||||
// The vfs driver has no runtime resources (e.g. mounts)
|
||||
// to clean up, so we don't need anything here
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exists checks to see if the directory exists for the given id.
|
||||
func (d *Driver) Exists(id string) bool {
|
||||
_, err := os.Stat(d.dir(id))
|
||||
return err == nil
|
||||
}
|
||||
37
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/vfs/vfs_test.go
generated
vendored
37
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/vfs/vfs_test.go
generated
vendored
@@ -1,37 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package vfs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver/graphtest"
|
||||
|
||||
"github.com/hyperhq/hypercli/pkg/reexec"
|
||||
)
|
||||
|
||||
func init() {
|
||||
reexec.Init()
|
||||
}
|
||||
|
||||
// This avoids creating a new driver for each test if all tests are run
|
||||
// Make sure to put new tests between TestVfsSetup and TestVfsTeardown
|
||||
func TestVfsSetup(t *testing.T) {
|
||||
graphtest.GetDriver(t, "vfs")
|
||||
}
|
||||
|
||||
func TestVfsCreateEmpty(t *testing.T) {
|
||||
graphtest.DriverTestCreateEmpty(t, "vfs")
|
||||
}
|
||||
|
||||
func TestVfsCreateBase(t *testing.T) {
|
||||
graphtest.DriverTestCreateBase(t, "vfs")
|
||||
}
|
||||
|
||||
func TestVfsCreateSnap(t *testing.T) {
|
||||
graphtest.DriverTestCreateSnap(t, "vfs")
|
||||
}
|
||||
|
||||
func TestVfsTeardown(t *testing.T) {
|
||||
graphtest.PutDriver(t)
|
||||
}
|
||||
670
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/windows/windows.go
generated
vendored
670
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/windows/windows.go
generated
vendored
@@ -1,670 +0,0 @@
|
||||
//+build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Microsoft/hcsshim"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver"
|
||||
"github.com/hyperhq/hypercli/pkg/archive"
|
||||
"github.com/hyperhq/hypercli/pkg/chrootarchive"
|
||||
"github.com/hyperhq/hypercli/pkg/idtools"
|
||||
"github.com/hyperhq/hypercli/pkg/ioutils"
|
||||
"github.com/hyperhq/hypercli/pkg/random"
|
||||
)
|
||||
|
||||
// init registers the windows graph drivers to the register.
|
||||
func init() {
|
||||
graphdriver.Register("windowsfilter", InitFilter)
|
||||
graphdriver.Register("windowsdiff", InitDiff)
|
||||
}
|
||||
|
||||
const (
|
||||
// diffDriver is an hcsshim driver type
|
||||
diffDriver = iota
|
||||
// filterDriver is an hcsshim driver type
|
||||
filterDriver
|
||||
)
|
||||
|
||||
// Driver represents a windows graph driver.
|
||||
type Driver struct {
|
||||
// info stores the shim driver information
|
||||
info hcsshim.DriverInfo
|
||||
// Mutex protects concurrent modification to active
|
||||
sync.Mutex
|
||||
// active stores references to the activated layers
|
||||
active map[string]int
|
||||
}
|
||||
|
||||
// InitFilter returns a new Windows storage filter driver.
|
||||
func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
logrus.Debugf("WindowsGraphDriver InitFilter at %s", home)
|
||||
d := &Driver{
|
||||
info: hcsshim.DriverInfo{
|
||||
HomeDir: home,
|
||||
Flavour: filterDriver,
|
||||
},
|
||||
active: make(map[string]int),
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// InitDiff returns a new Windows differencing disk driver.
|
||||
func InitDiff(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
logrus.Debugf("WindowsGraphDriver InitDiff at %s", home)
|
||||
d := &Driver{
|
||||
info: hcsshim.DriverInfo{
|
||||
HomeDir: home,
|
||||
Flavour: diffDriver,
|
||||
},
|
||||
active: make(map[string]int),
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// String returns the string representation of a driver.
|
||||
func (d *Driver) String() string {
|
||||
switch d.info.Flavour {
|
||||
case diffDriver:
|
||||
return "windowsdiff"
|
||||
case filterDriver:
|
||||
return "windowsfilter"
|
||||
default:
|
||||
return "Unknown driver flavour"
|
||||
}
|
||||
}
|
||||
|
||||
// Status returns the status of the driver.
|
||||
func (d *Driver) Status() [][2]string {
|
||||
return [][2]string{
|
||||
{"Windows", ""},
|
||||
}
|
||||
}
|
||||
|
||||
// Exists returns true if the given id is registered with this driver.
|
||||
func (d *Driver) Exists(id string) bool {
|
||||
rID, err := d.resolveID(id)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
result, err := hcsshim.LayerExists(d.info, rID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Create creates a new layer with the given id.
|
||||
func (d *Driver) Create(id, parent, mountLabel string) error {
|
||||
rPId, err := d.resolveID(parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parentChain, err := d.getLayerChain(rPId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var layerChain []string
|
||||
|
||||
parentIsInit := strings.HasSuffix(rPId, "-init")
|
||||
|
||||
if !parentIsInit && rPId != "" {
|
||||
parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
layerChain = []string{parentPath}
|
||||
}
|
||||
|
||||
layerChain = append(layerChain, parentChain...)
|
||||
|
||||
if parentIsInit {
|
||||
if len(layerChain) == 0 {
|
||||
return fmt.Errorf("Cannot create a read/write layer without a parent layer.")
|
||||
}
|
||||
if err := hcsshim.CreateSandboxLayer(d.info, id, layerChain[0], layerChain); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := hcsshim.CreateLayer(d.info, id, rPId); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Lstat(d.dir(parent)); err != nil {
|
||||
if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil {
|
||||
logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2)
|
||||
}
|
||||
return fmt.Errorf("Cannot create layer with missing parent %s: %s", parent, err)
|
||||
}
|
||||
|
||||
if err := d.setLayerChain(id, layerChain); err != nil {
|
||||
if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil {
|
||||
logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// dir returns the absolute path to the layer.
|
||||
func (d *Driver) dir(id string) string {
|
||||
return filepath.Join(d.info.HomeDir, filepath.Base(id))
|
||||
}
|
||||
|
||||
// Remove unmounts and removes the dir information.
|
||||
func (d *Driver) Remove(id string) error {
|
||||
rID, err := d.resolveID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.RemoveAll(filepath.Join(d.info.HomeDir, "sysfile-backups", rID)) // ok to fail
|
||||
return hcsshim.DestroyLayer(d.info, rID)
|
||||
}
|
||||
|
||||
// Get returns the rootfs path for the id. This will mount the dir at it's given path.
|
||||
func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel)
|
||||
var dir string
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
rID, err := d.resolveID(id)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Getting the layer paths must be done outside of the lock.
|
||||
layerChain, err := d.getLayerChain(rID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if d.active[rID] == 0 {
|
||||
if err := hcsshim.ActivateLayer(d.info, rID); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
|
||||
if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
|
||||
logrus.Warnf("Failed to Deactivate %s: %s", id, err)
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
mountPath, err := hcsshim.GetLayerMountPath(d.info, rID)
|
||||
if err != nil {
|
||||
if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
|
||||
logrus.Warnf("Failed to Deactivate %s: %s", id, err)
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
d.active[rID]++
|
||||
|
||||
// If the layer has a mount path, use that. Otherwise, use the
|
||||
// folder path.
|
||||
if mountPath != "" {
|
||||
dir = mountPath
|
||||
} else {
|
||||
dir = d.dir(id)
|
||||
}
|
||||
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// Put adds a new layer to the driver.
|
||||
func (d *Driver) Put(id string) error {
|
||||
logrus.Debugf("WindowsGraphDriver Put() id %s", id)
|
||||
|
||||
rID, err := d.resolveID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
if d.active[rID] > 1 {
|
||||
d.active[rID]--
|
||||
} else if d.active[rID] == 1 {
|
||||
if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hcsshim.DeactivateLayer(d.info, rID); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(d.active, rID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cleanup ensures the information the driver stores is properly removed.
|
||||
func (d *Driver) Cleanup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Diff produces an archive of the changes between the specified
|
||||
// layer and its parent layer which may be "".
|
||||
func (d *Driver) Diff(id, parent string) (arch archive.Archive, err error) {
|
||||
rID, err := d.resolveID(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Getting the layer paths must be done outside of the lock.
|
||||
layerChain, err := d.getLayerChain(rID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
d.Lock()
|
||||
|
||||
// To support export, a layer must be activated but not prepared.
|
||||
if d.info.Flavour == filterDriver {
|
||||
if d.active[rID] == 0 {
|
||||
if err = hcsshim.ActivateLayer(d.info, rID); err != nil {
|
||||
d.Unlock()
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := hcsshim.DeactivateLayer(d.info, rID); err != nil {
|
||||
logrus.Warnf("Failed to Deactivate %s: %s", rID, err)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
if err = hcsshim.UnprepareLayer(d.info, rID); err != nil {
|
||||
d.Unlock()
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
|
||||
logrus.Warnf("Failed to re-PrepareLayer %s: %s", rID, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
d.Unlock()
|
||||
|
||||
return d.exportLayer(rID, layerChain)
|
||||
}
|
||||
|
||||
// Changes produces a list of changes between the specified layer
|
||||
// and its parent layer. If parent is "", then all changes will be ADD changes.
|
||||
func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
|
||||
return nil, fmt.Errorf("The Windows graphdriver does not support Changes()")
|
||||
}
|
||||
|
||||
// ApplyDiff extracts the changeset from the given diff into the
|
||||
// layer with the specified id and parent, returning the size of the
|
||||
// new layer in bytes.
|
||||
func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) {
|
||||
rPId, err := d.resolveID(parent)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if d.info.Flavour == diffDriver {
|
||||
start := time.Now().UTC()
|
||||
logrus.Debugf("WindowsGraphDriver ApplyDiff: Start untar layer")
|
||||
destination := d.dir(id)
|
||||
destination = filepath.Dir(destination)
|
||||
if size, err = chrootarchive.ApplyUncompressedLayer(destination, diff, nil); err != nil {
|
||||
return
|
||||
}
|
||||
logrus.Debugf("WindowsGraphDriver ApplyDiff: Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
parentChain, err := d.getLayerChain(rPId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
layerChain := []string{parentPath}
|
||||
layerChain = append(layerChain, parentChain...)
|
||||
|
||||
if size, err = d.importLayer(id, diff, layerChain); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = d.setLayerChain(id, layerChain); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DiffSize calculates the changes between the specified layer
|
||||
// and its parent and returns the size in bytes of the changes
|
||||
// relative to its base filesystem directory.
|
||||
func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
|
||||
rPId, err := d.resolveID(parent)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
changes, err := d.Changes(id, rPId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
layerFs, err := d.Get(id, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer d.Put(id)
|
||||
|
||||
return archive.ChangesSize(layerFs, changes), nil
|
||||
}
|
||||
|
||||
// CustomImageInfo is the object returned by the driver describing the base
|
||||
// image.
|
||||
type CustomImageInfo struct {
|
||||
ID string
|
||||
Name string
|
||||
Version string
|
||||
Path string
|
||||
Size int64
|
||||
CreatedTime time.Time
|
||||
}
|
||||
|
||||
// GetCustomImageInfos returns the image infos for window specific
|
||||
// base images which should always be present.
|
||||
func (d *Driver) GetCustomImageInfos() ([]CustomImageInfo, error) {
|
||||
strData, err := hcsshim.GetSharedBaseImages()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to restore base images: %s", err)
|
||||
}
|
||||
|
||||
type customImageInfoList struct {
|
||||
Images []CustomImageInfo
|
||||
}
|
||||
|
||||
var infoData customImageInfoList
|
||||
|
||||
if err = json.Unmarshal([]byte(strData), &infoData); err != nil {
|
||||
err = fmt.Errorf("JSON unmarshal returned error=%s", err)
|
||||
logrus.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var images []CustomImageInfo
|
||||
|
||||
for _, imageData := range infoData.Images {
|
||||
folderName := filepath.Base(imageData.Path)
|
||||
|
||||
// Use crypto hash of the foldername to generate a docker style id.
|
||||
h := sha512.Sum384([]byte(folderName))
|
||||
id := fmt.Sprintf("%x", h[:32])
|
||||
|
||||
if err := d.Create(id, "", ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Create the alternate ID file.
|
||||
if err := d.setID(id, folderName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
imageData.ID = id
|
||||
images = append(images, imageData)
|
||||
}
|
||||
|
||||
return images, nil
|
||||
}
|
||||
|
||||
// GetMetadata returns custom driver information.
|
||||
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
|
||||
m := make(map[string]string)
|
||||
m["dir"] = d.dir(id)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// exportLayer generates an archive from a layer based on the given ID.
|
||||
func (d *Driver) exportLayer(id string, parentLayerPaths []string) (arch archive.Archive, err error) {
|
||||
layerFolder := d.dir(id)
|
||||
|
||||
tempFolder := layerFolder + "-" + strconv.FormatUint(uint64(random.Rand.Uint32()), 10)
|
||||
if err = os.MkdirAll(tempFolder, 0755); err != nil {
|
||||
logrus.Errorf("Could not create %s %s", tempFolder, err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_, folderName := filepath.Split(tempFolder)
|
||||
if err2 := hcsshim.DestroyLayer(d.info, folderName); err2 != nil {
|
||||
logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = hcsshim.ExportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
archive, err := archive.Tar(tempFolder, archive.Uncompressed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return ioutils.NewReadCloserWrapper(archive, func() error {
|
||||
err := archive.Close()
|
||||
d.Put(id)
|
||||
_, folderName := filepath.Split(tempFolder)
|
||||
if err2 := hcsshim.DestroyLayer(d.info, folderName); err2 != nil {
|
||||
logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
|
||||
}
|
||||
return err
|
||||
}), nil
|
||||
|
||||
}
|
||||
|
||||
// importLayer adds a new layer to the tag and graph store based on the given data.
|
||||
func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPaths []string) (size int64, err error) {
|
||||
layerFolder := d.dir(id)
|
||||
|
||||
tempFolder := layerFolder + "-" + strconv.FormatUint(uint64(random.Rand.Uint32()), 10)
|
||||
if err = os.MkdirAll(tempFolder, 0755); err != nil {
|
||||
logrus.Errorf("Could not create %s %s", tempFolder, err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_, folderName := filepath.Split(tempFolder)
|
||||
if err2 := hcsshim.DestroyLayer(d.info, folderName); err2 != nil {
|
||||
logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
|
||||
}
|
||||
}()
|
||||
|
||||
start := time.Now().UTC()
|
||||
logrus.Debugf("Start untar layer")
|
||||
if size, err = chrootarchive.ApplyLayer(tempFolder, layerData); err != nil {
|
||||
return
|
||||
}
|
||||
err = copySysFiles(tempFolder, filepath.Join(d.info.HomeDir, "sysfile-backups", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
|
||||
|
||||
if err = hcsshim.ImportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// resolveID computes the layerID information based on the given id.
|
||||
func (d *Driver) resolveID(id string) (string, error) {
|
||||
content, err := ioutil.ReadFile(filepath.Join(d.dir(id), "layerID"))
|
||||
if os.IsNotExist(err) {
|
||||
return id, nil
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
// setID stores the layerId in disk.
|
||||
func (d *Driver) setID(id, altID string) error {
|
||||
err := ioutil.WriteFile(filepath.Join(d.dir(id), "layerId"), []byte(altID), 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getLayerChain returns the layer chain information.
|
||||
func (d *Driver) getLayerChain(id string) ([]string, error) {
|
||||
jPath := filepath.Join(d.dir(id), "layerchain.json")
|
||||
content, err := ioutil.ReadFile(jPath)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("Unable to read layerchain file - %s", err)
|
||||
}
|
||||
|
||||
var layerChain []string
|
||||
err = json.Unmarshal(content, &layerChain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to unmarshall layerchain json - %s", err)
|
||||
}
|
||||
|
||||
return layerChain, nil
|
||||
}
|
||||
|
||||
// setLayerChain stores the layer chain information in disk.
|
||||
func (d *Driver) setLayerChain(id string, chain []string) error {
|
||||
content, err := json.Marshal(&chain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to marshall layerchain json - %s", err)
|
||||
}
|
||||
|
||||
jPath := filepath.Join(d.dir(id), "layerchain.json")
|
||||
err = ioutil.WriteFile(jPath, content, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to write layerchain file - %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiffPath returns a directory that contains files needed to construct layer diff.
|
||||
func (d *Driver) DiffPath(id string) (path string, release func() error, err error) {
|
||||
id, err = d.resolveID(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Getting the layer paths must be done outside of the lock.
|
||||
layerChain, err := d.getLayerChain(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
layerFolder := d.dir(id)
|
||||
tempFolder := layerFolder + "-" + strconv.FormatUint(uint64(random.Rand.Uint32()), 10)
|
||||
if err = os.MkdirAll(tempFolder, 0755); err != nil {
|
||||
logrus.Errorf("Could not create %s %s", tempFolder, err)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_, folderName := filepath.Split(tempFolder)
|
||||
if err2 := hcsshim.DestroyLayer(d.info, folderName); err2 != nil {
|
||||
logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = hcsshim.ExportLayer(d.info, id, tempFolder, layerChain); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = copySysFiles(filepath.Join(d.info.HomeDir, "sysfile-backups", id), tempFolder)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return tempFolder, func() error {
|
||||
// TODO: activate layers and release here?
|
||||
_, folderName := filepath.Split(tempFolder)
|
||||
return hcsshim.DestroyLayer(d.info, folderName)
|
||||
}, nil
|
||||
}
|
||||
|
||||
var sysFileWhiteList = []string{
|
||||
"Hives\\*",
|
||||
"Files\\BOOTNXT",
|
||||
"tombstones.txt",
|
||||
}
|
||||
|
||||
// note this only handles files
|
||||
func copySysFiles(src string, dest string) error {
|
||||
if err := os.MkdirAll(dest, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
|
||||
rel, err := filepath.Rel(src, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, sysfile := range sysFileWhiteList {
|
||||
if matches, err := filepath.Match(sysfile, rel); err != nil || !matches {
|
||||
continue
|
||||
}
|
||||
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !fi.Mode().IsRegular() {
|
||||
continue
|
||||
}
|
||||
|
||||
targetPath := filepath.Join(dest, rel)
|
||||
if err = os.MkdirAll(filepath.Dir(targetPath), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
in, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := os.Create(targetPath)
|
||||
if err != nil {
|
||||
in.Close()
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(out, in)
|
||||
in.Close()
|
||||
out.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
2
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/zfs/MAINTAINERS
generated
vendored
2
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/zfs/MAINTAINERS
generated
vendored
@@ -1,2 +0,0 @@
|
||||
Jörg Thalheim <joerg@higgsboson.tk> (@Mic92)
|
||||
Arthur Gautier <baloo@gandi.net> (@baloose)
|
||||
333
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/zfs/zfs.go
generated
vendored
333
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/zfs/zfs.go
generated
vendored
@@ -1,333 +0,0 @@
|
||||
// +build linux freebsd
|
||||
|
||||
package zfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver"
|
||||
"github.com/hyperhq/hypercli/pkg/idtools"
|
||||
"github.com/hyperhq/hypercli/pkg/mount"
|
||||
"github.com/hyperhq/hypercli/pkg/parsers"
|
||||
zfs "github.com/mistifyio/go-zfs"
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
)
|
||||
|
||||
type zfsOptions struct {
|
||||
fsName string
|
||||
mountPath string
|
||||
}
|
||||
|
||||
func init() {
|
||||
graphdriver.Register("zfs", Init)
|
||||
}
|
||||
|
||||
// Logger returns a zfs logger implementation.
|
||||
type Logger struct{}
|
||||
|
||||
// Log wraps log message from ZFS driver with a prefix '[zfs]'.
|
||||
func (*Logger) Log(cmd []string) {
|
||||
logrus.Debugf("[zfs] %s", strings.Join(cmd, " "))
|
||||
}
|
||||
|
||||
// Init returns a new ZFS driver.
|
||||
// It takes base mount path and a array of options which are represented as key value pairs.
|
||||
// Each option is in the for key=value. 'zfs.fsname' is expected to be a valid key in the options.
|
||||
func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
var err error
|
||||
|
||||
if _, err := exec.LookPath("zfs"); err != nil {
|
||||
logrus.Debugf("[zfs] zfs command is not available: %v", err)
|
||||
return nil, graphdriver.ErrPrerequisites
|
||||
}
|
||||
|
||||
file, err := os.OpenFile("/dev/zfs", os.O_RDWR, 600)
|
||||
if err != nil {
|
||||
logrus.Debugf("[zfs] cannot open /dev/zfs: %v", err)
|
||||
return nil, graphdriver.ErrPrerequisites
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
options, err := parseOptions(opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.mountPath = base
|
||||
|
||||
rootdir := path.Dir(base)
|
||||
|
||||
if options.fsName == "" {
|
||||
err = checkRootdirFs(rootdir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if options.fsName == "" {
|
||||
options.fsName, err = lookupZfsDataset(rootdir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
zfs.SetLogger(new(Logger))
|
||||
|
||||
filesystems, err := zfs.Filesystems(options.fsName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Cannot find root filesystem %s: %v", options.fsName, err)
|
||||
}
|
||||
|
||||
filesystemsCache := make(map[string]bool, len(filesystems))
|
||||
var rootDataset *zfs.Dataset
|
||||
for _, fs := range filesystems {
|
||||
if fs.Name == options.fsName {
|
||||
rootDataset = fs
|
||||
}
|
||||
filesystemsCache[fs.Name] = true
|
||||
}
|
||||
|
||||
if rootDataset == nil {
|
||||
return nil, fmt.Errorf("BUG: zfs get all -t filesystem -rHp '%s' should contain '%s'", options.fsName, options.fsName)
|
||||
}
|
||||
|
||||
d := &Driver{
|
||||
dataset: rootDataset,
|
||||
options: options,
|
||||
filesystemsCache: filesystemsCache,
|
||||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
}
|
||||
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
|
||||
}
|
||||
|
||||
func parseOptions(opt []string) (zfsOptions, error) {
|
||||
var options zfsOptions
|
||||
options.fsName = ""
|
||||
for _, option := range opt {
|
||||
key, val, err := parsers.ParseKeyValueOpt(option)
|
||||
if err != nil {
|
||||
return options, err
|
||||
}
|
||||
key = strings.ToLower(key)
|
||||
switch key {
|
||||
case "zfs.fsname":
|
||||
options.fsName = val
|
||||
default:
|
||||
return options, fmt.Errorf("Unknown option %s", key)
|
||||
}
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func lookupZfsDataset(rootdir string) (string, error) {
|
||||
var stat syscall.Stat_t
|
||||
if err := syscall.Stat(rootdir, &stat); err != nil {
|
||||
return "", fmt.Errorf("Failed to access '%s': %s", rootdir, err)
|
||||
}
|
||||
wantedDev := stat.Dev
|
||||
|
||||
mounts, err := mount.GetMounts()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, m := range mounts {
|
||||
if err := syscall.Stat(m.Mountpoint, &stat); err != nil {
|
||||
logrus.Debugf("[zfs] failed to stat '%s' while scanning for zfs mount: %v", m.Mountpoint, err)
|
||||
continue // may fail on fuse file systems
|
||||
}
|
||||
|
||||
if stat.Dev == wantedDev && m.Fstype == "zfs" {
|
||||
return m.Source, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Failed to find zfs dataset mounted on '%s' in /proc/mounts", rootdir)
|
||||
}
|
||||
|
||||
// Driver holds information about the driver, such as zfs dataset, options and cache.
|
||||
type Driver struct {
|
||||
dataset *zfs.Dataset
|
||||
options zfsOptions
|
||||
sync.Mutex // protects filesystem cache against concurrent access
|
||||
filesystemsCache map[string]bool
|
||||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
}
|
||||
|
||||
func (d *Driver) String() string {
|
||||
return "zfs"
|
||||
}
|
||||
|
||||
// Cleanup is used to implement graphdriver.ProtoDriver. There is no cleanup required for this driver.
|
||||
func (d *Driver) Cleanup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Status returns information about the ZFS filesystem. It returns a two dimensional array of information
|
||||
// such as pool name, dataset name, disk usage, parent quota and compression used.
|
||||
// Currently it return 'Zpool', 'Zpool Health', 'Parent Dataset', 'Space Used By Parent',
|
||||
// 'Space Available', 'Parent Quota' and 'Compression'.
|
||||
func (d *Driver) Status() [][2]string {
|
||||
parts := strings.Split(d.dataset.Name, "/")
|
||||
pool, err := zfs.GetZpool(parts[0])
|
||||
|
||||
var poolName, poolHealth string
|
||||
if err == nil {
|
||||
poolName = pool.Name
|
||||
poolHealth = pool.Health
|
||||
} else {
|
||||
poolName = fmt.Sprintf("error while getting pool information %v", err)
|
||||
poolHealth = "not available"
|
||||
}
|
||||
|
||||
quota := "no"
|
||||
if d.dataset.Quota != 0 {
|
||||
quota = strconv.FormatUint(d.dataset.Quota, 10)
|
||||
}
|
||||
|
||||
return [][2]string{
|
||||
{"Zpool", poolName},
|
||||
{"Zpool Health", poolHealth},
|
||||
{"Parent Dataset", d.dataset.Name},
|
||||
{"Space Used By Parent", strconv.FormatUint(d.dataset.Used, 10)},
|
||||
{"Space Available", strconv.FormatUint(d.dataset.Avail, 10)},
|
||||
{"Parent Quota", quota},
|
||||
{"Compression", d.dataset.Compression},
|
||||
}
|
||||
}
|
||||
|
||||
// GetMetadata returns image/container metadata related to graph driver
|
||||
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *Driver) cloneFilesystem(name, parentName string) error {
|
||||
snapshotName := fmt.Sprintf("%d", time.Now().Nanosecond())
|
||||
parentDataset := zfs.Dataset{Name: parentName}
|
||||
snapshot, err := parentDataset.Snapshot(snapshotName /*recursive */, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = snapshot.Clone(name, map[string]string{"mountpoint": "legacy"})
|
||||
if err == nil {
|
||||
d.Lock()
|
||||
d.filesystemsCache[name] = true
|
||||
d.Unlock()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
snapshot.Destroy(zfs.DestroyDeferDeletion)
|
||||
return err
|
||||
}
|
||||
return snapshot.Destroy(zfs.DestroyDeferDeletion)
|
||||
}
|
||||
|
||||
func (d *Driver) zfsPath(id string) string {
|
||||
return d.options.fsName + "/" + id
|
||||
}
|
||||
|
||||
func (d *Driver) mountPath(id string) string {
|
||||
return path.Join(d.options.mountPath, "graph", getMountpoint(id))
|
||||
}
|
||||
|
||||
// Create prepares the dataset and filesystem for the ZFS driver for the given id under the parent.
|
||||
func (d *Driver) Create(id string, parent string, mountLabel string) error {
|
||||
err := d.create(id, parent)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if zfsError, ok := err.(*zfs.Error); ok {
|
||||
if !strings.HasSuffix(zfsError.Stderr, "dataset already exists\n") {
|
||||
return err
|
||||
}
|
||||
// aborted build -> cleanup
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
dataset := zfs.Dataset{Name: d.zfsPath(id)}
|
||||
if err := dataset.Destroy(zfs.DestroyRecursiveClones); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// retry
|
||||
return d.create(id, parent)
|
||||
}
|
||||
|
||||
func (d *Driver) create(id, parent string) error {
|
||||
name := d.zfsPath(id)
|
||||
if parent == "" {
|
||||
mountoptions := map[string]string{"mountpoint": "legacy"}
|
||||
fs, err := zfs.CreateFilesystem(name, mountoptions)
|
||||
if err == nil {
|
||||
d.Lock()
|
||||
d.filesystemsCache[fs.Name] = true
|
||||
d.Unlock()
|
||||
}
|
||||
return err
|
||||
}
|
||||
return d.cloneFilesystem(name, d.zfsPath(parent))
|
||||
}
|
||||
|
||||
// Remove deletes the dataset, filesystem and the cache for the given id.
|
||||
func (d *Driver) Remove(id string) error {
|
||||
name := d.zfsPath(id)
|
||||
dataset := zfs.Dataset{Name: name}
|
||||
err := dataset.Destroy(zfs.DestroyRecursive)
|
||||
if err == nil {
|
||||
d.Lock()
|
||||
delete(d.filesystemsCache, name)
|
||||
d.Unlock()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Get returns the mountpoint for the given id after creating the target directories if necessary.
|
||||
func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
mountpoint := d.mountPath(id)
|
||||
filesystem := d.zfsPath(id)
|
||||
options := label.FormatMountLabel("", mountLabel)
|
||||
logrus.Debugf(`[zfs] mount("%s", "%s", "%s")`, filesystem, mountpoint, options)
|
||||
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Create the target directories if they don't exist
|
||||
if err := idtools.MkdirAllAs(mountpoint, 0755, rootUID, rootGID); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = mount.Mount(filesystem, mountpoint, "zfs", options)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err)
|
||||
}
|
||||
|
||||
return mountpoint, nil
|
||||
}
|
||||
|
||||
// Put removes the existing mountpoint for the given id if it exists.
|
||||
func (d *Driver) Put(id string) error {
|
||||
mountpoint := d.mountPath(id)
|
||||
logrus.Debugf(`[zfs] unmount("%s")`, mountpoint)
|
||||
|
||||
if err := mount.Unmount(mountpoint); err != nil {
|
||||
return fmt.Errorf("error unmounting to %s: %v", mountpoint, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exists checks to see if the cache entry exists for the given id.
|
||||
func (d *Driver) Exists(id string) bool {
|
||||
return d.filesystemsCache[d.zfsPath(id)] == true
|
||||
}
|
||||
38
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/zfs/zfs_freebsd.go
generated
vendored
38
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/zfs/zfs_freebsd.go
generated
vendored
@@ -1,38 +0,0 @@
|
||||
package zfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver"
|
||||
)
|
||||
|
||||
func checkRootdirFs(rootdir string) error {
|
||||
var buf syscall.Statfs_t
|
||||
if err := syscall.Statfs(rootdir, &buf); err != nil {
|
||||
return fmt.Errorf("Failed to access '%s': %s", rootdir, err)
|
||||
}
|
||||
|
||||
// on FreeBSD buf.Fstypename contains ['z', 'f', 's', 0 ... ]
|
||||
if (buf.Fstypename[0] != 122) || (buf.Fstypename[1] != 102) || (buf.Fstypename[2] != 115) || (buf.Fstypename[3] != 0) {
|
||||
logrus.Debugf("[zfs] no zfs dataset found for rootdir '%s'", rootdir)
|
||||
return graphdriver.ErrPrerequisites
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMountpoint(id string) string {
|
||||
maxlen := 12
|
||||
|
||||
// we need to preserve filesystem suffix
|
||||
suffix := strings.SplitN(id, "-", 2)
|
||||
|
||||
if len(suffix) > 1 {
|
||||
return id[:maxlen] + "-" + suffix[1]
|
||||
}
|
||||
|
||||
return id[:maxlen]
|
||||
}
|
||||
27
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/zfs/zfs_linux.go
generated
vendored
27
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/zfs/zfs_linux.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
package zfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver"
|
||||
)
|
||||
|
||||
func checkRootdirFs(rootdir string) error {
|
||||
var buf syscall.Statfs_t
|
||||
if err := syscall.Statfs(rootdir, &buf); err != nil {
|
||||
return fmt.Errorf("Failed to access '%s': %s", rootdir, err)
|
||||
}
|
||||
|
||||
if graphdriver.FsMagic(buf.Type) != graphdriver.FsMagicZfs {
|
||||
logrus.Debugf("[zfs] no zfs dataset found for rootdir '%s'", rootdir)
|
||||
return graphdriver.ErrPrerequisites
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMountpoint(id string) string {
|
||||
return id
|
||||
}
|
||||
31
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/zfs/zfs_test.go
generated
vendored
31
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/zfs/zfs_test.go
generated
vendored
@@ -1,31 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package zfs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/graphdriver/graphtest"
|
||||
)
|
||||
|
||||
// This avoids creating a new driver for each test if all tests are run
|
||||
// Make sure to put new tests between TestZfsSetup and TestZfsTeardown
|
||||
func TestZfsSetup(t *testing.T) {
|
||||
graphtest.GetDriver(t, "zfs")
|
||||
}
|
||||
|
||||
func TestZfsCreateEmpty(t *testing.T) {
|
||||
graphtest.DriverTestCreateEmpty(t, "zfs")
|
||||
}
|
||||
|
||||
func TestZfsCreateBase(t *testing.T) {
|
||||
graphtest.DriverTestCreateBase(t, "zfs")
|
||||
}
|
||||
|
||||
func TestZfsCreateSnap(t *testing.T) {
|
||||
graphtest.DriverTestCreateSnap(t, "zfs")
|
||||
}
|
||||
|
||||
func TestZfsTeardown(t *testing.T) {
|
||||
graphtest.PutDriver(t)
|
||||
}
|
||||
11
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/zfs/zfs_unsupported.go
generated
vendored
11
vendor/github.com/hyperhq/hypercli/daemon/graphdriver/zfs/zfs_unsupported.go
generated
vendored
@@ -1,11 +0,0 @@
|
||||
// +build !linux,!freebsd
|
||||
|
||||
package zfs
|
||||
|
||||
func checkRootdirFs(rootdir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMountpoint(id string) string {
|
||||
return id
|
||||
}
|
||||
Reference in New Issue
Block a user