Home | History | Annotate | Download | only in zip
      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 	Content    []byte // if blank, will attempt to compare against File
     32 	ContentErr error
     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"
     35 	Mode       os.FileMode
     36 }
     37 
     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.
     47 
     48 var tests = []ZipTest{
     49 	{
     50 		Name:    "test.zip",
     51 		Comment: "This is a zipfile comment.",
     52 		File: []ZipTestFile{
     53 			{
     54 				Name:    "test.txt",
     55 				Content: []byte("This is a test text file.\n"),
     56 				Mtime:   "09-05-10 12:12:02",
     57 				Mode:    0644,
     58 			},
     59 			{
     60 				Name:  "gophercolor16x16.png",
     61 				File:  "gophercolor16x16.png",
     62 				Mtime: "09-05-10 15:52:58",
     63 				Mode:  0644,
     64 			},
     65 		},
     66 	},
     67 	{
     68 		Name:    "test-trailing-junk.zip",
     69 		Comment: "This is a zipfile comment.",
     70 		File: []ZipTestFile{
     71 			{
     72 				Name:    "test.txt",
     73 				Content: []byte("This is a test text file.\n"),
     74 				Mtime:   "09-05-10 12:12:02",
     75 				Mode:    0644,
     76 			},
     77 			{
     78 				Name:  "gophercolor16x16.png",
     79 				File:  "gophercolor16x16.png",
     80 				Mtime: "09-05-10 15:52:58",
     81 				Mode:  0644,
     82 			},
     83 		},
     84 	},
     85 	{
     86 		Name:   "r.zip",
     87 		Source: returnRecursiveZip,
     88 		File: []ZipTestFile{
     89 			{
     90 				Name:    "r/r.zip",
     91 				Content: rZipBytes(),
     92 				Mtime:   "03-04-10 00:24:16",
     93 				Mode:    0666,
     94 			},
     95 		},
     96 	},
     97 	{
     98 		Name: "symlink.zip",
     99 		File: []ZipTestFile{
    100 			{
    101 				Name:    "symlink",
    102 				Content: []byte("../target"),
    103 				Mode:    0777 | os.ModeSymlink,
    104 			},
    105 		},
    106 	},
    107 	{
    108 		Name: "readme.zip",
    109 	},
    110 	{
    111 		Name:  "readme.notzip",
    112 		Error: ErrFormat,
    113 	},
    114 	{
    115 		Name: "dd.zip",
    116 		File: []ZipTestFile{
    117 			{
    118 				Name:    "filename",
    119 				Content: []byte("This is a test textfile.\n"),
    120 				Mtime:   "02-02-11 13:06:20",
    121 				Mode:    0666,
    122 			},
    123 		},
    124 	},
    125 	{
    126 		// created in windows XP file manager.
    127 		Name: "winxp.zip",
    128 		File: crossPlatform,
    129 	},
    130 	{
    131 		// created by Zip 3.0 under Linux
    132 		Name: "unix.zip",
    133 		File: crossPlatform,
    134 	},
    135 	{
    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",
    139 		File: []ZipTestFile{
    140 			{
    141 				Name:    "foo.txt",
    142 				Content: []byte("foo\n"),
    143 				Mtime:   "03-08-12 16:59:10",
    144 				Mode:    0644,
    145 			},
    146 			{
    147 				Name:    "bar.txt",
    148 				Content: []byte("bar\n"),
    149 				Mtime:   "03-08-12 16:59:12",
    150 				Mode:    0644,
    151 			},
    152 		},
    153 	},
    154 	{
    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",
    158 		File: []ZipTestFile{
    159 			{
    160 				Name:    "foo.txt",
    161 				Content: []byte("foo\n"),
    162 				Mode:    0666,
    163 			},
    164 			{
    165 				Name:    "bar.txt",
    166 				Content: []byte("bar\n"),
    167 				Mode:    0666,
    168 			},
    169 		},
    170 	},
    171 	{
    172 		Name:   "Bad-CRC32-in-data-descriptor",
    173 		Source: returnCorruptCRC32Zip,
    174 		File: []ZipTestFile{
    175 			{
    176 				Name:       "foo.txt",
    177 				Content:    []byte("foo\n"),
    178 				Mode:       0666,
    179 				ContentErr: ErrChecksum,
    180 			},
    181 			{
    182 				Name:    "bar.txt",
    183 				Content: []byte("bar\n"),
    184 				Mode:    0666,
    185 			},
    186 		},
    187 	},
    188 	// Tests that we verify (and accept valid) crc32s on files
    189 	// with crc32s in their file header (not in data descriptors)
    190 	{
    191 		Name: "crc32-not-streamed.zip",
    192 		File: []ZipTestFile{
    193 			{
    194 				Name:    "foo.txt",
    195 				Content: []byte("foo\n"),
    196 				Mtime:   "03-08-12 16:59:10",
    197 				Mode:    0644,
    198 			},
    199 			{
    200 				Name:    "bar.txt",
    201 				Content: []byte("bar\n"),
    202 				Mtime:   "03-08-12 16:59:12",
    203 				Mode:    0644,
    204 			},
    205 		},
    206 	},
    207 	// Tests that we verify (and reject invalid) crc32s on files
    208 	// with crc32s in their file header (not in data descriptors)
    209 	{
    210 		Name:   "crc32-not-streamed.zip",
    211 		Source: returnCorruptNotStreamedZip,
    212 		File: []ZipTestFile{
    213 			{
    214 				Name:       "foo.txt",
    215 				Content:    []byte("foo\n"),
    216 				Mtime:      "03-08-12 16:59:10",
    217 				Mode:       0644,
    218 				ContentErr: ErrChecksum,
    219 			},
    220 			{
    221 				Name:    "bar.txt",
    222 				Content: []byte("bar\n"),
    223 				Mtime:   "03-08-12 16:59:12",
    224 				Mode:    0644,
    225 			},
    226 		},
    227 	},
    228 	{
    229 		Name: "zip64.zip",
    230 		File: []ZipTestFile{
    231 			{
    232 				Name:    "README",
    233 				Content: []byte("This small file is in ZIP64 format.\n"),
    234 				Mtime:   "08-10-12 14:33:32",
    235 				Mode:    0644,
    236 			},
    237 		},
    238 	},
    239 	// Another zip64 file with different Extras fields. (golang.org/issue/7069)
    240 	{
    241 		Name: "zip64-2.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 }
    252 
    253 var crossPlatform = []ZipTestFile{
    254 	{
    255 		Name:    "hello",
    256 		Content: []byte("world \r\n"),
    257 		Mode:    0666,
    258 	},
    259 	{
    260 		Name:    "dir/bar",
    261 		Content: []byte("foo \r\n"),
    262 		Mode:    0666,
    263 	},
    264 	{
    265 		Name:    "dir/empty/",
    266 		Content: []byte{},
    267 		Mode:    os.ModeDir | 0777,
    268 	},
    269 	{
    270 		Name:    "readonly",
    271 		Content: []byte("important \r\n"),
    272 		Mode:    0444,
    273 	},
    274 }
    275 
    276 func TestReader(t *testing.T) {
    277 	for _, zt := range tests {
    278 		readTestZip(t, zt)
    279 	}
    280 }
    281 
    282 func readTestZip(t *testing.T, zt ZipTest) {
    283 	var z *Reader
    284 	var err error
    285 	if zt.Source != nil {
    286 		rat, size := zt.Source()
    287 		z, err = NewReader(rat, size)
    288 	} else {
    289 		var rc *ReadCloser
    290 		rc, err = OpenReader(filepath.Join("testdata", zt.Name))
    291 		if err == nil {
    292 			defer rc.Close()
    293 			z = &rc.Reader
    294 		}
    295 	}
    296 	if err != zt.Error {
    297 		t.Errorf("%s: error=%v, want %v", zt.Name, err, zt.Error)
    298 		return
    299 	}
    300 
    301 	// bail if file is not zip
    302 	if err == ErrFormat {
    303 		return
    304 	}
    305 
    306 	// bail here if no Files expected to be tested
    307 	// (there may actually be files in the zip, but we don't care)
    308 	if zt.File == nil {
    309 		return
    310 	}
    311 
    312 	if z.Comment != zt.Comment {
    313 		t.Errorf("%s: comment=%q, want %q", zt.Name, z.Comment, zt.Comment)
    314 	}
    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))
    317 	}
    318 
    319 	// test read of each file
    320 	for i, ft := range zt.File {
    321 		readTestFile(t, zt, ft, z.File[i])
    322 	}
    323 
    324 	// test simultaneous reads
    325 	n := 0
    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])
    331 				done <- true
    332 			}(j, ft)
    333 			n++
    334 		}
    335 	}
    336 	for ; n > 0; n-- {
    337 		<-done
    338 	}
    339 }
    340 
    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)
    344 	}
    345 
    346 	if ft.Mtime != "" {
    347 		mtime, err := time.Parse("01-02-06 15:04:05", ft.Mtime)
    348 		if err != nil {
    349 			t.Error(err)
    350 			return
    351 		}
    352 		if ft := f.ModTime(); !ft.Equal(mtime) {
    353 			t.Errorf("%s: %s: mtime=%s, want %s", zt.Name, f.Name, ft, mtime)
    354 		}
    355 	}
    356 
    357 	testFileMode(t, zt.Name, f, ft.Mode)
    358 
    359 	var b bytes.Buffer
    360 	r, err := f.Open()
    361 	if err != nil {
    362 		t.Errorf("%s: %v", zt.Name, err)
    363 		return
    364 	}
    365 
    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)
    369 	}
    370 	if err != nil {
    371 		return
    372 	}
    373 	r.Close()
    374 
    375 	size := uint64(f.UncompressedSize)
    376 	if size == uint32max {
    377 		size = f.UncompressedSize64
    378 	}
    379 	if g := uint64(b.Len()); g != size {
    380 		t.Errorf("%v: read %v bytes but f.UncompressedSize == %v", f.Name, g, size)
    381 	}
    382 
    383 	var c []byte
    384 	if ft.Content != nil {
    385 		c = ft.Content
    386 	} else if c, err = ioutil.ReadFile("testdata/" + ft.File); err != nil {
    387 		t.Error(err)
    388 		return
    389 	}
    390 
    391 	if b.Len() != len(c) {
    392 		t.Errorf("%s: len=%d, want %d", f.Name, b.Len(), len(c))
    393 		return
    394 	}
    395 
    396 	for i, b := range b.Bytes() {
    397 		if b != c[i] {
    398 			t.Errorf("%s: content[%d]=%q want %q", f.Name, i, b, c[i])
    399 			return
    400 		}
    401 	}
    402 }
    403 
    404 func testFileMode(t *testing.T, zipName string, f *File, want os.FileMode) {
    405 	mode := f.Mode()
    406 	if want == 0 {
    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)
    410 	}
    411 }
    412 
    413 func TestInvalidFiles(t *testing.T) {
    414 	const size = 1024 * 70 // 70kb
    415 	b := make([]byte, size)
    416 
    417 	// zeroes
    418 	_, err := NewReader(bytes.NewReader(b), size)
    419 	if err != ErrFormat {
    420 		t.Errorf("zeroes: error=%v, want %v", err, ErrFormat)
    421 	}
    422 
    423 	// repeated directoryEndSignatures
    424 	sig := make([]byte, 4)
    425 	binary.LittleEndian.PutUint32(sig, directoryEndSignature)
    426 	for i := 0; i < size-4; i += 4 {
    427 		copy(b[i:i+4], sig)
    428 	}
    429 	_, err = NewReader(bytes.NewReader(b), size)
    430 	if err != ErrFormat {
    431 		t.Errorf("sigs: error=%v, want %v", err, ErrFormat)
    432 	}
    433 }
    434 
    435 func messWith(fileName string, corrupter func(b []byte)) (r io.ReaderAt, size int64) {
    436 	data, err := ioutil.ReadFile(filepath.Join("testdata", fileName))
    437 	if err != nil {
    438 		panic("Error reading " + fileName + ": " + err.Error())
    439 	}
    440 	corrupter(data)
    441 	return bytes.NewReader(data), int64(len(data))
    442 }
    443 
    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:
    447 		b[0x2d]++
    448 	})
    449 }
    450 
    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)
    455 		b[0x11]++
    456 		b[0x9d]++
    457 
    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,
    463 		// is what matters.
    464 	})
    465 }
    466 
    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 {
    470 	s := `
    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)
    502 	if err != nil {
    503 		panic(err)
    504 	}
    505 	return b
    506 }
    507 
    508 func returnRecursiveZip() (r io.ReaderAt, size int64) {
    509 	b := rZipBytes()
    510 	return bytes.NewReader(b), int64(len(b))
    511 }
    512 
    513 func TestIssue8186(t *testing.T) {
    514 	// Directory headers & data found in the TOC of a JAR file.
    515 	dirEnts := []string{
    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\x98o\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",
    525 	}
    526 	for i, s := range dirEnts {
    527 		var f File
    528 		err := readDirectoryHeader(&f, strings.NewReader(s))
    529 		if err != nil {
    530 			t.Errorf("error reading #%d: %v", i, err)
    531 		}
    532 	}
    533 }
    534 
    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)))
    553 	if err != nil {
    554 		t.Fatal(err)
    555 	}
    556 	for i, f := range z.File {
    557 		r, err := f.Open()
    558 		if err != nil {
    559 			continue
    560 		}
    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)
    565 			}
    566 			if err == nil && uint64(n) != f.UncompressedSize64 {
    567 				t.Errorf("file %d: bad size: copied=%d; want=%d", i, n, f.UncompressedSize64)
    568 			}
    569 		}
    570 		r.Close()
    571 	}
    572 }
    573 
    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)
    583 	}
    584 }
    585 
    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)))
    595 	if err != nil {
    596 		t.Fatal(err)
    597 	}
    598 	r, err := z.File[0].Open()
    599 	if err != nil {
    600 		t.Fatal(err)
    601 	}
    602 	_, err = ioutil.ReadAll(r)
    603 	if err != io.ErrUnexpectedEOF {
    604 		t.Errorf("File[0] error = %v; want io.ErrUnexpectedEOF", err)
    605 	}
    606 	r.Close()
    607 }
    608