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