Resolve scan operands to their real paths (closes #144)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
scan now resolves every operand with its symlinks followed before the walk, and uses that real path for pruning, walking, records and warnings. A relative operand is joined to the working directory as the kernel reports it, so a ".." after a symlink counts from its target, as with realpath. A symlink operand is scanned at its target instead of skipped. An operand given through a symlink below another operand, to a directory outside it, is walked on its own instead of dropped with its records deleted. An operand that cannot be resolved is a fatal error naming the operand as given. The walk itself still follows no symlink. Model: opus-5-5
This commit is contained in:
@@ -146,33 +146,44 @@ var errHashSize = errors.New("hash is not 32 bytes")
|
||||
|
||||
// databasePath resolves the database location: SFDUPES_DATABASE when
|
||||
// set and non-empty, the compiled-in default otherwise. It returns the
|
||||
// path, with the working directory, its symlinks resolved, put in front
|
||||
// of it when it is relative, and where the path came from, "default" or
|
||||
// "from SFDUPES_DATABASE", for the line each subcommand prints first on
|
||||
// stderr.
|
||||
// path, made absolute by absolutePath, and where the path came from,
|
||||
// "default" or "from SFDUPES_DATABASE", for the line each subcommand
|
||||
// prints first on stderr.
|
||||
func databasePath() (string, string, error) {
|
||||
path, source := defaultDatabasePath, "default"
|
||||
if p := os.Getenv(databaseEnv); p != "" {
|
||||
path, source = p, "from "+databaseEnv
|
||||
}
|
||||
|
||||
if filepath.IsAbs(path) {
|
||||
return path, source, nil
|
||||
}
|
||||
|
||||
// The working directory as the kernel reports it, with no symlink
|
||||
// in it, as README "Database" says. os.Getwd may return PWD
|
||||
// instead, which can name it through a symlink.
|
||||
wd, err := unix.Getwd()
|
||||
abs, err := absolutePath(path)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("resolve database path %s: %w", path, err)
|
||||
}
|
||||
|
||||
// Not filepath.Abs or filepath.Join: they clean the path, which
|
||||
// drops each ".." together with the directory name before it. When
|
||||
// that directory is a symlink, the cleaned path names another file.
|
||||
return abs, source, nil
|
||||
}
|
||||
|
||||
// absolutePath returns path unchanged when it is absolute, and
|
||||
// otherwise the working directory, a "/" and path. The working
|
||||
// directory is the one the kernel reports, with no symlink in it;
|
||||
// os.Getwd may return PWD instead, which can name it through a symlink.
|
||||
// The result is not cleaned, as filepath.Abs and filepath.Join would
|
||||
// clean it. Cleaning drops each ".." together with the directory name
|
||||
// before it. When that name is a symlink, the cleaned path names
|
||||
// another file than the one the system reaches by following the symlink
|
||||
// first.
|
||||
func absolutePath(path string) (string, error) {
|
||||
if filepath.IsAbs(path) {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
wd, err := unix.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// The working directory ends in "/" only when it is the root.
|
||||
return strings.TrimSuffix(wd, "/") + "/" + path, source, nil
|
||||
return strings.TrimSuffix(wd, "/") + "/" + path, nil
|
||||
}
|
||||
|
||||
// createDatabaseDirectory creates the directory the database at path
|
||||
|
||||
Reference in New Issue
Block a user