mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
237 lines
5.6 KiB
Go
237 lines
5.6 KiB
Go
package graphdriver
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"github.com/dotcloud/docker/archive"
|
|
"github.com/dotcloud/docker/utils"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
const mountinfoFormat = "%d %d %d:%d %s %s %s - %s %s %s"
|
|
|
|
type InitFunc func(root string) (Driver, error)
|
|
|
|
type Driver interface {
|
|
String() string
|
|
|
|
Create(id, parent string) error
|
|
Remove(id string) error
|
|
|
|
Get(id string) (dir string, err error)
|
|
Exists(id string) bool
|
|
|
|
Status() [][2]string
|
|
|
|
Cleanup() error
|
|
}
|
|
|
|
type Differ interface {
|
|
Diff(id string) (archive.Archive, error)
|
|
Changes(id string) ([]archive.Change, error)
|
|
ApplyDiff(id string, diff archive.Archive) error
|
|
DiffSize(id string) (bytes int64, err error)
|
|
}
|
|
|
|
type Mount struct {
|
|
Device string
|
|
Target string
|
|
Type string
|
|
Options string
|
|
}
|
|
|
|
var (
|
|
DefaultDriver string
|
|
// All registred drivers
|
|
drivers map[string]InitFunc
|
|
// Slice of drivers that should be used in an order
|
|
priority = []string{
|
|
"aufs",
|
|
"devicemapper",
|
|
"vfs",
|
|
}
|
|
)
|
|
|
|
func init() {
|
|
drivers = make(map[string]InitFunc)
|
|
}
|
|
|
|
func Register(name string, initFunc InitFunc) error {
|
|
if _, exists := drivers[name]; exists {
|
|
return fmt.Errorf("Name already registered %s", name)
|
|
}
|
|
drivers[name] = initFunc
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetDriver(name, home string) (Driver, error) {
|
|
if initFunc, exists := drivers[name]; exists {
|
|
return initFunc(path.Join(home, name))
|
|
}
|
|
return nil, fmt.Errorf("No such driver: %s", name)
|
|
}
|
|
|
|
func New(root string) (driver Driver, err error) {
|
|
for _, name := range []string{os.Getenv("DOCKER_DRIVER"), DefaultDriver} {
|
|
if name != "" {
|
|
return GetDriver(name, root)
|
|
}
|
|
}
|
|
|
|
// Check for priority drivers first
|
|
for _, name := range priority {
|
|
if driver, err = GetDriver(name, root); err != nil {
|
|
utils.Debugf("Error loading driver %s: %s", name, err)
|
|
continue
|
|
}
|
|
return driver, nil
|
|
}
|
|
|
|
// Check all registered drivers if no priority driver is found
|
|
for _, initFunc := range drivers {
|
|
if driver, err = initFunc(root); err != nil {
|
|
continue
|
|
}
|
|
return driver, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
func (m *Mount) Mount(root string) error {
|
|
target := path.Join(root, m.Target)
|
|
if mounted, err := Mounted(target); err != nil || mounted {
|
|
return err
|
|
}
|
|
|
|
flag, data := parseOptions(m.Options)
|
|
if err := syscall.Mount(m.Device, target, m.Type, uintptr(flag), data); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parseOptions(options string) (int, string) {
|
|
var (
|
|
flag int
|
|
data []string
|
|
)
|
|
|
|
flags := map[string]struct {
|
|
clear bool
|
|
flag int
|
|
}{
|
|
"defaults": {false, 0},
|
|
"ro": {false, syscall.MS_RDONLY},
|
|
"rw": {true, syscall.MS_RDONLY},
|
|
"suid": {true, syscall.MS_NOSUID},
|
|
"nosuid": {false, syscall.MS_NOSUID},
|
|
"dev": {true, syscall.MS_NODEV},
|
|
"nodev": {false, syscall.MS_NODEV},
|
|
"exec": {true, syscall.MS_NOEXEC},
|
|
"noexec": {false, syscall.MS_NOEXEC},
|
|
"sync": {false, syscall.MS_SYNCHRONOUS},
|
|
"async": {true, syscall.MS_SYNCHRONOUS},
|
|
"dirsync": {false, syscall.MS_DIRSYNC},
|
|
"remount": {false, syscall.MS_REMOUNT},
|
|
"mand": {false, syscall.MS_MANDLOCK},
|
|
"nomand": {true, syscall.MS_MANDLOCK},
|
|
"atime": {true, syscall.MS_NOATIME},
|
|
"noatime": {false, syscall.MS_NOATIME},
|
|
"diratime": {true, syscall.MS_NODIRATIME},
|
|
"nodiratime": {false, syscall.MS_NODIRATIME},
|
|
"bind": {false, syscall.MS_BIND},
|
|
"rbind": {false, syscall.MS_BIND | syscall.MS_REC},
|
|
"relatime": {false, syscall.MS_RELATIME},
|
|
"norelatime": {true, syscall.MS_RELATIME},
|
|
"strictatime": {false, syscall.MS_STRICTATIME},
|
|
"nostrictatime": {true, syscall.MS_STRICTATIME},
|
|
}
|
|
|
|
for _, o := range strings.Split(options, ",") {
|
|
// If the option does not exist in the flags table then it is a
|
|
// data value for a specific fs type
|
|
if f, exists := flags[o]; exists {
|
|
if f.clear {
|
|
flag &= ^f.flag
|
|
} else {
|
|
flag |= f.flag
|
|
}
|
|
} else {
|
|
data = append(data, o)
|
|
}
|
|
}
|
|
return flag, strings.Join(data, ",")
|
|
}
|
|
|
|
func (m *Mount) Unmount(root string) (err error) {
|
|
target := path.Join(root, m.Target)
|
|
if mounted, err := Mounted(target); err != nil || !mounted {
|
|
return err
|
|
}
|
|
|
|
// Simple retry logic for unmount
|
|
for i := 0; i < 10; i++ {
|
|
if err = syscall.Unmount(target, 0); err == nil {
|
|
return nil
|
|
}
|
|
utils.Debugf("[Unmount] %s", err)
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
return
|
|
}
|
|
|
|
func Mounted(mountpoint string) (bool, error) {
|
|
entries, err := parseMountTable()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
// Search the table for the mountpoint
|
|
for _, e := range entries {
|
|
if e.mountpoint == mountpoint {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
// Represents one line from /proc/self/mountinfo
|
|
type procEntry struct {
|
|
id, parent, major, minor int
|
|
source, mountpoint, fstype, device string
|
|
vfsopts, opts string
|
|
}
|
|
|
|
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from bind mounts
|
|
//
|
|
// 180 20 0:2851 / /var/lib/docker/aufs/mnt/a22632d4ed3cb2438246064408f9f07734cbd331f50c81f1ca3dcbd78541ce83 rw,relatime - aufs none rw,si=e9663ac1adbdb4f8
|
|
func parseMountTable() ([]*procEntry, error) {
|
|
f, err := os.Open("/proc/self/mountinfo")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
s := bufio.NewScanner(f)
|
|
out := []*procEntry{}
|
|
for s.Scan() {
|
|
if err := s.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p := &procEntry{}
|
|
if _, err := fmt.Sscanf(s.Text(), mountinfoFormat,
|
|
&p.id, &p.parent, &p.major, &p.minor,
|
|
&p.source, &p.mountpoint, &p.vfsopts, &p.fstype,
|
|
&p.device, &p.opts); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, p)
|
|
}
|
|
return out, nil
|
|
}
|