Skip to content

Commit

Permalink
on win32 translate / to \ in symlink targets
Browse files Browse the repository at this point in the history
Windows, or at least NTFS, doesn't appear to follow symlinks
where the target contains the POSIX directory separator "/".

To fix that translate any / to \ in symlink targets.  This may
break code that checks the symlink target macthes a value set,
but I think it's more likely to fix code that blindly uses /
than break code that looks at the symlink target they just set.

Fixes Perl#20506
  • Loading branch information
tonycoz committed Nov 21, 2022
1 parent b06fe7c commit 91b49e6
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 1 deletion.
10 changes: 9 additions & 1 deletion t/win32/symlink.t
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,18 @@ close $fh if $fh;
ok(symlink($tmpfile1, $tmpfile2), "link to it");
ok(-l $tmpfile2, "-l sees a link");
ok(!-f _, "-f on the lstat result is false");
ok(-f $tmpfile2, "normal -d sees it as a file");
ok(-f $tmpfile2, "normal -f sees it as a file");
is(readlink($tmpfile2), $tmpfile1, "readlink works");
check_stat($tmpfile1, $tmpfile2, "check file and link stat are the same");
ok(unlink($tmpfile2), "unlink the symlink");

# make a relative link
unlike($tmpfile1, qr([\\/]), "temp filename has no path");
ok(symlink("./$tmpfile1", $tmpfile2), "UNIX (/) relative link to the file");
ok(-f $tmpfile2, "we can see it through the link");
system "dir";
ok(unlink($tmpfile2), "unlink the symlink");

ok(unlink($tmpfile1), "and the file");

# test we don't treat directory junctions like symlinks
Expand Down
18 changes: 18 additions & 0 deletions win32/win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -3674,6 +3674,24 @@ win32_symlink(const char *oldfile, const char *newfile)
*/
newfile = PerlDir_mapA(newfile);

if (strchr(oldfile, '/')) {
/* Win32 (or perhaps NTFS) won't follow symlinks containing
/, so replace any with \\
*/
char *temp = savepv(oldfile);
SAVEFREEPV(temp);
char *p = temp;
while (*p) {
if (*p == '/') {
*p = '\\';
}
++p;
}
*p = 0;
oldfile = temp;
oldfile_len = p - temp;
}

/* are we linking to a directory?
CreateSymlinkA() needs to know if the target is a directory,
If it looks like a directory name:
Expand Down

0 comments on commit 91b49e6

Please sign in to comment.