From 58d862574cf0a943b7f4fd944aa6d43fc7318446 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 29 Mar 2019 16:31:27 +0100 Subject: [PATCH] fix parseInfoFile does not handle spaces in filenames `/proc/self/mountinfo` uses `\040` for spaces, however, `parseInfoFile()` did not decode those spaces in paths, therefore attempting to use `\040` as a literal part of the path. This patch un-quotes the `root` and `mount point` fields to fix situations where paths contain spaces. Note that the `mount source` field is not modified, given that this field is documented (man `PROC(5)`) as: filesystem-specific information or "none" Which I interpreted as "the format in this field is undefined". Reported-by: Daniil Yaroslavtsev Reported-by: Nathan Ringo Based-on-patch-by: Diego Becciolini Based-on-patch-by: Sergei Utinski Signed-off-by: Sebastiaan van Stijn --- pkg/mount/mountinfo_linux.go | 18 ++++++++++-- pkg/mount/mountinfo_linux_test.go | 48 +++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/pkg/mount/mountinfo_linux.go b/pkg/mount/mountinfo_linux.go index c1dba01fc3..fe6e3ddba1 100644 --- a/pkg/mount/mountinfo_linux.go +++ b/pkg/mount/mountinfo_linux.go @@ -7,16 +7,21 @@ import ( "os" "strconv" "strings" + + "github.com/pkg/errors" ) func parseInfoFile(r io.Reader, filter FilterFunc) ([]*Info, error) { s := bufio.NewScanner(r) out := []*Info{} + var err error for s.Scan() { - if err := s.Err(); err != nil { + 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) @@ -52,8 +57,15 @@ func parseInfoFile(r io.Reader, filter FilterFunc) ([]*Info, error) { p.Major, _ = strconv.Atoi(mm[0]) p.Minor, _ = strconv.Atoi(mm[1]) - p.Root = fields[3] - p.Mountpoint = fields[4] + p.Root, err = strconv.Unquote(`"` + fields[3] + `"`) + if err != nil { + return nil, errors.Wrapf(err, "Parsing '%s' failed: unable to unquote root field", fields[3]) + } + + p.Mountpoint, err = strconv.Unquote(`"` + fields[4] + `"`) + if err != nil { + return nil, errors.Wrapf(err, "Parsing '%s' failed: unable to unquote mount point field", fields[4]) + } p.Opts = fields[5] var skip, stop bool diff --git a/pkg/mount/mountinfo_linux_test.go b/pkg/mount/mountinfo_linux_test.go index 64411ccaef..beebd3d312 100644 --- a/pkg/mount/mountinfo_linux_test.go +++ b/pkg/mount/mountinfo_linux_test.go @@ -422,6 +422,9 @@ const ( 286 15 0:3631 / /var/lib/docker/aufs/mnt/ff28c27d5f894363993622de26d5dd352dba072f219e4691d6498c19bbbc15a9 rw,relatime - aufs none rw,si=9b4a7642265b339c 289 15 0:3634 / /var/lib/docker/aufs/mnt/aa128fe0e64fdede333aa48fd9de39530c91a9244a0f0649a3c411c61e372daa rw,relatime - aufs none rw,si=9b4a764012ada39c 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` + + mountInfoWithSpaces = `486 28 252:1 / /mnt/foo\040bar rw,relatime shared:243 - ext4 /dev/vda1 rw,data=ordered +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` ) func TestParseFedoraMountinfo(t *testing.T) { @@ -477,6 +480,51 @@ func TestParseFedoraMountinfoFields(t *testing.T) { } } +func TestParseMountinfoWithSpaces(t *testing.T) { + r := bytes.NewBuffer([]byte(mountInfoWithSpaces)) + infos, err := parseInfoFile(r, nil) + if err != nil { + t.Fatal(err) + } + expected := []Info{ + { + ID: 486, + Parent: 28, + Major: 252, + Minor: 1, + Root: "/", + Mountpoint: "/mnt/foo bar", + Opts: "rw,relatime", + Optional: "shared:243", + Fstype: "ext4", + Source: "/dev/vda1", + VfsOpts: "rw,data=ordered", + }, + { + ID: 31, + Parent: 21, + Major: 0, + Minor: 23, + Root: "/", + Mountpoint: "/DATA/foo_bla_bla", + Opts: "rw,relatime", + Optional: "", + Fstype: "cifs", + Source: `//foo/BLA\040BLA\040BLA/`, + VfsOpts: `rw,sec=ntlm,cache=loose,unc=\\foo\BLA`, + }, + } + + if len(infos) != len(expected) { + t.Fatalf("expected %d entries, got %d", len(expected), len(infos)) + } + for i, mi := range expected { + if *infos[i] != mi { + t.Fatalf("expected %#v, got %#v", mi, infos[i]) + } + } +} + func TestParseMountinfoFilters(t *testing.T) { r := bytes.NewReader([]byte(fedoraMountinfo))