improved kv metacore
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
package snapshot
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PluginOutputArchivePrefix is where plugin-output files live inside the ZIP.
|
||||
// Trailing slash because ZIP entry names are forward-slash relative paths.
|
||||
const PluginOutputArchivePrefix = "cache/plugin-output/"
|
||||
|
||||
// addPluginOutput walks srcDir (typically $FILES_PATH/plugin) and copies every
|
||||
// file under it into zw at PluginOutputArchivePrefix + relPath.
|
||||
//
|
||||
// Returns the number of files added. A missing srcDir is not an error: it
|
||||
// just means no plugin has produced output yet, so the section stays empty.
|
||||
func addPluginOutput(zw *zip.Writer, srcDir string) (int, error) {
|
||||
info, err := os.Stat(srcDir)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return 0, nil
|
||||
}
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("stat %s: %w", srcDir, err)
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return 0, fmt.Errorf("%s is not a directory", srcDir)
|
||||
}
|
||||
|
||||
count := 0
|
||||
err = filepath.WalkDir(srcDir, func(path string, d os.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
rel, err := filepath.Rel(srcDir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entry := PluginOutputArchivePrefix + filepath.ToSlash(rel)
|
||||
|
||||
w, err := zw.Create(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if _, err := io.Copy(w, f); err != nil {
|
||||
return err
|
||||
}
|
||||
count++
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return count, fmt.Errorf("walk %s: %w", srcDir, err)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// extractPluginOutput pulls every entry under PluginOutputArchivePrefix from
|
||||
// the ZIP and writes it under dstDir. Existing files are overwritten when
|
||||
// overwrite=true; otherwise they're left alone (treats existing files as the
|
||||
// "mine wins" choice — same toggle the metadata import uses).
|
||||
//
|
||||
// Path traversal is rejected: any entry whose relative path resolves outside
|
||||
// dstDir is skipped with a warning.
|
||||
func extractPluginOutput(zr *zip.Reader, dstDir string, overwrite bool) (written int, skipped int, err error) {
|
||||
if dstDir == "" {
|
||||
return 0, 0, fmt.Errorf("plugin-output destination dir is empty")
|
||||
}
|
||||
if err := os.MkdirAll(dstDir, 0o755); err != nil {
|
||||
return 0, 0, fmt.Errorf("mkdir %s: %w", dstDir, err)
|
||||
}
|
||||
|
||||
cleanRoot, err := filepath.Abs(dstDir)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("abs %s: %w", dstDir, err)
|
||||
}
|
||||
|
||||
for _, f := range zr.File {
|
||||
if !strings.HasPrefix(f.Name, PluginOutputArchivePrefix) {
|
||||
continue
|
||||
}
|
||||
if strings.HasSuffix(f.Name, "/") {
|
||||
continue
|
||||
}
|
||||
rel := strings.TrimPrefix(f.Name, PluginOutputArchivePrefix)
|
||||
dst := filepath.Join(cleanRoot, filepath.FromSlash(rel))
|
||||
|
||||
// Reject anything that escapes the dst root.
|
||||
if !strings.HasPrefix(dst, cleanRoot+string(os.PathSeparator)) && dst != cleanRoot {
|
||||
log.Printf("[Snapshot] import: skipping path-escaping entry %q", f.Name)
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
|
||||
if !overwrite {
|
||||
if _, err := os.Stat(dst); err == nil {
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
||||
return written, skipped, fmt.Errorf("mkdir for %s: %w", dst, err)
|
||||
}
|
||||
|
||||
if err := writeZipFile(f, dst); err != nil {
|
||||
return written, skipped, err
|
||||
}
|
||||
written++
|
||||
}
|
||||
return written, skipped, nil
|
||||
}
|
||||
|
||||
func writeZipFile(f *zip.File, dst string) error {
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("open zip entry %s: %w", f.Name, err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create %s: %w", dst, err)
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
if _, err := io.Copy(out, rc); err != nil {
|
||||
return fmt.Errorf("write %s: %w", dst, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user