Support FreeBSD on pkg/mount

Docker-DCO-1.1-Signed-off-by: Kato Kazuyoshi <kato.kazuyoshi@gmail.com> (github: kzys)
This commit is contained in:
Kato Kazuyoshi 2014-03-21 00:52:12 +09:00
parent 243b3164c8
commit c0b1b13860
12 changed files with 312 additions and 137 deletions

62
mount/flags.go Normal file
View file

@ -0,0 +1,62 @@
package mount
import (
"strings"
)
// Parse fstab type mount options into mount() flags
// and device specific data
func parseOptions(options string) (int, string) {
var (
flag int
data []string
)
flags := map[string]struct {
clear bool
flag int
}{
"defaults": {false, 0},
"ro": {false, RDONLY},
"rw": {true, RDONLY},
"suid": {true, NOSUID},
"nosuid": {false, NOSUID},
"dev": {true, NODEV},
"nodev": {false, NODEV},
"exec": {true, NOEXEC},
"noexec": {false, NOEXEC},
"sync": {false, SYNCHRONOUS},
"async": {true, SYNCHRONOUS},
"dirsync": {false, DIRSYNC},
"remount": {false, REMOUNT},
"mand": {false, MANDLOCK},
"nomand": {true, MANDLOCK},
"atime": {true, NOATIME},
"noatime": {false, NOATIME},
"diratime": {true, NODIRATIME},
"nodiratime": {false, NODIRATIME},
"bind": {false, BIND},
"rbind": {false, RBIND},
"private": {false, PRIVATE},
"relatime": {false, RELATIME},
"norelatime": {true, RELATIME},
"strictatime": {false, STRICTATIME},
"nostrictatime": {true, STRICTATIME},
}
for _, o := range strings.Split(options, ",") {
// If the option does not exist in the flags table or the flag
// is not supported on the platform,
// then it is a data value for a specific fs type
if f, exists := flags[o]; exists && f.flag != 0 {
if f.clear {
flag &= ^f.flag
} else {
flag |= f.flag
}
} else {
data = append(data, o)
}
}
return flag, strings.Join(data, ",")
}

28
mount/flags_freebsd.go Normal file
View file

@ -0,0 +1,28 @@
// +build freebsd,cgo
package mount
/*
#include <sys/mount.h>
*/
import "C"
const (
RDONLY = C.MNT_RDONLY
NOSUID = C.MNT_NOSUID
NOEXEC = C.MNT_NOEXEC
SYNCHRONOUS = C.MNT_SYNCHRONOUS
NOATIME = C.MNT_NOATIME
BIND = 0
DIRSYNC = 0
MANDLOCK = 0
NODEV = 0
NODIRATIME = 0
PRIVATE = 0
RBIND = 0
RELATIVE = 0
RELATIME = 0
REMOUNT = 0
STRICTATIME = 0
)

View file

