mountinfo: remove unused functions
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
This commit is contained in:
parent
0f21829c2e
commit
e37c337a3b
@ -1,149 +0,0 @@
|
|||||||
package mount
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var 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},
|
|
||||||
"unbindable": {false, UNBINDABLE},
|
|
||||||
"runbindable": {false, RUNBINDABLE},
|
|
||||||
"private": {false, PRIVATE},
|
|
||||||
"rprivate": {false, RPRIVATE},
|
|
||||||
"shared": {false, SHARED},
|
|
||||||
"rshared": {false, RSHARED},
|
|
||||||
"slave": {false, SLAVE},
|
|
||||||
"rslave": {false, RSLAVE},
|
|
||||||
"relatime": {false, RELATIME},
|
|
||||||
"norelatime": {true, RELATIME},
|
|
||||||
"strictatime": {false, STRICTATIME},
|
|
||||||
"nostrictatime": {true, STRICTATIME},
|
|
||||||
}
|
|
||||||
|
|
||||||
var validFlags = map[string]bool{
|
|
||||||
"": true,
|
|
||||||
"size": true,
|
|
||||||
"mode": true,
|
|
||||||
"uid": true,
|
|
||||||
"gid": true,
|
|
||||||
"nr_inodes": true,
|
|
||||||
"nr_blocks": true,
|
|
||||||
"mpol": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
var propagationFlags = map[string]bool{
|
|
||||||
"bind": true,
|
|
||||||
"rbind": true,
|
|
||||||
"unbindable": true,
|
|
||||||
"runbindable": true,
|
|
||||||
"private": true,
|
|
||||||
"rprivate": true,
|
|
||||||
"shared": true,
|
|
||||||
"rshared": true,
|
|
||||||
"slave": true,
|
|
||||||
"rslave": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// MergeTmpfsOptions merge mount options to make sure there is no duplicate.
|
|
||||||
func MergeTmpfsOptions(options []string) ([]string, error) {
|
|
||||||
// We use collisions maps to remove duplicates.
|
|
||||||
// For flag, the key is the flag value (the key for propagation flag is -1)
|
|
||||||
// For data=value, the key is the data
|
|
||||||
flagCollisions := map[int]bool{}
|
|
||||||
dataCollisions := map[string]bool{}
|
|
||||||
|
|
||||||
var newOptions []string
|
|
||||||
// We process in reverse order
|
|
||||||
for i := len(options) - 1; i >= 0; i-- {
|
|
||||||
option := options[i]
|
|
||||||
if option == "defaults" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if f, ok := flags[option]; ok && f.flag != 0 {
|
|
||||||
// There is only one propagation mode
|
|
||||||
key := f.flag
|
|
||||||
if propagationFlags[option] {
|
|
||||||
key = -1
|
|
||||||
}
|
|
||||||
// Check to see if there is collision for flag
|
|
||||||
if !flagCollisions[key] {
|
|
||||||
// We prepend the option and add to collision map
|
|
||||||
newOptions = append([]string{option}, newOptions...)
|
|
||||||
flagCollisions[key] = true
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
opt := strings.SplitN(option, "=", 2)
|
|
||||||
if len(opt) != 2 || !validFlags[opt[0]] {
|
|
||||||
return nil, fmt.Errorf("Invalid tmpfs option %q", opt)
|
|
||||||
}
|
|
||||||
if !dataCollisions[opt[0]] {
|
|
||||||
// We prepend the option and add to collision map
|
|
||||||
newOptions = append([]string{option}, newOptions...)
|
|
||||||
dataCollisions[opt[0]] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newOptions, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse fstab type mount options into mount() flags
|
|
||||||
// and device specific data
|
|
||||||
func parseOptions(options string) (int, string) {
|
|
||||||
var (
|
|
||||||
flag int
|
|
||||||
data []string
|
|
||||||
)
|
|
||||||
|
|
||||||
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, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseTmpfsOptions parse fstab type mount options into flags and data
|
|
||||||
func ParseTmpfsOptions(options string) (int, string, error) {
|
|
||||||
flags, data := parseOptions(options)
|
|
||||||
for _, o := range strings.Split(data, ",") {
|
|
||||||
opt := strings.SplitN(o, "=", 2)
|
|
||||||
if !validFlags[opt[0]] {
|
|
||||||
return 0, "", fmt.Errorf("Invalid tmpfs option %q", opt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return flags, data, nil
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
// +build freebsd,cgo
|
|
||||||
|
|
||||||
package mount
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <sys/mount.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
const (
|
|
||||||
// RDONLY will mount the filesystem as read-only.
|
|
||||||
RDONLY = C.MNT_RDONLY
|
|
||||||
|
|
||||||
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
|
|
||||||
// take effect.
|
|
||||||
NOSUID = C.MNT_NOSUID
|
|
||||||
|
|
||||||
// NOEXEC will not allow execution of any binaries on the mounted file system.
|
|
||||||
NOEXEC = C.MNT_NOEXEC
|
|
||||||
|
|
||||||
// SYNCHRONOUS will allow any I/O to the file system to be done synchronously.
|
|
||||||
SYNCHRONOUS = C.MNT_SYNCHRONOUS
|
|
||||||
|
|
||||||
// NOATIME will not update the file access time when reading from a file.
|
|
||||||
NOATIME = C.MNT_NOATIME
|
|
||||||
)
|
|
||||||
|
|
||||||
// These flags are unsupported.
|
|
||||||
const (
|
|
||||||
BIND = 0
|
|
||||||
DIRSYNC = 0
|
|
||||||
MANDLOCK = 0
|
|
||||||
NODEV = 0
|
|
||||||
NODIRATIME = 0
|
|
||||||
UNBINDABLE = 0
|
|
||||||
RUNBINDABLE = 0
|
|
||||||
PRIVATE = 0
|
|
||||||
RPRIVATE = 0
|
|
||||||
SHARED = 0
|
|
||||||
RSHARED = 0
|
|
||||||
SLAVE = 0
|
|
||||||
RSLAVE = 0
|
|
||||||
RBIND = 0
|
|
||||||
RELATIVE = 0
|
|
||||||
RELATIME = 0
|
|
||||||
REMOUNT = 0
|
|
||||||
STRICTATIME = 0
|
|
||||||
mntDetach = 0
|
|
||||||
)
|
|
@ -1,87 +0,0 @@
|
|||||||
package mount
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// RDONLY will mount the file system read-only.
|
|
||||||
RDONLY = syscall.MS_RDONLY
|
|
||||||
|
|
||||||
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
|
|
||||||
// take effect.
|
|
||||||
NOSUID = syscall.MS_NOSUID
|
|
||||||
|
|
||||||
// NODEV will not interpret character or block special devices on the file
|
|
||||||
// system.
|
|
||||||
NODEV = syscall.MS_NODEV
|
|
||||||
|
|
||||||
// NOEXEC will not allow execution of any binaries on the mounted file system.
|
|
||||||
NOEXEC = syscall.MS_NOEXEC
|
|
||||||
|
|
||||||
// SYNCHRONOUS will allow I/O to the file system to be done synchronously.
|
|
||||||
SYNCHRONOUS = syscall.MS_SYNCHRONOUS
|
|
||||||
|
|
||||||
// DIRSYNC will force all directory updates within the file system to be done
|
|
||||||
// synchronously. This affects the following system calls: create, link,
|
|
||||||
// unlink, symlink, mkdir, rmdir, mknod and rename.
|
|
||||||
DIRSYNC = syscall.MS_DIRSYNC
|
|
||||||
|
|
||||||
// REMOUNT will attempt to remount an already-mounted file system. This is
|
|
||||||
// commonly used to change the mount flags for a file system, especially to
|
|
||||||
// make a readonly file system writeable. It does not change device or mount
|
|
||||||
// point.
|
|
||||||
REMOUNT = syscall.MS_REMOUNT
|
|
||||||
|
|
||||||
// MANDLOCK will force mandatory locks on a filesystem.
|
|
||||||
MANDLOCK = syscall.MS_MANDLOCK
|
|
||||||
|
|
||||||
// NOATIME will not update the file access time when reading from a file.
|
|
||||||
NOATIME = syscall.MS_NOATIME
|
|
||||||
|
|
||||||
// NODIRATIME will not update the directory access time.
|
|
||||||
NODIRATIME = syscall.MS_NODIRATIME
|
|
||||||
|
|
||||||
// BIND remounts a subtree somewhere else.
|
|
||||||
BIND = syscall.MS_BIND
|
|
||||||
|
|
||||||
// RBIND remounts a subtree and all possible submounts somewhere else.
|
|
||||||
RBIND = syscall.MS_BIND | syscall.MS_REC
|
|
||||||
|
|
||||||
// UNBINDABLE creates a mount which cannot be cloned through a bind operation.
|
|
||||||
UNBINDABLE = syscall.MS_UNBINDABLE
|
|
||||||
|
|
||||||
// RUNBINDABLE marks the entire mount tree as UNBINDABLE.
|
|
||||||
RUNBINDABLE = syscall.MS_UNBINDABLE | syscall.MS_REC
|
|
||||||
|
|
||||||
// PRIVATE creates a mount which carries no propagation abilities.
|
|
||||||
PRIVATE = syscall.MS_PRIVATE
|
|
||||||
|
|
||||||
// RPRIVATE marks the entire mount tree as PRIVATE.
|
|
||||||
RPRIVATE = syscall.MS_PRIVATE | syscall.MS_REC
|
|
||||||
|
|
||||||
// SLAVE creates a mount which receives propagation from its master, but not
|
|
||||||
// vice versa.
|
|
||||||
SLAVE = syscall.MS_SLAVE
|
|
||||||
|
|
||||||
// RSLAVE marks the entire mount tree as SLAVE.
|
|
||||||
RSLAVE = syscall.MS_SLAVE | syscall.MS_REC
|
|
||||||
|
|
||||||
// SHARED creates a mount which provides the ability to create mirrors of
|
|
||||||
// that mount such that mounts and unmounts within any of the mirrors
|
|
||||||
// propagate to the other mirrors.
|
|
||||||
SHARED = syscall.MS_SHARED
|
|
||||||
|
|
||||||
// RSHARED marks the entire mount tree as SHARED.
|
|
||||||
RSHARED = syscall.MS_SHARED | syscall.MS_REC
|
|
||||||
|
|
||||||
// RELATIME updates inode access times relative to modify or change time.
|
|
||||||
RELATIME = syscall.MS_RELATIME
|
|
||||||
|
|
||||||
// STRICTATIME allows to explicitly request full atime updates. This makes
|
|
||||||
// it possible for the kernel to default to relatime or noatime but still
|
|
||||||
// allow userspace to override it.
|
|
||||||
STRICTATIME = syscall.MS_STRICTATIME
|
|
||||||
|
|
||||||
mntDetach = syscall.MNT_DETACH
|
|
||||||
)
|
|
@ -1,31 +0,0 @@
|
|||||||
// +build !linux,!freebsd freebsd,!cgo solaris,!cgo
|
|
||||||
|
|
||||||
package mount
|
|
||||||
|
|
||||||
// These flags are unsupported.
|
|
||||||
const (
|
|
||||||
BIND = 0
|
|
||||||
DIRSYNC = 0
|
|
||||||
MANDLOCK = 0
|
|
||||||
NOATIME = 0
|
|
||||||
NODEV = 0
|
|
||||||
NODIRATIME = 0
|
|
||||||
NOEXEC = 0
|
|
||||||
NOSUID = 0
|
|
||||||
UNBINDABLE = 0
|
|
||||||
RUNBINDABLE = 0
|
|
||||||
PRIVATE = 0
|
|
||||||
RPRIVATE = 0
|
|
||||||
SHARED = 0
|
|
||||||
RSHARED = 0
|
|
||||||
SLAVE = 0
|
|
||||||
RSLAVE = 0
|
|
||||||
RBIND = 0
|
|
||||||
RELATIME = 0
|
|
||||||
RELATIVE = 0
|
|
||||||
REMOUNT = 0
|
|
||||||
STRICTATIME = 0
|
|
||||||
SYNCHRONOUS = 0
|
|
||||||
RDONLY = 0
|
|
||||||
mntDetach = 0
|
|
||||||
)
|
|
@ -1,55 +0,0 @@
|
|||||||
package mount
|
|
||||||
|
|
||||||
// GetMounts retrieves a list of mounts for the current running process.
|
|
||||||
func GetMounts() ([]*Info, error) {
|
|
||||||
return parseMountTable()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mounted determines if a specified mountpoint has been mounted.
|
|
||||||
// On Linux it looks at /proc/self/mountinfo and on Solaris at mnttab.
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mount will mount filesystem according to the specified configuration, on the
|
|
||||||
// condition that the target path is *not* already mounted. Options must be
|
|
||||||
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
|
|
||||||
// flags.go for supported option flags.
|
|
||||||
func Mount(device, target, mType, options string) error {
|
|
||||||
flag, _ := parseOptions(options)
|
|
||||||
if flag&REMOUNT != REMOUNT {
|
|
||||||
if mounted, err := Mounted(target); err != nil || mounted {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ForceMount(device, target, mType, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForceMount will mount a filesystem according to the specified configuration,
|
|
||||||
// *regardless* if the target path is not already mounted. Options must be
|
|
||||||
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
|
|
||||||
// flags.go for supported option flags.
|
|
||||||
func ForceMount(device, target, mType, options string) error {
|
|
||||||
flag, data := parseOptions(options)
|
|
||||||
return mount(device, target, mType, uintptr(flag), data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmount lazily unmounts a filesystem on supported platforms, otherwise
|
|
||||||
// does a normal unmount.
|
|
||||||
func Unmount(target string) error {
|
|
||||||
if mounted, err := Mounted(target); err != nil || !mounted {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return unmount(target, mntDetach)
|
|
||||||
}
|
|
@ -1,162 +0,0 @@
|
|||||||
// +build !windows,!solaris
|
|
||||||
|
|
||||||
package mount
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMountOptionsParsing(t *testing.T) {
|
|
||||||
options := "noatime,ro,size=10k"
|
|
||||||
|
|
||||||
flag, data := parseOptions(options)
|
|
||||||
|
|
||||||
if data != "size=10k" {
|
|
||||||
t.Fatalf("Expected size=10 got %s", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedFlag := NOATIME | RDONLY
|
|
||||||
|
|
||||||
if flag != expectedFlag {
|
|
||||||
t.Fatalf("Expected %d got %d", expectedFlag, flag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMounted(t *testing.T) {
|
|
||||||
tmp := path.Join(os.TempDir(), "mount-tests")
|
|
||||||
if err := os.MkdirAll(tmp, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
|
|
||||||
var (
|
|
||||||
sourceDir = path.Join(tmp, "source")
|
|
||||||
targetDir = path.Join(tmp, "target")
|
|
||||||
sourcePath = path.Join(sourceDir, "file.txt")
|
|
||||||
targetPath = path.Join(targetDir, "file.txt")
|
|
||||||
)
|
|
||||||
|
|
||||||
os.Mkdir(sourceDir, 0777)
|
|
||||||
os.Mkdir(targetDir, 0777)
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(targetDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
mounted, err := Mounted(targetDir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !mounted {
|
|
||||||
t.Fatalf("Expected %s to be mounted", targetDir)
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(targetDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMountReadonly(t *testing.T) {
|
|
||||||
tmp := path.Join(os.TempDir(), "mount-tests")
|
|
||||||
if err := os.MkdirAll(tmp, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
|
|
||||||
var (
|
|
||||||
sourceDir = path.Join(tmp, "source")
|
|
||||||
targetDir = path.Join(tmp, "target")
|
|
||||||
sourcePath = path.Join(sourceDir, "file.txt")
|
|
||||||
targetPath = path.Join(targetDir, "file.txt")
|
|
||||||
)
|
|
||||||
|
|
||||||
os.Mkdir(sourceDir, 0777)
|
|
||||||
os.Mkdir(targetDir, 0777)
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
if err := Mount(sourceDir, targetDir, "none", "bind,ro"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(targetDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
f, err = os.OpenFile(targetPath, os.O_RDWR, 0777)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should not be able to open a ro file as rw")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetMounts(t *testing.T) {
|
|
||||||
mounts, err := GetMounts()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
root := false
|
|
||||||
for _, entry := range mounts {
|
|
||||||
if entry.Mountpoint == "/" {
|
|
||||||
root = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !root {
|
|
||||||
t.Fatal("/ should be mounted at least")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMergeTmpfsOptions(t *testing.T) {
|
|
||||||
options := []string{"noatime", "ro", "size=10k", "defaults", "atime", "defaults", "rw", "rprivate", "size=1024k", "slave"}
|
|
||||||
expected := []string{"atime", "rw", "size=1024k", "slave"}
|
|
||||||
merged, err := MergeTmpfsOptions(options)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(expected) != len(merged) {
|
|
||||||
t.Fatalf("Expected %s got %s", expected, merged)
|
|
||||||
}
|
|
||||||
for index := range merged {
|
|
||||||
if merged[index] != expected[index] {
|
|
||||||
t.Fatalf("Expected %s for the %dth option, got %s", expected, index, merged)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
options = []string{"noatime", "ro", "size=10k", "atime", "rw", "rprivate", "size=1024k", "slave", "size"}
|
|
||||||
_, err = MergeTmpfsOptions(options)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Expected error got nil")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package mount
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ptypes is the set propagation types.
|
|
||||||
ptypes = syscall.MS_SHARED | syscall.MS_PRIVATE | syscall.MS_SLAVE | syscall.MS_UNBINDABLE
|
|
||||||
|
|
||||||
// pflags is the full set valid flags for a change propagation call.
|
|
||||||
pflags = ptypes | syscall.MS_REC | syscall.MS_SILENT
|
|
||||||
|
|
||||||
// broflags is the combination of bind and read only
|
|
||||||
broflags = syscall.MS_BIND | syscall.MS_RDONLY
|
|
||||||
)
|
|
||||||
|
|
||||||
// isremount returns true if either device name or flags identify a remount request, false otherwise.
|
|
||||||
func isremount(device string, flags uintptr) bool {
|
|
||||||
switch {
|
|
||||||
// We treat device "" and "none" as a remount request to provide compatibility with
|
|
||||||
// requests that don't explicitly set MS_REMOUNT such as those manipulating bind mounts.
|
|
||||||
case flags&syscall.MS_REMOUNT != 0, device == "", device == "none":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func mount(device, target, mType string, flags uintptr, data string) error {
|
|
||||||
oflags := flags &^ ptypes
|
|
||||||
if !isremount(device, flags) {
|
|
||||||
// Initial call applying all non-propagation flags.
|
|
||||||
if err := syscall.Mount(device, target, mType, oflags, data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if flags&ptypes != 0 {
|
|
||||||
// Change the propagation type.
|
|
||||||
if err := syscall.Mount("", target, "", flags&pflags, ""); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if oflags&broflags == broflags {
|
|
||||||
// Remount the bind to apply read only.
|
|
||||||
return syscall.Mount("", target, "", oflags|syscall.MS_REMOUNT, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmount(target string, flag int) error {
|
|
||||||
return syscall.Unmount(target, flag)
|
|
||||||
}
|
|
@ -1,195 +0,0 @@
|
|||||||
// +build linux
|
|
||||||
|
|
||||||
package mount
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMount(t *testing.T) {
|
|
||||||
if os.Getuid() != 0 {
|
|
||||||
t.Skip("not root tests would fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
source, err := ioutil.TempDir("", "mount-test-source-")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(source)
|
|
||||||
|
|
||||||
// Ensure we have a known start point by mounting tmpfs with given options
|
|
||||||
if err := Mount("tmpfs", source, "tmpfs", "private"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer ensureUnmount(t, source)
|
|
||||||
validateMount(t, source, "", "")
|
|
||||||
if t.Failed() {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
target, err := ioutil.TempDir("", "mount-test-target-")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(target)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
source string
|
|
||||||
ftype string
|
|
||||||
options string
|
|
||||||
expectedOpts string
|
|
||||||
expectedOptional string
|
|
||||||
}{
|
|
||||||
// No options
|
|
||||||
{"tmpfs", "tmpfs", "", "", ""},
|
|
||||||
// Default rw / ro test
|
|
||||||
{source, "", "bind", "", ""},
|
|
||||||
{source, "", "bind,private", "", ""},
|
|
||||||
{source, "", "bind,shared", "", "shared"},
|
|
||||||
{source, "", "bind,slave", "", "master"},
|
|
||||||
{source, "", "bind,unbindable", "", "unbindable"},
|
|
||||||
// Read Write tests
|
|
||||||
{source, "", "bind,rw", "rw", ""},
|
|
||||||
{source, "", "bind,rw,private", "rw", ""},
|
|
||||||
{source, "", "bind,rw,shared", "rw", "shared"},
|
|
||||||
{source, "", "bind,rw,slave", "rw", "master"},
|
|
||||||
{source, "", "bind,rw,unbindable", "rw", "unbindable"},
|
|
||||||
// Read Only tests
|
|
||||||
{source, "", "bind,ro", "ro", ""},
|
|
||||||
{source, "", "bind,ro,private", "ro", ""},
|
|
||||||
{source, "", "bind,ro,shared", "ro", "shared"},
|
|
||||||
{source, "", "bind,ro,slave", "ro", "master"},
|
|
||||||
{source, "", "bind,ro,unbindable", "ro", "unbindable"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
ftype, options := tc.ftype, tc.options
|
|
||||||
if tc.ftype == "" {
|
|
||||||
ftype = "none"
|
|
||||||
}
|
|
||||||
if tc.options == "" {
|
|
||||||
options = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("%v-%v", ftype, options), func(t *testing.T) {
|
|
||||||
if strings.Contains(tc.options, "slave") {
|
|
||||||
// Slave requires a shared source
|
|
||||||
if err := MakeShared(source); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := MakePrivate(source); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
if err := Mount(tc.source, target, tc.ftype, tc.options); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer ensureUnmount(t, target)
|
|
||||||
validateMount(t, target, tc.expectedOpts, tc.expectedOptional)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensureUnmount umounts mnt checking for errors
|
|
||||||
func ensureUnmount(t *testing.T, mnt string) {
|
|
||||||
if err := Unmount(mnt); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateMount checks that mnt has the given options
|
|
||||||
func validateMount(t *testing.T, mnt string, opts, optional string) {
|
|
||||||
info, err := GetMounts()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
wantedOpts := make(map[string]struct{})
|
|
||||||
if opts != "" {
|
|
||||||
for _, opt := range strings.Split(opts, ",") {
|
|
||||||
wantedOpts[opt] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wantedOptional := make(map[string]struct{})
|
|
||||||
if optional != "" {
|
|
||||||
for _, opt := range strings.Split(optional, ",") {
|
|
||||||
wantedOptional[opt] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mnts := make(map[int]*Info, len(info))
|
|
||||||
for _, mi := range info {
|
|
||||||
mnts[mi.ID] = mi
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, mi := range info {
|
|
||||||
if mi.Mountpoint != mnt {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use parent info as the defaults
|
|
||||||
p := mnts[mi.Parent]
|
|
||||||
pOpts := make(map[string]struct{})
|
|
||||||
if p.Opts != "" {
|
|
||||||
for _, opt := range strings.Split(p.Opts, ",") {
|
|
||||||
pOpts[clean(opt)] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pOptional := make(map[string]struct{})
|
|
||||||
if p.Optional != "" {
|
|
||||||
for _, field := range strings.Split(p.Optional, ",") {
|
|
||||||
pOptional[clean(field)] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate Opts
|
|
||||||
if mi.Opts != "" {
|
|
||||||
for _, opt := range strings.Split(mi.Opts, ",") {
|
|
||||||
opt = clean(opt)
|
|
||||||
if !has(wantedOpts, opt) && !has(pOpts, opt) {
|
|
||||||
t.Errorf("unexpected mount option %q expected %q", opt, opts)
|
|
||||||
}
|
|
||||||
delete(wantedOpts, opt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for opt := range wantedOpts {
|
|
||||||
t.Errorf("missing mount option %q found %q", opt, mi.Opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate Optional
|
|
||||||
if mi.Optional != "" {
|
|
||||||
for _, field := range strings.Split(mi.Optional, ",") {
|
|
||||||
field = clean(field)
|
|
||||||
if !has(wantedOptional, field) && !has(pOptional, field) {
|
|
||||||
t.Errorf("unexpected optional failed %q expected %q", field, optional)
|
|
||||||
}
|
|
||||||
delete(wantedOptional, field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for field := range wantedOptional {
|
|
||||||
t.Errorf("missing optional field %q found %q", field, mi.Optional)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Errorf("failed to find mount %q", mnt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean strips off any value param after the colon
|
|
||||||
func clean(v string) string {
|
|
||||||
return strings.SplitN(v, ":", 2)[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// has returns true if key is a member of m
|
|
||||||
func has(m map[string]struct{}, key string) bool {
|
|
||||||
_, ok := m[key]
|
|
||||||
return ok
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
// +build solaris,cgo
|
|
||||||
|
|
||||||
package mount
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// #include <stdlib.h>
|
|
||||||
// #include <stdio.h>
|
|
||||||
// #include <sys/mount.h>
|
|
||||||
// int Mount(const char *spec, const char *dir, int mflag,
|
|
||||||
// char *fstype, char *dataptr, int datalen, char *optptr, int optlen) {
|
|
||||||
// return mount(spec, dir, mflag, fstype, dataptr, datalen, optptr, optlen);
|
|
||||||
// }
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
func mount(device, target, mType string, flag uintptr, data string) error {
|
|
||||||
spec := C.CString(device)
|
|
||||||
dir := C.CString(target)
|
|
||||||
fstype := C.CString(mType)
|
|
||||||
_, err := C.Mount(spec, dir, C.int(flag), fstype, nil, 0, nil, 0)
|
|
||||||
C.free(unsafe.Pointer(spec))
|
|
||||||
C.free(unsafe.Pointer(dir))
|
|
||||||
C.free(unsafe.Pointer(fstype))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmount(target string, flag int) error {
|
|
||||||
err := unix.Unmount(target, flag)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
// +build !linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo
|
|
||||||
|
|
||||||
package mount
|
|
||||||
|
|
||||||
func mount(device, target, mType string, flag uintptr, data string) error {
|
|
||||||
panic("Not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmount(target string, flag int) error {
|
|
||||||
panic("Not implemented")
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package mount
|
package mountinfo
|
||||||
|
|
||||||
// Info reveals information about a particular mounted filesystem. This
|
// Info reveals information about a particular mounted filesystem. This
|
||||||
// struct is populated from the content in the /proc/<pid>/mountinfo file.
|
// struct is populated from the content in the /proc/<pid>/mountinfo file.
|
||||||
@ -38,3 +38,8 @@ type Info struct {
|
|||||||
// VfsOpts represents per super block options.
|
// VfsOpts represents per super block options.
|
||||||
VfsOpts string
|
VfsOpts string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMounts retrieves a list of mounts for the current running process.
|
||||||
|
func GetMounts() ([]*Info, error) {
|
||||||
|
return parseMountTable()
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package mount
|
package mountinfo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package mount
|
package mountinfo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package mount
|
package mountinfo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// +build solaris,cgo
|
// +build solaris,cgo
|
||||||
|
|
||||||
package mount
|
package mountinfo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// +build !windows,!linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo
|
// +build !windows,!linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo
|
||||||
|
|
||||||
package mount
|
package mountinfo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package mount
|
package mountinfo
|
||||||
|
|
||||||
func parseMountTable() ([]*Info, error) {
|
func parseMountTable() ([]*Info, error) {
|
||||||
// Do NOT return an error!
|
// Do NOT return an error!
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
// +build linux
|
|
||||||
|
|
||||||
package mount
|
|
||||||
|
|
||||||
// MakeShared ensures a mounted filesystem has the SHARED mount option enabled.
|
|
||||||
// See the supported options in flags.go for further reference.
|
|
||||||
func MakeShared(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "shared")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled.
|
|
||||||
// See the supported options in flags.go for further reference.
|
|
||||||
func MakeRShared(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "rshared")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled.
|
|
||||||
// See the supported options in flags.go for further reference.
|
|
||||||
func MakePrivate(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "private")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option
|
|
||||||
// enabled. See the supported options in flags.go for further reference.
|
|
||||||
func MakeRPrivate(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "rprivate")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled.
|
|
||||||
// See the supported options in flags.go for further reference.
|
|
||||||
func MakeSlave(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "slave")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled.
|
|
||||||
// See the supported options in flags.go for further reference.
|
|
||||||
func MakeRSlave(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "rslave")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option
|
|
||||||
// enabled. See the supported options in flags.go for further reference.
|
|
||||||
func MakeUnbindable(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "unbindable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount
|
|
||||||
// option enabled. See the supported options in flags.go for further reference.
|
|
||||||
func MakeRUnbindable(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "runbindable")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureMountedAs(mountPoint, options string) error {
|
|
||||||
mounted, err := Mounted(mountPoint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mounted {
|
|
||||||
if err := Mount(mountPoint, mountPoint, "none", "bind,rw"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err = Mounted(mountPoint); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ForceMount("", mountPoint, "none", options)
|
|
||||||
}
|
|
@ -1,331 +0,0 @@
|
|||||||
// +build linux
|
|
||||||
|
|
||||||
package mount
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"syscall"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// nothing is propagated in or out
|
|
||||||
func TestSubtreePrivate(t *testing.T) {
|
|
||||||
tmp := path.Join(os.TempDir(), "mount-tests")
|
|
||||||
if err := os.MkdirAll(tmp, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
|
|
||||||
var (
|
|
||||||
sourceDir = path.Join(tmp, "source")
|
|
||||||
targetDir = path.Join(tmp, "target")
|
|
||||||
outside1Dir = path.Join(tmp, "outside1")
|
|
||||||
outside2Dir = path.Join(tmp, "outside2")
|
|
||||||
|
|
||||||
outside1Path = path.Join(outside1Dir, "file.txt")
|
|
||||||
outside2Path = path.Join(outside2Dir, "file.txt")
|
|
||||||
outside1CheckPath = path.Join(targetDir, "a", "file.txt")
|
|
||||||
outside2CheckPath = path.Join(sourceDir, "b", "file.txt")
|
|
||||||
)
|
|
||||||
if err := os.MkdirAll(path.Join(sourceDir, "a"), 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(path.Join(sourceDir, "b"), 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.Mkdir(targetDir, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.Mkdir(outside1Dir, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.Mkdir(outside2Dir, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := createFile(outside1Path); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := createFile(outside2Path); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// mount the shared directory to a target
|
|
||||||
if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(targetDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// next, make the target private
|
|
||||||
if err := MakePrivate(targetDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(targetDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// mount in an outside path to a mounted path inside the _source_
|
|
||||||
if err := Mount(outside1Dir, path.Join(sourceDir, "a"), "none", "bind,rw"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(path.Join(sourceDir, "a")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// check that this file _does_not_ show in the _target_
|
|
||||||
if _, err := os.Stat(outside1CheckPath); err != nil && !os.IsNotExist(err) {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if err == nil {
|
|
||||||
t.Fatalf("%q should not be visible, but is", outside1CheckPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// next mount outside2Dir into the _target_
|
|
||||||
if err := Mount(outside2Dir, path.Join(targetDir, "b"), "none", "bind,rw"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(path.Join(targetDir, "b")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// check that this file _does_not_ show in the _source_
|
|
||||||
if _, err := os.Stat(outside2CheckPath); err != nil && !os.IsNotExist(err) {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if err == nil {
|
|
||||||
t.Fatalf("%q should not be visible, but is", outside2CheckPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Testing that when a target is a shared mount,
|
|
||||||
// then child mounts propagate to the source
|
|
||||||
func TestSubtreeShared(t *testing.T) {
|
|
||||||
tmp := path.Join(os.TempDir(), "mount-tests")
|
|
||||||
if err := os.MkdirAll(tmp, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
|
|
||||||
var (
|
|
||||||
sourceDir = path.Join(tmp, "source")
|
|
||||||
targetDir = path.Join(tmp, "target")
|
|
||||||
outsideDir = path.Join(tmp, "outside")
|
|
||||||
|
|
||||||
outsidePath = path.Join(outsideDir, "file.txt")
|
|
||||||
sourceCheckPath = path.Join(sourceDir, "a", "file.txt")
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := os.MkdirAll(path.Join(sourceDir, "a"), 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.Mkdir(targetDir, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.Mkdir(outsideDir, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := createFile(outsidePath); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// mount the source as shared
|
|
||||||
if err := MakeShared(sourceDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(sourceDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// mount the shared directory to a target
|
|
||||||
if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(targetDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// mount in an outside path to a mounted path inside the target
|
|
||||||
if err := Mount(outsideDir, path.Join(targetDir, "a"), "none", "bind,rw"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(path.Join(targetDir, "a")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// NOW, check that the file from the outside directory is available in the source directory
|
|
||||||
if _, err := os.Stat(sourceCheckPath); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// testing that mounts to a shared source show up in the slave target,
|
|
||||||
// and that mounts into a slave target do _not_ show up in the shared source
|
|
||||||
func TestSubtreeSharedSlave(t *testing.T) {
|
|
||||||
tmp := path.Join(os.TempDir(), "mount-tests")
|
|
||||||
if err := os.MkdirAll(tmp, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
|
|
||||||
var (
|
|
||||||
sourceDir = path.Join(tmp, "source")
|
|
||||||
targetDir = path.Join(tmp, "target")
|
|
||||||
outside1Dir = path.Join(tmp, "outside1")
|
|
||||||
outside2Dir = path.Join(tmp, "outside2")
|
|
||||||
|
|
||||||
outside1Path = path.Join(outside1Dir, "file.txt")
|
|
||||||
outside2Path = path.Join(outside2Dir, "file.txt")
|
|
||||||
outside1CheckPath = path.Join(targetDir, "a", "file.txt")
|
|
||||||
outside2CheckPath = path.Join(sourceDir, "b", "file.txt")
|
|
||||||
)
|
|
||||||
if err := os.MkdirAll(path.Join(sourceDir, "a"), 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(path.Join(sourceDir, "b"), 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.Mkdir(targetDir, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.Mkdir(outside1Dir, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.Mkdir(outside2Dir, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := createFile(outside1Path); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := createFile(outside2Path); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// mount the source as shared
|
|
||||||
if err := MakeShared(sourceDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(sourceDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// mount the shared directory to a target
|
|
||||||
if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(targetDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// next, make the target slave
|
|
||||||
if err := MakeSlave(targetDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(targetDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// mount in an outside path to a mounted path inside the _source_
|
|
||||||
if err := Mount(outside1Dir, path.Join(sourceDir, "a"), "none", "bind,rw"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(path.Join(sourceDir, "a")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// check that this file _does_ show in the _target_
|
|
||||||
if _, err := os.Stat(outside1CheckPath); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// next mount outside2Dir into the _target_
|
|
||||||
if err := Mount(outside2Dir, path.Join(targetDir, "b"), "none", "bind,rw"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(path.Join(targetDir, "b")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// check that this file _does_not_ show in the _source_
|
|
||||||
if _, err := os.Stat(outside2CheckPath); err != nil && !os.IsNotExist(err) {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if err == nil {
|
|
||||||
t.Fatalf("%q should not be visible, but is", outside2CheckPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubtreeUnbindable(t *testing.T) {
|
|
||||||
tmp := path.Join(os.TempDir(), "mount-tests")
|
|
||||||
if err := os.MkdirAll(tmp, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
|
|
||||||
var (
|
|
||||||
sourceDir = path.Join(tmp, "source")
|
|
||||||
targetDir = path.Join(tmp, "target")
|
|
||||||
)
|
|
||||||
if err := os.MkdirAll(sourceDir, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(targetDir, 0777); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// next, make the source unbindable
|
|
||||||
if err := MakeUnbindable(sourceDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(sourceDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// then attempt to mount it to target. It should fail
|
|
||||||
if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil && err != syscall.EINVAL {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if err == nil {
|
|
||||||
t.Fatalf("%q should not have been bindable", sourceDir)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := Unmount(targetDir); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func createFile(path string) error {
|
|
||||||
f, err := os.Create(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f.WriteString("hello world!")
|
|
||||||
return f.Close()
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
// +build solaris
|
|
||||||
|
|
||||||
package mount
|
|
||||||
|
|
||||||
// MakeShared ensures a mounted filesystem has the SHARED mount option enabled.
|
|
||||||
// See the supported options in flags.go for further reference.
|
|
||||||
func MakeShared(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "shared")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled.
|
|
||||||
// See the supported options in flags.go for further reference.
|
|
||||||
func MakeRShared(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "rshared")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled.
|
|
||||||
// See the supported options in flags.go for further reference.
|
|
||||||
func MakePrivate(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "private")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option
|
|
||||||
// enabled. See the supported options in flags.go for further reference.
|
|
||||||
func MakeRPrivate(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "rprivate")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled.
|
|
||||||
// See the supported options in flags.go for further reference.
|
|
||||||
func MakeSlave(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "slave")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled.
|
|
||||||
// See the supported options in flags.go for further reference.
|
|
||||||
func MakeRSlave(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "rslave")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option
|
|
||||||
// enabled. See the supported options in flags.go for further reference.
|
|
||||||
func MakeUnbindable(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "unbindable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount
|
|
||||||
// option enabled. See the supported options in flags.go for further reference.
|
|
||||||
func MakeRUnbindable(mountPoint string) error {
|
|
||||||
return ensureMountedAs(mountPoint, "runbindable")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureMountedAs(mountPoint, options string) error {
|
|
||||||
// TODO: Solaris does not support bind mounts.
|
|
||||||
// Evaluate lofs and also look at the relevant
|
|
||||||
// mount flags to be supported.
|
|
||||||
return nil
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user