summaryrefslogtreecommitdiff
path: root/vendor/github.com/moby/sys/mountinfo
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/moby/sys/mountinfo')
-rw-r--r--vendor/github.com/moby/sys/mountinfo/doc.go47
-rw-r--r--vendor/github.com/moby/sys/mountinfo/go.mod2
-rw-r--r--vendor/github.com/moby/sys/mountinfo/go.sum2
-rw-r--r--vendor/github.com/moby/sys/mountinfo/mounted_linux.go58
-rw-r--r--vendor/github.com/moby/sys/mountinfo/mounted_unix.go66
-rw-r--r--vendor/github.com/moby/sys/mountinfo/mountinfo.go23
-rw-r--r--vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go4
-rw-r--r--vendor/github.com/moby/sys/mountinfo/mountinfo_freebsd.go12
-rw-r--r--vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go76
-rw-r--r--vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go8
-rw-r--r--vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go4
11 files changed, 284 insertions, 18 deletions
diff --git a/vendor/github.com/moby/sys/mountinfo/doc.go b/vendor/github.com/moby/sys/mountinfo/doc.go
new file mode 100644
index 000000000..21aa8dd59
--- /dev/null
+++ b/vendor/github.com/moby/sys/mountinfo/doc.go
@@ -0,0 +1,47 @@
+// Package mountinfo provides a set of functions to retrieve information about OS mounts.
+// Currently it supports Linux. For historical reasons, there is also some support for FreeBSD,
+// and a shallow implementation for Windows, but in general this is Linux-only package, so
+// the rest of the document only applies to Linux, unless explicitly specified otherwise.
+//
+// In Linux, information about mounts seen by the current process is available from
+// /proc/self/mountinfo. Note that due to mount namespaces, different processes can
+// see different mounts. A per-process mountinfo table is available from /proc/<PID>/mountinfo,
+// where <PID> is a numerical process identifier.
+//
+// In general, /proc is not a very effective interface, and mountinfo is not an exception.
+// For example, there is no way to get information about a specific mount point (i.e. it
+// is all-or-nothing). This package tries to hide the /proc ineffectiveness by using
+// parse filters while reading mountinfo. A filter can skip some entries, or stop
+// processing the rest of the file once the needed information is found.
+//
+// For mountinfo filters that accept path as an argument, the path must be:
+// - absolute;
+// - having all symlinks resolved;
+// - being cleaned.
+//
+// One way to achieve all of the above is to employ filepath.Abs followed by
+// filepath.EvalSymlinks (the latter calls filepath.Clean on the result so
+// there is no need to explicitly call filepath.Clean).
+//
+// NOTE that in many cases there is no need to consult mountinfo at all. Here are some
+// of the cases where mountinfo should not be parsed:
+//
+// 1. Before performing a mount. Usually, this is not needed, but if required (say to
+// prevent overmounts), to check whether a directory is mounted, call os.Lstat
+// on it and its parent directory, and compare their st.Sys().(*syscall.Stat_t).Dev
+// fields -- if they differ, then the directory is the mount point. NOTE this does
+// not work for bind mounts. Optionally, the filesystem type can also be checked
+// by calling unix.Statfs and checking the Type field (i.e. filesystem type).
+//
+// 2. After performing a mount. If there is no error returned, the mount succeeded;
+// checking the mount table for a new mount is redundant and expensive.
+//
+// 3. Before performing an unmount. It is more efficient to do an unmount and ignore
+// a specific error (EINVAL) which tells the directory is not mounted.
+//
+// 4. After performing an unmount. If there is no error returned, the unmount succeeded.
+//
+// 5. To find the mount point root of a specific directory. You can perform os.Stat()
+// on the directory and traverse up until the Dev field of a parent directory differs.
+
+package mountinfo
diff --git a/vendor/github.com/moby/sys/mountinfo/go.mod b/vendor/github.com/moby/sys/mountinfo/go.mod
index 10d9a15a6..9749ea96d 100644
--- a/vendor/github.com/moby/sys/mountinfo/go.mod
+++ b/vendor/github.com/moby/sys/mountinfo/go.mod
@@ -1,3 +1,5 @@
module github.com/moby/sys/mountinfo
go 1.14
+
+require golang.org/x/sys v0.0.0-20200909081042-eff7692f9009
diff --git a/vendor/github.com/moby/sys/mountinfo/go.sum b/vendor/github.com/moby/sys/mountinfo/go.sum
new file mode 100644
index 000000000..2a5be7ea8
--- /dev/null
+++ b/vendor/github.com/moby/sys/mountinfo/go.sum
@@ -0,0 +1,2 @@
+golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM=
+golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/vendor/github.com/moby/sys/mountinfo/mounted_linux.go b/vendor/github.com/moby/sys/mountinfo/mounted_linux.go
new file mode 100644
index 000000000..bc9f6b2ad
--- /dev/null
+++ b/vendor/github.com/moby/sys/mountinfo/mounted_linux.go
@@ -0,0 +1,58 @@
+package mountinfo
+
+import (
+ "os"
+ "path/filepath"
+
+ "golang.org/x/sys/unix"
+)
+
+// mountedByOpenat2 is a method of detecting a mount that works for all kinds
+// of mounts (incl. bind mounts), but requires a recent (v5.6+) linux kernel.
+func mountedByOpenat2(path string) (bool, error) {
+ dir, last := filepath.Split(path)
+
+ dirfd, err := unix.Openat2(unix.AT_FDCWD, dir, &unix.OpenHow{
+ Flags: unix.O_PATH | unix.O_CLOEXEC,
+ })
+ if err != nil {
+ if err == unix.ENOENT { // not a mount
+ return false, nil
+ }
+ return false, &os.PathError{Op: "openat2", Path: dir, Err: err}
+ }
+ fd, err := unix.Openat2(dirfd, last, &unix.OpenHow{
+ Flags: unix.O_PATH | unix.O_CLOEXEC | unix.O_NOFOLLOW,
+ Resolve: unix.RESOLVE_NO_XDEV,
+ })
+ _ = unix.Close(dirfd)
+ switch err {
+ case nil: // definitely not a mount
+ _ = unix.Close(fd)
+ return false, nil
+ case unix.EXDEV: // definitely a mount
+ return true, nil
+ case unix.ENOENT: // not a mount
+ return false, nil
+ }
+ // not sure
+ return false, &os.PathError{Op: "openat2", Path: path, Err: err}
+}
+
+func mounted(path string) (bool, error) {
+ // Try a fast path, using openat2() with RESOLVE_NO_XDEV.
+ mounted, err := mountedByOpenat2(path)
+ if err == nil {
+ return mounted, nil
+ }
+ // Another fast path: compare st.st_dev fields.
+ mounted, err = mountedByStat(path)
+ // This does not work for bind mounts, so false negative
+ // is possible, therefore only trust if return is true.
+ if mounted && err == nil {
+ return mounted, nil
+ }
+
+ // Fallback to parsing mountinfo
+ return mountedByMountinfo(path)
+}
diff --git a/vendor/github.com/moby/sys/mountinfo/mounted_unix.go b/vendor/github.com/moby/sys/mountinfo/mounted_unix.go
new file mode 100644
index 000000000..c4d66b2f4
--- /dev/null
+++ b/vendor/github.com/moby/sys/mountinfo/mounted_unix.go
@@ -0,0 +1,66 @@
+// +build linux freebsd,cgo
+
+package mountinfo
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "golang.org/x/sys/unix"
+)
+
+func mountedByStat(path string) (bool, error) {
+ var st unix.Stat_t
+
+ if err := unix.Lstat(path, &st); err != nil {
+ if err == unix.ENOENT {
+ // Treat ENOENT as "not mounted".
+ return false, nil
+ }
+ return false, &os.PathError{Op: "stat", Path: path, Err: err}
+ }
+ dev := st.Dev
+ parent := filepath.Dir(path)
+ if err := unix.Lstat(parent, &st); err != nil {
+ return false, &os.PathError{Op: "stat", Path: parent, Err: err}
+ }
+ if dev != st.Dev {
+ // Device differs from that of parent,
+ // so definitely a mount point.
+ return true, nil
+ }
+ // NB: this does not detect bind mounts on Linux.
+ return false, nil
+}
+
+func normalizePath(path string) (realPath string, err error) {
+ if realPath, err = filepath.Abs(path); err != nil {
+ return "", fmt.Errorf("unable to get absolute path for %q: %w", path, err)
+ }
+ if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
+ return "", fmt.Errorf("failed to canonicalise path for %q: %w", path, err)
+ }
+ if _, err := os.Stat(realPath); err != nil {
+ return "", fmt.Errorf("failed to stat target of %q: %w", path, err)
+ }
+ return realPath, nil
+}
+
+func mountedByMountinfo(path string) (bool, error) {
+ path, err := normalizePath(path)
+ if err != nil {
+ if errors.Is(err, unix.ENOENT) {
+ // treat ENOENT as "not mounted"
+ return false, nil
+ }
+ return false, err
+ }
+ entries, err := GetMounts(SingleEntryFilter(path))
+ if err != nil {
+ return false, err
+ }
+
+ return len(entries) > 0, nil
+}
diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo.go b/vendor/github.com/moby/sys/mountinfo/mountinfo.go
index 136b14167..1987fcbb2 100644
--- a/vendor/github.com/moby/sys/mountinfo/mountinfo.go
+++ b/vendor/github.com/moby/sys/mountinfo/mountinfo.go
@@ -1,6 +1,9 @@
package mountinfo
-import "io"
+import (
+ "io"
+ "os"
+)
// GetMounts retrieves a list of mounts for the current running process,
// with an optional filter applied (use nil for no filter).
@@ -16,15 +19,17 @@ func GetMountsFromReader(reader io.Reader, f FilterFunc) ([]*Info, error) {
return parseInfoFile(reader, f)
}
-// Mounted determines if a specified mountpoint has been mounted.
-// On Linux it looks at /proc/self/mountinfo.
-func Mounted(mountpoint string) (bool, error) {
- entries, err := GetMounts(SingleEntryFilter(mountpoint))
- if err != nil {
- return false, err
+// Mounted determines if a specified path is a mount point.
+//
+// The argument must be an absolute path, with all symlinks resolved, and clean.
+// One way to ensure it is to process the path using filepath.Abs followed by
+// filepath.EvalSymlinks before calling this function.
+func Mounted(path string) (bool, error) {
+ // root is always mounted
+ if path == string(os.PathSeparator) {
+ return true, nil
}
-
- return len(entries) > 0, nil
+ return mounted(path)
}
// Info reveals information about a particular mounted filesystem. This
diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go
index 795026465..8aebe1ad4 100644
--- a/vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go
+++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go
@@ -15,7 +15,7 @@ import "strings"
type FilterFunc func(*Info) (skip, stop bool)
// PrefixFilter discards all entries whose mount points
-// do not start with a specific prefix
+// do not start with a specific prefix.
func PrefixFilter(prefix string) FilterFunc {
return func(m *Info) (bool, bool) {
skip := !strings.HasPrefix(m.Mountpoint, prefix)
@@ -23,7 +23,7 @@ func PrefixFilter(prefix string) FilterFunc {
}
}
-// SingleEntryFilter looks for a specific entry
+// SingleEntryFilter looks for a specific entry.
func SingleEntryFilter(mp string) FilterFunc {
return func(m *Info) (bool, bool) {
if m.Mountpoint == mp {
diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_freebsd.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_freebsd.go
index a7dbb1b46..b30dc1625 100644
--- a/vendor/github.com/moby/sys/mountinfo/mountinfo_freebsd.go
+++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_freebsd.go
@@ -51,3 +51,15 @@ func parseMountTable(filter FilterFunc) ([]*Info, error) {
}
return out, nil
}
+
+func mounted(path string) (bool, error) {
+ // Fast path: compare st.st_dev fields.
+ // This should always work for FreeBSD.
+ mounted, err := mountedByStat(path)
+ if err == nil {
+ return mounted, nil
+ }
+
+ // Fallback to parsing mountinfo
+ return mountedByMountinfo(path)
+}
diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go
index 2d630c8dc..cdfd37da5 100644
--- a/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go
+++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go
@@ -71,12 +71,18 @@ func parseInfoFile(r io.Reader, filter FilterFunc) ([]*Info, error) {
p := &Info{}
// Fill in the fields that a filter might check
- p.Mountpoint, err = strconv.Unquote(`"` + fields[4] + `"`)
+ p.Mountpoint, err = unescape(fields[4])
if err != nil {
- return nil, fmt.Errorf("Parsing '%s' failed: unable to unquote mount point field: %w", fields[4], err)
+ return nil, fmt.Errorf("Parsing '%s' failed: mount point: %w", fields[4], err)
+ }
+ p.Fstype, err = unescape(fields[sepIdx+1])
+ if err != nil {
+ return nil, fmt.Errorf("Parsing '%s' failed: fstype: %w", fields[sepIdx+1], err)
+ }
+ p.Source, err = unescape(fields[sepIdx+2])
+ if err != nil {
+ return nil, fmt.Errorf("Parsing '%s' failed: source: %w", fields[sepIdx+2], err)
}
- p.Fstype = fields[sepIdx+1]
- p.Source = fields[sepIdx+2]
p.VfsOpts = fields[sepIdx+3]
// Run a filter soon so we can skip parsing/adding entries
@@ -101,9 +107,9 @@ func parseInfoFile(r io.Reader, filter FilterFunc) ([]*Info, error) {
p.Major, _ = strconv.Atoi(mm[0])
p.Minor, _ = strconv.Atoi(mm[1])
- p.Root, err = strconv.Unquote(`"` + fields[3] + `"`)
+ p.Root, err = unescape(fields[3])
if err != nil {
- return nil, fmt.Errorf("Parsing '%s' failed: unable to unquote root field: %w", fields[3], err)
+ return nil, fmt.Errorf("Parsing '%s' failed: root: %w", fields[3], err)
}
p.Opts = fields[5]
@@ -150,3 +156,61 @@ func PidMountInfo(pid int) ([]*Info, error) {
return parseInfoFile(f, nil)
}
+
+// A few specific characters in mountinfo path entries (root and mountpoint)
+// are escaped using a backslash followed by a character's ascii code in octal.
+//
+// space -- as \040
+// tab (aka \t) -- as \011
+// newline (aka \n) -- as \012
+// backslash (aka \\) -- as \134
+//
+// This function converts path from mountinfo back, i.e. it unescapes the above sequences.
+func unescape(path string) (string, error) {
+ // try to avoid copying
+ if strings.IndexByte(path, '\\') == -1 {
+ return path, nil
+ }
+
+ // The following code is UTF-8 transparent as it only looks for some
+ // specific characters (backslach and 0..7) with values < utf8.RuneSelf,
+ // and everything else is passed through as is.
+ buf := make([]byte, len(path))
+ bufLen := 0
+ for i := 0; i < len(path); i++ {
+ if path[i] != '\\' {
+ buf[bufLen] = path[i]
+ bufLen++
+ continue
+ }
+ s := path[i:]
+ if len(s) < 4 {
+ // too short
+ return "", fmt.Errorf("bad escape sequence %q: too short", s)
+ }
+ c := s[1]
+ switch c {
+ case '0', '1', '2', '3', '4', '5', '6', '7':
+ v := c - '0'
+ for j := 2; j < 4; j++ { // one digit already; two more
+ if s[j] < '0' || s[j] > '7' {
+ return "", fmt.Errorf("bad escape sequence %q: not a digit", s[:3])
+ }
+ x := s[j] - '0'
+ v = (v << 3) | x
+ }
+ if v > 255 {
+ return "", fmt.Errorf("bad escape sequence %q: out of range" + s[:3])
+ }
+ buf[bufLen] = v
+ bufLen++
+ i += 3
+ continue
+ default:
+ return "", fmt.Errorf("bad escape sequence %q: not a digit" + s[:3])
+
+ }
+ }
+
+ return string(buf[:bufLen]), nil
+}
diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go
index dc1869211..1eb8558c8 100644
--- a/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go
+++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go
@@ -8,10 +8,16 @@ import (
"runtime"
)
+var errNotImplemented = fmt.Errorf("not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+
func parseMountTable(_ FilterFunc) ([]*Info, error) {
- return nil, fmt.Errorf("mount.parseMountTable is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+ return nil, errNotImplemented
}
func parseInfoFile(_ io.Reader, f FilterFunc) ([]*Info, error) {
return parseMountTable(f)
}
+
+func mounted(path string) (bool, error) {
+ return false, errNotImplemented
+}
diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go
index 69ffdc52b..5659c1b0f 100644
--- a/vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go
+++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go
@@ -10,3 +10,7 @@ func parseMountTable(_ FilterFunc) ([]*Info, error) {
func parseInfoFile(_ io.Reader, f FilterFunc) ([]*Info, error) {
return parseMountTable(f)
}
+
+func mounted(_ string) (bool, error) {
+ return false, nil
+}