Skip to content

Commit 22bb5eb

Browse files
authored
Merge pull request containerd#3159 from thaJeztah/fix_parseinfofile_parsing
Fix parseInfoFile does not handle spaces in filenames
2 parents bc8a189 + adc4fa2 commit 22bb5eb

File tree

2 files changed

+66
-8
lines changed

2 files changed

+66
-8
lines changed

mount/mountinfo_linux.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import (
2525
"os"
2626
"strconv"
2727
"strings"
28+
29+
"github.com/pkg/errors"
2830
)
2931

3032
// Self retrieves a list of mounts for the current running process.
@@ -41,13 +43,15 @@ func Self() ([]Info, error) {
4143
func parseInfoFile(r io.Reader) ([]Info, error) {
4244
s := bufio.NewScanner(r)
4345
out := []Info{}
44-
46+
var err error
4547
for s.Scan() {
46-
if err := s.Err(); err != nil {
48+
if err = s.Err(); err != nil {
4749
return nil, err
4850
}
4951

5052
/*
53+
See http://man7.org/linux/man-pages/man5/proc.5.html
54+
5155
36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
5256
(1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
5357
(1) mount ID: unique identifier of the mount (may be reused after umount)
@@ -68,21 +72,27 @@ func parseInfoFile(r io.Reader) ([]Info, error) {
6872
numFields := len(fields)
6973
if numFields < 10 {
7074
// should be at least 10 fields
71-
return nil, fmt.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields)
75+
return nil, errors.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields)
7276
}
7377
p := Info{}
7478
// ignore any numbers parsing errors, as there should not be any
7579
p.ID, _ = strconv.Atoi(fields[0])
7680
p.Parent, _ = strconv.Atoi(fields[1])
7781
mm := strings.Split(fields[2], ":")
7882
if len(mm) != 2 {
79-
return nil, fmt.Errorf("parsing '%s' failed: unexpected minor:major pair %s", text, mm)
83+
return nil, errors.Errorf("parsing '%s' failed: unexpected minor:major pair %s", text, mm)
8084
}
8185
p.Major, _ = strconv.Atoi(mm[0])
8286
p.Minor, _ = strconv.Atoi(mm[1])
8387

84-
p.Root = fields[3]
85-
p.Mountpoint = fields[4]
88+
p.Root, err = strconv.Unquote(`"` + fields[3] + `"`)
89+
if err != nil {
90+
return nil, errors.Wrapf(err, "parsing '%s' failed: unable to unquote root field", fields[3])
91+
}
92+
p.Mountpoint, err = strconv.Unquote(`"` + fields[4] + `"`)
93+
if err != nil {
94+
return nil, errors.Wrapf(err, "parsing '%s' failed: unable to unquote mount point field", fields[4])
95+
}
8696
p.Options = fields[5]
8797

8898
// one or more optional fields, when a separator (-)
@@ -101,11 +111,11 @@ func parseInfoFile(r io.Reader) ([]Info, error) {
101111
}
102112
}
103113
if i == numFields {
104-
return nil, fmt.Errorf("parsing '%s' failed: missing separator ('-')", text)
114+
return nil, errors.Errorf("parsing '%s' failed: missing separator ('-')", text)
105115
}
106116
// There should be 3 fields after the separator...
107117
if i+4 > numFields {
108-
return nil, fmt.Errorf("parsing '%s' failed: not enough fields after a separator", text)
118+
return nil, errors.Errorf("parsing '%s' failed: not enough fields after a separator", text)
109119
}
110120
// ... but in Linux <= 3.9 mounting a cifs with spaces in a share name
111121
// (like "//serv/My Documents") _may_ end up having a space in the last field

mount/mountinfo_linux_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,9 @@ const (
436436
286 15 0:3631 / /var/lib/docker/aufs/mnt/ff28c27d5f894363993622de26d5dd352dba072f219e4691d6498c19bbbc15a9 rw,relatime - aufs none rw,si=9b4a7642265b339c
437437
289 15 0:3634 / /var/lib/docker/aufs/mnt/aa128fe0e64fdede333aa48fd9de39530c91a9244a0f0649a3c411c61e372daa rw,relatime - aufs none rw,si=9b4a764012ada39c
438438
99 15 8:33 / /media/REMOVE\040ME rw,nosuid,nodev,relatime - fuseblk /dev/sdc1 rw,user_id=0,group_id=0,allow_other,blksize=4096`
439+
440+
mountInfoWithSpaces = `486 28 252:1 / /mnt/foo\040bar rw,relatime shared:243 - ext4 /dev/vda1 rw,data=ordered
441+
31 21 0:23 / /DATA/foo_bla_bla rw,relatime - cifs //foo/BLA\040BLA\040BLA/ rw,sec=ntlm,cache=loose,unc=\\foo\BLA BLA BLA,username=my_login,domain=mydomain.com,uid=12345678,forceuid,gid=12345678,forcegid,addr=10.1.30.10,file_mode=0755,dir_mode=0755,nounix,rsize=61440,wsize=65536,actimeo=1`
439442
)
440443

441444
func TestParseFedoraMountinfo(t *testing.T) {
@@ -490,3 +493,48 @@ func TestParseFedoraMountinfoFields(t *testing.T) {
490493
t.Fatalf("expected %#v, got %#v", mi, infos[0])
491494
}
492495
}
496+
497+
func TestParseMountinfoWithSpaces(t *testing.T) {
498+
r := bytes.NewBuffer([]byte(mountInfoWithSpaces))
499+
infos, err := parseInfoFile(r)
500+
if err != nil {
501+
t.Fatal(err)
502+
}
503+
expected := []Info{
504+
{
505+
ID: 486,
506+
Parent: 28,
507+
Major: 252,
508+
Minor: 1,
509+
Root: "/",
510+
Mountpoint: "/mnt/foo bar",
511+
Options: "rw,relatime",
512+
Optional: "shared:243",
513+
FSType: "ext4",
514+
Source: "/dev/vda1",
515+
VFSOptions: "rw,data=ordered",
516+
},
517+
{
518+
ID: 31,
519+
Parent: 21,
520+
Major: 0,
521+
Minor: 23,
522+
Root: "/",
523+
Mountpoint: "/DATA/foo_bla_bla",
524+
Options: "rw,relatime",
525+
Optional: "",
526+
FSType: "cifs",
527+
Source: `//foo/BLA\040BLA\040BLA/`,
528+
VFSOptions: `rw,sec=ntlm,cache=loose,unc=\\foo\BLA`,
529+
},
530+
}
531+
532+
if len(infos) != len(expected) {
533+
t.Fatalf("expected %d entries, got %d", len(expected), len(infos))
534+
}
535+
for i, mi := range expected {
536+
if infos[i] != mi {
537+
t.Fatalf("expected %#v, got %#v", mi, infos[i])
538+
}
539+
}
540+
}

0 commit comments

Comments
 (0)