From 99fb6d574af2a64698591bda01e6f251e178423d Mon Sep 17 00:00:00 2001 From: Mauri de Souza Meneguzzo Date: Wed, 26 Jul 2023 19:12:10 -0300 Subject: [PATCH] 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: #58000 --- api/next/58000.txt | 1 + src/archive/tar/writer.go | 34 ++++++++++++++++++++++++++++++++ src/archive/tar/writer_test.go | 36 ++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 api/next/58000.txt diff --git a/api/next/58000.txt b/api/next/58000.txt new file mode 100644 index 0000000000000..b7595e84ac577 --- /dev/null +++ b/api/next/58000.txt @@ -0,0 +1 @@ +pkg archive/tar, method (*Writer) AddFS(fsys fs.FS) error #58000 \ No newline at end of file diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go index 1c95f0738a864..d7d9ca479437a 100644 --- a/src/archive/tar/writer.go +++ b/src/archive/tar/writer.go @@ -7,6 +7,7 @@ package tar import ( "fmt" "io" + "io/fs" "path" "sort" "strings" @@ -403,6 +404,39 @@ 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 + } + 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) { diff --git a/src/archive/tar/writer_test.go b/src/archive/tar/writer_test.go index f6d75c5803230..0ca3ce49b8ebe 100644 --- a/src/archive/tar/writer_test.go +++ b/src/archive/tar/writer_test.go @@ -15,6 +15,7 @@ import ( "sort" "strings" "testing" + "testing/fstest" "testing/iotest" "time" ) @@ -1333,3 +1334,38 @@ func TestFileWriter(t *testing.T) { } } } + +func TestWriterAddFs(t *testing.T) { + expectedFiles := []string{ + "file.go", + "subfolder/another.go", + } + fsys := fstest.MapFS{ + "file.go": {}, + "subfolder/another.go": {}, + } + 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) + var foundFiles []string + for { + hdr, err := tr.Next() + if err == io.EOF { + break // End of archive + } + if err != nil { + t.Fatal(err) + } + foundFiles = append(foundFiles, hdr.Name) + } + + if !reflect.DeepEqual(expectedFiles, foundFiles) { + t.Fatalf("got %+v, want %+v", + foundFiles, expectedFiles) + } +}