mirror of
https://github.com/langgenius/dify.git
synced 2026-08-29 19:54:46 +08:00
104 lines
2.3 KiB
Go
104 lines
2.3 KiB
Go
package snapshot
|
|
|
|
import (
|
|
"archive/tar"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/klauspost/compress/zstd"
|
|
)
|
|
|
|
// SaveHome streams homeDir to dst as tar+zstd in a single pass with no
|
|
// intermediate spooling. The runtime's own defaults and the caller's
|
|
// gitignore-syntax excludes are skipped; see Excluder.
|
|
//
|
|
// An excluded directory is not descended into, so — as in git — a pattern
|
|
// cannot re-include anything beneath a directory that is already excluded.
|
|
//
|
|
// Symlinks are archived as symlinks; irregular files (sockets, fifos,
|
|
// devices) are skipped as runtime artifacts; ownership is not recorded.
|
|
// Callers wrap dst to hash or count the compressed bytes.
|
|
func SaveHome(ctx context.Context, dst io.Writer, homeDir string, excludes []string) error {
|
|
excluder := NewExcluder(excludes)
|
|
zw, err := zstd.NewWriter(dst,
|
|
zstd.WithEncoderLevel(zstd.SpeedDefault),
|
|
zstd.WithEncoderConcurrency(1),
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("create zstd writer: %w", err)
|
|
}
|
|
tw := tar.NewWriter(zw)
|
|
|
|
walkErr := filepath.WalkDir(homeDir, func(p string, d fs.DirEntry, inErr error) error {
|
|
if inErr != nil {
|
|
return inErr
|
|
}
|
|
if err := ctx.Err(); err != nil {
|
|
return err
|
|
}
|
|
rel, err := filepath.Rel(homeDir, p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if rel == "." {
|
|
return nil
|
|
}
|
|
relSlash := filepath.ToSlash(rel)
|
|
if excluder.Excluded(relSlash, d.IsDir()) {
|
|
if d.IsDir() {
|
|
return fs.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
info, err := d.Info()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var linkTarget string
|
|
switch {
|
|
case info.Mode()&fs.ModeSymlink != 0:
|
|
if linkTarget, err = os.Readlink(p); err != nil {
|
|
return err
|
|
}
|
|
case !info.Mode().IsRegular() && !info.IsDir():
|
|
return nil
|
|
}
|
|
hdr, err := tar.FileInfoHeader(info, linkTarget)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hdr.Name = relSlash
|
|
if info.IsDir() {
|
|
hdr.Name += "/"
|
|
}
|
|
hdr.Uid, hdr.Gid, hdr.Uname, hdr.Gname = 0, 0, "", ""
|
|
if err := tw.WriteHeader(hdr); err != nil {
|
|
return err
|
|
}
|
|
if info.Mode().IsRegular() {
|
|
f, err := os.Open(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = io.Copy(tw, f)
|
|
_ = f.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if walkErr != nil {
|
|
_ = zw.Close()
|
|
return walkErr
|
|
}
|
|
if err := tw.Close(); err != nil {
|
|
return err
|
|
}
|
|
return zw.Close()
|
|
}
|