Files
Ant-Browser/backend/app_backup_file_sync.go
ant-black 70132417ad publish: 1.2.0 snapshot (f3d7ec5)
channel: master

version: 1.2.0

source-ref: D:\code\open_source\ant-chrome master

source-commit: f3d7ec5

merged-public-base: 3d264eb
2026-05-05 18:08:10 +08:00

182 lines
3.4 KiB
Go

package backend
import (
"crypto/sha256"
"encoding/hex"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
)
func backupSyncDir(src, dst string, overwrite bool, stats *backupMergeStats, shouldSkip func(rel string) bool) error {
if !backupPathExists(src) {
return nil
}
if err := os.MkdirAll(dst, 0755); err != nil {
return err
}
return filepath.WalkDir(src, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.Type()&os.ModeSymlink != 0 {
stats.Skipped++
if d.IsDir() {
return filepath.SkipDir
}
return nil
}
rel, err := filepath.Rel(src, path)
if err != nil {
return err
}
if rel == "." {
return nil
}
rel = filepath.ToSlash(rel)
if shouldSkip != nil && shouldSkip(rel) {
if d.IsDir() {
return filepath.SkipDir
}
stats.Skipped++
return nil
}
target := filepath.Join(dst, filepath.FromSlash(rel))
if d.IsDir() {
return os.MkdirAll(target, 0755)
}
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
return err
}
if overwrite {
if err := backupCopyFile(path, target); err != nil {
return err
}
stats.Imported++
return nil
}
if _, err := os.Stat(target); os.IsNotExist(err) {
if err := backupCopyFile(path, target); err != nil {
return err
}
stats.Imported++
return nil
} else if err != nil {
return err
}
same, err := backupFilesSame(path, target)
if err != nil {
return err
}
if same {
stats.Skipped++
} else {
stats.Conflicts++
}
return nil
})
}
func backupCopyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
info, err := in.Stat()
if err != nil {
return err
}
tmpPath := dst + ".tmp"
out, err := os.OpenFile(tmpPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode())
if err != nil {
return err
}
if _, err := io.Copy(out, in); err != nil {
out.Close()
_ = os.Remove(tmpPath)
return err
}
if err := out.Close(); err != nil {
_ = os.Remove(tmpPath)
return err
}
if err := os.Rename(tmpPath, dst); err != nil {
_ = os.Remove(tmpPath)
return err
}
return nil
}
func backupFilesSame(a, b string) (bool, error) {
ainfo, err := os.Stat(a)
if err != nil {
return false, err
}
binfo, err := os.Stat(b)
if err != nil {
return false, err
}
if ainfo.Size() != binfo.Size() {
return false, nil
}
ah, err := backupSHA256File(a)
if err != nil {
return false, err
}
bh, err := backupSHA256File(b)
if err != nil {
return false, err
}
return ah == bh, nil
}
func backupSHA256File(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", err
}
return hex.EncodeToString(h.Sum(nil)), nil
}
func backupShouldSkipAppDBFile(rel string) bool {
r := strings.TrimSpace(filepath.ToSlash(rel))
return r == "app.db" || r == "app.db-wal" || r == "app.db-shm"
}
func backupRemoveContentsExcept(dir string, keep map[string]struct{}) error {
dir = strings.TrimSpace(dir)
if dir == "" {
return nil
}
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
entries, err := os.ReadDir(dir)
if err != nil {
return err
}
for _, entry := range entries {
p := filepath.Join(dir, entry.Name())
if backupPathInSet(p, keep) {
continue
}
if err := os.RemoveAll(p); err != nil {
return err
}
}
return nil
}