diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-14 21:27:22 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-14 21:27:22 +0200 |
| commit | aca389f0e63713b890214cad950ac3aee7e6aaf4 (patch) | |
| tree | bb4dab94ca594edb339ab8fd1743d583efe3d59c /internal/plan/conflict.go | |
| parent | 3bfafbc8664a2a1ba8efc3f64376ff63c3dc11b9 (diff) | |
| download | krino-aca389f0e63713b890214cad950ac3aee7e6aaf4.tar.gz krino-aca389f0e63713b890214cad950ac3aee7e6aaf4.zip | |
plan 9: overwrite never trashes a directory or another scanned file
Diffstat (limited to 'internal/plan/conflict.go')
| -rw-r--r-- | internal/plan/conflict.go | 15 |
1 files changed, 15 insertions, 0 deletions
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 |
