aboutsummaryrefslogtreecommitdiffhomepage
path: root/test/sandbox/mount.go
diff options
context:
space:
mode:
Diffstat (limited to 'test/sandbox/mount.go')
-rw-r--r--test/sandbox/mount.go215
1 files changed, 104 insertions, 111 deletions
diff --git a/test/sandbox/mount.go b/test/sandbox/mount.go
index d65824f2..27dc6bae 100644
--- a/test/sandbox/mount.go
+++ b/test/sandbox/mount.go
@@ -1,16 +1,18 @@
package sandbox
/*
+#cgo linux pkg-config: --static mount
+
#include <stdlib.h>
#include <stdio.h>
-#include <mntent.h>
+#include <libmount.h>
-const char *F_PROC_MOUNTS = "";
-const char *F_SET_TYPE = "r";
+const char *F_MOUNTINFO_PATH = "/proc/self/mountinfo";
*/
import "C"
import (
+ "errors"
"fmt"
"iter"
"runtime"
@@ -18,147 +20,138 @@ import (
"unsafe"
)
-type Mntent struct {
- /* name of mounted filesystem */
- FSName string `json:"fsname"`
- /* filesystem path prefix */
- Dir string `json:"dir"`
- /* mount type (see mntent.h) */
- Type string `json:"type"`
- /* mount options (see mntent.h) */
- Opts string `json:"opts"`
- /* dump frequency in days */
- Freq int `json:"freq"`
- /* pass number on parallel fsck */
- Passno int `json:"passno"`
-}
+var (
+ ErrMountinfoParse = errors.New("invalid mountinfo records")
+ ErrMountinfoIter = errors.New("cannot allocate iterator")
+ ErrMountinfoFault = errors.New("cannot iterate on filesystems")
+)
-func (e *Mntent) String() string {
- return fmt.Sprintf("%s %s %s %s %d %d",
- e.FSName, e.Dir, e.Type, e.Opts, e.Freq, e.Passno)
-}
+type (
+ Mountinfo struct {
+ mu sync.RWMutex
+ p string
+ err error
-func (e *Mntent) Is(want *Mntent) bool {
- if want == nil {
- return e == nil
- }
- return (e.FSName == want.FSName || want.FSName == "\x00") &&
- (e.Dir == want.Dir || want.Dir == "\x00") &&
- (e.Type == want.Type || want.Type == "\x00") &&
- (e.Opts == want.Opts || want.Opts == "\x00") &&
- (e.Freq == want.Freq || want.Freq == -1) &&
- (e.Passno == want.Passno || want.Passno == -1)
-}
-
-type MountsFile struct {
- m *mounts
- mu sync.Mutex
- done bool
-}
+ tb *C.struct_libmnt_table
+ itr *C.struct_libmnt_iter
-func OpenMounts(name string) (*MountsFile, error) {
- f := new(MountsFile)
- f.m = new(mounts)
- f.m.p = name
- return f, f.m.open()
-}
+ fs *C.struct_libmnt_fs
+ }
-func (f *MountsFile) Err() error { return f.m.Err() }
-func (f *MountsFile) Entries() iter.Seq[*Mntent] {
- return func(yield func(*Mntent) bool) {
- f.mu.Lock()
- defer f.mu.Unlock()
- if f.done {
- return
- }
+ // MountinfoEntry represents deterministic mountinfo parts of a libmnt_fs entry.
+ MountinfoEntry struct {
+ // mount ID: a unique ID for the mount (may be reused after umount(2)).
+ ID int `json:"id"`
+ // parent ID: the ID of the parent mount (or of self for the root of this mount namespace's mount tree).
+ Parent int `json:"parent"`
+ // root: the pathname of the directory in the filesystem which forms the root of this mount.
+ Root string `json:"root"`
+ // mount point: the pathname of the mount point relative to the process's root directory.
+ Target string `json:"target"`
+ // mount options: per-mount options (see mount(2)).
+ VfsOptstr string `json:"vfs_optstr"`
+ // filesystem type: the filesystem type in the form "type[.subtype]".
+ FsType string `json:"fstype"`
+ // mount source: filesystem-specific information or "none".
+ Source string `json:"source"`
+ // super options: per-superblock options (see mount(2)).
+ FsOptstr string `json:"fs_optstr"`
+ }
+)
- for f.m.scan() {
- e := new(Mntent)
- f.m.copy(e)
- if !yield(e) {
- return
- }
- }
- f.done = true
- f.m.close()
+func (m *Mountinfo) copy(v *MountinfoEntry) {
+ if m.fs == nil {
+ panic("invalid entry")
}
+ v.ID = int(C.mnt_fs_get_id(m.fs))
+ v.Parent = int(C.mnt_fs_get_parent_id(m.fs))
+ v.Root = C.GoString(C.mnt_fs_get_root(m.fs))
+ v.Target = C.GoString(C.mnt_fs_get_target(m.fs))
+ v.VfsOptstr = C.GoString(C.mnt_fs_get_vfs_options(m.fs))
+ v.FsType = C.GoString(C.mnt_fs_get_fstype(m.fs))
+ v.Source = C.GoString(C.mnt_fs_get_source(m.fs))
+ v.FsOptstr = C.GoString(C.mnt_fs_get_fs_options(m.fs))
}
-type mounts struct {
- p string
- f *C.FILE
- mu sync.RWMutex
+func NewMountinfo(p string) *Mountinfo { m := new(Mountinfo); m.p = p; return m }
- ent *C.struct_mntent
- err error
-}
+func (m *Mountinfo) Err() error { m.mu.RLock(); defer m.mu.RUnlock(); return m.err }
-func (m *mounts) open() error {
+func (m *Mountinfo) Parse() error {
m.mu.Lock()
defer m.mu.Unlock()
- if m.f != nil {
+ if m.tb != nil {
panic("open called twice")
}
if m.p == "" {
- m.p = "/proc/mounts"
+ m.tb = C.mnt_new_table_from_file(C.F_MOUNTINFO_PATH)
+ } else {
+ name := C.CString(m.p)
+ m.tb = C.mnt_new_table_from_file(name)
+ C.free(unsafe.Pointer(name))
}
-
- name := C.CString(m.p)
- f, err := C.setmntent(name, C.F_SET_TYPE)
- C.free(unsafe.Pointer(name))
-
- if f == nil {
- return err
+ if m.tb == nil {
+ return ErrMountinfoParse
}
- m.f = f
- runtime.SetFinalizer(m, (*mounts).close)
- return err
+ m.itr = C.mnt_new_iter(C.MNT_ITER_FORWARD)
+ if m.itr == nil {
+ C.mnt_unref_table(m.tb)
+ return ErrMountinfoIter
+ }
+
+ runtime.SetFinalizer(m, (*Mountinfo).Unref)
+ return nil
}
-func (m *mounts) close() {
+func (m *Mountinfo) Unref() {
m.mu.Lock()
defer m.mu.Unlock()
- if m.f == nil {
- panic("close called before open")
+ if m.tb == nil {
+ panic("unref called before parse")
}
- C.endmntent(m.f)
+ C.mnt_unref_table(m.tb)
+ C.mnt_free_iter(m.itr)
runtime.SetFinalizer(m, nil)
}
-func (m *mounts) scan() bool {
- m.mu.Lock()
- defer m.mu.Unlock()
+func (m *Mountinfo) Entries() iter.Seq[*MountinfoEntry] {
+ return func(yield func(*MountinfoEntry) bool) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
- if m.f == nil {
- panic("invalid file")
- }
+ C.mnt_reset_iter(m.itr, -1)
- m.ent, m.err = C.getmntent(m.f)
- return m.ent != nil
+ var rc C.int
+ ent := new(MountinfoEntry)
+ for rc = C.mnt_table_next_fs(m.tb, m.itr, &m.fs); rc == 0; rc = C.mnt_table_next_fs(m.tb, m.itr, &m.fs) {
+ m.copy(ent)
+ if !yield(ent) {
+ return
+ }
+ }
+ if rc < 0 {
+ m.err = ErrMountinfoFault
+ return
+ }
+ }
}
-func (m *mounts) Err() error {
- m.mu.RLock()
- defer m.mu.RUnlock()
-
- return m.err
+func (e *MountinfoEntry) EqualWithIgnore(want *MountinfoEntry, ignore string) bool {
+ return (e.ID == want.ID || want.ID == -1) &&
+ (e.Parent == want.Parent || want.Parent == -1) &&
+ (e.Root == want.Root || want.Root == ignore) &&
+ (e.Target == want.Target || want.Target == ignore) &&
+ (e.VfsOptstr == want.VfsOptstr || want.VfsOptstr == ignore) &&
+ (e.FsType == want.FsType || want.FsType == ignore) &&
+ (e.Source == want.Source || want.Source == ignore) &&
+ (e.FsOptstr == want.FsOptstr || want.FsOptstr == ignore)
}
-func (m *mounts) copy(v *Mntent) {
- m.mu.RLock()
- defer m.mu.RUnlock()
-
- if m.ent == nil {
- panic("invalid entry")
- }
- v.FSName = C.GoString(m.ent.mnt_fsname)
- v.Dir = C.GoString(m.ent.mnt_dir)
- v.Type = C.GoString(m.ent.mnt_type)
- v.Opts = C.GoString(m.ent.mnt_opts)
- v.Freq = int(m.ent.mnt_freq)
- v.Passno = int(m.ent.mnt_passno)
+func (e *MountinfoEntry) String() string {
+ return fmt.Sprintf("%d %d %s %s %s %s %s %s",
+ e.ID, e.Parent, e.Root, e.Target, e.VfsOptstr, e.FsType, e.Source, e.FsOptstr)
}