Skip to content

Commit 0081f17

Browse files
songgaogopherbot
authored andcommitted
archive/{zip,tar}: fix Writer.AddFS to include empty directories
This change modifies the `(*Writer).AddFS` implementation in both `archive/zip` and `archive/tar` to always write a directory header. This fixes a bug where any empty directories in the fs were omitted when a zip or tar archive was created from `AddFS` method. Fixes #66831 Change-Id: Id32c9c747f9f65ec7db4aeefeaffa83567215bfc Reviewed-on: https://go-review.googlesource.com/c/go/+/578415 Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Carlos Amedee <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 7678fe1 commit 0081f17

File tree

5 files changed

+63
-34
lines changed

5 files changed

+63
-34
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The `(*Writer).AddFS` implementations in both `archive/zip` and `archive/tar`
2+
now write a directory header for an empty directory.

src/archive/tar/writer.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -413,15 +413,15 @@ func (tw *Writer) AddFS(fsys fs.FS) error {
413413
if err != nil {
414414
return err
415415
}
416-
if d.IsDir() {
416+
if name == "." {
417417
return nil
418418
}
419419
info, err := d.Info()
420420
if err != nil {
421421
return err
422422
}
423423
// TODO(#49580): Handle symlinks when fs.ReadLinkFS is available.
424-
if !info.Mode().IsRegular() {
424+
if !d.IsDir() && !info.Mode().IsRegular() {
425425
return errors.New("tar: cannot add non-regular file")
426426
}
427427
h, err := FileInfoHeader(info, "")
@@ -432,6 +432,9 @@ func (tw *Writer) AddFS(fsys fs.FS) error {
432432
if err := tw.WriteHeader(h); err != nil {
433433
return err
434434
}
435+
if d.IsDir() {
436+
return nil
437+
}
435438
f, err := fsys.Open(name)
436439
if err != nil {
437440
return err
@@ -668,6 +671,7 @@ func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) {
668671
func (sw sparseFileWriter) logicalRemaining() int64 {
669672
return sw.sp[len(sw.sp)-1].endOffset() - sw.pos
670673
}
674+
671675
func (sw sparseFileWriter) physicalRemaining() int64 {
672676
return sw.fw.physicalRemaining()
673677
}

src/archive/tar/writer_test.go

+40-17
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"os"
1515
"path"
1616
"slices"
17+
"sort"
1718
"strings"
1819
"testing"
1920
"testing/fstest"
@@ -1338,29 +1339,40 @@ func TestFileWriter(t *testing.T) {
13381339

13391340
func TestWriterAddFS(t *testing.T) {
13401341
fsys := fstest.MapFS{
1342+
"emptyfolder": {Mode: 0o755 | os.ModeDir},
13411343
"file.go": {Data: []byte("hello")},
13421344
"subfolder/another.go": {Data: []byte("world")},
1345+
// Notably missing here is the "subfolder" directory. This makes sure even
1346+
// if we don't have a subfolder directory listed.
13431347
}
13441348
var buf bytes.Buffer
13451349
tw := NewWriter(&buf)
13461350
if err := tw.AddFS(fsys); err != nil {
13471351
t.Fatal(err)
13481352
}
1353+
if err := tw.Close(); err != nil {
1354+
t.Fatal(err)
1355+
}
1356+
1357+
// Add subfolder into fsys to match what we'll read from the tar.
1358+
fsys["subfolder"] = &fstest.MapFile{Mode: 0o555 | os.ModeDir}
13491359

13501360
// Test that we can get the files back from the archive
13511361
tr := NewReader(&buf)
13521362

1353-
entries, err := fsys.ReadDir(".")
1354-
if err != nil {
1355-
t.Fatal(err)
1363+
names := make([]string, 0, len(fsys))
1364+
for name := range fsys {
1365+
names = append(names, name)
13561366
}
1367+
sort.Strings(names)
13571368

1358-
var curfname string
1359-
for _, entry := range entries {
1360-
curfname = entry.Name()
1361-
if entry.IsDir() {
1362-
curfname += "/"
1363-
continue
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)
13641376
}
13651377
hdr, err := tr.Next()
13661378
if err == io.EOF {
@@ -1370,22 +1382,33 @@ func TestWriterAddFS(t *testing.T) {
13701382
t.Fatal(err)
13711383
}
13721384

1373-
data, err := io.ReadAll(tr)
1374-
if err != nil {
1375-
t.Fatal(err)
1385+
if hdr.Name != name {
1386+
t.Errorf("test fs has filename %v; archive header has %v",
1387+
name, hdr.Name)
13761388
}
13771389

1378-
if hdr.Name != curfname {
1379-
t.Fatalf("got filename %v, want %v",
1380-
curfname, hdr.Name)
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
13811397
}
13821398

1383-
origdata := fsys[curfname].Data
1399+
data, err := io.ReadAll(tr)
1400+
if err != nil {
1401+
t.Fatal(err)
1402+
}
1403+
origdata := fsys[name].Data
13841404
if string(data) != string(origdata) {
1385-
t.Fatalf("got file content %v, want %v",
1405+
t.Fatalf("test fs has file content %v; archive header has %v",
13861406
data, origdata)
13871407
}
13881408
}
1409+
if entriesLeft > 0 {
1410+
t.Fatalf("not all entries are in the archive")
1411+
}
13891412
}
13901413

13911414
func TestWriterAddFSNonRegularFiles(t *testing.T) {

src/archive/zip/writer.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -505,14 +505,14 @@ func (w *Writer) AddFS(fsys fs.FS) error {
505505
if err != nil {
506506
return err
507507
}
508-
if d.IsDir() {
508+
if name == "." {
509509
return nil
510510
}
511511
info, err := d.Info()
512512
if err != nil {
513513
return err
514514
}
515-
if !info.Mode().IsRegular() {
515+
if !d.IsDir() && !info.Mode().IsRegular() {
516516
return errors.New("zip: cannot add non-regular file")
517517
}
518518
h, err := FileInfoHeader(info)
@@ -525,6 +525,9 @@ func (w *Writer) AddFS(fsys fs.FS) error {
525525
if err != nil {
526526
return err
527527
}
528+
if d.IsDir() {
529+
return nil
530+
}
528531
f, err := fsys.Open(name)
529532
if err != nil {
530533
return err

src/archive/zip/writer_test.go

+10-13
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func TestWriter(t *testing.T) {
108108

109109
// TestWriterComment is test for EOCD comment read/write.
110110
func TestWriterComment(t *testing.T) {
111-
var tests = []struct {
111+
tests := []struct {
112112
comment string
113113
ok bool
114114
}{
@@ -158,7 +158,7 @@ func TestWriterComment(t *testing.T) {
158158
}
159159

160160
func TestWriterUTF8(t *testing.T) {
161-
var utf8Tests = []struct {
161+
utf8Tests := []struct {
162162
name string
163163
comment string
164164
nonUTF8 bool
@@ -619,26 +619,23 @@ func TestWriterAddFS(t *testing.T) {
619619
buf := new(bytes.Buffer)
620620
w := NewWriter(buf)
621621
tests := []WriteTest{
622-
{
623-
Name: "file.go",
624-
Data: []byte("hello"),
625-
Mode: 0644,
626-
},
627-
{
628-
Name: "subfolder/another.go",
629-
Data: []byte("world"),
630-
Mode: 0644,
631-
},
622+
{Name: "emptyfolder", Mode: 0o755 | os.ModeDir},
623+
{Name: "file.go", Data: []byte("hello"), Mode: 0644},
624+
{Name: "subfolder/another.go", Data: []byte("world"), Mode: 0644},
625+
// Notably missing here is the "subfolder" directory. This makes sure even
626+
// if we don't have a subfolder directory listed.
632627
}
633628
err := w.AddFS(writeTestsToFS(tests))
634629
if err != nil {
635630
t.Fatal(err)
636631
}
637-
638632
if err := w.Close(); err != nil {
639633
t.Fatal(err)
640634
}
641635

636+
// Add subfolder into fsys to match what we'll read from the tar.
637+
tests = append(tests[:2:2], WriteTest{Name: "subfolder", Mode: 0o555 | os.ModeDir}, tests[2])
638+
642639
// read it back
643640
r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
644641
if err != nil {

0 commit comments

Comments
 (0)