Source file
src/archive/tar/writer_test.go
1
2
3
4
5 package tar
6
7 import (
8 "bytes"
9 "encoding/hex"
10 "errors"
11 "io"
12 "io/fs"
13 "os"
14 "path"
15 "reflect"
16 "slices"
17 "strings"
18 "testing"
19 "testing/fstest"
20 "testing/iotest"
21 "time"
22 )
23
24 func bytediff(a, b []byte) string {
25 const (
26 uniqueA = "- "
27 uniqueB = "+ "
28 identity = " "
29 )
30 var ss []string
31 sa := strings.Split(strings.TrimSpace(hex.Dump(a)), "\n")
32 sb := strings.Split(strings.TrimSpace(hex.Dump(b)), "\n")
33 for len(sa) > 0 && len(sb) > 0 {
34 if sa[0] == sb[0] {
35 ss = append(ss, identity+sa[0])
36 } else {
37 ss = append(ss, uniqueA+sa[0])
38 ss = append(ss, uniqueB+sb[0])
39 }
40 sa, sb = sa[1:], sb[1:]
41 }
42 for len(sa) > 0 {
43 ss = append(ss, uniqueA+sa[0])
44 sa = sa[1:]
45 }
46 for len(sb) > 0 {
47 ss = append(ss, uniqueB+sb[0])
48 sb = sb[1:]
49 }
50 return strings.Join(ss, "\n")
51 }
52
53 func TestWriter(t *testing.T) {
54 type (
55 testHeader struct {
56 hdr Header
57 wantErr error
58 }
59 testWrite struct {
60 str string
61 wantCnt int
62 wantErr error
63 }
64 testReadFrom struct {
65 ops fileOps
66 wantCnt int64
67 wantErr error
68 }
69 testClose struct {
70 wantErr error
71 }
72 testFnc any
73 )
74
75 vectors := []struct {
76 file string
77 tests []testFnc
78 }{{
79
80
81
82
83 file: "testdata/writer.tar",
84 tests: []testFnc{
85 testHeader{Header{
86 Typeflag: TypeReg,
87 Name: "small.txt",
88 Size: 5,
89 Mode: 0640,
90 Uid: 73025,
91 Gid: 5000,
92 Uname: "dsymonds",
93 Gname: "eng",
94 ModTime: time.Unix(1246508266, 0),
95 }, nil},
96 testWrite{"Kilts", 5, nil},
97
98 testHeader{Header{
99 Typeflag: TypeReg,
100 Name: "small2.txt",
101 Size: 11,
102 Mode: 0640,
103 Uid: 73025,
104 Uname: "dsymonds",
105 Gname: "eng",
106 Gid: 5000,
107 ModTime: time.Unix(1245217492, 0),
108 }, nil},
109 testWrite{"Google.com\n", 11, nil},
110
111 testHeader{Header{
112 Typeflag: TypeSymlink,
113 Name: "link.txt",
114 Linkname: "small.txt",
115 Mode: 0777,
116 Uid: 1000,
117 Gid: 1000,
118 Uname: "strings",
119 Gname: "strings",
120 ModTime: time.Unix(1314603082, 0),
121 }, nil},
122 testWrite{"", 0, nil},
123
124 testClose{nil},
125 },
126 }, {
127
128
129
130 file: "testdata/writer-big.tar",
131 tests: []testFnc{
132 testHeader{Header{
133 Typeflag: TypeReg,
134 Name: "tmp/16gig.txt",
135 Size: 16 << 30,
136 Mode: 0640,
137 Uid: 73025,
138 Gid: 5000,
139 Uname: "dsymonds",
140 Gname: "eng",
141 ModTime: time.Unix(1254699560, 0),
142 Format: FormatGNU,
143 }, nil},
144 },
145 }, {
146
147
148
149
150
151
152
153 file: "testdata/writer-big-long.tar",
154 tests: []testFnc{
155 testHeader{Header{
156 Typeflag: TypeReg,
157 Name: strings.Repeat("longname/", 15) + "16gig.txt",
158 Size: 16 << 30,
159 Mode: 0644,
160 Uid: 1000,
161 Gid: 1000,
162 Uname: "guillaume",
163 Gname: "guillaume",
164 ModTime: time.Unix(1399583047, 0),
165 }, nil},
166 },
167 }, {
168
169
170 file: "testdata/ustar.tar",
171 tests: []testFnc{
172 testHeader{Header{
173 Typeflag: TypeReg,
174 Name: strings.Repeat("longname/", 15) + "file.txt",
175 Size: 6,
176 Mode: 0644,
177 Uid: 501,
178 Gid: 20,
179 Uname: "shane",
180 Gname: "staff",
181 ModTime: time.Unix(1360135598, 0),
182 }, nil},
183 testWrite{"hello\n", 6, nil},
184 testClose{nil},
185 },
186 }, {
187
188
189
190
191 file: "testdata/hardlink.tar",
192 tests: []testFnc{
193 testHeader{Header{
194 Typeflag: TypeReg,
195 Name: "file.txt",
196 Size: 15,
197 Mode: 0644,
198 Uid: 1000,
199 Gid: 100,
200 Uname: "vbatts",
201 Gname: "users",
202 ModTime: time.Unix(1425484303, 0),
203 }, nil},
204 testWrite{"Slartibartfast\n", 15, nil},
205
206 testHeader{Header{
207 Typeflag: TypeLink,
208 Name: "hard.txt",
209 Linkname: "file.txt",
210 Mode: 0644,
211 Uid: 1000,
212 Gid: 100,
213 Uname: "vbatts",
214 Gname: "users",
215 ModTime: time.Unix(1425484303, 0),
216 }, nil},
217 testWrite{"", 0, nil},
218
219 testClose{nil},
220 },
221 }, {
222 tests: []testFnc{
223 testHeader{Header{
224 Typeflag: TypeReg,
225 Name: "bad-null.txt",
226 Xattrs: map[string]string{"null\x00null\x00": "fizzbuzz"},
227 }, headerError{}},
228 },
229 }, {
230 tests: []testFnc{
231 testHeader{Header{
232 Typeflag: TypeReg,
233 Name: "null\x00.txt",
234 }, headerError{}},
235 },
236 }, {
237 file: "testdata/pax-records.tar",
238 tests: []testFnc{
239 testHeader{Header{
240 Typeflag: TypeReg,
241 Name: "file",
242 Uname: strings.Repeat("long", 10),
243 PAXRecords: map[string]string{
244 "path": "FILE",
245 "GNU.sparse.map": "0,0",
246 "comment": "Hello, 世界",
247 "GOLANG.pkg": "tar",
248 },
249 }, nil},
250 testClose{nil},
251 },
252 }, {
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283 file: "testdata/pax-global-records.tar",
284 tests: []testFnc{
285 testHeader{Header{
286 Typeflag: TypeXGlobalHeader,
287 PAXRecords: map[string]string{"path": "global1", "mtime": "1500000000.0"},
288 }, nil},
289 testHeader{Header{
290 Typeflag: TypeReg, Name: "file1",
291 }, nil},
292 testHeader{Header{
293 Typeflag: TypeReg,
294 Name: "file2",
295 PAXRecords: map[string]string{"path": "file2"},
296 }, nil},
297 testHeader{Header{
298 Typeflag: TypeXGlobalHeader,
299 PAXRecords: map[string]string{"path": ""},
300 }, nil},
301 testHeader{Header{
302 Typeflag: TypeReg, Name: "file3",
303 }, nil},
304 testHeader{Header{
305 Typeflag: TypeReg,
306 Name: "file4",
307 ModTime: time.Unix(1400000000, 0),
308 PAXRecords: map[string]string{"mtime": "1400000000"},
309 }, nil},
310 testClose{nil},
311 },
312 }, {
313 file: "testdata/gnu-utf8.tar",
314 tests: []testFnc{
315 testHeader{Header{
316 Typeflag: TypeReg,
317 Name: "☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹",
318 Mode: 0644,
319 Uid: 1000, Gid: 1000,
320 Uname: "☺",
321 Gname: "⚹",
322 ModTime: time.Unix(0, 0),
323 Format: FormatGNU,
324 }, nil},
325 testClose{nil},
326 },
327 }, {
328 file: "testdata/gnu-not-utf8.tar",
329 tests: []testFnc{
330 testHeader{Header{
331 Typeflag: TypeReg,
332 Name: "hi\x80\x81\x82\x83bye",
333 Mode: 0644,
334 Uid: 1000,
335 Gid: 1000,
336 Uname: "rawr",
337 Gname: "dsnet",
338 ModTime: time.Unix(0, 0),
339 Format: FormatGNU,
340 }, nil},
341 testClose{nil},
342 },
343
344
345
459 }, {
460 file: "testdata/trailing-slash.tar",
461 tests: []testFnc{
462 testHeader{Header{Name: strings.Repeat("123456789/", 30)}, nil},
463 testClose{nil},
464 },
465 }, {
466
467 file: "testdata/file-and-dir.tar",
468 tests: []testFnc{
469 testHeader{Header{Name: "small.txt", Size: 5}, nil},
470 testWrite{"Kilts", 5, nil},
471 testHeader{Header{Name: "dir/"}, nil},
472 testClose{nil},
473 },
474 }}
475
476 equalError := func(x, y error) bool {
477 _, ok1 := x.(headerError)
478 _, ok2 := y.(headerError)
479 if ok1 || ok2 {
480 return ok1 && ok2
481 }
482 return x == y
483 }
484 for _, v := range vectors {
485 t.Run(path.Base(v.file), func(t *testing.T) {
486 const maxSize = 10 << 10
487 buf := new(bytes.Buffer)
488 tw := NewWriter(iotest.TruncateWriter(buf, maxSize))
489
490 for i, tf := range v.tests {
491 switch tf := tf.(type) {
492 case testHeader:
493 err := tw.WriteHeader(&tf.hdr)
494 if !equalError(err, tf.wantErr) {
495 t.Fatalf("test %d, WriteHeader() = %v, want %v", i, err, tf.wantErr)
496 }
497 case testWrite:
498 got, err := tw.Write([]byte(tf.str))
499 if got != tf.wantCnt || !equalError(err, tf.wantErr) {
500 t.Fatalf("test %d, Write() = (%d, %v), want (%d, %v)", i, got, err, tf.wantCnt, tf.wantErr)
501 }
502 case testReadFrom:
503 f := &testFile{ops: tf.ops}
504 got, err := tw.readFrom(f)
505 if _, ok := err.(testError); ok {
506 t.Errorf("test %d, ReadFrom(): %v", i, err)
507 } else if got != tf.wantCnt || !equalError(err, tf.wantErr) {
508 t.Errorf("test %d, ReadFrom() = (%d, %v), want (%d, %v)", i, got, err, tf.wantCnt, tf.wantErr)
509 }
510 if len(f.ops) > 0 {
511 t.Errorf("test %d, expected %d more operations", i, len(f.ops))
512 }
513 case testClose:
514 err := tw.Close()
515 if !equalError(err, tf.wantErr) {
516 t.Fatalf("test %d, Close() = %v, want %v", i, err, tf.wantErr)
517 }
518 default:
519 t.Fatalf("test %d, unknown test operation: %T", i, tf)
520 }
521 }
522
523 if v.file != "" {
524 want, err := os.ReadFile(v.file)
525 if err != nil {
526 t.Fatalf("ReadFile() = %v, want nil", err)
527 }
528 got := buf.Bytes()
529 if !bytes.Equal(want, got) {
530 t.Fatalf("incorrect result: (-got +want)\n%v", bytediff(got, want))
531 }
532 }
533 })
534 }
535 }
536
537 func TestPax(t *testing.T) {
538
539 fileinfo, err := os.Stat("testdata/small.txt")
540 if err != nil {
541 t.Fatal(err)
542 }
543 hdr, err := FileInfoHeader(fileinfo, "")
544 if err != nil {
545 t.Fatalf("os.Stat: %v", err)
546 }
547
548 longName := strings.Repeat("ab", 100)
549 contents := strings.Repeat(" ", int(hdr.Size))
550 hdr.Name = longName
551 var buf bytes.Buffer
552 writer := NewWriter(&buf)
553 if err := writer.WriteHeader(hdr); err != nil {
554 t.Fatal(err)
555 }
556 if _, err = writer.Write([]byte(contents)); err != nil {
557 t.Fatal(err)
558 }
559 if err := writer.Close(); err != nil {
560 t.Fatal(err)
561 }
562
563 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
564 t.Fatal("Expected at least one PAX header to be written.")
565 }
566
567 reader := NewReader(&buf)
568 hdr, err = reader.Next()
569 if err != nil {
570 t.Fatal(err)
571 }
572 if hdr.Name != longName {
573 t.Fatal("Couldn't recover long file name")
574 }
575 }
576
577 func TestPaxSymlink(t *testing.T) {
578
579 fileinfo, err := os.Stat("testdata/small.txt")
580 if err != nil {
581 t.Fatal(err)
582 }
583 hdr, err := FileInfoHeader(fileinfo, "")
584 if err != nil {
585 t.Fatalf("os.Stat:1 %v", err)
586 }
587 hdr.Typeflag = TypeSymlink
588
589 longLinkname := strings.Repeat("1234567890/1234567890", 10)
590 hdr.Linkname = longLinkname
591
592 hdr.Size = 0
593 var buf bytes.Buffer
594 writer := NewWriter(&buf)
595 if err := writer.WriteHeader(hdr); err != nil {
596 t.Fatal(err)
597 }
598 if err := writer.Close(); err != nil {
599 t.Fatal(err)
600 }
601
602 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
603 t.Fatal("Expected at least one PAX header to be written.")
604 }
605
606 reader := NewReader(&buf)
607 hdr, err = reader.Next()
608 if err != nil {
609 t.Fatal(err)
610 }
611 if hdr.Linkname != longLinkname {
612 t.Fatal("Couldn't recover long link name")
613 }
614 }
615
616 func TestPaxNonAscii(t *testing.T) {
617
618
619 fileinfo, err := os.Stat("testdata/small.txt")
620 if err != nil {
621 t.Fatal(err)
622 }
623
624 hdr, err := FileInfoHeader(fileinfo, "")
625 if err != nil {
626 t.Fatalf("os.Stat:1 %v", err)
627 }
628
629
630 chineseFilename := "文件名"
631 chineseGroupname := "組"
632 chineseUsername := "用戶名"
633
634 hdr.Name = chineseFilename
635 hdr.Gname = chineseGroupname
636 hdr.Uname = chineseUsername
637
638 contents := strings.Repeat(" ", int(hdr.Size))
639
640 var buf bytes.Buffer
641 writer := NewWriter(&buf)
642 if err := writer.WriteHeader(hdr); err != nil {
643 t.Fatal(err)
644 }
645 if _, err = writer.Write([]byte(contents)); err != nil {
646 t.Fatal(err)
647 }
648 if err := writer.Close(); err != nil {
649 t.Fatal(err)
650 }
651
652 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
653 t.Fatal("Expected at least one PAX header to be written.")
654 }
655
656 reader := NewReader(&buf)
657 hdr, err = reader.Next()
658 if err != nil {
659 t.Fatal(err)
660 }
661 if hdr.Name != chineseFilename {
662 t.Fatal("Couldn't recover unicode name")
663 }
664 if hdr.Gname != chineseGroupname {
665 t.Fatal("Couldn't recover unicode group")
666 }
667 if hdr.Uname != chineseUsername {
668 t.Fatal("Couldn't recover unicode user")
669 }
670 }
671
672 func TestPaxXattrs(t *testing.T) {
673 xattrs := map[string]string{
674 "user.key": "value",
675 }
676
677
678 fileinfo, err := os.Stat("testdata/small.txt")
679 if err != nil {
680 t.Fatal(err)
681 }
682 hdr, err := FileInfoHeader(fileinfo, "")
683 if err != nil {
684 t.Fatalf("os.Stat: %v", err)
685 }
686 contents := "Kilts"
687 hdr.Xattrs = xattrs
688 var buf bytes.Buffer
689 writer := NewWriter(&buf)
690 if err := writer.WriteHeader(hdr); err != nil {
691 t.Fatal(err)
692 }
693 if _, err = writer.Write([]byte(contents)); err != nil {
694 t.Fatal(err)
695 }
696 if err := writer.Close(); err != nil {
697 t.Fatal(err)
698 }
699
700 reader := NewReader(&buf)
701 hdr, err = reader.Next()
702 if err != nil {
703 t.Fatal(err)
704 }
705 if !reflect.DeepEqual(hdr.Xattrs, xattrs) {
706 t.Fatalf("xattrs did not survive round trip: got %+v, want %+v",
707 hdr.Xattrs, xattrs)
708 }
709 }
710
711 func TestPaxHeadersSorted(t *testing.T) {
712 fileinfo, err := os.Stat("testdata/small.txt")
713 if err != nil {
714 t.Fatal(err)
715 }
716 hdr, err := FileInfoHeader(fileinfo, "")
717 if err != nil {
718 t.Fatalf("os.Stat: %v", err)
719 }
720 contents := strings.Repeat(" ", int(hdr.Size))
721
722 hdr.Xattrs = map[string]string{
723 "foo": "foo",
724 "bar": "bar",
725 "baz": "baz",
726 "qux": "qux",
727 }
728
729 var buf bytes.Buffer
730 writer := NewWriter(&buf)
731 if err := writer.WriteHeader(hdr); err != nil {
732 t.Fatal(err)
733 }
734 if _, err = writer.Write([]byte(contents)); err != nil {
735 t.Fatal(err)
736 }
737 if err := writer.Close(); err != nil {
738 t.Fatal(err)
739 }
740
741 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
742 t.Fatal("Expected at least one PAX header to be written.")
743 }
744
745
746 indices := []int{
747 bytes.Index(buf.Bytes(), []byte("bar=bar")),
748 bytes.Index(buf.Bytes(), []byte("baz=baz")),
749 bytes.Index(buf.Bytes(), []byte("foo=foo")),
750 bytes.Index(buf.Bytes(), []byte("qux=qux")),
751 }
752 if !slices.IsSorted(indices) {
753 t.Fatal("PAX headers are not sorted")
754 }
755 }
756
757 func TestUSTARLongName(t *testing.T) {
758
759 fileinfo, err := os.Stat("testdata/small.txt")
760 if err != nil {
761 t.Fatal(err)
762 }
763 hdr, err := FileInfoHeader(fileinfo, "")
764 if err != nil {
765 t.Fatalf("os.Stat:1 %v", err)
766 }
767 hdr.Typeflag = TypeDir
768
769
770 longName := "/0000_0000000/00000-000000000/0000_0000000/00000-0000000000000/0000_0000000/00000-0000000-00000000/0000_0000000/00000000/0000_0000000/000/0000_0000000/00000000v00/0000_0000000/000000/0000_0000000/0000000/0000_0000000/00000y-00/0000/0000/00000000/0x000000/"
771 hdr.Name = longName
772
773 hdr.Size = 0
774 var buf bytes.Buffer
775 writer := NewWriter(&buf)
776 if err := writer.WriteHeader(hdr); err != nil {
777 t.Fatal(err)
778 }
779 if err := writer.Close(); err != nil {
780 t.Fatal(err)
781 }
782
783 reader := NewReader(&buf)
784 hdr, err = reader.Next()
785 if err != nil && err != ErrInsecurePath {
786 t.Fatal(err)
787 }
788 if hdr.Name != longName {
789 t.Fatal("Couldn't recover long name")
790 }
791 }
792
793 func TestValidTypeflagWithPAXHeader(t *testing.T) {
794 var buffer bytes.Buffer
795 tw := NewWriter(&buffer)
796
797 fileName := strings.Repeat("ab", 100)
798
799 hdr := &Header{
800 Name: fileName,
801 Size: 4,
802 Typeflag: 0,
803 }
804 if err := tw.WriteHeader(hdr); err != nil {
805 t.Fatalf("Failed to write header: %s", err)
806 }
807 if _, err := tw.Write([]byte("fooo")); err != nil {
808 t.Fatalf("Failed to write the file's data: %s", err)
809 }
810 tw.Close()
811
812 tr := NewReader(&buffer)
813
814 for {
815 header, err := tr.Next()
816 if err == io.EOF {
817 break
818 }
819 if err != nil {
820 t.Fatalf("Failed to read header: %s", err)
821 }
822 if header.Typeflag != TypeReg {
823 t.Fatalf("Typeflag should've been %d, found %d", TypeReg, header.Typeflag)
824 }
825 }
826 }
827
828
829 type failOnceWriter bool
830
831 func (w *failOnceWriter) Write(b []byte) (int, error) {
832 if !*w {
833 return 0, io.ErrShortWrite
834 }
835 *w = true
836 return len(b), nil
837 }
838
839 func TestWriterErrors(t *testing.T) {
840 t.Run("HeaderOnly", func(t *testing.T) {
841 tw := NewWriter(new(bytes.Buffer))
842 hdr := &Header{Name: "dir/", Typeflag: TypeDir}
843 if err := tw.WriteHeader(hdr); err != nil {
844 t.Fatalf("WriteHeader() = %v, want nil", err)
845 }
846 if _, err := tw.Write([]byte{0x00}); err != ErrWriteTooLong {
847 t.Fatalf("Write() = %v, want %v", err, ErrWriteTooLong)
848 }
849 })
850
851 t.Run("NegativeSize", func(t *testing.T) {
852 tw := NewWriter(new(bytes.Buffer))
853 hdr := &Header{Name: "small.txt", Size: -1}
854 if err := tw.WriteHeader(hdr); err == nil {
855 t.Fatalf("WriteHeader() = nil, want non-nil error")
856 }
857 })
858
859 t.Run("BeforeHeader", func(t *testing.T) {
860 tw := NewWriter(new(bytes.Buffer))
861 if _, err := tw.Write([]byte("Kilts")); err != ErrWriteTooLong {
862 t.Fatalf("Write() = %v, want %v", err, ErrWriteTooLong)
863 }
864 })
865
866 t.Run("AfterClose", func(t *testing.T) {
867 tw := NewWriter(new(bytes.Buffer))
868 hdr := &Header{Name: "small.txt"}
869 if err := tw.WriteHeader(hdr); err != nil {
870 t.Fatalf("WriteHeader() = %v, want nil", err)
871 }
872 if err := tw.Close(); err != nil {
873 t.Fatalf("Close() = %v, want nil", err)
874 }
875 if _, err := tw.Write([]byte("Kilts")); err != ErrWriteAfterClose {
876 t.Fatalf("Write() = %v, want %v", err, ErrWriteAfterClose)
877 }
878 if err := tw.Flush(); err != ErrWriteAfterClose {
879 t.Fatalf("Flush() = %v, want %v", err, ErrWriteAfterClose)
880 }
881 if err := tw.Close(); err != nil {
882 t.Fatalf("Close() = %v, want nil", err)
883 }
884 })
885
886 t.Run("PrematureFlush", func(t *testing.T) {
887 tw := NewWriter(new(bytes.Buffer))
888 hdr := &Header{Name: "small.txt", Size: 5}
889 if err := tw.WriteHeader(hdr); err != nil {
890 t.Fatalf("WriteHeader() = %v, want nil", err)
891 }
892 if err := tw.Flush(); err == nil {
893 t.Fatalf("Flush() = %v, want non-nil error", err)
894 }
895 })
896
897 t.Run("PrematureClose", func(t *testing.T) {
898 tw := NewWriter(new(bytes.Buffer))
899 hdr := &Header{Name: "small.txt", Size: 5}
900 if err := tw.WriteHeader(hdr); err != nil {
901 t.Fatalf("WriteHeader() = %v, want nil", err)
902 }
903 if err := tw.Close(); err == nil {
904 t.Fatalf("Close() = %v, want non-nil error", err)
905 }
906 })
907
908 t.Run("Persistence", func(t *testing.T) {
909 tw := NewWriter(new(failOnceWriter))
910 if err := tw.WriteHeader(&Header{}); err != io.ErrShortWrite {
911 t.Fatalf("WriteHeader() = %v, want %v", err, io.ErrShortWrite)
912 }
913 if err := tw.WriteHeader(&Header{Name: "small.txt"}); err == nil {
914 t.Errorf("WriteHeader() = got %v, want non-nil error", err)
915 }
916 if _, err := tw.Write(nil); err == nil {
917 t.Errorf("Write() = %v, want non-nil error", err)
918 }
919 if err := tw.Flush(); err == nil {
920 t.Errorf("Flush() = %v, want non-nil error", err)
921 }
922 if err := tw.Close(); err == nil {
923 t.Errorf("Close() = %v, want non-nil error", err)
924 }
925 })
926 }
927
928 func TestSplitUSTARPath(t *testing.T) {
929 sr := strings.Repeat
930
931 vectors := []struct {
932 input string
933 prefix string
934 suffix string
935 ok bool
936 }{
937 {"", "", "", false},
938 {"abc", "", "", false},
939 {"用戶名", "", "", false},
940 {sr("a", nameSize), "", "", false},
941 {sr("a", nameSize) + "/", "", "", false},
942 {sr("a", nameSize) + "/a", sr("a", nameSize), "a", true},
943 {sr("a", prefixSize) + "/", "", "", false},
944 {sr("a", prefixSize) + "/a", sr("a", prefixSize), "a", true},
945 {sr("a", nameSize+1), "", "", false},
946 {sr("/", nameSize+1), sr("/", nameSize-1), "/", true},
947 {sr("a", prefixSize) + "/" + sr("b", nameSize),
948 sr("a", prefixSize), sr("b", nameSize), true},
949 {sr("a", prefixSize) + "//" + sr("b", nameSize), "", "", false},
950 {sr("a/", nameSize), sr("a/", 77) + "a", sr("a/", 22), true},
951 }
952
953 for _, v := range vectors {
954 prefix, suffix, ok := splitUSTARPath(v.input)
955 if prefix != v.prefix || suffix != v.suffix || ok != v.ok {
956 t.Errorf("splitUSTARPath(%q):\ngot (%q, %q, %v)\nwant (%q, %q, %v)",
957 v.input, prefix, suffix, ok, v.prefix, v.suffix, v.ok)
958 }
959 }
960 }
961
962
963
964
965 func TestIssue12594(t *testing.T) {
966 names := []string{
967 "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/file.txt",
968 "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/file.txt",
969 "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/333/file.txt",
970 "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/file.txt",
971 "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/file.txt",
972 "/home/support/.openoffice.org/3/user/uno_packages/cache/registry/com.sun.star.comp.deployment.executable.PackageRegistryBackend",
973 }
974
975 for i, name := range names {
976 var b bytes.Buffer
977
978 tw := NewWriter(&b)
979 if err := tw.WriteHeader(&Header{
980 Name: name,
981 Uid: 1 << 25,
982 }); err != nil {
983 t.Errorf("test %d, unexpected WriteHeader error: %v", i, err)
984 }
985 if err := tw.Close(); err != nil {
986 t.Errorf("test %d, unexpected Close error: %v", i, err)
987 }
988
989
990 var blk block
991 copy(blk[:], b.Bytes())
992 prefix := string(blk.toUSTAR().prefix())
993 prefix, _, _ = strings.Cut(prefix, "\x00")
994 if blk.getFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) {
995 t.Errorf("test %d, found prefix in GNU format: %s", i, prefix)
996 }
997
998 tr := NewReader(&b)
999 hdr, err := tr.Next()
1000 if err != nil && err != ErrInsecurePath {
1001 t.Errorf("test %d, unexpected Next error: %v", i, err)
1002 }
1003 if hdr.Name != name {
1004 t.Errorf("test %d, hdr.Name = %s, want %s", i, hdr.Name, name)
1005 }
1006 }
1007 }
1008
1009 func TestWriteLongHeader(t *testing.T) {
1010 for _, test := range []struct {
1011 name string
1012 h *Header
1013 }{{
1014 name: "name too long",
1015 h: &Header{Name: strings.Repeat("a", maxSpecialFileSize)},
1016 }, {
1017 name: "linkname too long",
1018 h: &Header{Linkname: strings.Repeat("a", maxSpecialFileSize)},
1019 }, {
1020 name: "uname too long",
1021 h: &Header{Uname: strings.Repeat("a", maxSpecialFileSize)},
1022 }, {
1023 name: "gname too long",
1024 h: &Header{Gname: strings.Repeat("a", maxSpecialFileSize)},
1025 }, {
1026 name: "PAX header too long",
1027 h: &Header{PAXRecords: map[string]string{"GOLANG.x": strings.Repeat("a", maxSpecialFileSize)}},
1028 }} {
1029 w := NewWriter(io.Discard)
1030 if err := w.WriteHeader(test.h); err != ErrFieldTooLong {
1031 t.Errorf("%v: w.WriteHeader() = %v, want ErrFieldTooLong", test.name, err)
1032 }
1033 }
1034 }
1035
1036
1037
1038 type testNonEmptyWriter struct{ io.Writer }
1039
1040 func (w testNonEmptyWriter) Write(b []byte) (int, error) {
1041 if len(b) == 0 {
1042 return 0, errors.New("unexpected empty Write call")
1043 }
1044 return w.Writer.Write(b)
1045 }
1046
1047 func TestFileWriter(t *testing.T) {
1048 type (
1049 testWrite struct {
1050 str string
1051 wantCnt int
1052 wantErr error
1053 }
1054 testReadFrom struct {
1055 ops fileOps
1056 wantCnt int64
1057 wantErr error
1058 }
1059 testRemaining struct {
1060 wantLCnt int64
1061 wantPCnt int64
1062 }
1063 testFnc any
1064 )
1065
1066 type (
1067 makeReg struct {
1068 size int64
1069 wantStr string
1070 }
1071 makeSparse struct {
1072 makeReg makeReg
1073 sph sparseHoles
1074 size int64
1075 }
1076 fileMaker any
1077 )
1078
1079 vectors := []struct {
1080 maker fileMaker
1081 tests []testFnc
1082 }{{
1083 maker: makeReg{0, ""},
1084 tests: []testFnc{
1085 testRemaining{0, 0},
1086 testWrite{"", 0, nil},
1087 testWrite{"a", 0, ErrWriteTooLong},
1088 testReadFrom{fileOps{""}, 0, nil},
1089 testReadFrom{fileOps{"a"}, 0, ErrWriteTooLong},
1090 testRemaining{0, 0},
1091 },
1092 }, {
1093 maker: makeReg{1, "a"},
1094 tests: []testFnc{
1095 testRemaining{1, 1},
1096 testWrite{"", 0, nil},
1097 testWrite{"a", 1, nil},
1098 testWrite{"bcde", 0, ErrWriteTooLong},
1099 testWrite{"", 0, nil},
1100 testReadFrom{fileOps{""}, 0, nil},
1101 testReadFrom{fileOps{"a"}, 0, ErrWriteTooLong},
1102 testRemaining{0, 0},
1103 },
1104 }, {
1105 maker: makeReg{5, "hello"},
1106 tests: []testFnc{
1107 testRemaining{5, 5},
1108 testWrite{"hello", 5, nil},
1109 testRemaining{0, 0},
1110 },
1111 }, {
1112 maker: makeReg{5, "\x00\x00\x00\x00\x00"},
1113 tests: []testFnc{
1114 testRemaining{5, 5},
1115 testReadFrom{fileOps{"\x00\x00\x00\x00\x00"}, 5, nil},
1116 testRemaining{0, 0},
1117 },
1118 }, {
1119 maker: makeReg{5, "\x00\x00\x00\x00\x00"},
1120 tests: []testFnc{
1121 testRemaining{5, 5},
1122 testReadFrom{fileOps{"\x00\x00\x00\x00\x00extra"}, 5, ErrWriteTooLong},
1123 testRemaining{0, 0},
1124 },
1125 }, {
1126 maker: makeReg{5, "abc\x00\x00"},
1127 tests: []testFnc{
1128 testRemaining{5, 5},
1129 testWrite{"abc", 3, nil},
1130 testRemaining{2, 2},
1131 testReadFrom{fileOps{"\x00\x00"}, 2, nil},
1132 testRemaining{0, 0},
1133 },
1134 }, {
1135 maker: makeReg{5, "\x00\x00abc"},
1136 tests: []testFnc{
1137 testRemaining{5, 5},
1138 testWrite{"\x00\x00", 2, nil},
1139 testRemaining{3, 3},
1140 testWrite{"abc", 3, nil},
1141 testReadFrom{fileOps{"z"}, 0, ErrWriteTooLong},
1142 testWrite{"z", 0, ErrWriteTooLong},
1143 testRemaining{0, 0},
1144 },
1145 }, {
1146 maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
1147 tests: []testFnc{
1148 testRemaining{8, 5},
1149 testWrite{"ab\x00\x00\x00cde", 8, nil},
1150 testWrite{"a", 0, ErrWriteTooLong},
1151 testRemaining{0, 0},
1152 },
1153 }, {
1154 maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
1155 tests: []testFnc{
1156 testWrite{"ab\x00\x00\x00cdez", 8, ErrWriteTooLong},
1157 testRemaining{0, 0},
1158 },
1159 }, {
1160 maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
1161 tests: []testFnc{
1162 testWrite{"ab\x00", 3, nil},
1163 testRemaining{5, 3},
1164 testWrite{"\x00\x00cde", 5, nil},
1165 testWrite{"a", 0, ErrWriteTooLong},
1166 testRemaining{0, 0},
1167 },
1168 }, {
1169 maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
1170 tests: []testFnc{
1171 testWrite{"ab", 2, nil},
1172 testRemaining{6, 3},
1173 testReadFrom{fileOps{int64(3), "cde"}, 6, nil},
1174 testRemaining{0, 0},
1175 },
1176 }, {
1177 maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
1178 tests: []testFnc{
1179 testReadFrom{fileOps{"ab", int64(3), "cde"}, 8, nil},
1180 testRemaining{0, 0},
1181 },
1182 }, {
1183 maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
1184 tests: []testFnc{
1185 testReadFrom{fileOps{"ab", int64(3), "cdeX"}, 8, ErrWriteTooLong},
1186 testRemaining{0, 0},
1187 },
1188 }, {
1189 maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8},
1190 tests: []testFnc{
1191 testReadFrom{fileOps{"ab", int64(3), "cd"}, 7, io.ErrUnexpectedEOF},
1192 testRemaining{1, 0},
1193 },
1194 }, {
1195 maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8},
1196 tests: []testFnc{
1197 testReadFrom{fileOps{"ab", int64(3), "cde"}, 7, errMissData},
1198 testRemaining{1, 0},
1199 },
1200 }, {
1201 maker: makeSparse{makeReg{6, "abcde"}, sparseHoles{{2, 3}}, 8},
1202 tests: []testFnc{
1203 testReadFrom{fileOps{"ab", int64(3), "cde"}, 8, errUnrefData},
1204 testRemaining{0, 1},
1205 },
1206 }, {
1207 maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8},
1208 tests: []testFnc{
1209 testWrite{"ab", 2, nil},
1210 testRemaining{6, 2},
1211 testWrite{"\x00\x00\x00", 3, nil},
1212 testRemaining{3, 2},
1213 testWrite{"cde", 2, errMissData},
1214 testRemaining{1, 0},
1215 },
1216 }, {
1217 maker: makeSparse{makeReg{6, "abcde"}, sparseHoles{{2, 3}}, 8},
1218 tests: []testFnc{
1219 testWrite{"ab", 2, nil},
1220 testRemaining{6, 4},
1221 testWrite{"\x00\x00\x00", 3, nil},
1222 testRemaining{3, 4},
1223 testWrite{"cde", 3, errUnrefData},
1224 testRemaining{0, 1},
1225 },
1226 }, {
1227 maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1228 tests: []testFnc{
1229 testRemaining{7, 3},
1230 testWrite{"\x00\x00abc\x00\x00", 7, nil},
1231 testRemaining{0, 0},
1232 },
1233 }, {
1234 maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1235 tests: []testFnc{
1236 testRemaining{7, 3},
1237 testReadFrom{fileOps{int64(2), "abc", int64(1), "\x00"}, 7, nil},
1238 testRemaining{0, 0},
1239 },
1240 }, {
1241 maker: makeSparse{makeReg{3, ""}, sparseHoles{{0, 2}, {5, 2}}, 7},
1242 tests: []testFnc{
1243 testWrite{"abcdefg", 0, errWriteHole},
1244 },
1245 }, {
1246 maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1247 tests: []testFnc{
1248 testWrite{"\x00\x00abcde", 5, errWriteHole},
1249 },
1250 }, {
1251 maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1252 tests: []testFnc{
1253 testWrite{"\x00\x00abc\x00\x00z", 7, ErrWriteTooLong},
1254 testRemaining{0, 0},
1255 },
1256 }, {
1257 maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1258 tests: []testFnc{
1259 testWrite{"\x00\x00", 2, nil},
1260 testRemaining{5, 3},
1261 testWrite{"abc", 3, nil},
1262 testRemaining{2, 0},
1263 testWrite{"\x00\x00", 2, nil},
1264 testRemaining{0, 0},
1265 },
1266 }, {
1267 maker: makeSparse{makeReg{2, "ab"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1268 tests: []testFnc{
1269 testWrite{"\x00\x00", 2, nil},
1270 testWrite{"abc", 2, errMissData},
1271 testWrite{"\x00\x00", 0, errMissData},
1272 },
1273 }, {
1274 maker: makeSparse{makeReg{4, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
1275 tests: []testFnc{
1276 testWrite{"\x00\x00", 2, nil},
1277 testWrite{"abc", 3, nil},
1278 testWrite{"\x00\x00", 2, errUnrefData},
1279 },
1280 }}
1281
1282 for i, v := range vectors {
1283 var wantStr string
1284 bb := new(strings.Builder)
1285 w := testNonEmptyWriter{bb}
1286 var fw fileWriter
1287 switch maker := v.maker.(type) {
1288 case makeReg:
1289 fw = ®FileWriter{w, maker.size}
1290 wantStr = maker.wantStr
1291 case makeSparse:
1292 if !validateSparseEntries(maker.sph, maker.size) {
1293 t.Fatalf("invalid sparse map: %v", maker.sph)
1294 }
1295 spd := invertSparseEntries(maker.sph, maker.size)
1296 fw = ®FileWriter{w, maker.makeReg.size}
1297 fw = &sparseFileWriter{fw, spd, 0}
1298 wantStr = maker.makeReg.wantStr
1299 default:
1300 t.Fatalf("test %d, unknown make operation: %T", i, maker)
1301 }
1302
1303 for j, tf := range v.tests {
1304 switch tf := tf.(type) {
1305 case testWrite:
1306 got, err := fw.Write([]byte(tf.str))
1307 if got != tf.wantCnt || err != tf.wantErr {
1308 t.Errorf("test %d.%d, Write(%s):\ngot (%d, %v)\nwant (%d, %v)", i, j, tf.str, got, err, tf.wantCnt, tf.wantErr)
1309 }
1310 case testReadFrom:
1311 f := &testFile{ops: tf.ops}
1312 got, err := fw.ReadFrom(f)
1313 if _, ok := err.(testError); ok {
1314 t.Errorf("test %d.%d, ReadFrom(): %v", i, j, err)
1315 } else if got != tf.wantCnt || err != tf.wantErr {
1316 t.Errorf("test %d.%d, ReadFrom() = (%d, %v), want (%d, %v)", i, j, got, err, tf.wantCnt, tf.wantErr)
1317 }
1318 if len(f.ops) > 0 {
1319 t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops))
1320 }
1321 case testRemaining:
1322 if got := fw.logicalRemaining(); got != tf.wantLCnt {
1323 t.Errorf("test %d.%d, logicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt)
1324 }
1325 if got := fw.physicalRemaining(); got != tf.wantPCnt {
1326 t.Errorf("test %d.%d, physicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt)
1327 }
1328 default:
1329 t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf)
1330 }
1331 }
1332
1333 if got := bb.String(); got != wantStr {
1334 t.Fatalf("test %d, String() = %q, want %q", i, got, wantStr)
1335 }
1336 }
1337 }
1338
1339 func TestWriterAddFS(t *testing.T) {
1340 fsys := fstest.MapFS{
1341 "file.go": {Data: []byte("hello")},
1342 "subfolder/another.go": {Data: []byte("world")},
1343 }
1344 var buf bytes.Buffer
1345 tw := NewWriter(&buf)
1346 if err := tw.AddFS(fsys); err != nil {
1347 t.Fatal(err)
1348 }
1349
1350
1351 tr := NewReader(&buf)
1352
1353 entries, err := fsys.ReadDir(".")
1354 if err != nil {
1355 t.Fatal(err)
1356 }
1357
1358 var curfname string
1359 for _, entry := range entries {
1360 curfname = entry.Name()
1361 if entry.IsDir() {
1362 curfname += "/"
1363 continue
1364 }
1365 hdr, err := tr.Next()
1366 if err == io.EOF {
1367 break
1368 }
1369 if err != nil {
1370 t.Fatal(err)
1371 }
1372
1373 data, err := io.ReadAll(tr)
1374 if err != nil {
1375 t.Fatal(err)
1376 }
1377
1378 if hdr.Name != curfname {
1379 t.Fatalf("got filename %v, want %v",
1380 curfname, hdr.Name)
1381 }
1382
1383 origdata := fsys[curfname].Data
1384 if string(data) != string(origdata) {
1385 t.Fatalf("got file content %v, want %v",
1386 data, origdata)
1387 }
1388 }
1389 }
1390
1391 func TestWriterAddFSNonRegularFiles(t *testing.T) {
1392 fsys := fstest.MapFS{
1393 "device": {Data: []byte("hello"), Mode: 0755 | fs.ModeDevice},
1394 "symlink": {Data: []byte("world"), Mode: 0755 | fs.ModeSymlink},
1395 }
1396 var buf bytes.Buffer
1397 tw := NewWriter(&buf)
1398 if err := tw.AddFS(fsys); err == nil {
1399 t.Fatal("expected error, got nil")
1400 }
1401 }
1402
View as plain text