]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
rename: honor no-overwrite for dangling symlinks
authordongshengyuan <545258830@qq.com>
Fri, 17 Jul 2026 10:20:18 +0000 (18:20 +0800)
committerdongshengyuan <545258830@qq.com>
Fri, 17 Jul 2026 10:20:18 +0000 (18:20 +0800)
Change:
  Use lstat() for destination existence checks.
  Treat dangling symlinks as existing entries.

Before:
  rename --no-overwrite replaced the dangling symlink.
  rename --copy --no-overwrite followed it and created target.

Follow:
  eb4aea8a4756be04655f31b286bc33b593f2689f
  d021e24bc185cca05cf81a257648ec5d2d94f0e4

misc-utils/rename.c
tests/expected/rename/overwrite
tests/ts/rename/overwrite

index 4a18b061cac1a8441655dd67225a0e385bfa77ac..9269dabb770ccdbac04aaf96da73669065f8559e 100644 (file)
@@ -227,7 +227,7 @@ static int do_file(char *from, char *to, char *s, int verbose, int noact,
        if (string_replace(from, to, s, &newname) != 0)
                return 0;
 
-       if ((nooverwrite || interactive) && access(newname, F_OK) != 0)
+       if ((nooverwrite || interactive) && lstat(newname, &sb) != 0)
                nooverwrite = interactive = 0;
 
        if (nooverwrite || (interactive && (noact || ask(newname) != 0))) {
@@ -265,7 +265,7 @@ static int do_copy(char *from, char *to, char *s, int verbose, int noact,
        if (string_replace(from, to, s, &newname) != 0)
                return 0;
 
-       if ((nooverwrite || interactive) && access(newname, F_OK) != 0)
+       if ((nooverwrite || interactive) && lstat(newname, &sb) != 0)
                nooverwrite = interactive = 0;
 
        if (nooverwrite || (interactive && (noact || ask(newname) != 0))) {
index b16f799c3a418625c426cb24f00034edc7699163..28b8898918c95af47bea5a9fa2972b0f8aa44d4f 100644 (file)
@@ -1 +1,3 @@
 Skipping existing file: `rename_to'
+Skipping existing file: `rename_dangling_to'
+Skipping existing file: `rename_copy_to'
index c9891ee8bfbed26c5a4258a1349308b6daeb0a99..fb975451b3a2123c61b7e2e8cdb076b60c6fcb30 100755 (executable)
@@ -28,4 +28,29 @@ touch rename_from rename_to
 $TS_CMD_RENAME -v --no-overwrite from to rename_from >> "$TS_OUTPUT" 2>> "$TS_ERRLOG"
 rm rename_from rename_to >> "$TS_OUTPUT" 2>> "$TS_ERRLOG"
 
+touch rename_dangling_from
+ln -s rename_missing_target rename_dangling_to
+$TS_CMD_RENAME -v --no-overwrite dangling_from dangling_to rename_dangling_from >> "$TS_OUTPUT" 2>> "$TS_ERRLOG"
+if [ ! -e rename_dangling_from ]; then
+       echo "error: original file was renamed despite --no-overwrite" >> "$TS_OUTPUT"
+fi
+if [ ! -L rename_dangling_to ]; then
+       echo "error: dangling symlink was overwritten despite --no-overwrite" >> "$TS_OUTPUT"
+fi
+rm rename_dangling_from rename_dangling_to >> "$TS_OUTPUT" 2>> "$TS_ERRLOG"
+
+echo "data" > rename_copy_from
+ln -s rename_copy_missing rename_copy_to
+$TS_CMD_RENAME -v --copy --no-overwrite copy_from copy_to rename_copy_from >> "$TS_OUTPUT" 2>> "$TS_ERRLOG"
+if [ ! -e rename_copy_from ]; then
+       echo "error: original file was removed by --copy" >> "$TS_OUTPUT"
+fi
+if [ ! -L rename_copy_to ]; then
+       echo "error: dangling symlink was overwritten by --copy --no-overwrite" >> "$TS_OUTPUT"
+fi
+if [ -e rename_copy_missing ]; then
+       echo "error: --copy followed dangling destination symlink" >> "$TS_OUTPUT"
+fi
+rm rename_copy_from rename_copy_to >> "$TS_OUTPUT" 2>> "$TS_ERRLOG"
+
 ts_finalize