mountinfo: refactored

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
This commit is contained in:
Akihiro Suda 2017-05-10 04:53:59 +00:00
parent d5707d3ac7
commit 8cd218237b
9 changed files with 71 additions and 67 deletions

View File

@ -23,23 +23,18 @@ type Info struct {
// Mountpoint indicates the mount point relative to the process's root.
Mountpoint string
// Opts represents mount-specific options.
Opts string
// Options represents mount-specific options.
Options string
// Optional represents optional fields.
Optional string
// Fstype indicates the type of filesystem, such as EXT3.
Fstype string
// FSType indicates the type of filesystem, such as EXT3.
FSType string
// Source indicates filesystem specific information or "none".
Source string
// 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()
// VFSOptions represents per super block options.
VFSOptions string
}

View File

@ -13,9 +13,8 @@ import (
"unsafe"
)
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
// bind mounts.
func parseMountTable() ([]*Info, error) {
// Self retrieves a list of mounts for the current running process.
func Self() ([]Info, error) {
var rawEntries *C.struct_statfs
count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT))
@ -29,13 +28,18 @@ func parseMountTable() ([]*Info, error) {
header.Len = count
header.Data = uintptr(unsafe.Pointer(rawEntries))
var out []*Info
var out []Info
for _, entry := range entries {
var mountinfo Info
mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0])
mountinfo.Source = C.GoString(&entry.f_mntfromname[0])
mountinfo.Fstype = C.GoString(&entry.f_fstypename[0])
out = append(out, &mountinfo)
mountinfo.FSType = C.GoString(&entry.f_fstypename[0])
out = append(out, mountinfo)
}
return out, nil
}
// PID collects the mounts for a specific process ID.
func PID(pid int) ([]Info, error) {
return nil, fmt.Errorf("mountinfo.PID is not implemented on freebsd")
}

View File

