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