Skip to content

Commit

Permalink
osutil: fix parsing super-block options with spaces
Browse files Browse the repository at this point in the history
On any Windows + WSL 2 + Docker system, running any snap command is immediately
preceded by the two errors:

  2023/06/17 23:59:52.071511 system_key.go:129: cannot determine nfs usage in
  generateSystemKey: cannot parse mountinfo: incorrect number of tail fields,
  expected 3 but found 4
  2023/06/17 23:59:52.080033 cmd_run.go:1046: WARNING: cannot create user data
  directory: cannot determine SELinux status: failed to obtain SELinux mount
  path: incorrect number of tail fields, expected 3 but found 4

Those errors confuse tools that parse program output and generally cause a lot of havoc.

The root of the issue is caused by this specific entry in /proc/self/mountinfo
(not wrapped to preserve the problematic part more prominently):

  1146 77 0:149 / /Docker/host rw,noatime - 9p drvfs rw,dirsync,aname=drvfs;path=C:\Program Files\Docker\Docker\resources;symlinkroot=/mnt/,mmap,access=client,msize=262144,trans=virtio

Note that the super-block mount option contains a key=value pair where the
value contains un-escaped spaces. Historically mountinfo has been an utter mess
to parse, with several bugs in both the userspace parsing and several bugs in
the fragile kernel interface generating the contents of said file.

To work around this problem allow spaces in the super-block options and
silently parse them as if they had been escaped correctly.

Signed-off-by: Zygmunt Krynicki <me@zygoon.pl>
  • Loading branch information
zyga authored and mvo5 committed Aug 3, 2023
1 parent 84a2be4 commit 3d72b03
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 5 deletions.
5 changes: 3 additions & 2 deletions osutil/mountinfo_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,13 @@ func ParseMountInfoEntry(s string) (*MountInfoEntry, error) {
}
// Parse the last three fixed fields.
tailFields := fields[i+1:]
if len(tailFields) != 3 {
// XXX: The last field (options) *may* contain spaces that are incorrectly escaped by some file-systems.
if len(tailFields) < 3 {
return nil, fmt.Errorf("incorrect number of tail fields, expected 3 but found %d", len(tailFields))
}
e.FsType = unescape(tailFields[0])
e.MountSource = unescape(tailFields[1])
e.SuperOptions = parseMountOpts(unescape(tailFields[2]))
e.SuperOptions = parseMountOpts(unescape(strings.Join(tailFields[2:], " ")))
return &e, nil
}

Expand Down
22 changes: 19 additions & 3 deletions osutil/mountinfo_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,27 @@ func (s *mountinfoSuite) TestParseMountInfoEntry3(c *C) {
c.Assert(entry.SuperOptions, DeepEquals, map[string]string{"rw ": "", "errors": "continue"})
}

func (s *mountinfoSuite) TestBrokenEscapingPlan9(c *C) {
// This is a real sample collected on WSL-2 with Docker installed on the Windows host.
mi, err := osutil.ParseMountInfoEntry(`1146 77 0:149 / /Docker/host rw,noatime - 9p drvfs rw,dirsync,aname=drvfs;path=C:\Program Files\Docker\Docker\resources;symlinkroot=/mnt/,mmap,access=client,msize=262144,trans=virtio`)
c.Assert(err, IsNil)
c.Check(mi.SuperOptions, DeepEquals, map[string]string{
"rw": "",
"dirsync": "",
// XXX: what is the likelihood that comma is properly escaped in the mount option value?
"aname": "drvfs;path=C:\\Program Files\\Docker\\Docker\\resources;symlinkroot=/mnt/",
"mmap": "",
"access": "client",
"msize": "262144",
"trans": "virtio",
})
}

// Check that various malformed entries are detected.
func (s *mountinfoSuite) TestParseMountInfoEntry4(c *C) {
var err error
_, err = osutil.ParseMountInfoEntry("36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue foo")
c.Assert(err, ErrorMatches, "incorrect number of tail fields, expected 3 but found 4")
mi, err := osutil.ParseMountInfoEntry("36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue foo")
c.Assert(err, IsNil)
c.Check(mi.SuperOptions, DeepEquals, map[string]string{"rw": "", "errors": "continue foo"})
_, err = osutil.ParseMountInfoEntry("36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root")
c.Assert(err, ErrorMatches, "incorrect number of tail fields, expected 3 but found 2")
_, err = osutil.ParseMountInfoEntry("36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3")
Expand Down

0 comments on commit 3d72b03

Please sign in to comment.