github.com/moby/docker@v26.1.3+incompatible/volume/mounts/windows_parser_test.go (about) 1 package mounts // import "github.com/docker/docker/volume/mounts" 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "github.com/docker/docker/api/types/mount" 9 "gotest.tools/v3/assert" 10 is "gotest.tools/v3/assert/cmp" 11 ) 12 13 func TestWindowsParseMountRaw(t *testing.T) { 14 valid := []string{ 15 `d:\`, 16 `d:`, 17 `d:\path`, 18 `d:\path with space`, 19 `c:\:d:\`, 20 `c:\windows\:d:`, 21 `c:\windows:d:\s p a c e`, 22 `c:\windows:d:\s p a c e:RW`, 23 `c:\program files:d:\s p a c e i n h o s t d i r`, 24 `0123456789name:d:`, 25 `MiXeDcAsEnAmE:d:`, 26 `test-aux-volume:d:`, // includes reserved word, but is not one itself 27 `name:D:`, 28 `name:D::rW`, 29 `name:D::RW`, 30 `name:D::RO`, 31 `c:/:d:/forward/slashes/are/good/too`, 32 `c:/:d:/including with/spaces:ro`, 33 `c:\Windows`, // With capital 34 `c:\Program Files (x86)`, // With capitals and brackets 35 `\\?\c:\windows\:d:`, // Long path handling (source) 36 `c:\windows\:\\?\d:\`, // Long path handling (target) 37 `\\.\pipe\foo:\\.\pipe\foo`, // named pipe 38 `//./pipe/foo://./pipe/foo`, // named pipe forward slashes 39 } 40 41 invalid := map[string]string{ 42 ``: "invalid volume specification: ", 43 `.`: "invalid volume specification: ", 44 `..\`: "invalid volume specification: ", 45 `c:\:..\`: "invalid volume specification: ", 46 `c:\:d:\:xyzzy`: "invalid volume specification: ", 47 `c:`: "cannot be `c:`", 48 `c:\`: "cannot be `c:`", 49 `c:\notexist:d:`: `source path does not exist: c:\notexist`, 50 `c:\windows\system32\ntdll.dll:d:`: `source path must be a directory`, 51 `name<:d:`: `invalid volume specification`, 52 `name>:d:`: `invalid volume specification`, 53 `name::d:`: `invalid volume specification`, 54 `name":d:`: `invalid volume specification`, 55 `name\:d:`: `invalid volume specification`, 56 `name*:d:`: `invalid volume specification`, 57 `name|:d:`: `invalid volume specification`, 58 `name?:d:`: `invalid volume specification`, 59 `name/:d:`: `invalid volume specification`, 60 `d:\pathandmode:rw`: `invalid volume specification`, 61 `d:\pathandmode:ro`: `invalid volume specification`, 62 `con:d:`: `cannot be a reserved word for Windows filenames`, 63 `PRN:d:`: `cannot be a reserved word for Windows filenames`, 64 `aUx:d:`: `cannot be a reserved word for Windows filenames`, 65 `nul:d:`: `cannot be a reserved word for Windows filenames`, 66 `com1:d:`: `cannot be a reserved word for Windows filenames`, 67 `com2:d:`: `cannot be a reserved word for Windows filenames`, 68 `com3:d:`: `cannot be a reserved word for Windows filenames`, 69 `com4:d:`: `cannot be a reserved word for Windows filenames`, 70 `com5:d:`: `cannot be a reserved word for Windows filenames`, 71 `com6:d:`: `cannot be a reserved word for Windows filenames`, 72 `com7:d:`: `cannot be a reserved word for Windows filenames`, 73 `com8:d:`: `cannot be a reserved word for Windows filenames`, 74 `com9:d:`: `cannot be a reserved word for Windows filenames`, 75 `lpt1:d:`: `cannot be a reserved word for Windows filenames`, 76 `lpt2:d:`: `cannot be a reserved word for Windows filenames`, 77 `lpt3:d:`: `cannot be a reserved word for Windows filenames`, 78 `lpt4:d:`: `cannot be a reserved word for Windows filenames`, 79 `lpt5:d:`: `cannot be a reserved word for Windows filenames`, 80 `lpt6:d:`: `cannot be a reserved word for Windows filenames`, 81 `lpt7:d:`: `cannot be a reserved word for Windows filenames`, 82 `lpt8:d:`: `cannot be a reserved word for Windows filenames`, 83 `lpt9:d:`: `cannot be a reserved word for Windows filenames`, 84 `c:\windows\system32\ntdll.dll`: `Only directories can be mapped on this platform`, 85 `\\.\pipe\foo:c:\pipe`: `'c:\pipe' is not a valid pipe path`, 86 } 87 88 parser := NewWindowsParser() 89 if p, ok := parser.(*windowsParser); ok { 90 p.fi = mockFiProvider{} 91 } 92 93 for _, path := range valid { 94 if _, err := parser.ParseMountRaw(path, "local"); err != nil { 95 t.Errorf("ParseMountRaw(`%q`) should succeed: error %q", path, err) 96 } 97 } 98 99 for path, expectedError := range invalid { 100 if mp, err := parser.ParseMountRaw(path, "local"); err == nil { 101 t.Errorf("ParseMountRaw(`%q`) should have failed validation. Err '%v' - MP: %v", path, err, mp) 102 } else { 103 if !strings.Contains(err.Error(), expectedError) { 104 t.Errorf("ParseMountRaw(`%q`) error should contain %q, got %v", path, expectedError, err.Error()) 105 } 106 } 107 } 108 } 109 110 func TestWindowsParseMountRawSplit(t *testing.T) { 111 cases := []struct { 112 bind string 113 driver string 114 expType mount.Type 115 expDest string 116 expSource string 117 expName string 118 expDriver string 119 expRW bool 120 fail bool 121 }{ 122 { 123 bind: `c:\:d:`, 124 driver: "local", 125 expType: mount.TypeBind, 126 expDest: `d:`, 127 expSource: `c:\`, 128 expRW: true, 129 }, 130 { 131 bind: `c:\:d:\`, 132 driver: "local", 133 expType: mount.TypeBind, 134 expDest: `d:\`, 135 expSource: `c:\`, 136 expRW: true, 137 }, 138 { 139 bind: `c:\:d:\:ro`, 140 driver: "local", 141 expType: mount.TypeBind, 142 expDest: `d:\`, 143 expSource: `c:\`, 144 }, 145 { 146 bind: `c:\:d:\:rw`, 147 driver: "local", 148 expType: mount.TypeBind, 149 expDest: `d:\`, 150 expSource: `c:\`, 151 expRW: true, 152 }, 153 { 154 bind: `c:\:d:\:foo`, 155 driver: "local", 156 expType: mount.TypeBind, 157 expDest: `d:\`, 158 expSource: `c:\`, 159 fail: true, 160 }, 161 { 162 bind: `name:d::rw`, 163 driver: "local", 164 expType: mount.TypeVolume, 165 expDest: `d:`, 166 expName: `name`, 167 expDriver: "local", 168 expRW: true, 169 }, 170 { 171 bind: `name:d:`, 172 driver: "local", 173 expType: mount.TypeVolume, 174 expDest: `d:`, 175 expName: `name`, 176 expDriver: "local", 177 expRW: true, 178 }, 179 { 180 bind: `name:d::ro`, 181 driver: "local", 182 expType: mount.TypeVolume, 183 expDest: `d:`, 184 expName: `name`, 185 expDriver: "local", 186 }, 187 { 188 bind: `name:c:`, 189 expType: mount.TypeVolume, 190 expRW: true, 191 fail: true, 192 }, 193 { 194 bind: `driver/name:c:`, 195 expType: mount.TypeVolume, 196 expRW: true, 197 fail: true, 198 }, 199 { 200 bind: `\\.\pipe\foo:\\.\pipe\bar`, 201 driver: "local", 202 expType: mount.TypeNamedPipe, 203 expDest: `\\.\pipe\bar`, 204 expSource: `\\.\pipe\foo`, 205 expRW: true, 206 }, 207 { 208 bind: `\\.\pipe\foo:c:\foo\bar`, 209 driver: "local", 210 expType: mount.TypeNamedPipe, 211 expRW: true, 212 fail: true, 213 }, 214 { 215 bind: `c:\foo\bar:\\.\pipe\foo`, 216 driver: "local", 217 expType: mount.TypeNamedPipe, 218 expRW: true, 219 fail: true, 220 }, 221 } 222 223 parser := NewWindowsParser() 224 if p, ok := parser.(*windowsParser); ok { 225 p.fi = mockFiProvider{} 226 } 227 228 for _, tc := range cases { 229 tc := tc 230 t.Run(tc.bind, func(t *testing.T) { 231 m, err := parser.ParseMountRaw(tc.bind, tc.driver) 232 if tc.fail { 233 assert.Check(t, is.ErrorContains(err, ""), "expected an error") 234 return 235 } 236 237 assert.NilError(t, err) 238 assert.Check(t, is.Equal(m.Destination, tc.expDest)) 239 assert.Check(t, is.Equal(m.Source, tc.expSource)) 240 assert.Check(t, is.Equal(m.Name, tc.expName)) 241 assert.Check(t, is.Equal(m.Driver, tc.expDriver)) 242 assert.Check(t, is.Equal(m.RW, tc.expRW)) 243 assert.Check(t, is.Equal(m.Type, tc.expType)) 244 }) 245 } 246 } 247 248 // TestWindowsParseMountSpecBindWithFileinfoError makes sure that the parser returns 249 // the error produced by the fileinfo provider. 250 // 251 // Some extra context for the future in case of changes and possible wtf are we 252 // testing this for: 253 // 254 // Currently this "fileInfoProvider" returns (bool, bool, error) 255 // The 1st bool is "does this path exist" 256 // The 2nd bool is "is this path a dir" 257 // Then of course the error is an error. 258 // 259 // The issue is the parser was ignoring the error and only looking at the 260 // "does this path exist" boolean, which is always false if there is an error. 261 // Then the error returned to the caller was a (slightly, maybe) friendlier 262 // error string than what comes from `os.Stat` 263 // So ...the caller was always getting an error saying the path doesn't exist 264 // even if it does exist but got some other error (like a permission error). 265 // This is confusing to users. 266 func TestWindowsParseMountSpecBindWithFileinfoError(t *testing.T) { 267 parser := NewWindowsParser() 268 testErr := fmt.Errorf("some crazy error") 269 if pr, ok := parser.(*windowsParser); ok { 270 pr.fi = &mockFiProviderWithError{err: testErr} 271 } 272 273 _, err := parser.ParseMountSpec(mount.Mount{ 274 Type: mount.TypeBind, 275 Source: `c:\bananas`, 276 Target: `c:\bananas`, 277 }) 278 assert.ErrorContains(t, err, testErr.Error()) 279 }