@ -3,62 +3,23 @@
package mount package mount
import ( import (
"strings"
"syscall" "syscall"
) )
// Parse fstab type mount options into mount() flags const (
// and device specific data RDONLY = syscall.MS_RDONLY
func parseOptions(options string) (int, string) { NOSUID = syscall.MS_NOSUID
var ( NODEV = syscall.MS_NODEV
flag int NOEXEC = syscall.MS_NOEXEC
data []string SYNCHRONOUS = syscall.MS_SYNCHRONOUS
) DIRSYNC = syscall.MS_DIRSYNC
REMOUNT = syscall.MS_REMOUNT
flags := map[string]struct { MANDLOCK = syscall.MS_MANDLOCK
clear bool NOATIME = syscall.MS_NOATIME
flag int NODIRATIME = syscall.MS_NODIRATIME
}{ BIND = syscall.MS_BIND
"defaults": {false, 0}, RBIND = syscall.MS_BIND | syscall.MS_REC
"ro": {false, syscall.MS_RDONLY}, PRIVATE = syscall.MS_PRIVATE
"rw": {true, syscall.MS_RDONLY}, RELATIME = syscall.MS_RELATIME
"suid": {true, syscall.MS_NOSUID}, STRICTATIME = syscall.MS_STRICTATIME
"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},
"private": {false, syscall.MS_PRIVATE},
"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, ",")
}

View file

@ -1,7 +1,22 @@
// +build !linux !amd64 // +build !linux,!freebsd linux,!amd64 freebsd,!cgo
package mount package mount
func parseOptions(options string) (int, string) { const (
panic("Not implemented") BIND = 0
} DIRSYNC = 0
MANDLOCK = 0
NOATIME = 0
NODEV = 0
NODIRATIME = 0
NOEXEC = 0
NOSUID = 0
PRIVATE = 0
RBIND = 0
RELATIME = 0
RELATIVE = 0
REMOUNT = 0
STRICTATIME = 0
SYNCHRONOUS = 0
RDONLY = 0
)

View file

@ -3,12 +3,11 @@ package mount
import ( import (
"os" "os"
"path" "path"
"syscall"
"testing" "testing"
) )
func TestMountOptionsParsing(t *testing.T) { func TestMountOptionsParsing(t *testing.T) {
options := "bind,ro,size=10k" options := "noatime,ro,size=10k"
flag, data := parseOptions(options) flag, data := parseOptions(options)
@ -16,7 +15,7 @@ func TestMountOptionsParsing(t *testing.T) {
t.Fatalf("Expected size=10 got %s", data) t.Fatalf("Expected size=10 got %s", data)
} }
expectedFlag := syscall.MS_BIND | syscall.MS_RDONLY expectedFlag := NOATIME | RDONLY
if flag != expectedFlag { if flag != expectedFlag {
t.Fatalf("Expected %d got %d", expectedFlag, flag) t.Fatalf("Expected %d got %d", expectedFlag, flag)
@ -108,7 +107,7 @@ func TestMountReadonly(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
if err := Unmount(targetPath); err != nil { if err := Unmount(targetDir); err != nil {
t.Fatal(err) t.Fatal(err)
} }
}() }()

59
mount/mounter_freebsd.go Normal file
View file

@ -0,0 +1,59 @@
package mount
/*
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/_iovec.h>
#include <sys/mount.h>
#include <sys/param.h>
*/
import "C"
import (
"fmt"
"strings"
"syscall"
"unsafe"
)
func allocateIOVecs(options []string) []C.struct_iovec {
out := make([]C.struct_iovec, len(options))
for i, option := range options {
out[i].iov_base = unsafe.Pointer(C.CString(option))
out[i].iov_len = C.size_t(len(option) + 1)
}
return out
}
func mount(device, target, mType string, flag uintptr, data string) error {
isNullFS := false
xs := strings.Split(data, ",")
for _, x := range xs {
if x == "bind" {
isNullFS = true
}
}
options := []string{"fspath", target}
if isNullFS {
options = append(options, "fstype", "nullfs", "target", device)
} else {
options = append(options, "fstype", mType, "from", device)
}
rawOptions := allocateIOVecs(options)
for _, rawOption := range rawOptions {
defer C.free(rawOption.iov_base)
}
if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 {
reason := C.GoString(C.strerror(*C.__error()))
return fmt.Errorf("Failed to call nmount: %s", reason)
}
return nil
}
func unmount(target string, flag int) error {
return syscall.Unmount(target, flag)
}

View file

@ -1,4 +1,4 @@
// +build !linux !amd64 // +build !linux,!freebsd linux,!amd64 freebsd,!cgo
package mount package mount

View file

@ -1,79 +1,7 @@
package mount package mount
import (
"bufio"
"fmt"
"io"
"os"
"strings"
)
const (
/* 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
(1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
(1) mount ID: unique identifier of the mount (may be reused after umount)
(2) parent ID: ID of parent (or of self for the top of the mount tree)
(3) major:minor: value of st_dev for files on filesystem
(4) root: root of the mount within the filesystem
(5) mount point: mount point relative to the process's root
(6) mount options: per mount options
(7) optional fields: zero or more fields of the form "tag[:value]"
(8) separator: marks the end of the optional fields
(9) filesystem type: name of filesystem of the form "type[.subtype]"
(10) mount source: filesystem specific information or "none"
(11) super options: per super block options*/
mountinfoFormat = "%d %d %d:%d %s %s %s "
)
type MountInfo struct { type MountInfo struct {
Id, Parent, Major, Minor int Id, Parent, Major, Minor int
Root, Mountpoint, Opts string Root, Mountpoint, Opts string
Fstype, Source, VfsOpts string Fstype, Source, VfsOpts string
} }
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from bind mounts
func parseMountTable() ([]*MountInfo, error) {
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return nil, err
}
defer f.Close()
return parseInfoFile(f)
}
func parseInfoFile(r io.Reader) ([]*MountInfo, error) {
var (
s = bufio.NewScanner(r)
out = []*MountInfo{}
)
for s.Scan() {
if err := s.Err(); err != nil {
return nil, err
}
var (
p = &MountInfo{}
text = s.Text()
)
if _, err := fmt.Sscanf(text, mountinfoFormat,
&p.Id, &p.Parent, &p.Major, &p.Minor,
&p.Root, &p.Mountpoint, &p.Opts); err != nil {
return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err)
}
// Safe as mountinfo encodes mountpoints with spaces as \040.
index := strings.Index(text, " - ")
postSeparatorFields := strings.Fields(text[index+3:])
if len(postSeparatorFields) != 3 {
return nil, fmt.Errorf("Error did not find 3 fields post '-' in '%s'", text)
}
p.Fstype = postSeparatorFields[0]
p.Source = postSeparatorFields[1]
p.VfsOpts = postSeparatorFields[2]
out = append(out, p)
}
return out, nil
}

View file

@ -0,0 +1,38 @@
package mount
/*
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
*/
import "C"
import (
"fmt"
"reflect"
"unsafe"
)
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from bind mounts
func parseMountTable() ([]*MountInfo, error) {
var rawEntries *C.struct_statfs
count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT))
if count == 0 {
return nil, fmt.Errorf("Failed to call getmntinfo")
}
var entries []C.struct_statfs
header := (*reflect.SliceHeader)(unsafe.Pointer(&entries))
header.Cap = count
header.Len = count
header.Data = uintptr(unsafe.Pointer(rawEntries))
var out []*MountInfo
for _, entry := range entries {
var mountinfo MountInfo
mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0])
out = append(out, &mountinfo)
}
return out, nil
}

