]> Cypherpunks.ru repositories - gostls13.git/commitdiff
archive/tar: add AddFS method to Writer
authorMauri de Souza Meneguzzo <mauri870@gmail.com>
Thu, 10 Aug 2023 21:13:11 +0000 (21:13 +0000)
committerGopher Robot <gobot@golang.org>
Wed, 13 Sep 2023 13:59:01 +0000 (13:59 +0000)
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 #58000

Change-Id: I0e6abac76b76bc5d95b74b7e5b37634ce3f76c85
GitHub-Last-Rev: 11a62bfd7ebc7ca93b1832da15018e6805c92520
GitHub-Pull-Request: golang/go#61599
Reviewed-on: https://go-review.googlesource.com/c/go/+/513316
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Auto-Submit: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Joseph Tsai <joetsai@digital-static.net>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>

api/next/58000.txt [new file with mode: 0644]
src/archive/tar/writer.go
src/archive/tar/writer_test.go

diff --git a/api/next/58000.txt b/api/next/58000.txt
new file mode 100644 (file)
index 0000000..94db963
--- /dev/null
@@ -0,0 +1 @@
+pkg archive/tar, method (*Writer) AddFS(fs.FS) error #58000
index 1c95f0738a864f38aa250825e2ca05aac8a21cd1..d805e266d0594c150948b5ddfef814f91489f4ca 100644 (file)
@@ -5,8 +5,10 @@
 package tar
 
 import (
+       "errors"
        "fmt"
        "io"
+       "io/fs"
        "path"
        "sort"
        "strings"
@@ -403,6 +405,43 @@ func (tw *Writer) writeRawHeader(blk *block, size int64, flag byte) error {
        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) {
index f6d75c580323051d3465c1ec690de9e37f173cce..a9936d6cd526fe54d7424c22cc20fcded4152ca2 100644 (file)
@@ -9,12 +9,14 @@ import (
        "encoding/hex"
        "errors"
        "io"
+       "io/fs"
        "os"
        "path"
        "reflect"
        "sort"
        "strings"
        "testing"
+       "testing/fstest"
        "testing/iotest"
        "time"
 )
@@ -1333,3 +1335,67 @@ func TestFileWriter(t *testing.T) {
                }
        }
 }
+
+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")
+       }
+}