Revert "lib/suitar: fix Writer block-boundary handling and directory headers"
diff --git a/lib/suitar/suitar.go b/lib/suitar/suitar.go index 3a6212c..6055806 100644 --- a/lib/suitar/suitar.go +++ b/lib/suitar/suitar.go
@@ -322,8 +322,10 @@ w.err = errBadHeader return w.err } - n := len(h.Name) - copy(w.nameBuf[:], h.Name) + w.header = *h + + n := len(w.header.Name) + copy(w.nameBuf[:], w.header.Name) w.nameBuf[n] = 0 initBlock(&w.block, typeGNULongName, int64(n+1), Mode644, 0) @@ -332,10 +334,6 @@ return w.err } - // The w.Write call below writes this entry's name, not its contents, so - // w.header must not (yet) say TypeDir or Write would reject it. Set - // w.header after that call. - w.header = Header{Typeflag: TypeReg} w.remaining = int64(n + 1) if _, err := w.Write(w.nameBuf[:n+1]); err != nil { @@ -346,7 +344,6 @@ return w.err } - w.header = *h initBlock(&w.block, w.header.Typeflag, w.header.Size, w.header.Mode, w.header.ModTime.Unix()) if _, err := w.w.Write(w.block[:]); err != nil { w.err = err @@ -460,7 +457,6 @@ w.err = err break } - w.bIndex = 0 } if tooMuch && (w.err == nil) {
diff --git a/lib/suitar/suitar_test.go b/lib/suitar/suitar_test.go index 33e2aa1..7fed0f5 100644 --- a/lib/suitar/suitar_test.go +++ b/lib/suitar/suitar_test.go
@@ -13,13 +13,11 @@ import ( "archive/tar" "bytes" - "errors" "fmt" "hash/crc32" "io" "os" "testing" - "time" ) type crcWriter uint32 @@ -186,163 +184,3 @@ func TestReaderDenseIgnore(tt *testing.T) { testReader(tt, false, true) } func TestReaderSparseCheck(tt *testing.T) { testReader(tt, true, false) } func TestReaderSparseIgnore(tt *testing.T) { testReader(tt, true, true) } - -// cappedWriter returns an error once the total number of bytes written would -// exceed limit, so that a Writer which runs away fails this test instead of -// hanging it. -type cappedWriter struct { - buf bytes.Buffer - limit int -} - -func (w *cappedWriter) Write(b []byte) (int, error) { - if w.buf.Len()+len(b) > w.limit { - return 0, errCappedWriter - } - return w.buf.Write(b) -} - -var errCappedWriter = errors.New("suitar_test: capped writer") - -// writeOneFile returns a SUITAR archive holding a single file whose contents -// are size bytes long, written in chunks of chunk bytes (or all at once if -// chunk is non-positive). -func writeOneFile(tt *testing.T, size int, chunk int) []byte { - tt.Helper() - - cw := &cappedWriter{limit: 0x10000} - w := NewWriter(cw) - err := w.WriteHeader(&Header{ - Typeflag: TypeReg, - Name: "file.bin", - Size: int64(size), - Mode: Mode644, - ModTime: time.Unix(0x5E3A5C50, 0), - }) - if err != nil { - tt.Fatalf("size=%d chunk=%d: WriteHeader: %v", size, chunk, err) - } - - pix := make([]byte, size) - for i := range pix { - pix[i] = byte(i) - } - - if chunk <= 0 { - if _, err := w.Write(pix); err != nil { - tt.Fatalf("size=%d chunk=%d: Write: %v", size, chunk, err) - } - } else { - for i := 0; i < size; i += chunk { - j := min(i+chunk, size) - if _, err := w.Write(pix[i:j]); err != nil { - tt.Fatalf("size=%d chunk=%d: Write[%d:%d]: %v", size, chunk, i, j, err) - } - } - } - - if err := w.Close(); err != nil { - tt.Fatalf("size=%d chunk=%d: Close: %v", size, chunk, err) - } - return cw.buf.Bytes() -} - -// TestWriterChunked checks that how the file contents are split up over Write -// calls doesn't matter, as long as the total is Header.Size bytes long. A -// chunk size that isn't a multiple of 512 used to loop forever, writing -// 512-byte blocks non-stop, and also to duplicate one 512-byte block. -func TestWriterChunked(tt *testing.T) { - for _, size := range []int{0, 1, 300, 511, 512, 513, 700, 1000, 1024, 5000} { - want := writeOneFile(tt, size, 0) - - for _, chunk := range []int{1, 100, 300, 511, 512, 700, 1024} { - if got := writeOneFile(tt, size, chunk); !bytes.Equal(got, want) { - tt.Fatalf("size=%d chunk=%d: chunk size changed the archive bytes", size, chunk) - } - } - - r := NewReader(bytes.NewReader(want)) - h, err := r.Next() - if err != nil { - tt.Fatalf("size=%d: Next: %v", size, err) - } else if (h.Typeflag != TypeReg) || (h.Size != int64(size)) { - tt.Fatalf("size=%d: got T:'%c' S:%d", size, h.Typeflag, h.Size) - } - got, err := io.ReadAll(r) - if err != nil { - tt.Fatalf("size=%d: ReadAll: %v", size, err) - } else if len(got) != size { - tt.Fatalf("size=%d: read back %d bytes", size, len(got)) - } else { - for i, c := range got { - if c != byte(i) { - tt.Fatalf("size=%d: contents differ at %d", size, i) - break - } - } - } - if _, err := r.Next(); err != io.EOF { - tt.Fatalf("size=%d: Next after the last entry: %v", size, err) - } - } -} - -// TestWriterDir checks that a directory entry, which has no contents, can be -// written and read back. -func TestWriterDir(tt *testing.T) { - buf := bytes.Buffer{} - w := NewWriter(&buf) - for _, h := range []*Header{ - { - Typeflag: TypeDir, - Name: "a/b", - Mode: Mode755, - ModTime: time.Unix(0x5E3A5C50, 0), - }, - { - Typeflag: TypeReg, - Name: "a/b/c.txt", - Size: 5, - Mode: Mode644, - ModTime: time.Unix(0x5E3A5C50, 0), - }, - } { - if err := w.WriteHeader(h); err != nil { - tt.Fatalf("WriteHeader(%q): %v", h.Name, err) - } - if h.Typeflag == TypeReg { - if _, err := w.Write([]byte("hello")); err != nil { - tt.Fatalf("Write: %v", err) - } - } - } - if err := w.Close(); err != nil { - tt.Fatalf("Close: %v", err) - } - - r := NewReader(bytes.NewReader(buf.Bytes())) - for _, want := range []struct { - typeflag byte - name string - contents string - }{ - {TypeDir, "a/b", ""}, - {TypeReg, "a/b/c.txt", "hello"}, - } { - h, err := r.Next() - if err != nil { - tt.Fatalf("Next: %v", err) - } else if (h.Typeflag != want.typeflag) || (h.Name != want.name) { - tt.Fatalf("got T:'%c' N:%s, want T:'%c' N:%s", - h.Typeflag, h.Name, want.typeflag, want.name) - } - if got, err := io.ReadAll(r); err != nil { - tt.Fatalf("ReadAll: %v", err) - } else if string(got) != want.contents { - tt.Fatalf("N:%s: got %q, want %q", want.name, got, want.contents) - } - } - if _, err := r.Next(); err != io.EOF { - tt.Fatalf("Next after the last entry: %v", err) - } -}