diff options
| author | Alexander Larsson <alexl@redhat.com> | 2013-11-21 21:45:48 +0100 |
|---|---|---|
| committer | Alexander Larsson <alexl@redhat.com> | 2014-01-22 21:44:09 +0100 |
| commit | e51af36a85126aca6bf6da5291eaf960fd82aa56 (patch) | |
| tree | 093185c738454c1d527deb80b60b9d4bbb448756 /graphdriver | |
| parent | 700db10c850f4721ab890c1d4f0fa22a2fbaa29d (diff) | |
| download | docker-e51af36a85126aca6bf6da5291eaf960fd82aa56.tar.gz | |
Add experimenta btrfs driver
This is an experimental btrfs driver. To use it you must have
/var/lib/docker mounted on a btrfs filesystem and explicitly
specify DOCKER_DRIVER=btrfs in the docker daemon environment.
It works by using subvolumes for the docker image/container layers.
Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
Diffstat (limited to 'graphdriver')
| -rw-r--r-- | graphdriver/btrfs/btrfs.go | 215 | ||||
| -rw-r--r-- | graphdriver/btrfs/dummy_unsupported.go | 3 | ||||
| -rw-r--r-- | graphdriver/driver.go | 2 |
3 files changed, 220 insertions, 0 deletions
diff --git a/graphdriver/btrfs/btrfs.go b/graphdriver/btrfs/btrfs.go new file mode 100644 index 0000000000..52ed66b174 --- /dev/null +++ b/graphdriver/btrfs/btrfs.go @@ -0,0 +1,215 @@ +// +build linux + +package btrfs + +/* +#include <stdlib.h> +#include <sys/ioctl.h> +#include <linux/fs.h> +#include <errno.h> +#include <sys/types.h> +#include <dirent.h> +#include <linux/btrfs.h> + +*/ +import "C" +import ( + "fmt" + "github.com/dotcloud/docker/graphdriver" + "os" + "path" + "syscall" + "unsafe" +) + +func init() { + graphdriver.Register("btrfs", Init) +} + +func Init(home string) (graphdriver.Driver, error) { + rootdir := path.Dir(home) + + var buf syscall.Statfs_t + if err := syscall.Statfs(rootdir, &buf); err != nil { + return nil, err + } + + if buf.Type != 0x9123683E { + return nil, fmt.Errorf("%s is not a btrfs filesystem", rootdir) + } + + return &Driver{ + home: home, + }, nil +} + +type Driver struct { + home string +} + +func (d *Driver) String() string { + return "btrfs" +} + +func (d *Driver) Status() [][2]string { + return nil +} + +func (d *Driver) Cleanup() error { + return nil +} + +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("Can't create subvolume") + } + 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("Can't create subvolume") + } + return nil +} + +func subvolDelete(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_SNAP_DESTROY, + uintptr(unsafe.Pointer(&args))) + if errno != 0 { + return fmt.Errorf("Can't create subvolume") + } + 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) +} + +func (d *Driver) Create(id string, parent string) error { + subvolumes := path.Join(d.home, "subvolumes") + if err := os.MkdirAll(subvolumes, 0700); 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 + } + } + return nil +} + +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 + } + return os.RemoveAll(dir) +} + +func (d *Driver) Get(id 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 +} + +func (d *Driver) Put(id string) { +} + +func (d *Driver) Exists(id string) bool { + dir := d.subvolumesDirId(id) + _, err := os.Stat(dir) + return err == nil +} diff --git a/graphdriver/btrfs/dummy_unsupported.go b/graphdriver/btrfs/dummy_unsupported.go new file mode 100644 index 0000000000..5efd18081f --- /dev/null +++ b/graphdriver/btrfs/dummy_unsupported.go @@ -0,0 +1,3 @@ +// +build !linux + +package btrfs diff --git a/graphdriver/driver.go b/graphdriver/driver.go index 2be3f05f3a..c0ed00b0ad 100644 --- a/graphdriver/driver.go +++ b/graphdriver/driver.go @@ -41,6 +41,8 @@ var ( "aufs", "devicemapper", "vfs", + // experimental, has to be enabled manually for now + "btrfs", } ) |
