mount: add mount.Lookup for ease of implementing snapshotters

`func Lookup(dir string) (Info, error)` returns the mount
info that corresponds to the dir

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
This commit is contained in:
Akihiro Suda
2017-05-14 14:08:43 +00:00
parent 4ae34cccc5
commit 0ab2227377
9 changed files with 204 additions and 237 deletions

View File

@@ -0,0 +1,3 @@
package lookuptest
// FIXME: without this dummy file, `make build` fails with "no buildable Go source files" error

View File

@@ -0,0 +1,59 @@
// +build linux
// FIXME: we can't put this test to the mount package:
// import cycle not allowed in test
// package github.com/containerd/containerd/mount (test)
// imports github.com/containerd/containerd/testutil
// imports github.com/containerd/containerd/mount
//
// NOTE: we can't have this as lookup_test (compilation fails)
package lookuptest
import (
"io/ioutil"
"os"
"os/exec"
"strings"
"testing"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/testutil"
"github.com/stretchr/testify/assert"
)
func testLookup(t *testing.T, fsType string) {
testutil.RequiresRoot(t)
mnt, err := ioutil.TempDir("", "containerd-mountinfo-test-lookup")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(mnt)
deviceName, cleanupDevice := testutil.NewLoopback(t, 100<<20) // 100 MB
if out, err := exec.Command("mkfs", "-t", fsType, deviceName).CombinedOutput(); err != nil {
// not fatal
t.Skipf("could not mkfs (%s) %s: %v (out: %q)", fsType, deviceName, err, string(out))
}
if out, err := exec.Command("mount", deviceName, mnt).CombinedOutput(); err != nil {
// not fatal
t.Skipf("could not mount %s: %v (out: %q)", deviceName, err, string(out))
}
defer func() {
testutil.Unmount(t, mnt)
cleanupDevice()
}()
assert.True(t, strings.HasPrefix(deviceName, "/dev/loop"))
info, err := mount.Lookup(mnt)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, fsType, info.FSType)
}
func TestLookupWithExt4(t *testing.T) {
testLookup(t, "ext4")
}
func TestLookupWithXFS(t *testing.T) {
testLookup(t, "xfs")
}

37
mount/lookup_unix.go Normal file
View File

@@ -0,0 +1,37 @@
// +build !windows
package mount
import (
"fmt"
"syscall"
"github.com/pkg/errors"
)
// Lookup returns the mount info corresponds to the path.
func Lookup(dir string) (Info, error) {
var dirStat syscall.Stat_t
if err := syscall.Stat(dir, &dirStat); err != nil {
return Info{}, errors.Wrapf(err, "failed to access %q", dir)
}
mounts, err := Self()
if err != nil {
return Info{}, err
}
for _, m := range mounts {
// Note that m.{Major, Minor} are generally unreliable for our purpose here
// https://www.spinics.net/lists/linux-btrfs/msg58908.html
var st syscall.Stat_t
if err := syscall.Stat(m.Mountpoint, &st); err != nil {
// may fail; ignore err
continue
}
if st.Dev == dirStat.Dev {
return m, nil
}
}
return Info{}, fmt.Errorf("failed to find the mount info for %q", dir)
}

View File

@@ -0,0 +1,13 @@
// +build windows
package mount
import (
"fmt"
"runtime"
)
// Lookup returns the mount info corresponds to the path.
func Lookup(dir string) (Info, error) {
return Info{}, fmt.Errorf("mount.Lookup is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}