github.com/rigado/snapd@v2.42.5-go-mod+incompatible/osutil/mountinfo_linux_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package osutil_test 21 22 import ( 23 "io/ioutil" 24 "path/filepath" 25 "strings" 26 27 . "gopkg.in/check.v1" 28 29 "github.com/snapcore/snapd/osutil" 30 ) 31 32 type mountinfoSuite struct{} 33 34 var _ = Suite(&mountinfoSuite{}) 35 36 // Check that parsing the example from kernel documentation works correctly. 37 func (s *mountinfoSuite) TestParseMountInfoEntry1(c *C) { 38 real := "36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue" 39 canonical := "36 35 98:0 /mnt1 /mnt2 noatime,rw master:1 - ext3 /dev/root errors=continue,rw" 40 entry, err := osutil.ParseMountInfoEntry(real) 41 c.Assert(err, IsNil) 42 c.Assert(entry.String(), Equals, canonical) 43 44 c.Assert(entry.MountID, Equals, 36) 45 c.Assert(entry.ParentID, Equals, 35) 46 c.Assert(entry.DevMajor, Equals, 98) 47 c.Assert(entry.DevMinor, Equals, 0) 48 c.Assert(entry.Root, Equals, "/mnt1") 49 c.Assert(entry.MountDir, Equals, "/mnt2") 50 c.Assert(entry.MountOptions, DeepEquals, map[string]string{"rw": "", "noatime": ""}) 51 c.Assert(entry.OptionalFields, DeepEquals, []string{"master:1"}) 52 c.Assert(entry.FsType, Equals, "ext3") 53 c.Assert(entry.MountSource, Equals, "/dev/root") 54 c.Assert(entry.SuperOptions, DeepEquals, map[string]string{"rw": "", "errors": "continue"}) 55 } 56 57 // Check that various combinations of optional fields are parsed correctly. 58 func (s *mountinfoSuite) TestParseMountInfoEntry2(c *C) { 59 // No optional fields. 60 real := "36 35 98:0 /mnt1 /mnt2 rw,noatime - ext3 /dev/root rw,errors=continue" 61 canonical := "36 35 98:0 /mnt1 /mnt2 noatime,rw - ext3 /dev/root errors=continue,rw" 62 entry, err := osutil.ParseMountInfoEntry(real) 63 c.Assert(err, IsNil) 64 c.Assert(entry.String(), Equals, canonical) 65 66 c.Assert(entry.MountOptions, DeepEquals, map[string]string{"rw": "", "noatime": ""}) 67 c.Assert(entry.OptionalFields, HasLen, 0) 68 c.Assert(entry.FsType, Equals, "ext3") 69 // One optional field. 70 entry, err = osutil.ParseMountInfoEntry( 71 "36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue") 72 c.Assert(err, IsNil) 73 c.Assert(entry.MountOptions, DeepEquals, map[string]string{"rw": "", "noatime": ""}) 74 c.Assert(entry.OptionalFields, DeepEquals, []string{"master:1"}) 75 c.Assert(entry.FsType, Equals, "ext3") 76 // Two optional fields. 77 entry, err = osutil.ParseMountInfoEntry( 78 "36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 slave:2 - ext3 /dev/root rw,errors=continue") 79 c.Assert(err, IsNil) 80 c.Assert(entry.MountOptions, DeepEquals, map[string]string{"rw": "", "noatime": ""}) 81 c.Assert(entry.OptionalFields, DeepEquals, []string{"master:1", "slave:2"}) 82 c.Assert(entry.FsType, Equals, "ext3") 83 } 84 85 // Check that white-space is unescaped correctly. 86 func (s *mountinfoSuite) TestParseMountInfoEntry3(c *C) { 87 real := `36 35 98:0 /mnt\0401 /mnt\0402 noatime,rw\040 mas\040ter:1 - ext\0403 /dev/ro\040ot rw\040,errors=continue` 88 canonical := `36 35 98:0 /mnt\0401 /mnt\0402 noatime,rw\040 mas\040ter:1 - ext\0403 /dev/ro\040ot errors=continue,rw\040` 89 entry, err := osutil.ParseMountInfoEntry(real) 90 c.Assert(err, IsNil) 91 c.Assert(entry.String(), Equals, canonical) 92 93 c.Assert(entry.MountID, Equals, 36) 94 c.Assert(entry.ParentID, Equals, 35) 95 c.Assert(entry.DevMajor, Equals, 98) 96 c.Assert(entry.DevMinor, Equals, 0) 97 c.Assert(entry.Root, Equals, "/mnt 1") 98 c.Assert(entry.MountDir, Equals, "/mnt 2") 99 c.Assert(entry.MountOptions, DeepEquals, map[string]string{"rw ": "", "noatime": ""}) 100 // This field is still escaped as it is space-separated and needs further parsing. 101 c.Assert(entry.OptionalFields, DeepEquals, []string{"mas ter:1"}) 102 c.Assert(entry.FsType, Equals, "ext 3") 103 c.Assert(entry.MountSource, Equals, "/dev/ro ot") 104 c.Assert(entry.SuperOptions, DeepEquals, map[string]string{"rw ": "", "errors": "continue"}) 105 } 106 107 // Check that various malformed entries are detected. 108 func (s *mountinfoSuite) TestParseMountInfoEntry4(c *C) { 109 var err error 110 _, err = osutil.ParseMountInfoEntry("36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue foo") 111 c.Assert(err, ErrorMatches, "incorrect number of tail fields, expected 3 but found 4") 112 _, err = osutil.ParseMountInfoEntry("36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root") 113 c.Assert(err, ErrorMatches, "incorrect number of tail fields, expected 3 but found 2") 114 _, err = osutil.ParseMountInfoEntry("36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3") 115 c.Assert(err, ErrorMatches, "incorrect number of fields, expected at least 10 but found 9") 116 _, err = osutil.ParseMountInfoEntry("36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 -") 117 c.Assert(err, ErrorMatches, "incorrect number of fields, expected at least 10 but found 8") 118 _, err = osutil.ParseMountInfoEntry("36 35 98:0 /mnt1 /mnt2 rw,noatime master:1") 119 c.Assert(err, ErrorMatches, "incorrect number of fields, expected at least 10 but found 7") 120 _, err = osutil.ParseMountInfoEntry("36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 garbage1 garbage2 garbage3") 121 c.Assert(err, ErrorMatches, "list of optional fields is not terminated properly") 122 _, err = osutil.ParseMountInfoEntry("foo 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue foo") 123 c.Assert(err, ErrorMatches, `cannot parse mount ID: "foo"`) 124 _, err = osutil.ParseMountInfoEntry("36 bar 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue foo") 125 c.Assert(err, ErrorMatches, `cannot parse parent mount ID: "bar"`) 126 _, err = osutil.ParseMountInfoEntry("36 35 froz:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue foo") 127 c.Assert(err, ErrorMatches, `cannot parse device major number: "froz"`) 128 _, err = osutil.ParseMountInfoEntry("36 35 98:bot /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue foo") 129 c.Assert(err, ErrorMatches, `cannot parse device minor number: "bot"`) 130 _, err = osutil.ParseMountInfoEntry("36 35 corrupt /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue foo") 131 c.Assert(err, ErrorMatches, `cannot parse device major:minor number pair: "corrupt"`) 132 } 133 134 // Check that \r is parsed correctly. 135 func (s *mountinfoSuite) TestParseMountInfoEntry5(c *C) { 136 real := "2074 27 0:54 / /tmp/strange\rdir rw,relatime shared:1039 - tmpfs tmpfs rw" 137 canonical := "2074 27 0:54 / /tmp/strange\rdir relatime,rw shared:1039 - tmpfs tmpfs rw" 138 entry, err := osutil.ParseMountInfoEntry(real) 139 c.Assert(err, IsNil) 140 c.Assert(entry.String(), Equals, canonical) 141 c.Assert(entry.MountDir, Equals, "/tmp/strange\rdir") 142 } 143 144 // Test that empty mountinfo is parsed without errors. 145 func (s *mountinfoSuite) TestReadMountInfo1(c *C) { 146 entries, err := osutil.ReadMountInfo(strings.NewReader("")) 147 c.Assert(err, IsNil) 148 c.Assert(entries, HasLen, 0) 149 } 150 151 const mountInfoSample = "" + 152 "19 25 0:18 / /sys rw,nosuid,nodev,noexec,relatime shared:7 - sysfs sysfs rw\n" + 153 "20 25 0:4 / /proc rw,nosuid,nodev,noexec,relatime shared:13 - proc proc rw\n" + 154 "21 25 0:6 / /dev rw,nosuid,relatime shared:2 - devtmpfs udev rw,size=1937696k,nr_inodes=484424,mode=755\n" 155 156 // Test that mountinfo is parsed without errors. 157 func (s *mountinfoSuite) TestReadMountInfo2(c *C) { 158 entries, err := osutil.ReadMountInfo(strings.NewReader(mountInfoSample)) 159 c.Assert(err, IsNil) 160 c.Assert(entries, HasLen, 3) 161 } 162 163 // Test that loading mountinfo from a file works as expected. 164 func (s *mountinfoSuite) TestLoadMountInfo1(c *C) { 165 fname := filepath.Join(c.MkDir(), "mountinfo") 166 err := ioutil.WriteFile(fname, []byte(mountInfoSample), 0644) 167 c.Assert(err, IsNil) 168 entries, err := osutil.LoadMountInfo(fname) 169 c.Assert(err, IsNil) 170 c.Assert(entries, HasLen, 3) 171 } 172 173 // Test that loading mountinfo from a missing file reports an error. 174 func (s *mountinfoSuite) TestLoadMountInfo2(c *C) { 175 fname := filepath.Join(c.MkDir(), "mountinfo") 176 _, err := osutil.LoadMountInfo(fname) 177 c.Assert(err, ErrorMatches, "*. no such file or directory") 178 }