1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Fix unmount issues

This commit is contained in:
Michael Crosby 2013-12-17 18:40:20 -08:00
parent 45d7dcfea2
commit a6fdc5d208
4 changed files with 150 additions and 46 deletions

View file

@ -295,7 +295,7 @@ func (a *Driver) unmount(id string) error {
func (a *Driver) mounted(id string) (bool, error) { func (a *Driver) mounted(id string) (bool, error) {
target := path.Join(a.rootPath(), "mnt", id) target := path.Join(a.rootPath(), "mnt", id)
return Mounted(target) return graphdriver.Mounted(target)
} }
// During cleanup aufs needs to unmount all mountpoints // During cleanup aufs needs to unmount all mountpoints

View file

@ -2,9 +2,7 @@ package aufs
import ( import (
"github.com/dotcloud/docker/utils" "github.com/dotcloud/docker/utils"
"os"
"os/exec" "os/exec"
"path/filepath"
"syscall" "syscall"
) )
@ -17,21 +15,3 @@ func Unmount(target string) error {
} }
return nil return nil
} }
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
}

View file

@ -1,6 +1,7 @@
package graphdriver package graphdriver
import ( import (
"bufio"
"fmt" "fmt"
"github.com/dotcloud/docker/archive" "github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/utils" "github.com/dotcloud/docker/utils"
@ -8,8 +9,11 @@ import (
"path" "path"
"strings" "strings"
"syscall" "syscall"
"time"
) )
const mountinfoFormat = "%d %d %d:%d %s %s %s - %s %s %s"
type InitFunc func(root string) (Driver, error) type InitFunc func(root string) (Driver, error)
type Driver interface { type Driver interface {
@ -99,16 +103,24 @@ func New(root string) (driver Driver, err error) {
} }
func (m *Mount) Mount(root string) error { func (m *Mount) Mount(root string) error {
var ( target := path.Join(root, m.Target)
flag int
data []string
target = path.Join(root, m.Target)
)
if mounted, err := Mounted(target); err != nil || mounted { if mounted, err := Mounted(target); err != nil || mounted {
return err 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 { flags := map[string]struct {
clear bool clear bool
flag int flag int
@ -140,7 +152,7 @@ func (m *Mount) Mount(root string) error {
"nostrictatime": {true, syscall.MS_STRICTATIME}, "nostrictatime": {true, syscall.MS_STRICTATIME},
} }
for _, o := range strings.Split(m.Options, ",") { for _, o := range strings.Split(options, ",") {
// If the option does not exist in the flags table then it is a // If the option does not exist in the flags table then it is a
// data value for a specific fs type // data value for a specific fs type
if f, exists := flags[o]; exists { if f, exists := flags[o]; exists {
@ -153,35 +165,73 @@ func (m *Mount) Mount(root string) error {
data = append(data, o) data = append(data, o)
} }
} }
return flag, strings.Join(data, ",")
if err := syscall.Mount(m.Device, target, m.Type, uintptr(flag), strings.Join(data, ",")); err != nil {
panic(err)
}
return nil
} }
func (m *Mount) Unmount(root string) error { func (m *Mount) Unmount(root string) (err error) {
target := path.Join(root, m.Target) target := path.Join(root, m.Target)
if mounted, err := Mounted(target); err != nil || !mounted { if mounted, err := Mounted(target); err != nil || !mounted {
return err return err
} }
return syscall.Unmount(target, 0)
// 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) { func Mounted(mountpoint string) (bool, error) {
mntpoint, err := os.Stat(mountpoint) entries, err := parseMountTable()
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
parent, err := os.Stat(path.Join(mountpoint, ".."))
if err != nil { if err != nil {
return false, err return false, err
} }
mntpointSt := mntpoint.Sys().(*syscall.Stat_t)
parentSt := parent.Sys().(*syscall.Stat_t)
return mntpointSt.Dev != parentSt.Dev, nil // 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
} }

View file

@ -0,0 +1,74 @@
package graphdriver
import (
"os"
"path"
"syscall"
"testing"
)
func TestMountOptionsParsing(t *testing.T) {
options := "bind,ro,size=10k"
flag, data := parseOptions(options)
if data != "size=10k" {
t.Fatalf("Expected size=10 got %s", data)
}
expectedFlag := syscall.MS_BIND | syscall.MS_RDONLY
if flag != expectedFlag {
t.Fatalf("Expected %d got %d", expectedFlag, flag)
}
}
func TestMounted(t *testing.T) {
tmp := path.Join(os.TempDir(), "graphdriver-tests")
if err := os.MkdirAll(tmp, 0777); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmp)
var (
sourcePath = path.Join(tmp, "sourcefile.txt")
targetPath = path.Join(tmp, "targetfile.txt")
)
f, err := os.Create(sourcePath)
if err != nil {
t.Fatal(err)
}
f.WriteString("hello")
f.Close()
f, err = os.Create(targetPath)
if err != nil {
t.Fatal(err)
}
f.Close()
mount := &Mount{
Device: sourcePath,
Target: targetPath,
Type: "none",
Options: "bind,ro",
}
if err := mount.Mount("/"); err != nil {
t.Fatal(err)
}
defer func() {
if err := mount.Unmount("/"); err != nil {
t.Fatal(err)
}
}()
mounted, err := Mounted(targetPath)
if err != nil {
t.Fatal(err)
}
if !mounted {
t.Fatalf("Expected %s to be mounted", targetPath)
}
}