github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/pkg/mount/mounter_linux_test.go (about) 1 // +build linux 2 3 package mount 4 5 import ( 6 "fmt" 7 "io/ioutil" 8 "os" 9 "strings" 10 "testing" 11 12 selinux "github.com/opencontainers/selinux/go-selinux" 13 ) 14 15 func TestMount(t *testing.T) { 16 if os.Getuid() != 0 { 17 t.Skip("not root tests would fail") 18 } 19 20 source, err := ioutil.TempDir("", "mount-test-source-") 21 if err != nil { 22 t.Fatal(err) 23 } 24 defer os.RemoveAll(source) 25 26 // Ensure we have a known start point by mounting tmpfs with given options 27 if err := Mount("tmpfs", source, "tmpfs", "private"); err != nil { 28 t.Fatal(err) 29 } 30 defer ensureUnmount(t, source) 31 validateMount(t, source, "", "", "") 32 if t.Failed() { 33 t.FailNow() 34 } 35 36 target, err := ioutil.TempDir("", "mount-test-target-") 37 if err != nil { 38 t.Fatal(err) 39 } 40 defer os.RemoveAll(target) 41 42 tests := []struct { 43 source string 44 ftype string 45 options string 46 expectedOpts string 47 expectedOptional string 48 expectedVFS string 49 }{ 50 // No options 51 {"tmpfs", "tmpfs", "", "", "", ""}, 52 // Default rw / ro test 53 {source, "", "bind", "", "", ""}, 54 {source, "", "bind,private", "", "", ""}, 55 {source, "", "bind,shared", "", "shared", ""}, 56 {source, "", "bind,slave", "", "master", ""}, 57 {source, "", "bind,unbindable", "", "unbindable", ""}, 58 // Read Write tests 59 {source, "", "bind,rw", "rw", "", ""}, 60 {source, "", "bind,rw,private", "rw", "", ""}, 61 {source, "", "bind,rw,shared", "rw", "shared", ""}, 62 {source, "", "bind,rw,slave", "rw", "master", ""}, 63 {source, "", "bind,rw,unbindable", "rw", "unbindable", ""}, 64 // Read Only tests 65 {source, "", "bind,ro", "ro", "", ""}, 66 {source, "", "bind,ro,private", "ro", "", ""}, 67 {source, "", "bind,ro,shared", "ro", "shared", ""}, 68 {source, "", "bind,ro,slave", "ro", "master", ""}, 69 {source, "", "bind,ro,unbindable", "ro", "unbindable", ""}, 70 // Remount tests to change per filesystem options 71 {"", "", "remount,size=128k", "rw", "", "rw,size=128k"}, 72 {"", "", "remount,ro,size=128k", "ro", "", "ro,size=128k"}, 73 } 74 75 for _, tc := range tests { 76 ftype, options := tc.ftype, tc.options 77 if tc.ftype == "" { 78 ftype = "none" 79 } 80 if tc.options == "" { 81 options = "none" 82 } 83 84 t.Run(fmt.Sprintf("%v-%v", ftype, options), func(t *testing.T) { 85 if strings.Contains(tc.options, "slave") { 86 // Slave requires a shared source 87 if err := MakeShared(source); err != nil { 88 t.Fatal(err) 89 } 90 defer func() { 91 if err := MakePrivate(source); err != nil { 92 t.Fatal(err) 93 } 94 }() 95 } 96 if strings.Contains(tc.options, "remount") { 97 // create a new mount to remount first 98 if err := Mount("tmpfs", target, "tmpfs", ""); err != nil { 99 t.Fatal(err) 100 } 101 } 102 if err := Mount(tc.source, target, tc.ftype, tc.options); err != nil { 103 t.Fatal(err) 104 } 105 defer ensureUnmount(t, target) 106 expectedVFS := tc.expectedVFS 107 if selinux.GetEnabled() && expectedVFS != "" { 108 expectedVFS = expectedVFS + ",seclabel" 109 } 110 validateMount(t, target, tc.expectedOpts, tc.expectedOptional, expectedVFS) 111 }) 112 } 113 } 114 115 // ensureUnmount umounts mnt checking for errors 116 func ensureUnmount(t *testing.T, mnt string) { 117 if err := Unmount(mnt); err != nil { 118 t.Error(err) 119 } 120 } 121 122 // validateMount checks that mnt has the given options 123 func validateMount(t *testing.T, mnt string, opts, optional, vfs string) { 124 info, err := GetMounts() 125 if err != nil { 126 t.Fatal(err) 127 } 128 129 wantedOpts := make(map[string]struct{}) 130 if opts != "" { 131 for _, opt := range strings.Split(opts, ",") { 132 wantedOpts[opt] = struct{}{} 133 } 134 } 135 136 wantedOptional := make(map[string]struct{}) 137 if optional != "" { 138 for _, opt := range strings.Split(optional, ",") { 139 wantedOptional[opt] = struct{}{} 140 } 141 } 142 143 wantedVFS := make(map[string]struct{}) 144 if vfs != "" { 145 for _, opt := range strings.Split(vfs, ",") { 146 wantedVFS[opt] = struct{}{} 147 } 148 } 149 150 mnts := make(map[int]*Info, len(info)) 151 for _, mi := range info { 152 mnts[mi.ID] = mi 153 } 154 155 for _, mi := range info { 156 if mi.Mountpoint != mnt { 157 continue 158 } 159 160 // Use parent info as the defaults 161 p := mnts[mi.Parent] 162 pOpts := make(map[string]struct{}) 163 if p.Opts != "" { 164 for _, opt := range strings.Split(p.Opts, ",") { 165 pOpts[clean(opt)] = struct{}{} 166 } 167 } 168 pOptional := make(map[string]struct{}) 169 if p.Optional != "" { 170 for _, field := range strings.Split(p.Optional, ",") { 171 pOptional[clean(field)] = struct{}{} 172 } 173 } 174 175 // Validate Opts 176 if mi.Opts != "" { 177 for _, opt := range strings.Split(mi.Opts, ",") { 178 opt = clean(opt) 179 if !has(wantedOpts, opt) && !has(pOpts, opt) { 180 t.Errorf("unexpected mount option %q expected %q", opt, opts) 181 } 182 delete(wantedOpts, opt) 183 } 184 } 185 for opt := range wantedOpts { 186 t.Errorf("missing mount option %q found %q", opt, mi.Opts) 187 } 188 189 // Validate Optional 190 if mi.Optional != "" { 191 for _, field := range strings.Split(mi.Optional, ",") { 192 field = clean(field) 193 if !has(wantedOptional, field) && !has(pOptional, field) { 194 t.Errorf("unexpected optional failed %q expected %q", field, optional) 195 } 196 delete(wantedOptional, field) 197 } 198 } 199 for field := range wantedOptional { 200 t.Errorf("missing optional field %q found %q", field, mi.Optional) 201 } 202 203 // Validate VFS if set 204 if vfs != "" { 205 if mi.VfsOpts != "" { 206 for _, opt := range strings.Split(mi.VfsOpts, ",") { 207 opt = clean(opt) 208 if !has(wantedVFS, opt) { 209 t.Errorf("unexpected mount option %q expected %q", opt, vfs) 210 } 211 delete(wantedVFS, opt) 212 } 213 } 214 for opt := range wantedVFS { 215 t.Errorf("missing mount option %q found %q", opt, mi.VfsOpts) 216 } 217 } 218 219 return 220 } 221 222 t.Errorf("failed to find mount %q", mnt) 223 } 224 225 // clean strips off any value param after the colon 226 func clean(v string) string { 227 return strings.SplitN(v, ":", 2)[0] 228 } 229 230 // has returns true if key is a member of m 231 func has(m map[string]struct{}, key string) bool { 232 _, ok := m[key] 233 return ok 234 }