diff --git a/mountinfo/flags.go b/mountinfo/flags.go deleted file mode 100644 index 607dbed43..000000000 --- a/mountinfo/flags.go +++ /dev/null @@ -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 -} diff --git a/mountinfo/flags_freebsd.go b/mountinfo/flags_freebsd.go deleted file mode 100644 index 5f76f331b..000000000 --- a/mountinfo/flags_freebsd.go +++ /dev/null @@ -1,49 +0,0 @@ -// +build freebsd,cgo - -package mount - -/* -#include -*/ -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 -) diff --git a/mountinfo/flags_linux.go b/mountinfo/flags_linux.go deleted file mode 100644 index 25f466183..000000000 --- a/mountinfo/flags_linux.go +++ /dev/null @@ -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 -) diff --git a/mountinfo/flags_unsupported.go b/mountinfo/flags_unsupported.go deleted file mode 100644 index 9ed741e3f..000000000 --- a/mountinfo/flags_unsupported.go +++ /dev/null @@ -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 -) diff --git a/mountinfo/mount.go b/mountinfo/mount.go deleted file mode 100644 index a57f3979b..000000000 --- a/mountinfo/mount.go +++ /dev/null @@ -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) -} diff --git a/mountinfo/mount_unix_test.go b/mountinfo/mount_unix_test.go deleted file mode 100644 index 253aff3b8..000000000 --- a/mountinfo/mount_unix_test.go +++ /dev/null @@ -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") - } -} diff --git a/mountinfo/mounter_freebsd.go b/mountinfo/mounter_freebsd.go deleted file mode 100644 index bb870e6f5..000000000 --- a/mountinfo/mounter_freebsd.go +++ /dev/null @@ -1,59 +0,0 @@ -package mount - -/* -#include -#include -#include -#include -#include -#include -*/ -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) -} diff --git a/mountinfo/mounter_linux.go b/mountinfo/mounter_linux.go deleted file mode 100644 index 3ef2ce6f0..000000000 --- a/mountinfo/mounter_linux.go +++ /dev/null @@ -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) -} diff --git a/mountinfo/mounter_linux_test.go b/mountinfo/mounter_linux_test.go deleted file mode 100644 index 9c74b1f51..000000000 --- a/mountinfo/mounter_linux_test.go +++ /dev/null @@ -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 -} diff --git a/mountinfo/mounter_solaris.go b/mountinfo/mounter_solaris.go deleted file mode 100644 index c684aa81f..000000000 --- a/mountinfo/mounter_solaris.go +++ /dev/null @@ -1,33 +0,0 @@ -// +build solaris,cgo - -package mount - -import ( - "golang.org/x/sys/unix" - "unsafe" -) - -// #include -// #include -// #include -// 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 -} diff --git a/mountinfo/mounter_unsupported.go b/mountinfo/mounter_unsupported.go deleted file mode 100644 index a2a3bb457..000000000 --- a/mountinfo/mounter_unsupported.go +++ /dev/null @@ -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") -} diff --git a/mountinfo/mountinfo.go b/mountinfo/mountinfo.go index e3fc3535e..9f5176599 100644 --- a/mountinfo/mountinfo.go +++ b/mountinfo/mountinfo.go @@ -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//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() +} diff --git a/mountinfo/mountinfo_freebsd.go b/mountinfo/mountinfo_freebsd.go index 4f32edcd9..29936716b 100644 --- a/mountinfo/mountinfo_freebsd.go +++ b/mountinfo/mountinfo_freebsd.go @@ -1,4 +1,4 @@ -package mount +package mountinfo /* #include diff --git a/mountinfo/mountinfo_linux.go b/mountinfo/mountinfo_linux.go index be69fee1d..22f2a44ee 100644 --- a/mountinfo/mountinfo_linux.go +++ b/mountinfo/mountinfo_linux.go @@ -1,6 +1,6 @@ // +build linux -package mount +package mountinfo import ( "bufio" diff --git a/mountinfo/mountinfo_linux_test.go b/mountinfo/mountinfo_linux_test.go index bd100e1d4..d5fa98da5 100644 --- a/mountinfo/mountinfo_linux_test.go +++ b/mountinfo/mountinfo_linux_test.go @@ -1,6 +1,6 @@ // +build linux -package mount +package mountinfo import ( "bytes" diff --git a/mountinfo/mountinfo_solaris.go b/mountinfo/mountinfo_solaris.go index ad9ab57f8..d9f3e0d12 100644 --- a/mountinfo/mountinfo_solaris.go +++ b/mountinfo/mountinfo_solaris.go @@ -1,6 +1,6 @@ // +build solaris,cgo -package mount +package mountinfo /* #include diff --git a/mountinfo/mountinfo_unsupported.go b/mountinfo/mountinfo_unsupported.go index 7fbcf1921..15f43f07a 100644 --- a/mountinfo/mountinfo_unsupported.go +++ b/mountinfo/mountinfo_unsupported.go @@ -1,6 +1,6 @@ // +build !windows,!linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo -package mount +package mountinfo import ( "fmt" diff --git a/mountinfo/mountinfo_windows.go b/mountinfo/mountinfo_windows.go index dab8a37ed..9564ab90f 100644 --- a/mountinfo/mountinfo_windows.go +++ b/mountinfo/mountinfo_windows.go @@ -1,4 +1,4 @@ -package mount +package mountinfo func parseMountTable() ([]*Info, error) { // Do NOT return an error! diff --git a/mountinfo/sharedsubtree_linux.go b/mountinfo/sharedsubtree_linux.go deleted file mode 100644 index 8ceec84bc..000000000 --- a/mountinfo/sharedsubtree_linux.go +++ /dev/null @@ -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) -} diff --git a/mountinfo/sharedsubtree_linux_test.go b/mountinfo/sharedsubtree_linux_test.go deleted file mode 100644 index c1837942e..000000000 --- a/mountinfo/sharedsubtree_linux_test.go +++ /dev/null @@ -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() -} diff --git a/mountinfo/sharedsubtree_solaris.go b/mountinfo/sharedsubtree_solaris.go deleted file mode 100644 index 09f6b03cb..000000000 --- a/mountinfo/sharedsubtree_solaris.go +++ /dev/null @@ -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 -}