aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg/osutil/tar.go67
-rw-r--r--pkg/osutil/tar_test.go60
2 files changed, 127 insertions, 0 deletions
diff --git a/pkg/osutil/tar.go b/pkg/osutil/tar.go
new file mode 100644
index 000000000..32500e7e4
--- /dev/null
+++ b/pkg/osutil/tar.go
@@ -0,0 +1,67 @@
+// Copyright 2025 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package osutil
+
+import (
+ "archive/tar"
+ "compress/gzip"
+ "io"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+func TarGzDirectory(dir string, writer io.Writer) error {
+ gzw := gzip.NewWriter(writer)
+ defer gzw.Close()
+ return tarDirectory(dir, gzw)
+}
+
+func tarDirectory(dir string, writer io.Writer) error {
+ tw := tar.NewWriter(writer)
+ defer tw.Close()
+
+ return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
+ if err != nil || path == dir {
+ return err
+ }
+ typ := d.Type()
+ if !typ.IsDir() && !typ.IsRegular() {
+ // Only folders and regular files.
+ return nil
+ }
+ relPath, err := filepath.Rel(dir, path)
+ if err != nil {
+ return err
+ }
+ relPath = filepath.ToSlash(relPath)
+ info, err := d.Info()
+ if err != nil {
+ return err
+ }
+ header, err := tar.FileInfoHeader(info, "")
+ if err != nil {
+ return err
+ }
+ header.Name = relPath
+ if typ.IsDir() && !strings.HasSuffix(header.Name, "/") {
+ header.Name += "/"
+ }
+ if err := tw.WriteHeader(header); err != nil {
+ return err
+ }
+ if typ.IsDir() {
+ return nil
+ }
+ // Write the file content.
+ f, err := os.Open(path)
+ if err != nil {
+ return err
+ }
+ _, err = io.Copy(tw, f)
+ f.Close()
+ return err
+ })
+}
diff --git a/pkg/osutil/tar_test.go b/pkg/osutil/tar_test.go
new file mode 100644
index 000000000..a0e5a22bd
--- /dev/null
+++ b/pkg/osutil/tar_test.go
@@ -0,0 +1,60 @@
+// Copyright 2025 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package osutil
+
+import (
+ "archive/tar"
+ "bytes"
+ "io"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTarDirectory(t *testing.T) {
+ dir := t.TempDir()
+
+ items := map[string]string{
+ "file1.txt": "first file content",
+ "dir/file2.txt": "second file content",
+ "empty.txt": "",
+ }
+
+ for path, content := range items {
+ fullPath := filepath.Join(dir, path)
+ dirPath := filepath.Dir(fullPath)
+ if err := MkdirAll(dirPath); err != nil {
+ t.Fatalf("mkdir %q failed: %v", dirPath, err)
+ }
+ if err := WriteFile(fullPath, []byte(content)); err != nil {
+ t.Fatalf("write file failed: %v", err)
+ }
+ }
+
+ var buf bytes.Buffer
+ err := tarDirectory(dir, &buf)
+ assert.NoError(t, err)
+
+ tr := tar.NewReader(&buf)
+ found := make(map[string]string)
+ for {
+ hdr, err := tr.Next()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ if hdr.Typeflag == tar.TypeReg {
+ contentBytes, err := io.ReadAll(tr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ found[hdr.Name] = string(contentBytes)
+ }
+ }
+
+ assert.Equal(t, items, found)
+}