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:
parent
45d7dcfea2
commit
a6fdc5d208
4 changed files with 150 additions and 46 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
74
graphdriver/driver_test.go
Normal file
74
graphdriver/driver_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue