From aca389f0e63713b890214cad950ac3aee7e6aaf4 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Mon, 14 Sep 2026 21:27:22 +0200 Subject: plan 9: overwrite never trashes a directory or another scanned file --- internal/plan/conflict.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'internal/plan/conflict.go') diff --git a/internal/plan/conflict.go b/internal/plan/conflict.go index 8747669..c4c6b64 100644 --- a/internal/plan/conflict.go +++ b/internal/plan/conflict.go @@ -15,6 +15,7 @@ import ( // tests can supply a stub and Build stays pure otherwise. type Disk interface { Exists(path string) bool + Regular(path string) bool SameContent(a, b string) (bool, error) } @@ -29,6 +30,13 @@ func (OS) Exists(path string) bool { return err == nil } +// Regular reports whether path is a regular file, not following a symlink +// at path itself. +func (OS) Regular(path string) bool { + fi, err := os.Lstat(path) + return err == nil && fi.Mode().IsRegular() +} + // SameContent delegates to internal/dup, the one place content identity is // decided. func (OS) SameContent(a, b string) (bool, error) { @@ -40,6 +48,7 @@ func (OS) SameContent(a, b string) (bool, error) { type NoDisk struct{} func (NoDisk) Exists(string) bool { return false } +func (NoDisk) Regular(string) bool { return false } func (NoDisk) SameContent(string, string) (bool, error) { return false, nil } // claimed is the set of destination paths already spoken for by an earlier @@ -89,6 +98,12 @@ func resolveConflict(kind Kind, policy config.Conflict, src, dst string, d Disk, return dst, "target exists", "" case config.ConflictOverwrite: if onDisk && !c[dst] { + // Only a regular file is ever trashed to make room (review M4): + // a directory or link of the same name stays, and so does the + // step - skipped, saying why. + if !d.Regular(dst) { + return dst, "target is not a regular file", "" + } // The existing file is trashed first (plan 4). Only the first // step to reach this path may displace it: once another step // in this same plan has already claimed dst, that path will -- cgit v1.3