blob: 79cc0b4b7b42839d6f1fa7ca364766547ad2ba0d [file] [log] [blame]
Nan Zhang674dd932018-01-26 18:30:36 -08001// Copyright 2018 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package zip
16
17import (
Colin Cross05518bc2018-09-27 15:06:19 -070018 "bytes"
19 "hash/crc32"
20 "io"
21 "os"
Nan Zhang674dd932018-01-26 18:30:36 -080022 "reflect"
Colin Cross05518bc2018-09-27 15:06:19 -070023 "syscall"
Nan Zhang674dd932018-01-26 18:30:36 -080024 "testing"
Colin Cross05518bc2018-09-27 15:06:19 -070025
26 "android/soong/third_party/zip"
27
28 "github.com/google/blueprint/pathtools"
Nan Zhang674dd932018-01-26 18:30:36 -080029)
30
Colin Cross05518bc2018-09-27 15:06:19 -070031var (
32 fileA = []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
33 fileB = []byte("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB")
34 fileC = []byte("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC")
35 fileEmpty = []byte("")
36 fileManifest = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\n\n")
37
38 fileCustomManifest = []byte("Custom manifest: true\n")
39 customManifestAfter = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\nCustom manifest: true\n\n")
40)
41
42var mockFs = pathtools.MockFs(map[string][]byte{
Colin Cross9cb51db2019-06-17 14:12:41 -070043 "a/a/a": fileA,
44 "a/a/b": fileB,
45 "a/a/c -> ../../c": nil,
46 "dangling -> missing": nil,
47 "a/a/d -> b": nil,
48 "c": fileC,
Colin Crosscaf4d4c2021-02-03 15:15:14 -080049 "l_nl": []byte("a/a/a\na/a/b\nc\n\\[\n"),
50 "l_sp": []byte("a/a/a a/a/b c \\["),
Colin Cross9cb51db2019-06-17 14:12:41 -070051 "l2": []byte("missing\n"),
Colin Crosscaf4d4c2021-02-03 15:15:14 -080052 "rsp": []byte("'a/a/a'\na/a/b\n'@'\n'foo'\\''bar'\n'['"),
Colin Cross053fca12020-08-19 13:51:47 -070053 "@ -> c": nil,
54 "foo'bar -> c": nil,
Colin Cross9cb51db2019-06-17 14:12:41 -070055 "manifest.txt": fileCustomManifest,
Colin Crosscaf4d4c2021-02-03 15:15:14 -080056 "[": fileEmpty,
Colin Cross05518bc2018-09-27 15:06:19 -070057})
58
59func fh(name string, contents []byte, method uint16) zip.FileHeader {
60 return zip.FileHeader{
61 Name: name,
62 Method: method,
63 CRC32: crc32.ChecksumIEEE(contents),
64 UncompressedSize64: uint64(len(contents)),
Chris Grossfa5b4e92021-06-02 12:56:08 -070065 ExternalAttrs: (syscall.S_IFREG | 0644) << 16,
Colin Cross05518bc2018-09-27 15:06:19 -070066 }
67}
68
69func fhManifest(contents []byte) zip.FileHeader {
70 return zip.FileHeader{
71 Name: "META-INF/MANIFEST.MF",
72 Method: zip.Store,
73 CRC32: crc32.ChecksumIEEE(contents),
74 UncompressedSize64: uint64(len(contents)),
Chris Grossfa5b4e92021-06-02 12:56:08 -070075 ExternalAttrs: (syscall.S_IFREG | 0644) << 16,
Colin Cross05518bc2018-09-27 15:06:19 -070076 }
77}
78
79func fhLink(name string, to string) zip.FileHeader {
80 return zip.FileHeader{
81 Name: name,
82 Method: zip.Store,
83 CRC32: crc32.ChecksumIEEE([]byte(to)),
84 UncompressedSize64: uint64(len(to)),
85 ExternalAttrs: (syscall.S_IFLNK | 0777) << 16,
86 }
87}
88
89func fhDir(name string) zip.FileHeader {
90 return zip.FileHeader{
91 Name: name,
92 Method: zip.Store,
93 CRC32: crc32.ChecksumIEEE(nil),
94 UncompressedSize64: 0,
Chris Grossfa5b4e92021-06-02 12:56:08 -070095 ExternalAttrs: (syscall.S_IFDIR|0755)<<16 | 0x10,
Colin Cross05518bc2018-09-27 15:06:19 -070096 }
97}
98
99func fileArgsBuilder() *FileArgsBuilder {
100 return &FileArgsBuilder{
101 fs: mockFs,
102 }
103}
104
105func TestZip(t *testing.T) {
106 testCases := []struct {
Colin Cross4be8f9e2018-09-28 15:16:48 -0700107 name string
108 args *FileArgsBuilder
109 compressionLevel int
110 emulateJar bool
111 nonDeflatedFiles map[string]bool
112 dirEntries bool
113 manifest string
114 storeSymlinks bool
115 ignoreMissingFiles bool
Colin Cross05518bc2018-09-27 15:06:19 -0700116
117 files []zip.FileHeader
118 err error
119 }{
120 {
121 name: "empty args",
122 args: fileArgsBuilder(),
123
124 files: []zip.FileHeader{},
125 },
126 {
127 name: "files",
128 args: fileArgsBuilder().
129 File("a/a/a").
130 File("a/a/b").
Colin Crosscaf4d4c2021-02-03 15:15:14 -0800131 File("c").
132 File(`\[`),
Colin Cross05518bc2018-09-27 15:06:19 -0700133 compressionLevel: 9,
134
135 files: []zip.FileHeader{
136 fh("a/a/a", fileA, zip.Deflate),
137 fh("a/a/b", fileB, zip.Deflate),
138 fh("c", fileC, zip.Deflate),
Colin Crosscaf4d4c2021-02-03 15:15:14 -0800139 fh("[", fileEmpty, zip.Store),
Colin Cross05518bc2018-09-27 15:06:19 -0700140 },
141 },
142 {
Colin Cross1d98ee22018-09-18 17:05:15 -0700143 name: "files glob",
144 args: fileArgsBuilder().
145 SourcePrefixToStrip("a").
146 File("a/**/*"),
147 compressionLevel: 9,
Colin Cross09f11052018-09-21 15:12:39 -0700148 storeSymlinks: true,
Colin Cross1d98ee22018-09-18 17:05:15 -0700149
150 files: []zip.FileHeader{
151 fh("a/a", fileA, zip.Deflate),
152 fh("a/b", fileB, zip.Deflate),
153 fhLink("a/c", "../../c"),
154 fhLink("a/d", "b"),
155 },
156 },
157 {
158 name: "dir",
159 args: fileArgsBuilder().
160 SourcePrefixToStrip("a").
161 Dir("a"),
162 compressionLevel: 9,
Colin Cross09f11052018-09-21 15:12:39 -0700163 storeSymlinks: true,
Colin Cross1d98ee22018-09-18 17:05:15 -0700164
165 files: []zip.FileHeader{
166 fh("a/a", fileA, zip.Deflate),
167 fh("a/b", fileB, zip.Deflate),
168 fhLink("a/c", "../../c"),
169 fhLink("a/d", "b"),
170 },
171 },
172 {
Colin Cross05518bc2018-09-27 15:06:19 -0700173 name: "stored files",
174 args: fileArgsBuilder().
175 File("a/a/a").
176 File("a/a/b").
177 File("c"),
178 compressionLevel: 0,
179
180 files: []zip.FileHeader{
181 fh("a/a/a", fileA, zip.Store),
182 fh("a/a/b", fileB, zip.Store),
183 fh("c", fileC, zip.Store),
184 },
185 },
186 {
187 name: "symlinks in zip",
188 args: fileArgsBuilder().
189 File("a/a/a").
190 File("a/a/b").
191 File("a/a/c").
192 File("a/a/d"),
193 compressionLevel: 9,
Colin Cross09f11052018-09-21 15:12:39 -0700194 storeSymlinks: true,
Colin Cross05518bc2018-09-27 15:06:19 -0700195
196 files: []zip.FileHeader{
197 fh("a/a/a", fileA, zip.Deflate),
198 fh("a/a/b", fileB, zip.Deflate),
199 fhLink("a/a/c", "../../c"),
200 fhLink("a/a/d", "b"),
201 },
202 },
203 {
Colin Cross09f11052018-09-21 15:12:39 -0700204 name: "follow symlinks",
205 args: fileArgsBuilder().
206 File("a/a/a").
207 File("a/a/b").
208 File("a/a/c").
209 File("a/a/d"),
210 compressionLevel: 9,
211 storeSymlinks: false,
212
213 files: []zip.FileHeader{
214 fh("a/a/a", fileA, zip.Deflate),
215 fh("a/a/b", fileB, zip.Deflate),
216 fh("a/a/c", fileC, zip.Deflate),
217 fh("a/a/d", fileB, zip.Deflate),
218 },
219 },
220 {
Colin Cross9cb51db2019-06-17 14:12:41 -0700221 name: "dangling symlinks",
222 args: fileArgsBuilder().
223 File("dangling"),
224 compressionLevel: 9,
225 storeSymlinks: true,
226
227 files: []zip.FileHeader{
228 fhLink("dangling", "missing"),
229 },
230 },
231 {
Colin Cross05518bc2018-09-27 15:06:19 -0700232 name: "list",
233 args: fileArgsBuilder().
Jiyong Park04bbf982019-11-04 13:18:41 +0900234 List("l_nl"),
235 compressionLevel: 9,
236
237 files: []zip.FileHeader{
238 fh("a/a/a", fileA, zip.Deflate),
239 fh("a/a/b", fileB, zip.Deflate),
240 fh("c", fileC, zip.Deflate),
Colin Crosscaf4d4c2021-02-03 15:15:14 -0800241 fh("[", fileEmpty, zip.Store),
Jiyong Park04bbf982019-11-04 13:18:41 +0900242 },
243 },
244 {
245 name: "list",
246 args: fileArgsBuilder().
247 List("l_sp"),
Colin Cross05518bc2018-09-27 15:06:19 -0700248 compressionLevel: 9,
249
250 files: []zip.FileHeader{
251 fh("a/a/a", fileA, zip.Deflate),
252 fh("a/a/b", fileB, zip.Deflate),
253 fh("c", fileC, zip.Deflate),
Colin Crosscaf4d4c2021-02-03 15:15:14 -0800254 fh("[", fileEmpty, zip.Store),
Colin Cross05518bc2018-09-27 15:06:19 -0700255 },
256 },
257 {
Colin Cross053fca12020-08-19 13:51:47 -0700258 name: "rsp",
259 args: fileArgsBuilder().
260 RspFile("rsp"),
261 compressionLevel: 9,
262
263 files: []zip.FileHeader{
264 fh("a/a/a", fileA, zip.Deflate),
265 fh("a/a/b", fileB, zip.Deflate),
266 fh("@", fileC, zip.Deflate),
267 fh("foo'bar", fileC, zip.Deflate),
Colin Crosscaf4d4c2021-02-03 15:15:14 -0800268 fh("[", fileEmpty, zip.Store),
Colin Cross053fca12020-08-19 13:51:47 -0700269 },
270 },
271 {
Colin Cross05518bc2018-09-27 15:06:19 -0700272 name: "prefix in zip",
273 args: fileArgsBuilder().
274 PathPrefixInZip("foo").
275 File("a/a/a").
276 File("a/a/b").
277 File("c"),
278 compressionLevel: 9,
279
280 files: []zip.FileHeader{
281 fh("foo/a/a/a", fileA, zip.Deflate),
282 fh("foo/a/a/b", fileB, zip.Deflate),
283 fh("foo/c", fileC, zip.Deflate),
284 },
285 },
286 {
287 name: "relative root",
288 args: fileArgsBuilder().
289 SourcePrefixToStrip("a").
290 File("a/a/a").
291 File("a/a/b"),
292 compressionLevel: 9,
293
294 files: []zip.FileHeader{
295 fh("a/a", fileA, zip.Deflate),
296 fh("a/b", fileB, zip.Deflate),
297 },
298 },
299 {
300 name: "multiple relative root",
301 args: fileArgsBuilder().
302 SourcePrefixToStrip("a").
303 File("a/a/a").
304 SourcePrefixToStrip("a/a").
305 File("a/a/b"),
306 compressionLevel: 9,
307
308 files: []zip.FileHeader{
309 fh("a/a", fileA, zip.Deflate),
310 fh("b", fileB, zip.Deflate),
311 },
312 },
313 {
314 name: "emulate jar",
315 args: fileArgsBuilder().
316 File("a/a/a").
317 File("a/a/b"),
318 compressionLevel: 9,
319 emulateJar: true,
320
321 files: []zip.FileHeader{
322 fhDir("META-INF/"),
323 fhManifest(fileManifest),
324 fhDir("a/"),
325 fhDir("a/a/"),
326 fh("a/a/a", fileA, zip.Deflate),
327 fh("a/a/b", fileB, zip.Deflate),
328 },
329 },
330 {
331 name: "emulate jar with manifest",
332 args: fileArgsBuilder().
333 File("a/a/a").
334 File("a/a/b"),
335 compressionLevel: 9,
336 emulateJar: true,
337 manifest: "manifest.txt",
338
339 files: []zip.FileHeader{
340 fhDir("META-INF/"),
341 fhManifest(customManifestAfter),
342 fhDir("a/"),
343 fhDir("a/a/"),
344 fh("a/a/a", fileA, zip.Deflate),
345 fh("a/a/b", fileB, zip.Deflate),
346 },
347 },
348 {
349 name: "dir entries",
350 args: fileArgsBuilder().
351 File("a/a/a").
352 File("a/a/b"),
353 compressionLevel: 9,
354 dirEntries: true,
355
356 files: []zip.FileHeader{
357 fhDir("a/"),
358 fhDir("a/a/"),
359 fh("a/a/a", fileA, zip.Deflate),
360 fh("a/a/b", fileB, zip.Deflate),
361 },
362 },
363 {
364 name: "junk paths",
365 args: fileArgsBuilder().
366 JunkPaths(true).
367 File("a/a/a").
368 File("a/a/b"),
369 compressionLevel: 9,
370
371 files: []zip.FileHeader{
372 fh("a", fileA, zip.Deflate),
373 fh("b", fileB, zip.Deflate),
374 },
375 },
376 {
377 name: "non deflated files",
378 args: fileArgsBuilder().
379 File("a/a/a").
380 File("a/a/b"),
381 compressionLevel: 9,
382 nonDeflatedFiles: map[string]bool{"a/a/a": true},
383
384 files: []zip.FileHeader{
385 fh("a/a/a", fileA, zip.Store),
386 fh("a/a/b", fileB, zip.Deflate),
387 },
388 },
Colin Cross4be8f9e2018-09-28 15:16:48 -0700389 {
390 name: "ignore missing files",
391 args: fileArgsBuilder().
392 File("a/a/a").
393 File("a/a/b").
394 File("missing"),
395 compressionLevel: 9,
396 ignoreMissingFiles: true,
397
398 files: []zip.FileHeader{
399 fh("a/a/a", fileA, zip.Deflate),
400 fh("a/a/b", fileB, zip.Deflate),
401 },
402 },
Colin Cross05518bc2018-09-27 15:06:19 -0700403
404 // errors
405 {
406 name: "error missing file",
407 args: fileArgsBuilder().
408 File("missing"),
409 err: os.ErrNotExist,
410 },
411 {
Colin Cross1d98ee22018-09-18 17:05:15 -0700412 name: "error missing dir",
413 args: fileArgsBuilder().
414 Dir("missing"),
415 err: os.ErrNotExist,
416 },
417 {
Colin Cross05518bc2018-09-27 15:06:19 -0700418 name: "error missing file in list",
419 args: fileArgsBuilder().
420 List("l2"),
421 err: os.ErrNotExist,
422 },
Colin Cross1d98ee22018-09-18 17:05:15 -0700423 {
424 name: "error incorrect relative root",
425 args: fileArgsBuilder().
426 SourcePrefixToStrip("b").
427 File("a/a/a"),
428 err: IncorrectRelativeRootError{},
429 },
Colin Cross05518bc2018-09-27 15:06:19 -0700430 }
431
432 for _, test := range testCases {
433 t.Run(test.name, func(t *testing.T) {
434 if test.args.Error() != nil {
435 t.Fatal(test.args.Error())
436 }
437
438 args := ZipArgs{}
439 args.FileArgs = test.args.FileArgs()
440 args.CompressionLevel = test.compressionLevel
441 args.EmulateJar = test.emulateJar
442 args.AddDirectoryEntriesToZip = test.dirEntries
443 args.NonDeflatedFiles = test.nonDeflatedFiles
444 args.ManifestSourcePath = test.manifest
Colin Cross09f11052018-09-21 15:12:39 -0700445 args.StoreSymlinks = test.storeSymlinks
Colin Cross4be8f9e2018-09-28 15:16:48 -0700446 args.IgnoreMissingFiles = test.ignoreMissingFiles
Colin Cross05518bc2018-09-27 15:06:19 -0700447 args.Filesystem = mockFs
Colin Cross4be8f9e2018-09-28 15:16:48 -0700448 args.Stderr = &bytes.Buffer{}
Colin Cross05518bc2018-09-27 15:06:19 -0700449
450 buf := &bytes.Buffer{}
Sasha Smundak8eedba62020-11-16 19:00:27 -0800451 err := zipTo(args, buf)
Colin Cross05518bc2018-09-27 15:06:19 -0700452
453 if (err != nil) != (test.err != nil) {
454 t.Fatalf("want error %v, got %v", test.err, err)
455 } else if test.err != nil {
456 if os.IsNotExist(test.err) {
457 if !os.IsNotExist(test.err) {
458 t.Fatalf("want error %v, got %v", test.err, err)
459 }
Colin Cross1d98ee22018-09-18 17:05:15 -0700460 } else if _, wantRelativeRootErr := test.err.(IncorrectRelativeRootError); wantRelativeRootErr {
461 if _, gotRelativeRootErr := err.(IncorrectRelativeRootError); !gotRelativeRootErr {
462 t.Fatalf("want error %v, got %v", test.err, err)
463 }
Colin Cross05518bc2018-09-27 15:06:19 -0700464 } else {
465 t.Fatalf("want error %v, got %v", test.err, err)
466 }
467 return
468 }
469
470 br := bytes.NewReader(buf.Bytes())
471 zr, err := zip.NewReader(br, int64(br.Len()))
472 if err != nil {
473 t.Fatal(err)
474 }
475
476 var files []zip.FileHeader
477 for _, f := range zr.File {
478 r, err := f.Open()
479 if err != nil {
480 t.Fatalf("error when opening %s: %s", f.Name, err)
481 }
482
483 crc := crc32.NewIEEE()
484 len, err := io.Copy(crc, r)
485 r.Close()
486 if err != nil {
487 t.Fatalf("error when reading %s: %s", f.Name, err)
488 }
489
490 if uint64(len) != f.UncompressedSize64 {
491 t.Errorf("incorrect length for %s, want %d got %d", f.Name, f.UncompressedSize64, len)
492 }
493
494 if crc.Sum32() != f.CRC32 {
495 t.Errorf("incorrect crc for %s, want %x got %x", f.Name, f.CRC32, crc)
496 }
497
498 files = append(files, f.FileHeader)
499 }
500
501 if len(files) != len(test.files) {
502 t.Fatalf("want %d files, got %d", len(test.files), len(files))
503 }
504
505 for i := range files {
506 want := test.files[i]
507 got := files[i]
508
509 if want.Name != got.Name {
510 t.Errorf("incorrect file %d want %q got %q", i, want.Name, got.Name)
511 continue
512 }
513
514 if want.UncompressedSize64 != got.UncompressedSize64 {
515 t.Errorf("incorrect file %s length want %v got %v", want.Name,
516 want.UncompressedSize64, got.UncompressedSize64)
517 }
518
519 if want.ExternalAttrs != got.ExternalAttrs {
520 t.Errorf("incorrect file %s attrs want %x got %x", want.Name,
521 want.ExternalAttrs, got.ExternalAttrs)
522 }
523
524 if want.CRC32 != got.CRC32 {
525 t.Errorf("incorrect file %s crc want %v got %v", want.Name,
526 want.CRC32, got.CRC32)
527 }
528
529 if want.Method != got.Method {
530 t.Errorf("incorrect file %s method want %v got %v", want.Name,
531 want.Method, got.Method)
532 }
533 }
534 })
535 }
536}
537
Colin Cross9cb51db2019-06-17 14:12:41 -0700538func TestSrcJar(t *testing.T) {
539 mockFs := pathtools.MockFs(map[string][]byte{
540 "wrong_package.java": []byte("package foo;"),
541 "foo/correct_package.java": []byte("package foo;"),
542 "src/no_package.java": nil,
543 "src2/parse_error.java": []byte("error"),
544 })
545
546 want := []string{
547 "foo/",
548 "foo/wrong_package.java",
549 "foo/correct_package.java",
550 "no_package.java",
551 "src2/",
552 "src2/parse_error.java",
553 }
554
555 args := ZipArgs{}
556 args.FileArgs = NewFileArgsBuilder().File("**/*.java").FileArgs()
557
558 args.SrcJar = true
559 args.AddDirectoryEntriesToZip = true
560 args.Filesystem = mockFs
561 args.Stderr = &bytes.Buffer{}
562
563 buf := &bytes.Buffer{}
Sasha Smundak8eedba62020-11-16 19:00:27 -0800564 err := zipTo(args, buf)
Colin Cross9cb51db2019-06-17 14:12:41 -0700565 if err != nil {
566 t.Fatalf("got error %v", err)
567 }
568
569 br := bytes.NewReader(buf.Bytes())
570 zr, err := zip.NewReader(br, int64(br.Len()))
571 if err != nil {
572 t.Fatal(err)
573 }
574
575 var got []string
576 for _, f := range zr.File {
577 r, err := f.Open()
578 if err != nil {
579 t.Fatalf("error when opening %s: %s", f.Name, err)
580 }
581
582 crc := crc32.NewIEEE()
583 len, err := io.Copy(crc, r)
584 r.Close()
585 if err != nil {
586 t.Fatalf("error when reading %s: %s", f.Name, err)
587 }
588
589 if uint64(len) != f.UncompressedSize64 {
590 t.Errorf("incorrect length for %s, want %d got %d", f.Name, f.UncompressedSize64, len)
591 }
592
593 if crc.Sum32() != f.CRC32 {
594 t.Errorf("incorrect crc for %s, want %x got %x", f.Name, f.CRC32, crc)
595 }
596
597 got = append(got, f.Name)
598 }
599
600 if !reflect.DeepEqual(want, got) {
601 t.Errorf("want files %q, got %q", want, got)
602 }
603}