Skip to content

Commit

Permalink
PWX-30476: Upgrade GetMounts call to use the latest (libopenstorage#2293
Browse files Browse the repository at this point in the history
)

* PWX-30476: Upgrade GetMounts call to use the latest

Signed-off-by: pnookala-px <[email protected]>

* PWX-30476: Fix Test path

Signed-off-by: pnookala-px <[email protected]>

---------

Signed-off-by: pnookala-px <[email protected]>
Co-authored-by: Harsh Desai <[email protected]>
  • Loading branch information
pnookala-px and harsh-px authored Sep 15, 2023
1 parent cc1276c commit 4cfa6df
Show file tree
Hide file tree
Showing 12 changed files with 171 additions and 190 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ require (
github.com/libopenstorage/gossip v0.0.0-20220309192431-44c895e0923e
github.com/libopenstorage/secrets v0.0.0-20200207034622-cdb443738c67
github.com/libopenstorage/systemutils v0.0.0-20160208220149-44ac83be3ce1
github.com/moby/sys/mount v0.2.0
github.com/moby/sys/mountinfo v0.4.0
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.16.0
Expand Down Expand Up @@ -103,8 +105,6 @@ require (
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/sys/mount v0.2.0 // indirect
github.com/moby/sys/mountinfo v0.4.0 // indirect
github.com/moby/sys/symlink v0.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
Expand Down
4 changes: 2 additions & 2 deletions pkg/mount/bind_mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"regexp"
"strings"

"github.com/docker/docker/pkg/mount"
"github.com/moby/sys/mountinfo"
"github.com/libopenstorage/openstorage/pkg/keylock"
)

Expand Down Expand Up @@ -60,7 +60,7 @@ func (b *bindMounter) Load(rootSubstrings []*regexp.Regexp) error {
return b.load(rootSubstrings, bindFindMountPoint)
}

func bindFindMountPoint(sInfo *mount.Info, destination *regexp.Regexp, infos []*mount.Info) (bool, string, string) {
func bindFindMountPoint(sInfo *mountinfo.Info, destination *regexp.Regexp, infos []*mountinfo.Info) (bool, string, string) {
for _, dInfo := range infos {
if !destination.MatchString(dInfo.Mountpoint) {
continue
Expand Down
168 changes: 144 additions & 24 deletions pkg/mount/consistent_read.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ import (
"io"
"io/ioutil"
"strings"
"strconv"

"github.com/docker/docker/pkg/mount"
"github.com/moby/sys/mountinfo"
)

const (
Expand All @@ -58,7 +59,7 @@ var ErrLimitReached = errors.New("the read limit is reached")
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
// bind mounts. function is originally from
// https://github.com/moby/sys/blob/65f80e71a828ef17e6f573176dc569e55f519937/mountinfo/mountinfo_linux.go
func parseMountTable() ([]*mount.Info, error) {
func parseMountTable() ([]*mountinfo.Info, error) {
mountInfoBytes, err := consistentRead("/proc/self/mountinfo", 3)
if err != nil {
return nil, err
Expand Down Expand Up @@ -90,43 +91,162 @@ func consistentRead(filename string, attempts int) ([]byte, error) {
return nil, fmt.Errorf("could not get consistent content of mount table after %d attempts", attempts)
}

func parseInfoFile(r io.Reader) ([]*mount.Info, error) {
func parseInfoFile(r io.Reader) ([]*mountinfo.Info, error) {
var (
s = bufio.NewScanner(r)
out = []*mount.Info{}
out = []*mountinfo.Info{}
)

for s.Scan() {
if err := s.Err(); err != nil {
var err error
if err = s.Err(); err != nil {
return nil, err
}
/*
See http://man7.org/linux/man-pages/man5/proc.5.html
36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
(1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
(1) mount ID: unique identifier of the mount (may be reused after umount)
(2) parent ID: ID of parent (or of self for the top of the mount tree)
(3) major:minor: value of st_dev for files on filesystem
(4) root: root of the mount within the filesystem
(5) mount point: mount point relative to the process's root
(6) mount options: per mount options
(7) optional fields: zero or more fields of the form "tag[:value]"
(8) separator: marks the end of the optional fields
(9) filesystem type: name of filesystem of the form "type[.subtype]"
(10) mount source: filesystem specific information or "none"
(11) super options: per super block options
In other words, we have:
* 6 mandatory fields (1)..(6)
* 0 or more optional fields (7)
* a separator field (8)
* 3 mandatory fields (9)..(11)
*/

text := s.Text()
fields := strings.Split(text, " ")
numFields := len(fields)
if numFields < 10 {
// should be at least 10 fields
return nil, fmt.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields)
}

// separator field
sepIdx := numFields - 4
// In Linux <= 3.9 mounting a cifs with spaces in a share
// name (like "//srv/My Docs") _may_ end up having a space
// in the last field of mountinfo (like "unc=//serv/My Docs").
// Since kernel 3.10-rc1, cifs option "unc=" is ignored,
// so spaces should not appear.
//
// Check for a separator, and work around the spaces bug
for fields[sepIdx] != "-" {
sepIdx--
if sepIdx == 5 {
return nil, fmt.Errorf("parsing '%s' failed: missing - separator", text)
}
}

var (
p = &mount.Info{}
text = s.Text()
optionalFields string
)
p := &mountinfo.Info{}

if _, err := fmt.Sscanf(text, mountinfoFormat,
&p.ID, &p.Parent, &p.Major, &p.Minor,
&p.Root, &p.Mountpoint, &p.Opts, &optionalFields); err != nil {
return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err)
p.Mountpoint, err = unescape(fields[4])
if err != nil {
return nil, fmt.Errorf("parsing '%s' failed: mount point: %w", fields[4], err)
}
// Safe as mountinfo encodes mountpoints with spaces as \040.
index := strings.Index(text, " - ")
postSeparatorFields := strings.Fields(text[index+3:])
if len(postSeparatorFields) < 3 {
return nil, fmt.Errorf("Error found less than 3 fields post '-' in %q", text)
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.VFSOptions = fields[sepIdx+3]

// ignore any numbers parsing errors, as there should not be any
p.ID, _ = strconv.Atoi(fields[0])
p.Parent, _ = strconv.Atoi(fields[1])
mm := strings.SplitN(fields[2], ":", 3)
if len(mm) != 2 {
return nil, fmt.Errorf("parsing '%s' failed: unexpected major:minor pair %s", text, mm)
}
p.Major, _ = strconv.Atoi(mm[0])
p.Minor, _ = strconv.Atoi(mm[1])

if optionalFields != "-" {
p.Optional = optionalFields
p.Root, err = unescape(fields[3])
if err != nil {
return nil, fmt.Errorf("parsing '%s' failed: root: %w", fields[3], err)
}

p.Fstype = postSeparatorFields[0]
p.Source = postSeparatorFields[1]
p.VfsOpts = strings.Join(postSeparatorFields[2:], " ")
p.Options = fields[5]

// zero or more optional fields
p.Optional = strings.Join(fields[6:sepIdx], " ")

out = append(out, p)
}
return out, 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 (backslash 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
}
4 changes: 2 additions & 2 deletions pkg/mount/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"regexp"
"strings"

"github.com/docker/docker/pkg/mount"
"github.com/moby/sys/mountinfo"
"github.com/libopenstorage/openstorage/pkg/keylock"
)

Expand Down Expand Up @@ -68,7 +68,7 @@ func (m *deviceMounter) Load(devRegexes []*regexp.Regexp) error {
return m.load(devRegexes, deviceFindMountPoint)
}

func deviceFindMountPoint(info *mount.Info, destination *regexp.Regexp, infos []*mount.Info) (bool, string, string) {
func deviceFindMountPoint(info *mountinfo.Info, destination *regexp.Regexp, infos []*mountinfo.Info) (bool, string, string) {
if destination.MatchString(info.Source) {
return true, info.Source, info.Source
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/mount/device_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"regexp"
"testing"

"github.com/docker/docker/pkg/mount"
"github.com/moby/sys/mountinfo"
"github.com/stretchr/testify/require"
)

Expand All @@ -21,8 +21,8 @@ func addMountEntry(t *testing.T, device string, mountpoint string) int {
_, err := os.Create(device)
require.NoError(t, err, "Failed to create test device: ", device)
index := len(testMounts)
info := &mount.Info{
Fstype: "ext4",
info := &mountinfo.Info{
FSType: "ext4",
Minor: index,
Mountpoint: mountpoint,
Source: device,
Expand Down
12 changes: 6 additions & 6 deletions pkg/mount/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"syscall"
"time"

"github.com/docker/docker/pkg/mount"
"github.com/moby/sys/mountinfo"
"github.com/libopenstorage/openstorage/pkg/chattr"
"github.com/libopenstorage/openstorage/pkg/keylock"
"github.com/libopenstorage/openstorage/pkg/options"
Expand Down Expand Up @@ -151,7 +151,7 @@ type Mounter struct {
trashLocation string
}

type findMountPoint func(source *mount.Info, destination *regexp.Regexp, mountInfo []*mount.Info) (bool, string, string)
type findMountPoint func(source *mountinfo.Info, destination *regexp.Regexp, mountInfo []*mountinfo.Info) (bool, string, string)

// DefaultMounter defaults to syscall implementation.
type DefaultMounter struct {
Expand Down Expand Up @@ -398,7 +398,7 @@ func (m *Mounter) load(prefixes []*regexp.Regexp, fmp findMountPoint) error {
if !ok {
mount = &Info{
Device: deviceSourcePath,
Fs: v.Fstype,
Fs: v.FSType,
Minor: v.Minor,
Mountpoint: make([]*PathInfo, 0),
}
Expand Down Expand Up @@ -833,7 +833,7 @@ func New(

// GetMounts is a wrapper over mount.GetMounts(). It is mainly used to add a switch
// to enable device mounter tests.
func GetMounts() ([]*mount.Info, error) {
func GetMounts() ([]*mountinfo.Info, error) {
if os.Getenv(testDeviceEnv) != "" {
return testGetMounts()
}
Expand All @@ -842,12 +842,12 @@ func GetMounts() ([]*mount.Info, error) {

var (
// testMounts is a global test list of mount table entries
testMounts []*mount.Info
testMounts []*mountinfo.Info
)

// testGetMounts is only used in tests to get the test list of mount table
// entries
func testGetMounts() ([]*mount.Info, error) {
func testGetMounts() ([]*mountinfo.Info, error) {
var err error
if len(testMounts) == 0 {
testMounts, err = parseMountTable()
Expand Down
12 changes: 6 additions & 6 deletions pkg/mount/nfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"regexp"
"strings"

"github.com/docker/docker/pkg/mount"
"github.com/moby/sys/mountinfo"
"github.com/libopenstorage/openstorage/pkg/keylock"
)

Expand Down Expand Up @@ -83,8 +83,8 @@ func (m *nfsMounter) serverExists(server string) bool {

// normalizeSource - NFS source is returned as IP:share or just :share
// normalize that to always IP:share
func (m *nfsMounter) normalizeSource(info *mount.Info, host string) {
if info.Fstype != "nfs" {
func (m *nfsMounter) normalizeSource(info *mountinfo.Info, host string) {
if info.FSType != "nfs" {
return
}

Expand All @@ -110,10 +110,10 @@ MountLoop:
for _, v := range info {
host := "localhost"
if len(m.servers) != 0 {
if !strings.HasPrefix(v.Fstype, "nfs") {
if !strings.HasPrefix(v.FSType, "nfs") {
continue
}
matches := re.FindStringSubmatch(v.VfsOpts)
matches := re.FindStringSubmatch(v.VFSOptions)
if len(matches) != 2 {
continue
}
Expand All @@ -128,7 +128,7 @@ MountLoop:
if !ok {
mount = &Info{
Device: v.Source,
Fs: v.Fstype,
Fs: v.FSType,
Minor: v.Minor,
Mountpoint: make([]*PathInfo, 0),
}
Expand Down
Loading

0 comments on commit 4cfa6df

Please sign in to comment.