package platform import ( "archive/tar" "archive/zip" "compress/gzip" "context" "fmt" "io" "os" "path/filepath" "strings" ) func safeJoin(dest, name string) (string, error) { p := filepath.Join(dest, name) cleanDest := filepath.Clean(dest) + string(os.PathSeparator) if !strings.HasPrefix(filepath.Clean(p)+string(os.PathSeparator), cleanDest) { return "", fmt.Errorf("unsafe path %q escapes %q", name, dest) } return p, nil } func ExtractTarGz(ctx context.Context, archivePath, destDir string) error { f, err := os.Open(archivePath) if err != nil { return err } defer f.Close() gz, err := gzip.NewReader(f) if err != nil { return err } defer gz.Close() tr := tar.NewReader(gz) for { if err := ctx.Err(); err != nil { return err } h, err := tr.Next() if err == io.EOF { break } if err != nil { return err } target, err := safeJoin(destDir, h.Name) if err != nil { return err } switch h.Typeflag { case tar.TypeDir: if err := os.MkdirAll(target, os.FileMode(h.Mode)); err != nil { return err } case tar.TypeReg: if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil { return err } w, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(h.Mode)) if err != nil { return err } if _, err := io.Copy(w, tr); err != nil { w.Close() return err } w.Close() } } return nil } func ExtractZip(ctx context.Context, archivePath, destDir string) error { zr, err := zip.OpenReader(archivePath) if err != nil { return err } defer zr.Close() for _, zf := range zr.File { if err := ctx.Err(); err != nil { return err } target, err := safeJoin(destDir, zf.Name) if err != nil { return err } if zf.FileInfo().IsDir() { if err := os.MkdirAll(target, zf.Mode()); err != nil { return err } continue } if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil { return err } rc, err := zf.Open() if err != nil { return err } w, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, zf.Mode()) if err != nil { rc.Close() return err } _, err = io.Copy(w, rc) rc.Close() w.Close() if err != nil { return err } } return nil }