73
mount/mountinfo_linux.go Normal file
View file

@ -0,0 +1,73 @@
package mount
import (
"bufio"
"fmt"
"io"
"os"
"strings"
)
const (
/* 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
(1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
(1) mount ID: unique identifier of the mount (may be reused after umount)
(2) parent ID: ID of parent (or of self for the top of the mount tree)
(3) major:minor: value of st_dev for files on filesystem
(4) root: root of the mount within the filesystem
(5) mount point: mount point relative to the process's root
(6) mount options: per mount options
(7) optional fields: zero or more fields of the form "tag[:value]"
(8) separator: marks the end of the optional fields
(9) filesystem type: name of filesystem of the form "type[.subtype]"
(10) mount source: filesystem specific information or "none"
(11) super options: per super block options*/
mountinfoFormat = "%d %d %d:%d %s %s %s "
)
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from bind mounts
func parseMountTable() ([]*MountInfo, error) {
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return nil, err
}
defer f.Close()
return parseInfoFile(f)
}
func parseInfoFile(r io.Reader) ([]*MountInfo, error) {
var (
s = bufio.NewScanner(r)
out = []*MountInfo{}
)
for s.Scan() {
if err := s.Err(); err != nil {
return nil, err
}
var (
p = &MountInfo{}
text = s.Text()
)
if _, err := fmt.Sscanf(text, mountinfoFormat,
&p.Id, &p.Parent, &p.Major, &p.Minor,
&p.Root, &p.Mountpoint, &p.Opts); err != nil {
return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err)
}
// Safe as mountinfo encodes mountpoints with spaces as \040.
index := strings.Index(text, " - ")
postSeparatorFields := strings.Fields(text[index+3:])
if len(postSeparatorFields) != 3 {
return nil, fmt.Errorf("Error did not find 3 fields post '-' in '%s'", text)
}
p.Fstype = postSeparatorFields[0]
p.Source = postSeparatorFields[1]
p.VfsOpts = postSeparatorFields[2]
out = append(out, p)
}
return out, nil
}

View file

@ -0,0 +1,12 @@
// +build !linux,!freebsd freebsd,!cgo
package mount
import (
"fmt"
"runtime"
)
func parseMountTable() ([]*MountInfo, error) {
return nil, fmt.Errorf("mount.parseMountTable is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}