mountinfo: remove unused functions

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
This commit is contained in:
Akihiro Suda 2017-05-06 16:32:46 +00:00
parent 0f21829c2e
commit e37c337a3b
21 changed files with 12 additions and 1352 deletions

View File

@ -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
}

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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)
}

View File

@ -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")
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -1,4 +1,4 @@
package mount
package mountinfo
// Info reveals information about a particular mounted filesystem. This
// 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 string
}
// GetMounts retrieves a list of mounts for the current running process.
func GetMounts() ([]*Info, error) {
return parseMountTable()
}

View File

@ -1,4 +1,4 @@
package mount
package mountinfo
/*
#include <sys/param.h>

View File

@ -1,6 +1,6 @@
// +build linux
package mount
package mountinfo
import (
"bufio"

View File

@ -1,6 +1,6 @@
// +build linux
package mount
package mountinfo
import (
"bytes"

View File

@ -1,6 +1,6 @@
// +build solaris,cgo
package mount
package mountinfo
/*
#include <stdio.h>

View File

@ -1,6 +1,6 @@
// +build !windows,!linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo
package mount
package mountinfo
import (
"fmt"

View File

@ -1,4 +1,4 @@
package mount
package mountinfo
func parseMountTable() ([]*Info, error) {
// Do NOT return an error!

View File

@ -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)
}

View File

@ -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()
}

View File

@ -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
}