1 // Copyright 2011 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 // Tests that involve both reading and writing. 6 7 package zip 8 9 import ( 10 "bytes" 11 "fmt" 12 "hash" 13 "io" 14 "io/ioutil" 15 "sort" 16 "strings" 17 "testing" 18 "time" 19 ) 20 21 func TestOver65kFiles(t *testing.T) { 22 buf := new(bytes.Buffer) 23 w := NewWriter(buf) 24 const nFiles = (1 << 16) + 42 25 for i := 0; i < nFiles; i++ { 26 _, err := w.CreateHeader(&FileHeader{ 27 Name: fmt.Sprintf("%d.dat", i), 28 Method: Store, // avoid Issue 6136 and Issue 6138 29 }) 30 if err != nil { 31 t.Fatalf("creating file %d: %v", i, err) 32 } 33 } 34 if err := w.Close(); err != nil { 35 t.Fatalf("Writer.Close: %v", err) 36 } 37 s := buf.String() 38 zr, err := NewReader(strings.NewReader(s), int64(len(s))) 39 if err != nil { 40 t.Fatalf("NewReader: %v", err) 41 } 42 if got := len(zr.File); got != nFiles { 43 t.Fatalf("File contains %d files, want %d", got, nFiles) 44 } 45 for i := 0; i < nFiles; i++ { 46 want := fmt.Sprintf("%d.dat", i) 47 if zr.File[i].Name != want { 48 t.Fatalf("File(%d) = %q, want %q", i, zr.File[i].Name, want) 49 } 50 } 51 } 52 53 func TestModTime(t *testing.T) { 54 var testTime = time.Date(2009, time.November, 10, 23, 45, 58, 0, time.UTC) 55 fh := new(FileHeader) 56 fh.SetModTime(testTime) 57 outTime := fh.ModTime() 58 if !outTime.Equal(testTime) { 59 t.Errorf("times don't match: got %s, want %s", outTime, testTime) 60 } 61 } 62 63 func testHeaderRoundTrip(fh *FileHeader, wantUncompressedSize uint32, wantUncompressedSize64 uint64, t *testing.T) { 64 fi := fh.FileInfo() 65 fh2, err := FileInfoHeader(fi) 66 if err != nil { 67 t.Fatal(err) 68 } 69 if got, want := fh2.Name, fh.Name; got != want { 70 t.Errorf("Name: got %s, want %s\n", got, want) 71 } 72 if got, want := fh2.UncompressedSize, wantUncompressedSize; got != want { 73 t.Errorf("UncompressedSize: got %d, want %d\n", got, want) 74 } 75 if got, want := fh2.UncompressedSize64, wantUncompressedSize64; got != want { 76 t.Errorf("UncompressedSize64: got %d, want %d\n", got, want) 77 } 78 if got, want := fh2.ModifiedTime, fh.ModifiedTime; got != want { 79 t.Errorf("ModifiedTime: got %d, want %d\n", got, want) 80 } 81 if got, want := fh2.ModifiedDate, fh.ModifiedDate; got != want { 82 t.Errorf("ModifiedDate: got %d, want %d\n", got, want) 83 } 84 85 if sysfh, ok := fi.Sys().(*FileHeader); !ok && sysfh != fh { 86 t.Errorf("Sys didn't return original *FileHeader") 87 } 88 } 89 90 func TestFileHeaderRoundTrip(t *testing.T) { 91 fh := &FileHeader{ 92 Name: "foo.txt", 93 UncompressedSize: 987654321, 94 ModifiedTime: 1234, 95 ModifiedDate: 5678, 96 } 97 testHeaderRoundTrip(fh, fh.UncompressedSize, uint64(fh.UncompressedSize), t) 98 } 99 100 func TestFileHeaderRoundTrip64(t *testing.T) { 101 fh := &FileHeader{ 102 Name: "foo.txt", 103 UncompressedSize64: 9876543210, 104 ModifiedTime: 1234, 105 ModifiedDate: 5678, 106 } 107 testHeaderRoundTrip(fh, uint32max, fh.UncompressedSize64, t) 108 } 109 110 type repeatedByte struct { 111 off int64 112 b byte 113 n int64 114 } 115 116 // rleBuffer is a run-length-encoded byte buffer. 117 // It's an io.Writer (like a bytes.Buffer) and also an io.ReaderAt, 118 // allowing random-access reads. 119 type rleBuffer struct { 120 buf []repeatedByte 121 } 122 123 func (r *rleBuffer) Size() int64 { 124 if len(r.buf) == 0 { 125 return 0 126 } 127 last := &r.buf[len(r.buf)-1] 128 return last.off + last.n 129 } 130 131 func (r *rleBuffer) Write(p []byte) (n int, err error) { 132 var rp *repeatedByte 133 if len(r.buf) > 0 { 134 rp = &r.buf[len(r.buf)-1] 135 // Fast path, if p is entirely the same byte repeated. 136 if lastByte := rp.b; len(p) > 0 && p[0] == lastByte { 137 all := true 138 for _, b := range p { 139 if b != lastByte { 140 all = false 141 break 142 } 143 } 144 if all { 145 rp.n += int64(len(p)) 146 return len(p), nil 147 } 148 } 149 } 150 151 for _, b := range p { 152 if rp == nil || rp.b != b { 153 r.buf = append(r.buf, repeatedByte{r.Size(), b, 1}) 154 rp = &r.buf[len(r.buf)-1] 155 } else { 156 rp.n++ 157 } 158 } 159 return len(p), nil 160 } 161 162 func (r *rleBuffer) ReadAt(p []byte, off int64) (n int, err error) { 163 if len(p) == 0 { 164 return 165 } 166 skipParts := sort.Search(len(r.buf), func(i int) bool { 167 part := &r.buf[i] 168 return part.off+part.n > off 169 }) 170 parts := r.buf[skipParts:] 171 if len(parts) > 0 { 172 skipBytes := off - parts[0].off 173 for len(parts) > 0 { 174 part := parts[0] 175 for i := skipBytes; i < part.n; i++ { 176 if n == len(p) { 177 return 178 } 179 p[n] = part.b 180 n++ 181 } 182 parts = parts[1:] 183 skipBytes = 0 184 } 185 } 186 if n != len(p) { 187 err = io.ErrUnexpectedEOF 188 } 189 return 190 } 191 192 // Just testing the rleBuffer used in the Zip64 test above. Not used by the zip code. 193 func TestRLEBuffer(t *testing.T) { 194 b := new(rleBuffer) 195 var all []byte 196 writes := []string{"abcdeee", "eeeeeee", "eeeefghaaiii"} 197 for _, w := range writes { 198 b.Write([]byte(w)) 199 all = append(all, w...) 200 } 201 if len(b.buf) != 10 { 202 t.Fatalf("len(b.buf) = %d; want 10", len(b.buf)) 203 } 204 205 for i := 0; i < len(all); i++ { 206 for j := 0; j < len(all)-i; j++ { 207 buf := make([]byte, j) 208 n, err := b.ReadAt(buf, int64(i)) 209 if err != nil || n != len(buf) { 210 t.Errorf("ReadAt(%d, %d) = %d, %v; want %d, nil", i, j, n, err, len(buf)) 211 } 212 if !bytes.Equal(buf, all[i:i+j]) { 213 t.Errorf("ReadAt(%d, %d) = %q; want %q", i, j, buf, all[i:i+j]) 214 } 215 } 216 } 217 } 218 219 // fakeHash32 is a dummy Hash32 that always returns 0. 220 type fakeHash32 struct { 221 hash.Hash32 222 } 223 224 func (fakeHash32) Write(p []byte) (int, error) { return len(p), nil } 225 func (fakeHash32) Sum32() uint32 { return 0 } 226 227 func TestZip64(t *testing.T) { 228 if testing.Short() { 229 t.Skip("slow test; skipping") 230 } 231 const size = 1 << 32 // before the "END\n" part 232 buf := testZip64(t, size) 233 testZip64DirectoryRecordLength(buf, t) 234 } 235 236 func testZip64(t testing.TB, size int64) *rleBuffer { 237 const chunkSize = 1024 238 chunks := int(size / chunkSize) 239 // write 2^32 bytes plus "END\n" to a zip file 240 buf := new(rleBuffer) 241 w := NewWriter(buf) 242 f, err := w.CreateHeader(&FileHeader{ 243 Name: "huge.txt", 244 Method: Store, 245 }) 246 if err != nil { 247 t.Fatal(err) 248 } 249 f.(*fileWriter).crc32 = fakeHash32{} 250 chunk := make([]byte, chunkSize) 251 for i := range chunk { 252 chunk[i] = '.' 253 } 254 for i := 0; i < chunks; i++ { 255 _, err := f.Write(chunk) 256 if err != nil { 257 t.Fatal("write chunk:", err) 258 } 259 } 260 end := []byte("END\n") 261 _, err = f.Write(end) 262 if err != nil { 263 t.Fatal("write end:", err) 264 } 265 if err := w.Close(); err != nil { 266 t.Fatal(err) 267 } 268 269 // read back zip file and check that we get to the end of it 270 r, err := NewReader(buf, int64(buf.Size())) 271 if err != nil { 272 t.Fatal("reader:", err) 273 } 274 f0 := r.File[0] 275 rc, err := f0.Open() 276 if err != nil { 277 t.Fatal("opening:", err) 278 } 279 rc.(*checksumReader).hash = fakeHash32{} 280 for i := 0; i < chunks; i++ { 281 _, err := io.ReadFull(rc, chunk) 282 if err != nil { 283 t.Fatal("read:", err) 284 } 285 } 286 gotEnd, err := ioutil.ReadAll(rc) 287 if err != nil { 288 t.Fatal("read end:", err) 289 } 290 if !bytes.Equal(gotEnd, end) { 291 t.Errorf("End of zip64 archive %q, want %q", gotEnd, end) 292 } 293 err = rc.Close() 294 if err != nil { 295 t.Fatal("closing:", err) 296 } 297 if size == 1<<32 { 298 if got, want := f0.UncompressedSize, uint32(uint32max); got != want { 299 t.Errorf("UncompressedSize %d, want %d", got, want) 300 } 301 } 302 303 if got, want := f0.UncompressedSize64, uint64(size)+uint64(len(end)); got != want { 304 t.Errorf("UncompressedSize64 %d, want %d", got, want) 305 } 306 307 return buf 308 } 309 310 // Issue 9857 311 func testZip64DirectoryRecordLength(buf *rleBuffer, t *testing.T) { 312 d := make([]byte, 1024) 313 if _, err := buf.ReadAt(d, buf.Size()-int64(len(d))); err != nil { 314 t.Fatal("read:", err) 315 } 316 317 sigOff := findSignatureInBlock(d) 318 dirOff, err := findDirectory64End(buf, buf.Size()-int64(len(d))+int64(sigOff)) 319 if err != nil { 320 t.Fatal("findDirectory64End:", err) 321 } 322 323 d = make([]byte, directory64EndLen) 324 if _, err := buf.ReadAt(d, dirOff); err != nil { 325 t.Fatal("read:", err) 326 } 327 328 b := readBuf(d) 329 if sig := b.uint32(); sig != directory64EndSignature { 330 t.Fatalf("Expected directory64EndSignature (%d), got %d", directory64EndSignature, sig) 331 } 332 333 size := b.uint64() 334 if size != directory64EndLen-12 { 335 t.Fatalf("Expected length of %d, got %d", directory64EndLen-12, size) 336 } 337 } 338 339 func testInvalidHeader(h *FileHeader, t *testing.T) { 340 var buf bytes.Buffer 341 z := NewWriter(&buf) 342 343 f, err := z.CreateHeader(h) 344 if err != nil { 345 t.Fatalf("error creating header: %v", err) 346 } 347 if _, err := f.Write([]byte("hi")); err != nil { 348 t.Fatalf("error writing content: %v", err) 349 } 350 if err := z.Close(); err != nil { 351 t.Fatalf("error closing zip writer: %v", err) 352 } 353 354 b := buf.Bytes() 355 if _, err = NewReader(bytes.NewReader(b), int64(len(b))); err != ErrFormat { 356 t.Fatalf("got %v, expected ErrFormat", err) 357 } 358 } 359 360 func testValidHeader(h *FileHeader, t *testing.T) { 361 var buf bytes.Buffer 362 z := NewWriter(&buf) 363 364 f, err := z.CreateHeader(h) 365 if err != nil { 366 t.Fatalf("error creating header: %v", err) 367 } 368 if _, err := f.Write([]byte("hi")); err != nil { 369 t.Fatalf("error writing content: %v", err) 370 } 371 if err := z.Close(); err != nil { 372 t.Fatalf("error closing zip writer: %v", err) 373 } 374 375 b := buf.Bytes() 376 if _, err = NewReader(bytes.NewReader(b), int64(len(b))); err != nil { 377 t.Fatalf("got %v, expected nil", err) 378 } 379 } 380 381 // Issue 4302. 382 func TestHeaderInvalidTagAndSize(t *testing.T) { 383 const timeFormat = "20060102T150405.000.txt" 384 385 ts := time.Now() 386 filename := ts.Format(timeFormat) 387 388 h := FileHeader{ 389 Name: filename, 390 Method: Deflate, 391 Extra: []byte(ts.Format(time.RFC3339Nano)), // missing tag and len 392 } 393 h.SetModTime(ts) 394 395 testInvalidHeader(&h, t) 396 } 397 398 func TestHeaderTooShort(t *testing.T) { 399 h := FileHeader{ 400 Name: "foo.txt", 401 Method: Deflate, 402 Extra: []byte{zip64ExtraId}, // missing size 403 } 404 testInvalidHeader(&h, t) 405 } 406 407 // Issue 4393. It is valid to have an extra data header 408 // which contains no body. 409 func TestZeroLengthHeader(t *testing.T) { 410 h := FileHeader{ 411 Name: "extadata.txt", 412 Method: Deflate, 413 Extra: []byte{ 414 85, 84, 5, 0, 3, 154, 144, 195, 77, // tag 21589 size 5 415 85, 120, 0, 0, // tag 30805 size 0 416 }, 417 } 418 testValidHeader(&h, t) 419 } 420 421 // Just benchmarking how fast the Zip64 test above is. Not related to 422 // our zip performance, since the test above disabled CRC32 and flate. 423 func BenchmarkZip64Test(b *testing.B) { 424 for i := 0; i < b.N; i++ { 425 testZip64(b, 1<<26) 426 } 427 } 428