mountinfo: refactored
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
This commit is contained in:
parent
d5707d3ac7
commit
8cd218237b
@ -23,23 +23,18 @@ type Info struct {
|
|||||||
// Mountpoint indicates the mount point relative to the process's root.
|
// Mountpoint indicates the mount point relative to the process's root.
|
||||||
Mountpoint string
|
Mountpoint string
|
||||||
|
|
||||||
// Opts represents mount-specific options.
|
// Options represents mount-specific options.
|
||||||
Opts string
|
Options string
|
||||||
|
|
||||||
// Optional represents optional fields.
|
// Optional represents optional fields.
|
||||||
Optional string
|
Optional string
|
||||||
|
|
||||||
// Fstype indicates the type of filesystem, such as EXT3.
|
// FSType indicates the type of filesystem, such as EXT3.
|
||||||
Fstype string
|
FSType string
|
||||||
|
|
||||||
// Source indicates filesystem specific information or "none".
|
// Source indicates filesystem specific information or "none".
|
||||||
Source string
|
Source string
|
||||||
|
|
||||||
// VfsOpts represents per super block options.
|
// VFSOptions represents per super block options.
|
||||||
VfsOpts string
|
VFSOptions string
|
||||||
}
|
|
||||||
|
|
||||||
// GetMounts retrieves a list of mounts for the current running process.
|
|
||||||
func GetMounts() ([]*Info, error) {
|
|
||||||
return parseMountTable()
|
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,8 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
|
// Self retrieves a list of mounts for the current running process.
|
||||||
// bind mounts.
|
func Self() ([]Info, error) {
|
||||||
func parseMountTable() ([]*Info, error) {
|
|
||||||
var rawEntries *C.struct_statfs
|
var rawEntries *C.struct_statfs
|
||||||
|
|
||||||
count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT))
|
count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT))
|
||||||
@ -29,13 +28,18 @@ func parseMountTable() ([]*Info, error) {
|
|||||||
header.Len = count
|
header.Len = count
|
||||||
header.Data = uintptr(unsafe.Pointer(rawEntries))
|
header.Data = uintptr(unsafe.Pointer(rawEntries))
|
||||||
|
|
||||||
var out []*Info
|
var out []Info
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
var mountinfo Info
|
var mountinfo Info
|
||||||
mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0])
|
mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0])
|
||||||
mountinfo.Source = C.GoString(&entry.f_mntfromname[0])
|
mountinfo.Source = C.GoString(&entry.f_mntfromname[0])
|
||||||
mountinfo.Fstype = C.GoString(&entry.f_fstypename[0])
|
mountinfo.FSType = C.GoString(&entry.f_fstypename[0])
|
||||||
out = append(out, &mountinfo)
|
out = append(out, mountinfo)
|
||||||
}
|
}
|
||||||
return out, nil
|
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")
|
||||||
|
}
|
||||||
|
@ -28,9 +28,8 @@ const (
|
|||||||
mountinfoFormat = "%d %d %d:%d %s %s %s %s"
|
mountinfoFormat = "%d %d %d:%d %s %s %s %s"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
|
// Self retrieves a list of mounts for the current running process.
|
||||||
// bind mounts
|
func Self() ([]Info, error) {
|
||||||
func parseMountTable() ([]*Info, error) {
|
|
||||||
f, err := os.Open("/proc/self/mountinfo")
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -40,10 +39,10 @@ func parseMountTable() ([]*Info, error) {
|
|||||||
return parseInfoFile(f)
|
return parseInfoFile(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseInfoFile(r io.Reader) ([]*Info, error) {
|
func parseInfoFile(r io.Reader) ([]Info, error) {
|
||||||
var (
|
var (
|
||||||
s = bufio.NewScanner(r)
|
s = bufio.NewScanner(r)
|
||||||
out = []*Info{}
|
out = []Info{}
|
||||||
)
|
)
|
||||||
|
|
||||||
for s.Scan() {
|
for s.Scan() {
|
||||||
@ -52,14 +51,14 @@ func parseInfoFile(r io.Reader) ([]*Info, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
p = &Info{}
|
p = Info{}
|
||||||
text = s.Text()
|
text = s.Text()
|
||||||
optionalFields string
|
optionalFields string
|
||||||
)
|
)
|
||||||
|
|
||||||
if _, err := fmt.Sscanf(text, mountinfoFormat,
|
if _, err := fmt.Sscanf(text, mountinfoFormat,
|
||||||
&p.ID, &p.Parent, &p.Major, &p.Minor,
|
&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)
|
return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err)
|
||||||
}
|
}
|
||||||
// Safe as mountinfo encodes mountpoints with spaces as \040.
|
// Safe as mountinfo encodes mountpoints with spaces as \040.
|
||||||
@ -73,18 +72,18 @@ func parseInfoFile(r io.Reader) ([]*Info, error) {
|
|||||||
p.Optional = optionalFields
|
p.Optional = optionalFields
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Fstype = postSeparatorFields[0]
|
p.FSType = postSeparatorFields[0]
|
||||||
p.Source = postSeparatorFields[1]
|
p.Source = postSeparatorFields[1]
|
||||||
p.VfsOpts = strings.Join(postSeparatorFields[2:], " ")
|
p.VFSOptions = strings.Join(postSeparatorFields[2:], " ")
|
||||||
out = append(out, p)
|
out = append(out, p)
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PidMountInfo collects the mounts for a specific process ID. If the process
|
// PID collects the mounts for a specific process ID. If the process
|
||||||
// ID is unknown, it is better to use `GetMounts` which will inspect
|
// ID is unknown, it is better to use `Self` which will inspect
|
||||||
// "/proc/self/mountinfo" instead.
|
// "/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))
|
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -463,14 +463,14 @@ func TestParseFedoraMountinfoFields(t *testing.T) {
|
|||||||
Minor: 3,
|
Minor: 3,
|
||||||
Root: "/",
|
Root: "/",
|
||||||
Mountpoint: "/proc",
|
Mountpoint: "/proc",
|
||||||
Opts: "rw,nosuid,nodev,noexec,relatime",
|
Options: "rw,nosuid,nodev,noexec,relatime",
|
||||||
Optional: "shared:5",
|
Optional: "shared:5",
|
||||||
Fstype: "proc",
|
FSType: "proc",
|
||||||
Source: "proc",
|
Source: "proc",
|
||||||
VfsOpts: "rw",
|
VFSOptions: "rw",
|
||||||
}
|
}
|
||||||
|
|
||||||
if *infos[0] != mi {
|
if infos[0] != mi {
|
||||||
t.Fatalf("expected %#v, got %#v", mi, infos[0])
|
t.Fatalf("expected %#v, got %#v", mi, infos[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,14 @@ import (
|
|||||||
"fmt"
|
"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"))
|
mnttab := C.fopen(C.CString(C.MNTTAB), C.CString("r"))
|
||||||
if mnttab == nil {
|
if mnttab == nil {
|
||||||
return nil, fmt.Errorf("Failed to open %s", C.MNTTAB)
|
return nil, fmt.Errorf("Failed to open %s", C.MNTTAB)
|
||||||
}
|
}
|
||||||
|
|
||||||
var out []*Info
|
var out []Info
|
||||||
var mp C.struct_mnttab
|
var mp C.struct_mnttab
|
||||||
|
|
||||||
ret := C.getmntent(mnttab, &mp)
|
ret := C.getmntent(mnttab, &mp)
|
||||||
@ -26,12 +27,17 @@ func parseMountTable() ([]*Info, error) {
|
|||||||
var mountinfo Info
|
var mountinfo Info
|
||||||
mountinfo.Mountpoint = C.GoString(mp.mnt_mountp)
|
mountinfo.Mountpoint = C.GoString(mp.mnt_mountp)
|
||||||
mountinfo.Source = C.GoString(mp.mnt_special)
|
mountinfo.Source = C.GoString(mp.mnt_special)
|
||||||
mountinfo.Fstype = C.GoString(mp.mnt_fstype)
|
mountinfo.FSType = C.GoString(mp.mnt_fstype)
|
||||||
mountinfo.Opts = C.GoString(mp.mnt_mntopts)
|
mountinfo.Options = C.GoString(mp.mnt_mntopts)
|
||||||
out = append(out, &mountinfo)
|
out = append(out, mountinfo)
|
||||||
ret = C.getmntent(mnttab, &mp)
|
ret = C.getmntent(mnttab, &mp)
|
||||||
}
|
}
|
||||||
|
|
||||||
C.fclose(mnttab)
|
C.fclose(mnttab)
|
||||||
return out, nil
|
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")
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// +build !windows,!linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo
|
// +build !linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo
|
||||||
|
|
||||||
package mountinfo
|
package mountinfo
|
||||||
|
|
||||||
@ -7,6 +7,12 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseMountTable() ([]*Info, error) {
|
// Self retrieves a list of mounts for the current running process.
|
||||||
return nil, fmt.Errorf("mount.parseMountTable is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
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)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
package mountinfo
|
|
||||||
|
|
||||||
func parseMountTable() ([]*Info, error) {
|
|
||||||
// Do NOT return an error!
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
@ -36,17 +36,17 @@ type snapshotter struct {
|
|||||||
ms *storage.MetaStore
|
ms *storage.MetaStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBtrfsDevice(root string, mounts []*mountinfo.Info) (string, error) {
|
func getBtrfsDevice(root string, mounts []mountinfo.Info) (string, error) {
|
||||||
device := ""
|
device := ""
|
||||||
deviceMountpoint := ""
|
deviceMountpoint := ""
|
||||||
for _, info := range mounts {
|
for _, info := range mounts {
|
||||||
if (info.Root == "/" || info.Root == "") && strings.HasPrefix(root, info.Mountpoint) {
|
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
|
device = info.Source
|
||||||
deviceMountpoint = info.Mountpoint
|
deviceMountpoint = info.Mountpoint
|
||||||
}
|
}
|
||||||
if root == info.Mountpoint && info.Fstype != "btrfs" {
|
if root == info.Mountpoint && info.FSType != "btrfs" {
|
||||||
return "", fmt.Errorf("%s needs to be btrfs, but seems %s", root, info.Fstype)
|
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.
|
// a file in the provided root.
|
||||||
// root needs to be a mount point of btrfs.
|
// root needs to be a mount point of btrfs.
|
||||||
func NewSnapshotter(root string) (snapshot.Snapshotter, error) {
|
func NewSnapshotter(root string) (snapshot.Snapshotter, error) {
|
||||||
mounts, err := mountinfo.GetMounts()
|
mounts, err := mountinfo.Self()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -222,45 +222,45 @@ func TestGetBtrfsDevice(t *testing.T) {
|
|||||||
expectedDevice string
|
expectedDevice string
|
||||||
expectedError string
|
expectedError string
|
||||||
root string
|
root string
|
||||||
mounts []*mountinfo.Info
|
mounts []mountinfo.Info
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
expectedDevice: "/dev/loop0",
|
expectedDevice: "/dev/loop0",
|
||||||
root: "/var/lib/containerd/snapshot/btrfs",
|
root: "/var/lib/containerd/snapshot/btrfs",
|
||||||
mounts: []*mountinfo.Info{
|
mounts: []mountinfo.Info{
|
||||||
{Root: "/", Mountpoint: "/", Fstype: "ext4", Source: "/dev/sda1"},
|
{Root: "/", Mountpoint: "/", FSType: "ext4", Source: "/dev/sda1"},
|
||||||
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", Fstype: "btrfs", Source: "/dev/loop0"},
|
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", FSType: "btrfs", Source: "/dev/loop0"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expectedError: "/var/lib/containerd/snapshot/btrfs is not mounted as btrfs",
|
expectedError: "/var/lib/containerd/snapshot/btrfs is not mounted as btrfs",
|
||||||
root: "/var/lib/containerd/snapshot/btrfs",
|
root: "/var/lib/containerd/snapshot/btrfs",
|
||||||
mounts: []*mountinfo.Info{
|
mounts: []mountinfo.Info{
|
||||||
{Root: "/", Mountpoint: "/", Fstype: "ext4", Source: "/dev/sda1"},
|
{Root: "/", Mountpoint: "/", FSType: "ext4", Source: "/dev/sda1"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expectedDevice: "/dev/sda1",
|
expectedDevice: "/dev/sda1",
|
||||||
root: "/var/lib/containerd/snapshot/btrfs",
|
root: "/var/lib/containerd/snapshot/btrfs",
|
||||||
mounts: []*mountinfo.Info{
|
mounts: []mountinfo.Info{
|
||||||
{Root: "/", Mountpoint: "/", Fstype: "btrfs", Source: "/dev/sda1"},
|
{Root: "/", Mountpoint: "/", FSType: "btrfs", Source: "/dev/sda1"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expectedDevice: "/dev/sda2",
|
expectedDevice: "/dev/sda2",
|
||||||
root: "/var/lib/containerd/snapshot/btrfs",
|
root: "/var/lib/containerd/snapshot/btrfs",
|
||||||
mounts: []*mountinfo.Info{
|
mounts: []mountinfo.Info{
|
||||||
{Root: "/", Mountpoint: "/", Fstype: "btrfs", Source: "/dev/sda1"},
|
{Root: "/", Mountpoint: "/", FSType: "btrfs", Source: "/dev/sda1"},
|
||||||
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", Fstype: "btrfs", Source: "/dev/sda2"},
|
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", FSType: "btrfs", Source: "/dev/sda2"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expectedDevice: "/dev/sda2",
|
expectedDevice: "/dev/sda2",
|
||||||
root: "/var/lib/containerd/snapshot/btrfs",
|
root: "/var/lib/containerd/snapshot/btrfs",
|
||||||
mounts: []*mountinfo.Info{
|
mounts: []mountinfo.Info{
|
||||||
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", Fstype: "btrfs", Source: "/dev/sda2"},
|
{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: "/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"},
|
{Root: "/", Mountpoint: "/", FSType: "btrfs", Source: "/dev/sda1"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user