package tar
import (
+ "errors"
"fmt"
"io"
+ "io/fs"
"path"
"sort"
"strings"
return nil
}
+// AddFS adds the files from fs.FS to the archive.
+// It walks the directory tree starting at the root of the filesystem
+// adding each file to the tar archive while maintaining the directory structure.
+func (tw *Writer) AddFS(fsys fs.FS) error {
+ return fs.WalkDir(fsys, ".", func(name string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if d.IsDir() {
+ return nil
+ }
+ info, err := d.Info()
+ if err != nil {
+ return err
+ }
+ // TODO(#49580): Handle symlinks when fs.ReadLinkFS is available.
+ if !info.Mode().IsRegular() {
+ return errors.New("tar: cannot add non-regular file")
+ }
+ h, err := FileInfoHeader(info, "")
+ if err != nil {
+ return err
+ }
+ h.Name = name
+ if err := tw.WriteHeader(h); err != nil {
+ return err
+ }
+ f, err := fsys.Open(name)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ _, err = io.Copy(tw, f)
+ return err
+ })
+}
+
// splitUSTARPath splits a path according to USTAR prefix and suffix rules.
// If the path is not splittable, then it will return ("", "", false).
func splitUSTARPath(name string) (prefix, suffix string, ok bool) {
"encoding/hex"
"errors"
"io"
+ "io/fs"
"os"
"path"
"reflect"
"sort"
"strings"
"testing"
+ "testing/fstest"
"testing/iotest"
"time"
)
}
}
}
+
+func TestWriterAddFS(t *testing.T) {
+ fsys := fstest.MapFS{
+ "file.go": {Data: []byte("hello")},
+ "subfolder/another.go": {Data: []byte("world")},
+ }
+ var buf bytes.Buffer
+ tw := NewWriter(&buf)
+ if err := tw.AddFS(fsys); err != nil {
+ t.Fatal(err)
+ }
+
+ // Test that we can get the files back from the archive
+ tr := NewReader(&buf)
+
+ entries, err := fsys.ReadDir(".")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var curfname string
+ for _, entry := range entries {
+ curfname = entry.Name()
+ if entry.IsDir() {
+ curfname += "/"
+ continue
+ }
+ hdr, err := tr.Next()
+ if err == io.EOF {
+ break // End of archive
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ data, err := io.ReadAll(tr)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if hdr.Name != curfname {
+ t.Fatalf("got filename %v, want %v",
+ curfname, hdr.Name)
+ }
+
+ origdata := fsys[curfname].Data
+ if string(data) != string(origdata) {
+ t.Fatalf("got file content %v, want %v",
+ data, origdata)
+ }
+ }
+}
+
+func TestWriterAddFSNonRegularFiles(t *testing.T) {
+ fsys := fstest.MapFS{
+ "device": {Data: []byte("hello"), Mode: 0755 | fs.ModeDevice},
+ "symlink": {Data: []byte("world"), Mode: 0755 | fs.ModeSymlink},
+ }
+ var buf bytes.Buffer
+ tw := NewWriter(&buf)
+ if err := tw.AddFS(fsys); err == nil {
+ t.Fatal("expected error, got nil")
+ }
+}