@ -28,9 +28,8 @@ const (
mountinfoFormat = "%d %d %d:%d %s %s %s %s"
)
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
// bind mounts
func parseMountTable() ([]*Info, error) {
// Self retrieves a list of mounts for the current running process.
func Self() ([]Info, error) {
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return nil, err
@ -40,10 +39,10 @@ func parseMountTable() ([]*Info, error) {
return parseInfoFile(f)
}
func parseInfoFile(r io.Reader) ([]*Info, error) {
func parseInfoFile(r io.Reader) ([]Info, error) {
var (
s = bufio.NewScanner(r)
out = []*Info{}
out = []Info{}
)
for s.Scan() {
@ -52,14 +51,14 @@ func parseInfoFile(r io.Reader) ([]*Info, error) {
}
var (
p = &Info{}
p = Info{}
text = s.Text()
optionalFields string
)
if _, err := fmt.Sscanf(text, mountinfoFormat,
&p.ID, &p.Parent, &p.Major, &p.Minor,
&p.Root, &p.Mountpoint, &p.Opts, &optionalFields); err != nil {
&p.Root, &p.Mountpoint, &p.Options, &optionalFields); err != nil {
return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err)
}
// Safe as mountinfo encodes mountpoints with spaces as \040.
@ -73,18 +72,18 @@ func parseInfoFile(r io.Reader) ([]*Info, error) {
p.Optional = optionalFields
}
p.Fstype = postSeparatorFields[0]
p.FSType = postSeparatorFields[0]
p.Source = postSeparatorFields[1]
p.VfsOpts = strings.Join(postSeparatorFields[2:], " ")
p.VFSOptions = strings.Join(postSeparatorFields[2:], " ")
out = append(out, p)
}
return out, nil
}
// PidMountInfo collects the mounts for a specific process ID. If the process
// ID is unknown, it is better to use `GetMounts` which will inspect
// PID collects the mounts for a specific process ID. If the process
// ID is unknown, it is better to use `Self` which will inspect
// "/proc/self/mountinfo" instead.
func PidMountInfo(pid int) ([]*Info, error) {
func PID(pid int) ([]Info, error) {
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
if err != nil {
return nil, err

View File

@ -463,14 +463,14 @@ func TestParseFedoraMountinfoFields(t *testing.T) {
Minor: 3,
Root: "/",
Mountpoint: "/proc",
Opts: "rw,nosuid,nodev,noexec,relatime",
Options: "rw,nosuid,nodev,noexec,relatime",
Optional: "shared:5",
Fstype: "proc",
FSType: "proc",
Source: "proc",
VfsOpts: "rw",
VFSOptions: "rw",
}
if *infos[0] != mi {
if infos[0] != mi {
t.Fatalf("expected %#v, got %#v", mi, infos[0])
}
}

View File

@ -12,13 +12,14 @@ import (
"fmt"
)
func parseMountTable() ([]*Info, error) {
// Self retrieves a list of mounts for the current running process.
func Self() ([]Info, error) {
mnttab := C.fopen(C.CString(C.MNTTAB), C.CString("r"))
if mnttab == nil {
return nil, fmt.Errorf("Failed to open %s", C.MNTTAB)
}
var out []*Info
var out []Info
var mp C.struct_mnttab
ret := C.getmntent(mnttab, &mp)
@ -26,12 +27,17 @@ func parseMountTable() ([]*Info, error) {
var mountinfo Info
mountinfo.Mountpoint = C.GoString(mp.mnt_mountp)
mountinfo.Source = C.GoString(mp.mnt_special)
mountinfo.Fstype = C.GoString(mp.mnt_fstype)
mountinfo.Opts = C.GoString(mp.mnt_mntopts)
out = append(out, &mountinfo)
mountinfo.FSType = C.GoString(mp.mnt_fstype)
mountinfo.Options = C.GoString(mp.mnt_mntopts)
out = append(out, mountinfo)
ret = C.getmntent(mnttab, &mp)
}
C.fclose(mnttab)
return out, nil
}
// PID collects the mounts for a specific process ID.
func PID(pid int) ([]Info, error) {
return nil, fmt.Errorf("mountinfo.PID is not implemented on solaris")
}

View File

@ -1,4 +1,4 @@
// +build !windows,!linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo
// +build !linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo
package mountinfo
@ -7,6 +7,12 @@ import (
"runtime"
)
func parseMountTable() ([]*Info, error) {
return nil, fmt.Errorf("mount.parseMountTable is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
// Self retrieves a list of mounts for the current running process.
func Self() ([]Info, error) {
return nil, fmt.Errorf("mountinfo.Self is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
// PID collects the mounts for a specific process ID.
func PID(pid int) ([]Info, error) {
return nil, fmt.Errorf("mountinfo.PID is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}

View File

@ -1,6 +0,0 @@
package mountinfo
func parseMountTable() ([]*Info, error) {
// Do NOT return an error!
return nil, nil
}

View File

@ -36,17 +36,17 @@ type snapshotter struct {
ms *storage.MetaStore
}
func getBtrfsDevice(root string, mounts []*mountinfo.Info) (string, error) {
func getBtrfsDevice(root string, mounts []mountinfo.Info) (string, error) {
device := ""
deviceMountpoint := ""
for _, info := range mounts {
if (info.Root == "/" || info.Root == "") && strings.HasPrefix(root, info.Mountpoint) {
if info.Fstype == "btrfs" && len(info.Mountpoint) > len(deviceMountpoint) {
if info.FSType == "btrfs" && len(info.Mountpoint) > len(deviceMountpoint) {
device = info.Source
deviceMountpoint = info.Mountpoint
}
if root == info.Mountpoint && info.Fstype != "btrfs" {
return "", fmt.Errorf("%s needs to be btrfs, but seems %s", root, info.Fstype)
if root == info.Mountpoint && info.FSType != "btrfs" {
return "", fmt.Errorf("%s needs to be btrfs, but seems %s", root, info.FSType)
}
}
}
@ -62,7 +62,7 @@ func getBtrfsDevice(root string, mounts []*mountinfo.Info) (string, error) {
// a file in the provided root.
// root needs to be a mount point of btrfs.
func NewSnapshotter(root string) (snapshot.Snapshotter, error) {
mounts, err := mountinfo.GetMounts()
mounts, err := mountinfo.Self()
if err != nil {
return nil, err
}

View File

@ -222,45 +222,45 @@ func TestGetBtrfsDevice(t *testing.T) {
expectedDevice string
expectedError string
root string
mounts []*mountinfo.Info
mounts []mountinfo.Info
}{
{
expectedDevice: "/dev/loop0",
root: "/var/lib/containerd/snapshot/btrfs",
mounts: []*mountinfo.Info{
{Root: "/", Mountpoint: "/", Fstype: "ext4", Source: "/dev/sda1"},
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", Fstype: "btrfs", Source: "/dev/loop0"},
mounts: []mountinfo.Info{
{Root: "/", Mountpoint: "/", FSType: "ext4", Source: "/dev/sda1"},
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", FSType: "btrfs", Source: "/dev/loop0"},
},
},
{
expectedError: "/var/lib/containerd/snapshot/btrfs is not mounted as btrfs",
root: "/var/lib/containerd/snapshot/btrfs",
mounts: []*mountinfo.Info{
{Root: "/", Mountpoint: "/", Fstype: "ext4", Source: "/dev/sda1"},
mounts: []mountinfo.Info{
{Root: "/", Mountpoint: "/", FSType: "ext4", Source: "/dev/sda1"},
},
},
{
expectedDevice: "/dev/sda1",
root: "/var/lib/containerd/snapshot/btrfs",
mounts: []*mountinfo.Info{
{Root: "/", Mountpoint: "/", Fstype: "btrfs", Source: "/dev/sda1"},
mounts: []mountinfo.Info{
{Root: "/", Mountpoint: "/", FSType: "btrfs", Source: "/dev/sda1"},
},
},
{
expectedDevice: "/dev/sda2",
root: "/var/lib/containerd/snapshot/btrfs",
mounts: []*mountinfo.Info{
{Root: "/", Mountpoint: "/", Fstype: "btrfs", Source: "/dev/sda1"},
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", Fstype: "btrfs", Source: "/dev/sda2"},
mounts: []mountinfo.Info{
{Root: "/", Mountpoint: "/", FSType: "btrfs", Source: "/dev/sda1"},
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", FSType: "btrfs", Source: "/dev/sda2"},
},
},
{
expectedDevice: "/dev/sda2",
root: "/var/lib/containerd/snapshot/btrfs",
mounts: []*mountinfo.Info{
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", Fstype: "btrfs", Source: "/dev/sda2"},
{Root: "/", Mountpoint: "/var/lib/foooooooooooooooooooo/baaaaaaaaaaaaaaaaaaaar", Fstype: "btrfs", Source: "/dev/sda3"}, // mountpoint length longer than /var/lib/containerd/snapshot/btrfs
{Root: "/", Mountpoint: "/", Fstype: "btrfs", Source: "/dev/sda1"},
mounts: []mountinfo.Info{
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", FSType: "btrfs", Source: "/dev/sda2"},
{Root: "/", Mountpoint: "/var/lib/foooooooooooooooooooo/baaaaaaaaaaaaaaaaaaaar", FSType: "btrfs", Source: "/dev/sda3"}, // mountpoint length longer than /var/lib/containerd/snapshot/btrfs
{Root: "/", Mountpoint: "/", FSType: "btrfs", Source: "/dev/sda1"},
},
},
}