Check rsync destination capacity before copying

This commit is contained in:
Mikei386
2026-06-15 17:20:07 +02:00
parent 6c795fd97d
commit e66d89598d
10 changed files with 110 additions and 9 deletions
+62
View File
@@ -108,6 +108,68 @@ func (r *Runner) Run(ctx context.Context, runID string, job model.Job, callback
return summary, nil
}
// Estimate performs rsync's complete comparison without writing destination data.
func (r *Runner) Estimate(ctx context.Context, runID string, job model.Job) (Summary, error) {
job.Rsync.DryRun = true
args := Arguments(job)
for index, arg := range args {
if strings.HasPrefix(arg, "--out-format=") {
args[index] = "--out-format="
}
}
cmd := exec.CommandContext(ctx, r.Binary, args...)
cmd.Env = append(os.Environ(), "LC_ALL=C", "LANG=C")
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.Cancel = func() error {
if cmd.Process == nil {
return os.ErrProcessDone
}
if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err != nil && !errors.Is(err, syscall.ESRCH) {
return err
}
return nil
}
stdout, err := cmd.StdoutPipe()
if err != nil {
return Summary{}, err
}
stderr, err := cmd.StderrPipe()
if err != nil {
return Summary{}, err
}
if err := cmd.Start(); err != nil {
if errors.Is(err, exec.ErrNotFound) {
return Summary{}, fmt.Errorf("environment: rsync binary not found: %w", err)
}
return Summary{}, err
}
unregister := r.register(runID, cmd.Process)
defer unregister()
var summary Summary
done := make(chan struct{})
go func() {
scanOutput(stdout, func(line string) {
if value, ok := parseCount(filesPattern, line); ok {
summary.Files = value
}
if value, ok := parseCount(bytesPattern, line); ok {
summary.Bytes = value
}
})
close(done)
}()
errBytes, _ := io.ReadAll(io.LimitReader(stderr, 1024*1024))
err = cmd.Wait()
<-done
if err != nil {
if errors.Is(ctx.Err(), context.Canceled) {
return Summary{}, fmt.Errorf("cancelled: %w", ctx.Err())
}
return Summary{}, fmt.Errorf("rsync preflight: %s", strings.TrimSpace(string(errBytes)))
}
return summary, nil
}
func Arguments(job model.Job) []string {
options := job.Rsync
args := []string{"--recursive", "--human-readable", "--info=progress2", "--stats", "--partial", "--out-format=FILE|%n"}