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.
23
Source func() (r io.ReaderAt, size int64) // if non-nil, used instead of testdata/<Name> file
26
Error error // the error that Opening this file should return
29
type ZipTestFile struct {
31
Content []byte // if blank, will attempt to compare against File
33
File string // name of file to compare to (relative to testdata/)
34
Mtime string // modified time in format "mm-dd-yy hh:mm:ss"
38
// Caution: The Mtime values found for the test files should correspond to
39
// the values listed with unzip -l <zipfile>. However, the values
40
// listed by unzip appear to be off by some hours. When creating
41
// fresh test files and testing them, this issue is not present.
42
// The test files were created in Sydney, so there might be a time
43
// zone issue. The time zone information does have to be encoded
44
// somewhere, because otherwise unzip -l could not provide a different
45
// time from what the archive/zip package provides, but there appears
46
// to be no documentation about this.
48
var tests = []ZipTest{
51
Comment: "This is a zipfile comment.",
55
Content: []byte("This is a test text file.\n"),
56
Mtime: "09-05-10 12:12:02",
60
Name: "gophercolor16x16.png",
61
File: "gophercolor16x16.png",
62
Mtime: "09-05-10 15:52:58",
68
Name: "test-trailing-junk.zip",
69
Comment: "This is a zipfile comment.",
73
Content: []byte("This is a test text file.\n"),
74
Mtime: "09-05-10 12:12:02",
78
Name: "gophercolor16x16.png",
79
File: "gophercolor16x16.png",
80
Mtime: "09-05-10 15:52:58",
87
Source: returnRecursiveZip,
92
Mtime: "03-04-10 00:24:16",
102
Content: []byte("../target"),
103
Mode: 0777 | os.ModeSymlink,
111
Name: "readme.notzip",
119
Content: []byte("This is a test textfile.\n"),
120
Mtime: "02-02-11 13:06:20",
126
// created in windows XP file manager.
131
// created by Zip 3.0 under Linux
136
// created by Go, before we wrote the "optional" data
137
// descriptor signatures (which are required by OS X)
138
Name: "go-no-datadesc-sig.zip",
142
Content: []byte("foo\n"),
143
Mtime: "03-08-12 16:59:10",
148
Content: []byte("bar\n"),
149
Mtime: "03-08-12 16:59:12",
155
// created by Go, after we wrote the "optional" data
156
// descriptor signatures (which are required by OS X)
157
Name: "go-with-datadesc-sig.zip",
161
Content: []byte("foo\n"),
166
Content: []byte("bar\n"),
172
Name: "Bad-CRC32-in-data-descriptor",
173
Source: returnCorruptCRC32Zip,
177
Content: []byte("foo\n"),
179
ContentErr: ErrChecksum,
183
Content: []byte("bar\n"),
188
// Tests that we verify (and accept valid) crc32s on files
189
// with crc32s in their file header (not in data descriptors)
191
Name: "crc32-not-streamed.zip",
195
Content: []byte("foo\n"),
196
Mtime: "03-08-12 16:59:10",
201
Content: []byte("bar\n"),
202
Mtime: "03-08-12 16:59:12",
207
// Tests that we verify (and reject invalid) crc32s on files
208
// with crc32s in their file header (not in data descriptors)
210
Name: "crc32-not-streamed.zip",
211
Source: returnCorruptNotStreamedZip,
215
Content: []byte("foo\n"),
216
Mtime: "03-08-12 16:59:10",
218
ContentErr: ErrChecksum,
222
Content: []byte("bar\n"),
223
Mtime: "03-08-12 16:59:12",
233
Content: []byte("This small file is in ZIP64 format.\n"),
234
Mtime: "08-10-12 14:33:32",
239
// Another zip64 file with different Extras fields. (golang.org/issue/7069)
245
Content: []byte("This small file is in ZIP64 format.\n"),
246
Mtime: "08-10-12 14:33:32",
253
var crossPlatform = []ZipTestFile{
256
Content: []byte("world \r\n"),
261
Content: []byte("foo \r\n"),
267
Mode: os.ModeDir | 0777,
271
Content: []byte("important \r\n"),
276
func TestReader(t *testing.T) {
277
for _, zt := range tests {
282
func readTestZip(t *testing.T, zt ZipTest) {
285
if zt.Source != nil {
286
rat, size := zt.Source()
287
z, err = NewReader(rat, size)
290
rc, err = OpenReader(filepath.Join("testdata", zt.Name))
297
t.Errorf("%s: error=%v, want %v", zt.Name, err, zt.Error)
301
// bail if file is not zip
302
if err == ErrFormat {
306
// bail here if no Files expected to be tested
307
// (there may actually be files in the zip, but we don't care)
312
if z.Comment != zt.Comment {
313
t.Errorf("%s: comment=%q, want %q", zt.Name, z.Comment, zt.Comment)
315
if len(z.File) != len(zt.File) {
316
t.Fatalf("%s: file count=%d, want %d", zt.Name, len(z.File), len(zt.File))
319
// test read of each file
320
for i, ft := range zt.File {
321
readTestFile(t, zt, ft, z.File[i])
324
// test simultaneous reads
326
done := make(chan bool)
327
for i := 0; i < 5; i++ {
328
for j, ft := range zt.File {
329
go func(j int, ft ZipTestFile) {
330
readTestFile(t, zt, ft, z.File[j])
341
func readTestFile(t *testing.T, zt ZipTest, ft ZipTestFile, f *File) {
342
if f.Name != ft.Name {
343
t.Errorf("%s: name=%q, want %q", zt.Name, f.Name, ft.Name)
347
mtime, err := time.Parse("01-02-06 15:04:05", ft.Mtime)
352
if ft := f.ModTime(); !ft.Equal(mtime) {
353
t.Errorf("%s: %s: mtime=%s, want %s", zt.Name, f.Name, ft, mtime)
357
testFileMode(t, zt.Name, f, ft.Mode)
362
t.Errorf("%s: %v", zt.Name, err)
366
_, err = io.Copy(&b, r)
367
if err != ft.ContentErr {
368
t.Errorf("%s: copying contents: %v (want %v)", zt.Name, err, ft.ContentErr)
375
size := uint64(f.UncompressedSize)
376
if size == uint32max {
377
size = f.UncompressedSize64
379
if g := uint64(b.Len()); g != size {
380
t.Errorf("%v: read %v bytes but f.UncompressedSize == %v", f.Name, g, size)
384
if ft.Content != nil {
386
} else if c, err = ioutil.ReadFile("testdata/" + ft.File); err != nil {
391
if b.Len() != len(c) {
392
t.Errorf("%s: len=%d, want %d", f.Name, b.Len(), len(c))
396
for i, b := range b.Bytes() {
398
t.Errorf("%s: content[%d]=%q want %q", f.Name, i, b, c[i])
404
func testFileMode(t *testing.T, zipName string, f *File, want os.FileMode) {
407
t.Errorf("%s: %s mode: got %v, want none", zipName, f.Name, mode)
408
} else if mode != want {
409
t.Errorf("%s: %s mode: want %v, got %v", zipName, f.Name, want, mode)
413
func TestInvalidFiles(t *testing.T) {
414
const size = 1024 * 70 // 70kb
415
b := make([]byte, size)
418
_, err := NewReader(bytes.NewReader(b), size)
419
if err != ErrFormat {
420
t.Errorf("zeroes: error=%v, want %v", err, ErrFormat)
423
// repeated directoryEndSignatures
424
sig := make([]byte, 4)
425
binary.LittleEndian.PutUint32(sig, directoryEndSignature)
426
for i := 0; i < size-4; i += 4 {
429
_, err = NewReader(bytes.NewReader(b), size)
430
if err != ErrFormat {
431
t.Errorf("sigs: error=%v, want %v", err, ErrFormat)
435
func messWith(fileName string, corrupter func(b []byte)) (r io.ReaderAt, size int64) {
436
data, err := ioutil.ReadFile(filepath.Join("testdata", fileName))
438
panic("Error reading " + fileName + ": " + err.Error())
441
return bytes.NewReader(data), int64(len(data))
444
func returnCorruptCRC32Zip() (r io.ReaderAt, size int64) {
445
return messWith("go-with-datadesc-sig.zip", func(b []byte) {
446
// Corrupt one of the CRC32s in the data descriptor:
451
func returnCorruptNotStreamedZip() (r io.ReaderAt, size int64) {
452
return messWith("crc32-not-streamed.zip", func(b []byte) {
453
// Corrupt foo.txt's final crc32 byte, in both
454
// the file header and TOC. (0x7e -> 0x7f)
458
// TODO(bradfitz): add a new test that only corrupts
459
// one of these values, and verify that that's also an
460
// error. Currently, the reader code doesn't verify the
461
// fileheader and TOC's crc32 match if they're both
462
// non-zero and only the second line above, the TOC,
467
// rZipBytes returns the bytes of a recursive zip file, without
468
// putting it on disk and triggering certain virus scanners.
469
func rZipBytes() []byte {
471
0000000 50 4b 03 04 14 00 00 00 08 00 08 03 64 3c f9 f4
472
0000010 89 64 48 01 00 00 b8 01 00 00 07 00 00 00 72 2f
473
0000020 72 2e 7a 69 70 00 25 00 da ff 50 4b 03 04 14 00
474
0000030 00 00 08 00 08 03 64 3c f9 f4 89 64 48 01 00 00
475
0000040 b8 01 00 00 07 00 00 00 72 2f 72 2e 7a 69 70 00
476
0000050 2f 00 d0 ff 00 25 00 da ff 50 4b 03 04 14 00 00
477
0000060 00 08 00 08 03 64 3c f9 f4 89 64 48 01 00 00 b8
478
0000070 01 00 00 07 00 00 00 72 2f 72 2e 7a 69 70 00 2f
479
0000080 00 d0 ff c2 54 8e 57 39 00 05 00 fa ff c2 54 8e
480
0000090 57 39 00 05 00 fa ff 00 05 00 fa ff 00 14 00 eb
481
00000a0 ff c2 54 8e 57 39 00 05 00 fa ff 00 05 00 fa ff
482
00000b0 00 14 00 eb ff 42 88 21 c4 00 00 14 00 eb ff 42
483
00000c0 88 21 c4 00 00 14 00 eb ff 42 88 21 c4 00 00 14
484
00000d0 00 eb ff 42 88 21 c4 00 00 14 00 eb ff 42 88 21
485
00000e0 c4 00 00 00 00 ff ff 00 00 00 ff ff 00 34 00 cb
486
00000f0 ff 42 88 21 c4 00 00 00 00 ff ff 00 00 00 ff ff
487
0000100 00 34 00 cb ff 42 e8 21 5e 0f 00 00 00 ff ff 0a
488
0000110 f0 66 64 12 61 c0 15 dc e8 a0 48 bf 48 af 2a b3
489
0000120 20 c0 9b 95 0d c4 67 04 42 53 06 06 06 40 00 06
490
0000130 00 f9 ff 6d 01 00 00 00 00 42 e8 21 5e 0f 00 00
491
0000140 00 ff ff 0a f0 66 64 12 61 c0 15 dc e8 a0 48 bf
492
0000150 48 af 2a b3 20 c0 9b 95 0d c4 67 04 42 53 06 06
493
0000160 06 40 00 06 00 f9 ff 6d 01 00 00 00 00 50 4b 01
494
0000170 02 14 00 14 00 00 00 08 00 08 03 64 3c f9 f4 89
495
0000180 64 48 01 00 00 b8 01 00 00 07 00 00 00 00 00 00
496
0000190 00 00 00 00 00 00 00 00 00 00 00 72 2f 72 2e 7a
497
00001a0 69 70 50 4b 05 06 00 00 00 00 01 00 01 00 35 00
498
00001b0 00 00 6d 01 00 00 00 00`
499
s = regexp.MustCompile(`[0-9a-f]{7}`).ReplaceAllString(s, "")
500
s = regexp.MustCompile(`\s+`).ReplaceAllString(s, "")
501
b, err := hex.DecodeString(s)
508
func returnRecursiveZip() (r io.ReaderAt, size int64) {
510
return bytes.NewReader(b), int64(len(b))
513
func TestIssue8186(t *testing.T) {
514
// Directory headers & data found in the TOC of a JAR file.
516
"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",
517
"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",
518
"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",
519
"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",
520
"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",
521
"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",
522
"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",
523
"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",
524
"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",
526
for i, s := range dirEnts {
528
err := readDirectoryHeader(&f, strings.NewReader(s))
530
t.Errorf("error reading #%d: %v", i, err)
535
// Verify we return ErrUnexpectedEOF when length is short.
536
func TestIssue10957(t *testing.T) {
537
data := []byte("PK\x03\x040000000PK\x01\x0200000" +
538
"0000000000000000000\x00" +
539
"\x00\x00\x00\x00\x00000000000000PK\x01" +
540
"\x020000000000000000000" +
541
"00000\v\x00\x00\x00\x00\x00000000000" +
542
"00000000000000PK\x01\x0200" +
543
"00000000000000000000" +
544
"00\v\x00\x00\x00\x00\x00000000000000" +
545
"00000000000PK\x01\x020000<" +
546
"0\x00\x0000000000000000\v\x00\v" +
547
"\x00\x00\x00\x00\x0000000000\x00\x00\x00\x00000" +
548
"00000000PK\x01\x0200000000" +
549
"0000000000000000\v\x00\x00\x00" +
550
"\x00\x0000PK\x05\x06000000\x05\x000000" +
551
"\v\x00\x00\x00\x00\x00")
552
z, err := NewReader(bytes.NewReader(data), int64(len(data)))
556
for i, f := range z.File {
561
if f.UncompressedSize64 < 1e6 {
562
n, err := io.Copy(ioutil.Discard, r)
563
if i == 3 && err != io.ErrUnexpectedEOF {
564
t.Errorf("File[3] error = %v; want io.ErrUnexpectedEOF", err)
566
if err == nil && uint64(n) != f.UncompressedSize64 {
567
t.Errorf("file %d: bad size: copied=%d; want=%d", i, n, f.UncompressedSize64)
574
// Verify the number of files is sane.
575
func TestIssue10956(t *testing.T) {
576
data := []byte("PK\x06\x06PK\x06\a0000\x00\x00\x00\x00\x00\x00\x00\x00" +
577
"0000PK\x05\x06000000000000" +
578
"0000\v\x00000\x00\x00\x00\x00\x00\x00\x000")
579
_, err := NewReader(bytes.NewReader(data), int64(len(data)))
580
const want = "TOC declares impossible 3472328296227680304 files in 57 byte"
581
if err == nil && !strings.Contains(err.Error(), want) {
582
t.Errorf("error = %v; want %q", err, want)
586
// Verify we return ErrUnexpectedEOF when reading truncated data descriptor.
587
func TestIssue11146(t *testing.T) {
588
data := []byte("PK\x03\x040000000000000000" +
589
"000000\x01\x00\x00\x000\x01\x00\x00\xff\xff0000" +
590
"0000000000000000PK\x01\x02" +
591
"0000\b0\b\x00000000000000" +
592
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000PK\x05\x06\x00\x00" +
593
"\x00\x0000\x01\x0000008\x00\x00\x00\x00\x00")
594
z, err := NewReader(bytes.NewReader(data), int64(len(data)))
598
r, err := z.File[0].Open()
602
_, err = ioutil.ReadAll(r)
603
if err != io.ErrUnexpectedEOF {
604
t.Errorf("File[0] error = %v; want io.ErrUnexpectedEOF", err)
609
// Verify we do not treat non-zip64 archives as zip64
610
func TestIssue12449(t *testing.T) {
612
0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00,
613
0x00, 0x00, 0x6b, 0xb4, 0xba, 0x46, 0x00, 0x00,
614
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
615
0x00, 0x00, 0x03, 0x00, 0x18, 0x00, 0xca, 0x64,
616
0x55, 0x75, 0x78, 0x0b, 0x00, 0x50, 0x4b, 0x05,
617
0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
618
0x00, 0x49, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
619
0x00, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x0a,
620
0x50, 0x4b, 0x07, 0x08, 0x1d, 0x88, 0x77, 0xb0,
621
0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
622
0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00,
623
0x08, 0x00, 0x00, 0x00, 0x6b, 0xb4, 0xba, 0x46,
624
0x1d, 0x88, 0x77, 0xb0, 0x07, 0x00, 0x00, 0x00,
625
0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x18, 0x00,
626
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
627
0xa0, 0x81, 0x00, 0x00, 0x00, 0x00, 0xca, 0x64,
628
0x55, 0x75, 0x78, 0x0b, 0x00, 0x50, 0x4b, 0x05,
629
0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
630
0x00, 0x49, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
631
0x00, 0x97, 0x2b, 0x49, 0x23, 0x05, 0xc5, 0x0b,
632
0xa7, 0xd1, 0x52, 0xa2, 0x9c, 0x50, 0x4b, 0x06,
633
0x07, 0xc8, 0x19, 0xc1, 0xaf, 0x94, 0x9c, 0x61,
634
0x44, 0xbe, 0x94, 0x19, 0x42, 0x58, 0x12, 0xc6,
635
0x5b, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00,
636
0x00, 0x01, 0x00, 0x01, 0x00, 0x69, 0x00, 0x00,
637
0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,
639
// Read in the archive.
640
_, err := NewReader(bytes.NewReader([]byte(data)), int64(len(data)))
642
t.Errorf("Error reading the archive: %v", err)