Dan Willemsen | 25a4e07 | 2016-08-05 16:34:03 -0700 | [diff] [blame] | 1 | // Copyright 2010 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package zip |
| 6 | |
| 7 | import ( |
| 8 | "bytes" |
| 9 | "encoding/binary" |
| 10 | "encoding/hex" |
| 11 | "io" |
| 12 | "io/ioutil" |
| 13 | "os" |
| 14 | "path/filepath" |
| 15 | "regexp" |
| 16 | "strings" |
| 17 | "testing" |
| 18 | "time" |
| 19 | ) |
| 20 | |
| 21 | type ZipTest struct { |
| 22 | Name string |
| 23 | Source func() (r io.ReaderAt, size int64) // if non-nil, used instead of testdata/<Name> file |
| 24 | Comment string |
| 25 | File []ZipTestFile |
| 26 | Error error // the error that Opening this file should return |
| 27 | } |
| 28 | |
| 29 | type ZipTestFile struct { |
| 30 | Name string |
| 31 | Mode os.FileMode |
| 32 | Mtime string // optional, modified time in format "mm-dd-yy hh:mm:ss" |
| 33 | |
| 34 | // Information describing expected zip file content. |
| 35 | // First, reading the entire content should produce the error ContentErr. |
| 36 | // Second, if ContentErr==nil, the content should match Content. |
| 37 | // If content is large, an alternative to setting Content is to set File, |
| 38 | // which names a file in the testdata/ directory containing the |
| 39 | // uncompressed expected content. |
| 40 | // If content is very large, an alternative to setting Content or File |
| 41 | // is to set Size, which will then be checked against the header-reported size |
| 42 | // but will bypass the decompressing of the actual data. |
| 43 | // This last option is used for testing very large (multi-GB) compressed files. |
| 44 | ContentErr error |
| 45 | Content []byte |
| 46 | File string |
| 47 | Size uint64 |
| 48 | } |
| 49 | |
| 50 | // Caution: The Mtime values found for the test files should correspond to |
| 51 | // the values listed with unzip -l <zipfile>. However, the values |
| 52 | // listed by unzip appear to be off by some hours. When creating |
| 53 | // fresh test files and testing them, this issue is not present. |
| 54 | // The test files were created in Sydney, so there might be a time |
| 55 | // zone issue. The time zone information does have to be encoded |
| 56 | // somewhere, because otherwise unzip -l could not provide a different |
| 57 | // time from what the archive/zip package provides, but there appears |
| 58 | // to be no documentation about this. |
| 59 | |
| 60 | var tests = []ZipTest{ |
| 61 | { |
| 62 | Name: "test.zip", |
| 63 | Comment: "This is a zipfile comment.", |
| 64 | File: []ZipTestFile{ |
| 65 | { |
| 66 | Name: "test.txt", |
| 67 | Content: []byte("This is a test text file.\n"), |
| 68 | Mtime: "09-05-10 12:12:02", |
| 69 | Mode: 0644, |
| 70 | }, |
| 71 | { |
| 72 | Name: "gophercolor16x16.png", |
| 73 | File: "gophercolor16x16.png", |
| 74 | Mtime: "09-05-10 15:52:58", |
| 75 | Mode: 0644, |
| 76 | }, |
| 77 | }, |
| 78 | }, |
| 79 | { |
| 80 | Name: "test-trailing-junk.zip", |
| 81 | Comment: "This is a zipfile comment.", |
| 82 | File: []ZipTestFile{ |
| 83 | { |
| 84 | Name: "test.txt", |
| 85 | Content: []byte("This is a test text file.\n"), |
| 86 | Mtime: "09-05-10 12:12:02", |
| 87 | Mode: 0644, |
| 88 | }, |
| 89 | { |
| 90 | Name: "gophercolor16x16.png", |
| 91 | File: "gophercolor16x16.png", |
| 92 | Mtime: "09-05-10 15:52:58", |
| 93 | Mode: 0644, |
| 94 | }, |
| 95 | }, |
| 96 | }, |
| 97 | { |
| 98 | Name: "r.zip", |
| 99 | Source: returnRecursiveZip, |
| 100 | File: []ZipTestFile{ |
| 101 | { |
| 102 | Name: "r/r.zip", |
| 103 | Content: rZipBytes(), |
| 104 | Mtime: "03-04-10 00:24:16", |
| 105 | Mode: 0666, |
| 106 | }, |
| 107 | }, |
| 108 | }, |
| 109 | { |
| 110 | Name: "symlink.zip", |
| 111 | File: []ZipTestFile{ |
| 112 | { |
| 113 | Name: "symlink", |
| 114 | Content: []byte("../target"), |
| 115 | Mode: 0777 | os.ModeSymlink, |
| 116 | }, |
| 117 | }, |
| 118 | }, |
| 119 | { |
| 120 | Name: "readme.zip", |
| 121 | }, |
| 122 | { |
| 123 | Name: "readme.notzip", |
| 124 | Error: ErrFormat, |
| 125 | }, |
| 126 | { |
| 127 | Name: "dd.zip", |
| 128 | File: []ZipTestFile{ |
| 129 | { |
| 130 | Name: "filename", |
| 131 | Content: []byte("This is a test textfile.\n"), |
| 132 | Mtime: "02-02-11 13:06:20", |
| 133 | Mode: 0666, |
| 134 | }, |
| 135 | }, |
| 136 | }, |
| 137 | { |
| 138 | // created in windows XP file manager. |
| 139 | Name: "winxp.zip", |
| 140 | File: crossPlatform, |
| 141 | }, |
| 142 | { |
| 143 | // created by Zip 3.0 under Linux |
| 144 | Name: "unix.zip", |
| 145 | File: crossPlatform, |
| 146 | }, |
| 147 | { |
| 148 | // created by Go, before we wrote the "optional" data |
| 149 | // descriptor signatures (which are required by OS X) |
| 150 | Name: "go-no-datadesc-sig.zip", |
| 151 | File: []ZipTestFile{ |
| 152 | { |
| 153 | Name: "foo.txt", |
| 154 | Content: []byte("foo\n"), |
| 155 | Mtime: "03-08-12 16:59:10", |
| 156 | Mode: 0644, |
| 157 | }, |
| 158 | { |
| 159 | Name: "bar.txt", |
| 160 | Content: []byte("bar\n"), |
| 161 | Mtime: "03-08-12 16:59:12", |
| 162 | Mode: 0644, |
| 163 | }, |
| 164 | }, |
| 165 | }, |
| 166 | { |
| 167 | // created by Go, after we wrote the "optional" data |
| 168 | // descriptor signatures (which are required by OS X) |
| 169 | Name: "go-with-datadesc-sig.zip", |
| 170 | File: []ZipTestFile{ |
| 171 | { |
| 172 | Name: "foo.txt", |
| 173 | Content: []byte("foo\n"), |
| 174 | Mode: 0666, |
| 175 | }, |
| 176 | { |
| 177 | Name: "bar.txt", |
| 178 | Content: []byte("bar\n"), |
| 179 | Mode: 0666, |
| 180 | }, |
| 181 | }, |
| 182 | }, |
| 183 | { |
| 184 | Name: "Bad-CRC32-in-data-descriptor", |
| 185 | Source: returnCorruptCRC32Zip, |
| 186 | File: []ZipTestFile{ |
| 187 | { |
| 188 | Name: "foo.txt", |
| 189 | Content: []byte("foo\n"), |
| 190 | Mode: 0666, |
| 191 | ContentErr: ErrChecksum, |
| 192 | }, |
| 193 | { |
| 194 | Name: "bar.txt", |
| 195 | Content: []byte("bar\n"), |
| 196 | Mode: 0666, |
| 197 | }, |
| 198 | }, |
| 199 | }, |
| 200 | // Tests that we verify (and accept valid) crc32s on files |
| 201 | // with crc32s in their file header (not in data descriptors) |
| 202 | { |
| 203 | Name: "crc32-not-streamed.zip", |
| 204 | File: []ZipTestFile{ |
| 205 | { |
| 206 | Name: "foo.txt", |
| 207 | Content: []byte("foo\n"), |
| 208 | Mtime: "03-08-12 16:59:10", |
| 209 | Mode: 0644, |
| 210 | }, |
| 211 | { |
| 212 | Name: "bar.txt", |
| 213 | Content: []byte("bar\n"), |
| 214 | Mtime: "03-08-12 16:59:12", |
| 215 | Mode: 0644, |
| 216 | }, |
| 217 | }, |
| 218 | }, |
| 219 | // Tests that we verify (and reject invalid) crc32s on files |
| 220 | // with crc32s in their file header (not in data descriptors) |
| 221 | { |
| 222 | Name: "crc32-not-streamed.zip", |
| 223 | Source: returnCorruptNotStreamedZip, |
| 224 | File: []ZipTestFile{ |
| 225 | { |
| 226 | Name: "foo.txt", |
| 227 | Content: []byte("foo\n"), |
| 228 | Mtime: "03-08-12 16:59:10", |
| 229 | Mode: 0644, |
| 230 | ContentErr: ErrChecksum, |
| 231 | }, |
| 232 | { |
| 233 | Name: "bar.txt", |
| 234 | Content: []byte("bar\n"), |
| 235 | Mtime: "03-08-12 16:59:12", |
| 236 | Mode: 0644, |
| 237 | }, |
| 238 | }, |
| 239 | }, |
| 240 | { |
| 241 | Name: "zip64.zip", |
| 242 | File: []ZipTestFile{ |
| 243 | { |
| 244 | Name: "README", |
| 245 | Content: []byte("This small file is in ZIP64 format.\n"), |
| 246 | Mtime: "08-10-12 14:33:32", |
| 247 | Mode: 0644, |
| 248 | }, |
| 249 | }, |
| 250 | }, |
| 251 | // Another zip64 file with different Extras fields. (golang.org/issue/7069) |
| 252 | { |
| 253 | Name: "zip64-2.zip", |
| 254 | File: []ZipTestFile{ |
| 255 | { |
| 256 | Name: "README", |
| 257 | Content: []byte("This small file is in ZIP64 format.\n"), |
| 258 | Mtime: "08-10-12 14:33:32", |
| 259 | Mode: 0644, |
| 260 | }, |
| 261 | }, |
| 262 | }, |
| 263 | // Largest possible non-zip64 file, with no zip64 header. |
| 264 | { |
| 265 | Name: "big.zip", |
| 266 | Source: returnBigZipBytes, |
| 267 | File: []ZipTestFile{ |
| 268 | { |
| 269 | Name: "big.file", |
| 270 | Content: nil, |
| 271 | Size: 1<<32 - 1, |
| 272 | Mode: 0666, |
| 273 | }, |
| 274 | }, |
| 275 | }, |
| 276 | } |
| 277 | |
| 278 | var crossPlatform = []ZipTestFile{ |
| 279 | { |
| 280 | Name: "hello", |
| 281 | Content: []byte("world \r\n"), |
| 282 | Mode: 0666, |
| 283 | }, |
| 284 | { |
| 285 | Name: "dir/bar", |
| 286 | Content: []byte("foo \r\n"), |
| 287 | Mode: 0666, |
| 288 | }, |
| 289 | { |
| 290 | Name: "dir/empty/", |
| 291 | Content: []byte{}, |
| 292 | Mode: os.ModeDir | 0777, |
| 293 | }, |
| 294 | { |
| 295 | Name: "readonly", |
| 296 | Content: []byte("important \r\n"), |
| 297 | Mode: 0444, |
| 298 | }, |
| 299 | } |
| 300 | |
| 301 | func TestReader(t *testing.T) { |
| 302 | for _, zt := range tests { |
| 303 | readTestZip(t, zt) |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | func readTestZip(t *testing.T, zt ZipTest) { |
| 308 | var z *Reader |
| 309 | var err error |
| 310 | if zt.Source != nil { |
| 311 | rat, size := zt.Source() |
| 312 | z, err = NewReader(rat, size) |
| 313 | } else { |
| 314 | var rc *ReadCloser |
| 315 | rc, err = OpenReader(filepath.Join("testdata", zt.Name)) |
| 316 | if err == nil { |
| 317 | defer rc.Close() |
| 318 | z = &rc.Reader |
| 319 | } |
| 320 | } |
| 321 | if err != zt.Error { |
| 322 | t.Errorf("%s: error=%v, want %v", zt.Name, err, zt.Error) |
| 323 | return |
| 324 | } |
| 325 | |
| 326 | // bail if file is not zip |
| 327 | if err == ErrFormat { |
| 328 | return |
| 329 | } |
| 330 | |
| 331 | // bail here if no Files expected to be tested |
| 332 | // (there may actually be files in the zip, but we don't care) |
| 333 | if zt.File == nil { |
| 334 | return |
| 335 | } |
| 336 | |
| 337 | if z.Comment != zt.Comment { |
| 338 | t.Errorf("%s: comment=%q, want %q", zt.Name, z.Comment, zt.Comment) |
| 339 | } |
| 340 | if len(z.File) != len(zt.File) { |
| 341 | t.Fatalf("%s: file count=%d, want %d", zt.Name, len(z.File), len(zt.File)) |
| 342 | } |
| 343 | |
| 344 | // test read of each file |
| 345 | for i, ft := range zt.File { |
| 346 | readTestFile(t, zt, ft, z.File[i]) |
| 347 | } |
| 348 | |
| 349 | // test simultaneous reads |
| 350 | n := 0 |
| 351 | done := make(chan bool) |
| 352 | for i := 0; i < 5; i++ { |
| 353 | for j, ft := range zt.File { |
| 354 | go func(j int, ft ZipTestFile) { |
| 355 | readTestFile(t, zt, ft, z.File[j]) |
| 356 | done <- true |
| 357 | }(j, ft) |
| 358 | n++ |
| 359 | } |
| 360 | } |
| 361 | for ; n > 0; n-- { |
| 362 | <-done |
| 363 | } |
| 364 | } |
| 365 | |
| 366 | func readTestFile(t *testing.T, zt ZipTest, ft ZipTestFile, f *File) { |
| 367 | if f.Name != ft.Name { |
| 368 | t.Errorf("%s: name=%q, want %q", zt.Name, f.Name, ft.Name) |
| 369 | } |
| 370 | |
| 371 | if ft.Mtime != "" { |
| 372 | mtime, err := time.Parse("01-02-06 15:04:05", ft.Mtime) |
| 373 | if err != nil { |
| 374 | t.Error(err) |
| 375 | return |
| 376 | } |
| 377 | if ft := f.ModTime(); !ft.Equal(mtime) { |
| 378 | t.Errorf("%s: %s: mtime=%s, want %s", zt.Name, f.Name, ft, mtime) |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | testFileMode(t, zt.Name, f, ft.Mode) |
| 383 | |
| 384 | size := uint64(f.UncompressedSize) |
| 385 | if size == uint32max { |
| 386 | size = f.UncompressedSize64 |
| 387 | } else if size != f.UncompressedSize64 { |
| 388 | t.Errorf("%v: UncompressedSize=%#x does not match UncompressedSize64=%#x", f.Name, size, f.UncompressedSize64) |
| 389 | } |
| 390 | |
| 391 | r, err := f.Open() |
| 392 | if err != nil { |
| 393 | t.Errorf("%s: %v", zt.Name, err) |
| 394 | return |
| 395 | } |
| 396 | |
| 397 | // For very large files, just check that the size is correct. |
| 398 | // The content is expected to be all zeros. |
| 399 | // Don't bother uncompressing: too big. |
| 400 | if ft.Content == nil && ft.File == "" && ft.Size > 0 { |
| 401 | if size != ft.Size { |
| 402 | t.Errorf("%v: uncompressed size %#x, want %#x", ft.Name, size, ft.Size) |
| 403 | } |
| 404 | r.Close() |
| 405 | return |
| 406 | } |
| 407 | |
| 408 | var b bytes.Buffer |
| 409 | _, err = io.Copy(&b, r) |
| 410 | if err != ft.ContentErr { |
| 411 | t.Errorf("%s: copying contents: %v (want %v)", zt.Name, err, ft.ContentErr) |
| 412 | } |
| 413 | if err != nil { |
| 414 | return |
| 415 | } |
| 416 | r.Close() |
| 417 | |
| 418 | if g := uint64(b.Len()); g != size { |
| 419 | t.Errorf("%v: read %v bytes but f.UncompressedSize == %v", f.Name, g, size) |
| 420 | } |
| 421 | |
| 422 | var c []byte |
| 423 | if ft.Content != nil { |
| 424 | c = ft.Content |
| 425 | } else if c, err = ioutil.ReadFile("testdata/" + ft.File); err != nil { |
| 426 | t.Error(err) |
| 427 | return |
| 428 | } |
| 429 | |
| 430 | if b.Len() != len(c) { |
| 431 | t.Errorf("%s: len=%d, want %d", f.Name, b.Len(), len(c)) |
| 432 | return |
| 433 | } |
| 434 | |
| 435 | for i, b := range b.Bytes() { |
| 436 | if b != c[i] { |
| 437 | t.Errorf("%s: content[%d]=%q want %q", f.Name, i, b, c[i]) |
| 438 | return |
| 439 | } |
| 440 | } |
| 441 | } |
| 442 | |
| 443 | func testFileMode(t *testing.T, zipName string, f *File, want os.FileMode) { |
| 444 | mode := f.Mode() |
| 445 | if want == 0 { |
| 446 | t.Errorf("%s: %s mode: got %v, want none", zipName, f.Name, mode) |
| 447 | } else if mode != want { |
| 448 | t.Errorf("%s: %s mode: want %v, got %v", zipName, f.Name, want, mode) |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | func TestInvalidFiles(t *testing.T) { |
| 453 | const size = 1024 * 70 // 70kb |
| 454 | b := make([]byte, size) |
| 455 | |
| 456 | // zeroes |
| 457 | _, err := NewReader(bytes.NewReader(b), size) |
| 458 | if err != ErrFormat { |
| 459 | t.Errorf("zeroes: error=%v, want %v", err, ErrFormat) |
| 460 | } |
| 461 | |
| 462 | // repeated directoryEndSignatures |
| 463 | sig := make([]byte, 4) |
| 464 | binary.LittleEndian.PutUint32(sig, directoryEndSignature) |
| 465 | for i := 0; i < size-4; i += 4 { |
| 466 | copy(b[i:i+4], sig) |
| 467 | } |
| 468 | _, err = NewReader(bytes.NewReader(b), size) |
| 469 | if err != ErrFormat { |
| 470 | t.Errorf("sigs: error=%v, want %v", err, ErrFormat) |
| 471 | } |
| 472 | } |
| 473 | |
| 474 | func messWith(fileName string, corrupter func(b []byte)) (r io.ReaderAt, size int64) { |
| 475 | data, err := ioutil.ReadFile(filepath.Join("testdata", fileName)) |
| 476 | if err != nil { |
| 477 | panic("Error reading " + fileName + ": " + err.Error()) |
| 478 | } |
| 479 | corrupter(data) |
| 480 | return bytes.NewReader(data), int64(len(data)) |
| 481 | } |
| 482 | |
| 483 | func returnCorruptCRC32Zip() (r io.ReaderAt, size int64) { |
| 484 | return messWith("go-with-datadesc-sig.zip", func(b []byte) { |
| 485 | // Corrupt one of the CRC32s in the data descriptor: |
| 486 | b[0x2d]++ |
| 487 | }) |
| 488 | } |
| 489 | |
| 490 | func returnCorruptNotStreamedZip() (r io.ReaderAt, size int64) { |
| 491 | return messWith("crc32-not-streamed.zip", func(b []byte) { |
| 492 | // Corrupt foo.txt's final crc32 byte, in both |
| 493 | // the file header and TOC. (0x7e -> 0x7f) |
| 494 | b[0x11]++ |
| 495 | b[0x9d]++ |
| 496 | |
| 497 | // TODO(bradfitz): add a new test that only corrupts |
| 498 | // one of these values, and verify that that's also an |
| 499 | // error. Currently, the reader code doesn't verify the |
| 500 | // fileheader and TOC's crc32 match if they're both |
| 501 | // non-zero and only the second line above, the TOC, |
| 502 | // is what matters. |
| 503 | }) |
| 504 | } |
| 505 | |
| 506 | // rZipBytes returns the bytes of a recursive zip file, without |
| 507 | // putting it on disk and triggering certain virus scanners. |
| 508 | func rZipBytes() []byte { |
| 509 | s := ` |
| 510 | 0000000 50 4b 03 04 14 00 00 00 08 00 08 03 64 3c f9 f4 |
| 511 | 0000010 89 64 48 01 00 00 b8 01 00 00 07 00 00 00 72 2f |
| 512 | 0000020 72 2e 7a 69 70 00 25 00 da ff 50 4b 03 04 14 00 |
| 513 | 0000030 00 00 08 00 08 03 64 3c f9 f4 89 64 48 01 00 00 |
| 514 | 0000040 b8 01 00 00 07 00 00 00 72 2f 72 2e 7a 69 70 00 |
| 515 | 0000050 2f 00 d0 ff 00 25 00 da ff 50 4b 03 04 14 00 00 |
| 516 | 0000060 00 08 00 08 03 64 3c f9 f4 89 64 48 01 00 00 b8 |
| 517 | 0000070 01 00 00 07 00 00 00 72 2f 72 2e 7a 69 70 00 2f |
| 518 | 0000080 00 d0 ff c2 54 8e 57 39 00 05 00 fa ff c2 54 8e |
| 519 | 0000090 57 39 00 05 00 fa ff 00 05 00 fa ff 00 14 00 eb |
| 520 | 00000a0 ff c2 54 8e 57 39 00 05 00 fa ff 00 05 00 fa ff |
| 521 | 00000b0 00 14 00 eb ff 42 88 21 c4 00 00 14 00 eb ff 42 |
| 522 | 00000c0 88 21 c4 00 00 14 00 eb ff 42 88 21 c4 00 00 14 |
| 523 | 00000d0 00 eb ff 42 88 21 c4 00 00 14 00 eb ff 42 88 21 |
| 524 | 00000e0 c4 00 00 00 00 ff ff 00 00 00 ff ff 00 34 00 cb |
| 525 | 00000f0 ff 42 88 21 c4 00 00 00 00 ff ff 00 00 00 ff ff |
| 526 | 0000100 00 34 00 cb ff 42 e8 21 5e 0f 00 00 00 ff ff 0a |
| 527 | 0000110 f0 66 64 12 61 c0 15 dc e8 a0 48 bf 48 af 2a b3 |
| 528 | 0000120 20 c0 9b 95 0d c4 67 04 42 53 06 06 06 40 00 06 |
| 529 | 0000130 00 f9 ff 6d 01 00 00 00 00 42 e8 21 5e 0f 00 00 |
| 530 | 0000140 00 ff ff 0a f0 66 64 12 61 c0 15 dc e8 a0 48 bf |
| 531 | 0000150 48 af 2a b3 20 c0 9b 95 0d c4 67 04 42 53 06 06 |
| 532 | 0000160 06 40 00 06 00 f9 ff 6d 01 00 00 00 00 50 4b 01 |
| 533 | 0000170 02 14 00 14 00 00 00 08 00 08 03 64 3c f9 f4 89 |
| 534 | 0000180 64 48 01 00 00 b8 01 00 00 07 00 00 00 00 00 00 |
| 535 | 0000190 00 00 00 00 00 00 00 00 00 00 00 72 2f 72 2e 7a |
| 536 | 00001a0 69 70 50 4b 05 06 00 00 00 00 01 00 01 00 35 00 |
| 537 | 00001b0 00 00 6d 01 00 00 00 00` |
| 538 | s = regexp.MustCompile(`[0-9a-f]{7}`).ReplaceAllString(s, "") |
| 539 | s = regexp.MustCompile(`\s+`).ReplaceAllString(s, "") |
| 540 | b, err := hex.DecodeString(s) |
| 541 | if err != nil { |
| 542 | panic(err) |
| 543 | } |
| 544 | return b |
| 545 | } |
| 546 | |
| 547 | func returnRecursiveZip() (r io.ReaderAt, size int64) { |
| 548 | b := rZipBytes() |
| 549 | return bytes.NewReader(b), int64(len(b)) |
| 550 | } |
| 551 | |
| 552 | // biggestZipBytes returns the bytes of a zip file biggest.zip |
| 553 | // that contains a zip file bigger.zip that contains a zip file |
| 554 | // big.zip that contains big.file, which contains 2³²-1 zeros. |
| 555 | // The big.zip file is interesting because it has no zip64 header, |
| 556 | // much like the innermost zip files in the well-known 42.zip. |
| 557 | // |
| 558 | // biggest.zip was generated by changing isZip64 to use > uint32max |
| 559 | // instead of >= uint32max and then running this program: |
| 560 | // |
| 561 | // package main |
| 562 | // |
| 563 | // import ( |
| 564 | // "archive/zip" |
| 565 | // "bytes" |
| 566 | // "io" |
| 567 | // "io/ioutil" |
| 568 | // "log" |
| 569 | // ) |
| 570 | // |
| 571 | // type zeros struct{} |
| 572 | // |
| 573 | // func (zeros) Read(b []byte) (int, error) { |
| 574 | // for i := range b { |
| 575 | // b[i] = 0 |
| 576 | // } |
| 577 | // return len(b), nil |
| 578 | // } |
| 579 | // |
| 580 | // func main() { |
| 581 | // bigZip := makeZip("big.file", io.LimitReader(zeros{}, 1<<32-1)) |
| 582 | // if err := ioutil.WriteFile("/tmp/big.zip", bigZip, 0666); err != nil { |
| 583 | // log.Fatal(err) |
| 584 | // } |
| 585 | // |
| 586 | // biggerZip := makeZip("big.zip", bytes.NewReader(bigZip)) |
| 587 | // if err := ioutil.WriteFile("/tmp/bigger.zip", biggerZip, 0666); err != nil { |
| 588 | // log.Fatal(err) |
| 589 | // } |
| 590 | // |
| 591 | // biggestZip := makeZip("bigger.zip", bytes.NewReader(biggerZip)) |
| 592 | // if err := ioutil.WriteFile("/tmp/biggest.zip", biggestZip, 0666); err != nil { |
| 593 | // log.Fatal(err) |
| 594 | // } |
| 595 | // } |
| 596 | // |
| 597 | // func makeZip(name string, r io.Reader) []byte { |
| 598 | // var buf bytes.Buffer |
| 599 | // w := zip.NewWriter(&buf) |
| 600 | // wf, err := w.Create(name) |
| 601 | // if err != nil { |
| 602 | // log.Fatal(err) |
| 603 | // } |
| 604 | // if _, err = io.Copy(wf, r); err != nil { |
| 605 | // log.Fatal(err) |
| 606 | // } |
| 607 | // if err := w.Close(); err != nil { |
| 608 | // log.Fatal(err) |
| 609 | // } |
| 610 | // return buf.Bytes() |
| 611 | // } |
| 612 | // |
| 613 | // The 4 GB of zeros compresses to 4 MB, which compresses to 20 kB, |
| 614 | // which compresses to 1252 bytes (in the hex dump below). |
| 615 | // |
| 616 | // It's here in hex for the same reason as rZipBytes above: to avoid |
| 617 | // problems with on-disk virus scanners or other zip processors. |
| 618 | // |
| 619 | func biggestZipBytes() []byte { |
| 620 | s := ` |
| 621 | 0000000 50 4b 03 04 14 00 08 00 08 00 00 00 00 00 00 00 |
| 622 | 0000010 00 00 00 00 00 00 00 00 00 00 0a 00 00 00 62 69 |
| 623 | 0000020 67 67 65 72 2e 7a 69 70 ec dc 6b 4c 53 67 18 07 |
| 624 | 0000030 f0 16 c5 ca 65 2e cb b8 94 20 61 1f 44 33 c7 cd |
| 625 | 0000040 c0 86 4a b5 c0 62 8a 61 05 c6 cd 91 b2 54 8c 1b |
| 626 | 0000050 63 8b 03 9c 1b 95 52 5a e3 a0 19 6c b2 05 59 44 |
| 627 | 0000060 64 9d 73 83 71 11 46 61 14 b9 1d 14 09 4a c3 60 |
| 628 | 0000070 2e 4c 6e a5 60 45 02 62 81 95 b6 94 9e 9e 77 e7 |
| 629 | 0000080 d0 43 b6 f8 71 df 96 3c e7 a4 69 ce bf cf e9 79 |
| 630 | 0000090 ce ef 79 3f bf f1 31 db b6 bb 31 76 92 e7 f3 07 |
| 631 | 00000a0 8b fc 9c ca cc 08 cc cb cc 5e d2 1c 88 d9 7e bb |
| 632 | 00000b0 4f bb 3a 3f 75 f1 5d 7f 8f c2 68 67 77 8f 25 ff |
| 633 | 00000c0 84 e2 93 2d ef a4 95 3d 71 4e 2c b9 b0 87 c3 be |
| 634 | 00000d0 3d f8 a7 60 24 61 c5 ef ae 9e c8 6c 6d 4e 69 c8 |
| 635 | 00000e0 67 65 34 f8 37 76 2d 76 5c 54 f3 95 65 49 c7 0f |
| 636 | 00000f0 18 71 4b 7e 5b 6a d1 79 47 61 41 b0 4e 2a 74 45 |
| 637 | 0000100 43 58 12 b2 5a a5 c6 7d 68 55 88 d4 98 75 18 6d |
| 638 | 0000110 08 d1 1f 8f 5a 9e 96 ee 45 cf a4 84 4e 4b e8 50 |
| 639 | 0000120 a7 13 d9 06 de 52 81 97 36 b2 d7 b8 fc 2b 5f 55 |
| 640 | 0000130 23 1f 32 59 cf 30 27 fb e2 8a b9 de 45 dd 63 9c |
| 641 | 0000140 4b b5 8b 96 4c 7a 62 62 cc a1 a7 cf fa f1 fe dd |
| 642 | 0000150 54 62 11 bf 36 78 b3 c7 b1 b5 f2 61 4d 4e dd 66 |
| 643 | 0000160 32 2e e6 70 34 5f f4 c9 e6 6c 43 6f da 6b c6 c3 |
| 644 | 0000170 09 2c ce 09 57 7f d2 7e b4 23 ba 7c 1b 99 bc 22 |
| 645 | 0000180 3e f1 de 91 2f e3 9c 1b 82 cc c2 84 39 aa e6 de |
| 646 | 0000190 b4 69 fc cc cb 72 a6 61 45 f0 d3 1d 26 19 7c 8d |
| 647 | 00001a0 29 c8 66 02 be 77 6a f9 3d 34 79 17 19 c8 96 24 |
| 648 | 00001b0 a3 ac e4 dd 3b 1a 8e c6 fe 96 38 6b bf 67 5a 23 |
| 649 | 00001c0 f4 16 f4 e6 8a b4 fc c2 cd bf 95 66 1d bb 35 aa |
| 650 | 00001d0 92 7d 66 d8 08 8d a5 1f 54 2a af 09 cf 61 ff d2 |
| 651 | 00001e0 85 9d 8f b6 d7 88 07 4a 86 03 db 64 f3 d9 92 73 |
| 652 | 00001f0 df ec a7 fc 23 4c 8d 83 79 63 2a d9 fd 8d b3 c8 |
| 653 | 0000200 8f 7e d4 19 85 e6 8d 1c 76 f0 8b 58 32 fd 9a d6 |
| 654 | 0000210 85 e2 48 ad c3 d5 60 6f 7e 22 dd ef 09 49 7c 7f |
| 655 | 0000220 3a 45 c3 71 b7 df f3 4c 63 fb b5 d9 31 5f 6e d6 |
| 656 | 0000230 24 1d a4 4a fe 32 a7 5c 16 48 5c 3e 08 6b 8a d3 |
| 657 | 0000240 25 1d a2 12 a5 59 24 ea 20 5f 52 6d ad 94 db 6b |
| 658 | 0000250 94 b9 5d eb 4b a7 5c 44 bb 1e f2 3c 6b cf 52 c9 |
| 659 | 0000260 e9 e5 ba 06 b9 c4 e5 0a d0 00 0d d0 00 0d d0 00 |
| 660 | 0000270 0d d0 00 0d d0 00 0d d0 00 0d d0 00 0d d0 00 0d |
| 661 | 0000280 d0 00 0d d0 00 0d d0 00 0d d0 00 0d d0 00 0d d0 |
| 662 | 0000290 00 0d d0 00 0d d0 00 0d d0 00 0d d0 00 0d d0 00 |
| 663 | 00002a0 0d d0 00 cd ff 9e 46 86 fa a7 7d 3a 43 d7 8e 10 |
| 664 | 00002b0 52 e9 be e6 6e cf eb 9e 85 4d 65 ce cc 30 c1 44 |
| 665 | 00002c0 c0 4e af bc 9c 6c 4b a0 d7 54 ff 1d d5 5c 89 fb |
| 666 | 00002d0 b5 34 7e c4 c2 9e f5 a0 f6 5b 7e 6e ca 73 c7 ef |
| 667 | 00002e0 5d be de f9 e8 81 eb a5 0a a5 63 54 2c d7 1c d1 |
| 668 | 00002f0 89 17 85 f8 16 94 f2 8a b2 a3 f5 b6 6d df 75 cd |
| 669 | 0000300 90 dd 64 bd 5d 55 4e f2 55 19 1b b7 cc ef 1b ea |
| 670 | 0000310 2e 05 9c f4 aa 1e a8 cd a6 82 c7 59 0f 5e 9d e0 |
| 671 | 0000320 bb fc 6c d6 99 23 eb 36 ad c6 c5 e1 d8 e1 e2 3e |
| 672 | 0000330 d9 90 5a f7 91 5d 6f bc 33 6d 98 47 d2 7c 2e 2f |
| 673 | 0000340 99 a4 25 72 85 49 2c be 0b 5b af 8f e5 6e 81 a6 |
| 674 | 0000350 a3 5a 6f 39 53 3a ab 7a 8b 1e 26 f7 46 6c 7d 26 |
| 675 | 0000360 53 b3 22 31 94 d3 83 f2 18 4d f5 92 33 27 53 97 |
| 676 | 0000370 0f d3 e6 55 9c a6 c5 31 87 6f d3 f3 ae 39 6f 56 |
| 677 | 0000380 10 7b ab 7e d0 b4 ca f2 b8 05 be 3f 0e 6e 5a 75 |
| 678 | 0000390 ab 0c f5 37 0e ba 8e 75 71 7a aa ed 7a dd 6a 63 |
| 679 | 00003a0 be 9b a0 97 27 6a 6f e7 d3 8b c4 7c ec d3 91 56 |
| 680 | 00003b0 d9 ac 5e bf 16 42 2f 00 1f 93 a2 23 87 bd e2 59 |
| 681 | 00003c0 a0 de 1a 66 c8 62 eb 55 8f 91 17 b4 61 42 7a 50 |
| 682 | 00003d0 40 03 34 40 03 34 40 03 34 40 03 34 40 03 34 40 |
| 683 | 00003e0 03 34 40 03 34 40 03 34 40 03 34 40 03 34 40 03 |
| 684 | 00003f0 34 40 03 34 40 03 34 ff 85 86 90 8b ea 67 90 0d |
| 685 | 0000400 e1 42 1b d2 61 d6 79 ec fd 3e 44 28 a4 51 6c 5c |
| 686 | 0000410 fc d2 72 ca ba 82 18 46 16 61 cd 93 a9 0f d1 24 |
| 687 | 0000420 17 99 e2 2c 71 16 84 0c c8 7a 13 0f 9a 5e c5 f0 |
| 688 | 0000430 79 64 e2 12 4d c8 82 a1 81 19 2d aa 44 6d 87 54 |
| 689 | 0000440 84 71 c1 f6 d4 ca 25 8c 77 b9 08 c7 c8 5e 10 8a |
| 690 | 0000450 8f 61 ed 8c ba 30 1f 79 9a c7 60 34 2b b9 8c f8 |
| 691 | 0000460 18 a6 83 1b e3 9f ad 79 fe fd 1b 8b f1 fc 41 6f |
| 692 | 0000470 d4 13 1f e3 b8 83 ba 64 92 e7 eb e4 77 05 8f ba |
| 693 | 0000480 fa 3b 00 00 ff ff 50 4b 07 08 a6 18 b1 91 5e 04 |
| 694 | 0000490 00 00 e4 47 00 00 50 4b 01 02 14 00 14 00 08 00 |
| 695 | 00004a0 08 00 00 00 00 00 a6 18 b1 91 5e 04 00 00 e4 47 |
| 696 | 00004b0 00 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 697 | 00004c0 00 00 00 00 62 69 67 67 65 72 2e 7a 69 70 50 4b |
| 698 | 00004d0 05 06 00 00 00 00 01 00 01 00 38 00 00 00 96 04 |
| 699 | 00004e0 00 00 00 00` |
| 700 | s = regexp.MustCompile(`[0-9a-f]{7}`).ReplaceAllString(s, "") |
| 701 | s = regexp.MustCompile(`\s+`).ReplaceAllString(s, "") |
| 702 | b, err := hex.DecodeString(s) |
| 703 | if err != nil { |
| 704 | panic(err) |
| 705 | } |
| 706 | return b |
| 707 | } |
| 708 | |
| 709 | func returnBigZipBytes() (r io.ReaderAt, size int64) { |
| 710 | b := biggestZipBytes() |
| 711 | for i := 0; i < 2; i++ { |
| 712 | r, err := NewReader(bytes.NewReader(b), int64(len(b))) |
| 713 | if err != nil { |
| 714 | panic(err) |
| 715 | } |
| 716 | f, err := r.File[0].Open() |
| 717 | if err != nil { |
| 718 | panic(err) |
| 719 | } |
| 720 | b, err = ioutil.ReadAll(f) |
| 721 | if err != nil { |
| 722 | panic(err) |
| 723 | } |
| 724 | } |
| 725 | return bytes.NewReader(b), int64(len(b)) |
| 726 | } |
| 727 | |
| 728 | func TestIssue8186(t *testing.T) { |
| 729 | // Directory headers & data found in the TOC of a JAR file. |
| 730 | dirEnts := []string{ |
| 731 | "PK\x01\x02\n\x00\n\x00\x00\b\x00\x004\x9d3?\xaa\x1b\x06\xf0\x81\x02\x00\x00\x81\x02\x00\x00-\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00res/drawable-xhdpi-v4/ic_actionbar_accept.png\xfe\xca\x00\x00\x00", |
| 732 | "PK\x01\x02\n\x00\n\x00\x00\b\x00\x004\x9d3?\x90K\x89\xc7t\n\x00\x00t\n\x00\x00\x0e\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd1\x02\x00\x00resources.arsc\x00\x00\x00", |
| 733 | "PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?\xff$\x18\xed3\x03\x00\x00\xb4\b\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00t\r\x00\x00AndroidManifest.xml", |
| 734 | "PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?\x14\xc5K\xab\x192\x02\x00\xc8\xcd\x04\x00\v\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe8\x10\x00\x00classes.dex", |
| 735 | "PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?E\x96\nD\xac\x01\x00\x00P\x03\x00\x00&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:C\x02\x00res/layout/actionbar_set_wallpaper.xml", |
| 736 | "PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?Ļ\x14\xe3\xd8\x01\x00\x00\xd8\x03\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:E\x02\x00res/layout/wallpaper_cropper.xml", |
| 737 | "PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?}\xc1\x15\x9eZ\x01\x00\x00!\x02\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`G\x02\x00META-INF/MANIFEST.MF", |
| 738 | "PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?\xe6\x98Ьo\x01\x00\x00\x84\x02\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfcH\x02\x00META-INF/CERT.SF", |
| 739 | "PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?\xbfP\x96b\x86\x04\x00\x00\xb2\x06\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa9J\x02\x00META-INF/CERT.RSA", |
| 740 | } |
| 741 | for i, s := range dirEnts { |
| 742 | var f File |
| 743 | err := readDirectoryHeader(&f, strings.NewReader(s)) |
| 744 | if err != nil { |
| 745 | t.Errorf("error reading #%d: %v", i, err) |
| 746 | } |
| 747 | } |
| 748 | } |
| 749 | |
| 750 | // Verify we return ErrUnexpectedEOF when length is short. |
| 751 | func TestIssue10957(t *testing.T) { |
| 752 | data := []byte("PK\x03\x040000000PK\x01\x0200000" + |
| 753 | "0000000000000000000\x00" + |
| 754 | "\x00\x00\x00\x00\x00000000000000PK\x01" + |
| 755 | "\x020000000000000000000" + |
| 756 | "00000\v\x00\x00\x00\x00\x00000000000" + |
| 757 | "00000000000000PK\x01\x0200" + |
| 758 | "00000000000000000000" + |
| 759 | "00\v\x00\x00\x00\x00\x00000000000000" + |
| 760 | "00000000000PK\x01\x020000<" + |
| 761 | "0\x00\x0000000000000000\v\x00\v" + |
| 762 | "\x00\x00\x00\x00\x0000000000\x00\x00\x00\x00000" + |
| 763 | "00000000PK\x01\x0200000000" + |
| 764 | "0000000000000000\v\x00\x00\x00" + |
| 765 | "\x00\x0000PK\x05\x06000000\x05\x000000" + |
| 766 | "\v\x00\x00\x00\x00\x00") |
| 767 | z, err := NewReader(bytes.NewReader(data), int64(len(data))) |
| 768 | if err != nil { |
| 769 | t.Fatal(err) |
| 770 | } |
| 771 | for i, f := range z.File { |
| 772 | r, err := f.Open() |
| 773 | if err != nil { |
| 774 | continue |
| 775 | } |
| 776 | if f.UncompressedSize64 < 1e6 { |
| 777 | n, err := io.Copy(ioutil.Discard, r) |
| 778 | if i == 3 && err != io.ErrUnexpectedEOF { |
| 779 | t.Errorf("File[3] error = %v; want io.ErrUnexpectedEOF", err) |
| 780 | } |
| 781 | if err == nil && uint64(n) != f.UncompressedSize64 { |
| 782 | t.Errorf("file %d: bad size: copied=%d; want=%d", i, n, f.UncompressedSize64) |
| 783 | } |
| 784 | } |
| 785 | r.Close() |
| 786 | } |
| 787 | } |
| 788 | |
| 789 | // Verify the number of files is sane. |
| 790 | func TestIssue10956(t *testing.T) { |
| 791 | data := []byte("PK\x06\x06PK\x06\a0000\x00\x00\x00\x00\x00\x00\x00\x00" + |
| 792 | "0000PK\x05\x06000000000000" + |
| 793 | "0000\v\x00000\x00\x00\x00\x00\x00\x00\x000") |
| 794 | _, err := NewReader(bytes.NewReader(data), int64(len(data))) |
| 795 | const want = "TOC declares impossible 3472328296227680304 files in 57 byte" |
| 796 | if err == nil && !strings.Contains(err.Error(), want) { |
| 797 | t.Errorf("error = %v; want %q", err, want) |
| 798 | } |
| 799 | } |
| 800 | |
| 801 | // Verify we return ErrUnexpectedEOF when reading truncated data descriptor. |
| 802 | func TestIssue11146(t *testing.T) { |
| 803 | data := []byte("PK\x03\x040000000000000000" + |
| 804 | "000000\x01\x00\x00\x000\x01\x00\x00\xff\xff0000" + |
| 805 | "0000000000000000PK\x01\x02" + |
| 806 | "0000\b0\b\x00000000000000" + |
| 807 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000PK\x05\x06\x00\x00" + |
| 808 | "\x00\x0000\x01\x0000008\x00\x00\x00\x00\x00") |
| 809 | z, err := NewReader(bytes.NewReader(data), int64(len(data))) |
| 810 | if err != nil { |
| 811 | t.Fatal(err) |
| 812 | } |
| 813 | r, err := z.File[0].Open() |
| 814 | if err != nil { |
| 815 | t.Fatal(err) |
| 816 | } |
| 817 | _, err = ioutil.ReadAll(r) |
| 818 | if err != io.ErrUnexpectedEOF { |
| 819 | t.Errorf("File[0] error = %v; want io.ErrUnexpectedEOF", err) |
| 820 | } |
| 821 | r.Close() |
| 822 | } |
| 823 | |
| 824 | // Verify we do not treat non-zip64 archives as zip64 |
| 825 | func TestIssue12449(t *testing.T) { |
| 826 | data := []byte{ |
| 827 | 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, |
| 828 | 0x00, 0x00, 0x6b, 0xb4, 0xba, 0x46, 0x00, 0x00, |
| 829 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 830 | 0x00, 0x00, 0x03, 0x00, 0x18, 0x00, 0xca, 0x64, |
| 831 | 0x55, 0x75, 0x78, 0x0b, 0x00, 0x50, 0x4b, 0x05, |
| 832 | 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, |
| 833 | 0x00, 0x49, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, |
| 834 | 0x00, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x0a, |
| 835 | 0x50, 0x4b, 0x07, 0x08, 0x1d, 0x88, 0x77, 0xb0, |
| 836 | 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, |
| 837 | 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, |
| 838 | 0x08, 0x00, 0x00, 0x00, 0x6b, 0xb4, 0xba, 0x46, |
| 839 | 0x1d, 0x88, 0x77, 0xb0, 0x07, 0x00, 0x00, 0x00, |
| 840 | 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x18, 0x00, |
| 841 | 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 842 | 0xa0, 0x81, 0x00, 0x00, 0x00, 0x00, 0xca, 0x64, |
| 843 | 0x55, 0x75, 0x78, 0x0b, 0x00, 0x50, 0x4b, 0x05, |
| 844 | 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, |
| 845 | 0x00, 0x49, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, |
| 846 | 0x00, 0x97, 0x2b, 0x49, 0x23, 0x05, 0xc5, 0x0b, |
| 847 | 0xa7, 0xd1, 0x52, 0xa2, 0x9c, 0x50, 0x4b, 0x06, |
| 848 | 0x07, 0xc8, 0x19, 0xc1, 0xaf, 0x94, 0x9c, 0x61, |
| 849 | 0x44, 0xbe, 0x94, 0x19, 0x42, 0x58, 0x12, 0xc6, |
| 850 | 0x5b, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, |
| 851 | 0x00, 0x01, 0x00, 0x01, 0x00, 0x69, 0x00, 0x00, |
| 852 | 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 853 | } |
| 854 | // Read in the archive. |
| 855 | _, err := NewReader(bytes.NewReader([]byte(data)), int64(len(data))) |
| 856 | if err != nil { |
| 857 | t.Errorf("Error reading the archive: %v", err) |
| 858 | } |
| 859 | } |