github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/pkg/chrootarchive/archive_test.go (about) 1 package chrootarchive // import "github.com/demonoid81/moby/pkg/chrootarchive" 2 3 import ( 4 "bytes" 5 "fmt" 6 "hash/crc32" 7 "io" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "testing" 14 "time" 15 16 "github.com/demonoid81/moby/pkg/archive" 17 "github.com/demonoid81/moby/pkg/reexec" 18 "github.com/demonoid81/moby/pkg/system" 19 "gotest.tools/v3/skip" 20 ) 21 22 func init() { 23 reexec.Init() 24 } 25 26 var chrootArchiver = NewArchiver(nil) 27 28 func TarUntar(src, dst string) error { 29 return chrootArchiver.TarUntar(src, dst) 30 } 31 32 func CopyFileWithTar(src, dst string) (err error) { 33 return chrootArchiver.CopyFileWithTar(src, dst) 34 } 35 36 func UntarPath(src, dst string) error { 37 return chrootArchiver.UntarPath(src, dst) 38 } 39 40 func CopyWithTar(src, dst string) error { 41 return chrootArchiver.CopyWithTar(src, dst) 42 } 43 44 func TestChrootTarUntar(t *testing.T) { 45 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 46 tmpdir, err := ioutil.TempDir("", "docker-TestChrootTarUntar") 47 if err != nil { 48 t.Fatal(err) 49 } 50 defer os.RemoveAll(tmpdir) 51 src := filepath.Join(tmpdir, "src") 52 if err := system.MkdirAll(src, 0700); err != nil { 53 t.Fatal(err) 54 } 55 if err := ioutil.WriteFile(filepath.Join(src, "toto"), []byte("hello toto"), 0644); err != nil { 56 t.Fatal(err) 57 } 58 if err := ioutil.WriteFile(filepath.Join(src, "lolo"), []byte("hello lolo"), 0644); err != nil { 59 t.Fatal(err) 60 } 61 stream, err := archive.Tar(src, archive.Uncompressed) 62 if err != nil { 63 t.Fatal(err) 64 } 65 dest := filepath.Join(tmpdir, "src") 66 if err := system.MkdirAll(dest, 0700); err != nil { 67 t.Fatal(err) 68 } 69 if err := Untar(stream, dest, &archive.TarOptions{ExcludePatterns: []string{"lolo"}}); err != nil { 70 t.Fatal(err) 71 } 72 } 73 74 // gh#10426: Verify the fix for having a huge excludes list (like on `docker load` with large # of 75 // local images) 76 func TestChrootUntarWithHugeExcludesList(t *testing.T) { 77 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 78 tmpdir, err := ioutil.TempDir("", "docker-TestChrootUntarHugeExcludes") 79 if err != nil { 80 t.Fatal(err) 81 } 82 defer os.RemoveAll(tmpdir) 83 src := filepath.Join(tmpdir, "src") 84 if err := system.MkdirAll(src, 0700); err != nil { 85 t.Fatal(err) 86 } 87 if err := ioutil.WriteFile(filepath.Join(src, "toto"), []byte("hello toto"), 0644); err != nil { 88 t.Fatal(err) 89 } 90 stream, err := archive.Tar(src, archive.Uncompressed) 91 if err != nil { 92 t.Fatal(err) 93 } 94 dest := filepath.Join(tmpdir, "dest") 95 if err := system.MkdirAll(dest, 0700); err != nil { 96 t.Fatal(err) 97 } 98 options := &archive.TarOptions{} 99 // 65534 entries of 64-byte strings ~= 4MB of environment space which should overflow 100 // on most systems when passed via environment or command line arguments 101 excludes := make([]string, 65534) 102 for i := 0; i < 65534; i++ { 103 excludes[i] = strings.Repeat(string(i), 64) 104 } 105 options.ExcludePatterns = excludes 106 if err := Untar(stream, dest, options); err != nil { 107 t.Fatal(err) 108 } 109 } 110 111 func TestChrootUntarEmptyArchive(t *testing.T) { 112 tmpdir, err := ioutil.TempDir("", "docker-TestChrootUntarEmptyArchive") 113 if err != nil { 114 t.Fatal(err) 115 } 116 defer os.RemoveAll(tmpdir) 117 if err := Untar(nil, tmpdir, nil); err == nil { 118 t.Fatal("expected error on empty archive") 119 } 120 } 121 122 func prepareSourceDirectory(numberOfFiles int, targetPath string, makeSymLinks bool) (int, error) { 123 fileData := []byte("fooo") 124 for n := 0; n < numberOfFiles; n++ { 125 fileName := fmt.Sprintf("file-%d", n) 126 if err := ioutil.WriteFile(filepath.Join(targetPath, fileName), fileData, 0700); err != nil { 127 return 0, err 128 } 129 if makeSymLinks { 130 if err := os.Symlink(filepath.Join(targetPath, fileName), filepath.Join(targetPath, fileName+"-link")); err != nil { 131 return 0, err 132 } 133 } 134 } 135 totalSize := numberOfFiles * len(fileData) 136 return totalSize, nil 137 } 138 139 func getHash(filename string) (uint32, error) { 140 stream, err := ioutil.ReadFile(filename) 141 if err != nil { 142 return 0, err 143 } 144 hash := crc32.NewIEEE() 145 hash.Write(stream) 146 return hash.Sum32(), nil 147 } 148 149 func compareDirectories(src string, dest string) error { 150 changes, err := archive.ChangesDirs(dest, src) 151 if err != nil { 152 return err 153 } 154 if len(changes) > 0 { 155 return fmt.Errorf("Unexpected differences after untar: %v", changes) 156 } 157 return nil 158 } 159 160 func compareFiles(src string, dest string) error { 161 srcHash, err := getHash(src) 162 if err != nil { 163 return err 164 } 165 destHash, err := getHash(dest) 166 if err != nil { 167 return err 168 } 169 if srcHash != destHash { 170 return fmt.Errorf("%s is different from %s", src, dest) 171 } 172 return nil 173 } 174 175 func TestChrootTarUntarWithSymlink(t *testing.T) { 176 skip.If(t, runtime.GOOS == "windows", "FIXME: figure out why this is failing") 177 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 178 tmpdir, err := ioutil.TempDir("", "docker-TestChrootTarUntarWithSymlink") 179 if err != nil { 180 t.Fatal(err) 181 } 182 defer os.RemoveAll(tmpdir) 183 src := filepath.Join(tmpdir, "src") 184 if err := system.MkdirAll(src, 0700); err != nil { 185 t.Fatal(err) 186 } 187 if _, err := prepareSourceDirectory(10, src, false); err != nil { 188 t.Fatal(err) 189 } 190 dest := filepath.Join(tmpdir, "dest") 191 if err := TarUntar(src, dest); err != nil { 192 t.Fatal(err) 193 } 194 if err := compareDirectories(src, dest); err != nil { 195 t.Fatal(err) 196 } 197 } 198 199 func TestChrootCopyWithTar(t *testing.T) { 200 skip.If(t, runtime.GOOS == "windows", "FIXME: figure out why this is failing") 201 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 202 tmpdir, err := ioutil.TempDir("", "docker-TestChrootCopyWithTar") 203 if err != nil { 204 t.Fatal(err) 205 } 206 defer os.RemoveAll(tmpdir) 207 src := filepath.Join(tmpdir, "src") 208 if err := system.MkdirAll(src, 0700); err != nil { 209 t.Fatal(err) 210 } 211 if _, err := prepareSourceDirectory(10, src, true); err != nil { 212 t.Fatal(err) 213 } 214 215 // Copy directory 216 dest := filepath.Join(tmpdir, "dest") 217 if err := CopyWithTar(src, dest); err != nil { 218 t.Fatal(err) 219 } 220 if err := compareDirectories(src, dest); err != nil { 221 t.Fatal(err) 222 } 223 224 // Copy file 225 srcfile := filepath.Join(src, "file-1") 226 dest = filepath.Join(tmpdir, "destFile") 227 destfile := filepath.Join(dest, "file-1") 228 if err := CopyWithTar(srcfile, destfile); err != nil { 229 t.Fatal(err) 230 } 231 if err := compareFiles(srcfile, destfile); err != nil { 232 t.Fatal(err) 233 } 234 235 // Copy symbolic link 236 srcLinkfile := filepath.Join(src, "file-1-link") 237 dest = filepath.Join(tmpdir, "destSymlink") 238 destLinkfile := filepath.Join(dest, "file-1-link") 239 if err := CopyWithTar(srcLinkfile, destLinkfile); err != nil { 240 t.Fatal(err) 241 } 242 if err := compareFiles(srcLinkfile, destLinkfile); err != nil { 243 t.Fatal(err) 244 } 245 } 246 247 func TestChrootCopyFileWithTar(t *testing.T) { 248 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 249 tmpdir, err := ioutil.TempDir("", "docker-TestChrootCopyFileWithTar") 250 if err != nil { 251 t.Fatal(err) 252 } 253 defer os.RemoveAll(tmpdir) 254 src := filepath.Join(tmpdir, "src") 255 if err := system.MkdirAll(src, 0700); err != nil { 256 t.Fatal(err) 257 } 258 if _, err := prepareSourceDirectory(10, src, true); err != nil { 259 t.Fatal(err) 260 } 261 262 // Copy directory 263 dest := filepath.Join(tmpdir, "dest") 264 if err := CopyFileWithTar(src, dest); err == nil { 265 t.Fatal("Expected error on copying directory") 266 } 267 268 // Copy file 269 srcfile := filepath.Join(src, "file-1") 270 dest = filepath.Join(tmpdir, "destFile") 271 destfile := filepath.Join(dest, "file-1") 272 if err := CopyFileWithTar(srcfile, destfile); err != nil { 273 t.Fatal(err) 274 } 275 if err := compareFiles(srcfile, destfile); err != nil { 276 t.Fatal(err) 277 } 278 279 // Copy symbolic link 280 srcLinkfile := filepath.Join(src, "file-1-link") 281 dest = filepath.Join(tmpdir, "destSymlink") 282 destLinkfile := filepath.Join(dest, "file-1-link") 283 if err := CopyFileWithTar(srcLinkfile, destLinkfile); err != nil { 284 t.Fatal(err) 285 } 286 if err := compareFiles(srcLinkfile, destLinkfile); err != nil { 287 t.Fatal(err) 288 } 289 } 290 291 func TestChrootUntarPath(t *testing.T) { 292 skip.If(t, runtime.GOOS == "windows", "FIXME: figure out why this is failing") 293 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 294 tmpdir, err := ioutil.TempDir("", "docker-TestChrootUntarPath") 295 if err != nil { 296 t.Fatal(err) 297 } 298 defer os.RemoveAll(tmpdir) 299 src := filepath.Join(tmpdir, "src") 300 if err := system.MkdirAll(src, 0700); err != nil { 301 t.Fatal(err) 302 } 303 if _, err := prepareSourceDirectory(10, src, false); err != nil { 304 t.Fatal(err) 305 } 306 dest := filepath.Join(tmpdir, "dest") 307 // Untar a directory 308 if err := UntarPath(src, dest); err == nil { 309 t.Fatal("Expected error on untaring a directory") 310 } 311 312 // Untar a tar file 313 stream, err := archive.Tar(src, archive.Uncompressed) 314 if err != nil { 315 t.Fatal(err) 316 } 317 buf := new(bytes.Buffer) 318 buf.ReadFrom(stream) 319 tarfile := filepath.Join(tmpdir, "src.tar") 320 if err := ioutil.WriteFile(tarfile, buf.Bytes(), 0644); err != nil { 321 t.Fatal(err) 322 } 323 if err := UntarPath(tarfile, dest); err != nil { 324 t.Fatal(err) 325 } 326 if err := compareDirectories(src, dest); err != nil { 327 t.Fatal(err) 328 } 329 } 330 331 type slowEmptyTarReader struct { 332 size int 333 offset int 334 chunkSize int 335 } 336 337 // Read is a slow reader of an empty tar (like the output of "tar c --files-from /dev/null") 338 func (s *slowEmptyTarReader) Read(p []byte) (int, error) { 339 time.Sleep(100 * time.Millisecond) 340 count := s.chunkSize 341 if len(p) < s.chunkSize { 342 count = len(p) 343 } 344 for i := 0; i < count; i++ { 345 p[i] = 0 346 } 347 s.offset += count 348 if s.offset > s.size { 349 return count, io.EOF 350 } 351 return count, nil 352 } 353 354 func TestChrootUntarEmptyArchiveFromSlowReader(t *testing.T) { 355 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 356 tmpdir, err := ioutil.TempDir("", "docker-TestChrootUntarEmptyArchiveFromSlowReader") 357 if err != nil { 358 t.Fatal(err) 359 } 360 defer os.RemoveAll(tmpdir) 361 dest := filepath.Join(tmpdir, "dest") 362 if err := system.MkdirAll(dest, 0700); err != nil { 363 t.Fatal(err) 364 } 365 stream := &slowEmptyTarReader{size: 10240, chunkSize: 1024} 366 if err := Untar(stream, dest, nil); err != nil { 367 t.Fatal(err) 368 } 369 } 370 371 func TestChrootApplyEmptyArchiveFromSlowReader(t *testing.T) { 372 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 373 tmpdir, err := ioutil.TempDir("", "docker-TestChrootApplyEmptyArchiveFromSlowReader") 374 if err != nil { 375 t.Fatal(err) 376 } 377 defer os.RemoveAll(tmpdir) 378 dest := filepath.Join(tmpdir, "dest") 379 if err := system.MkdirAll(dest, 0700); err != nil { 380 t.Fatal(err) 381 } 382 stream := &slowEmptyTarReader{size: 10240, chunkSize: 1024} 383 if _, err := ApplyLayer(dest, stream); err != nil { 384 t.Fatal(err) 385 } 386 } 387 388 func TestChrootApplyDotDotFile(t *testing.T) { 389 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 390 tmpdir, err := ioutil.TempDir("", "docker-TestChrootApplyDotDotFile") 391 if err != nil { 392 t.Fatal(err) 393 } 394 defer os.RemoveAll(tmpdir) 395 src := filepath.Join(tmpdir, "src") 396 if err := system.MkdirAll(src, 0700); err != nil { 397 t.Fatal(err) 398 } 399 if err := ioutil.WriteFile(filepath.Join(src, "..gitme"), []byte(""), 0644); err != nil { 400 t.Fatal(err) 401 } 402 stream, err := archive.Tar(src, archive.Uncompressed) 403 if err != nil { 404 t.Fatal(err) 405 } 406 dest := filepath.Join(tmpdir, "dest") 407 if err := system.MkdirAll(dest, 0700); err != nil { 408 t.Fatal(err) 409 } 410 if _, err := ApplyLayer(dest, stream); err != nil { 411 t.Fatal(err) 412 } 413 }