Skip to content

Commit 9e20db5

Browse files
committed
archive/tar: add AddFS method to Writer
The method AddFS can be used to add the contents of a fs.FS filesystem to a tar archive. This method walks the directory tree starting at the root of the filesystem and adds each file to the archive. Fixes: golang#58000
1 parent 58447d7 commit 9e20db5

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

api/next/58000.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pkg archive/tar, method (*Writer) AddFS(fsys fs.FS) error #58000

src/archive/tar/writer.go

+34
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package tar
77
import (
88
"fmt"
99
"io"
10+
"io/fs"
1011
"path"
1112
"sort"
1213
"strings"
@@ -403,6 +404,39 @@ func (tw *Writer) writeRawHeader(blk *block, size int64, flag byte) error {
403404
return nil
404405
}
405406

407+
// AddFS adds the files from fs.FS to the archive.
408+
// It walks the directory tree starting at the root of the filesystem
409+
// adding each file to the tar archive while maintaining the directory structure.
410+
func (tw *Writer) AddFS(fsys fs.FS) error {
411+
return fs.WalkDir(fsys, ".", func(name string, d fs.DirEntry, err error) error {
412+
if err != nil {
413+
return err
414+
}
415+
if d.IsDir() {
416+
return nil
417+
}
418+
info, err := d.Info()
419+
if err != nil {
420+
return err
421+
}
422+
h, err := FileInfoHeader(info, "")
423+
if err != nil {
424+
return err
425+
}
426+
h.Name = name
427+
if err := tw.WriteHeader(h); err != nil {
428+
return err
429+
}
430+
f, err := fsys.Open(name)
431+
if err != nil {
432+
return err
433+
}
434+
defer f.Close()
435+
_, err = io.Copy(tw, f)
436+
return err
437+
})
438+
}
439+
406440
// splitUSTARPath splits a path according to USTAR prefix and suffix rules.
407441
// If the path is not splittable, then it will return ("", "", false).
408442
func splitUSTARPath(name string) (prefix, suffix string, ok bool) {

src/archive/tar/writer_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"sort"
1616
"strings"
1717
"testing"
18+
"testing/fstest"
1819
"testing/iotest"
1920
"time"
2021
)
@@ -1333,3 +1334,38 @@ func TestFileWriter(t *testing.T) {
13331334
}
13341335
}
13351336
}
1337+
1338+
func TestWriterAddFs(t *testing.T) {
1339+
expectedFiles := []string{
1340+
"file.go",
1341+
"subfolder/another.go",
1342+
}
1343+
fsys := fstest.MapFS{
1344+
"file.go": {},
1345+
"subfolder/another.go": {},
1346+
}
1347+
var buf bytes.Buffer
1348+
tw := NewWriter(&buf)
1349+
if err := tw.AddFS(fsys); err != nil {
1350+
t.Fatal(err)
1351+
}
1352+
1353+
// Test that we can get the files back from the archive
1354+
tr := NewReader(&buf)
1355+
var foundFiles []string
1356+
for {
1357+
hdr, err := tr.Next()
1358+
if err == io.EOF {
1359+
break // End of archive
1360+
}
1361+
if err != nil {
1362+
t.Fatal(err)
1363+
}
1364+
foundFiles = append(foundFiles, hdr.Name)
1365+
}
1366+
1367+
if !reflect.DeepEqual(expectedFiles, foundFiles) {
1368+
t.Fatalf("got %+v, want %+v",
1369+
foundFiles, expectedFiles)
1370+
}
1371+
}

0 commit comments

Comments
 (0)