diff options
author | Tony Cook <tony@develop-help.com> | 2022-09-06 15:01:54 +1000 |
---|---|---|
committer | Tony Cook <tony@develop-help.com> | 2022-11-02 09:37:43 +1100 |
commit | 01052a1d77a5e152a908d2e93d8a34736b6391dd (patch) | |
tree | a356e09d4f8ecae9cca6740e937bd72d4c7baf32 /t | |
parent | 4f8b3850b207c4a8adaaba6caf82902c90cee8b6 (diff) | |
download | perl-01052a1d77a5e152a908d2e93d8a34736b6391dd.tar.gz |
Win32 stat() didn't handle AF_UNIX socket files
Unfortunately both symbolic links and sockets can only be
"statted" by opening with FILE_FLAG_OPEN_REPARSE_POINT which
obviously doesn't follow symbolic links.
So to find if a chain of symbolic links points to a socket,
is a broken chain, or loops, we need to follow the chain
ourselves.
Diffstat (limited to 't')
-rw-r--r-- | t/win32/stat.t | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/t/win32/stat.t b/t/win32/stat.t index c2fe60a326..7fa143750f 100644 --- a/t/win32/stat.t +++ b/t/win32/stat.t @@ -9,6 +9,8 @@ BEGIN { use strict; use Fcntl ":seek"; use Config; +use Errno; +use Cwd "getcwd"; Win32::FsType() eq 'NTFS' or skip_all("need NTFS"); @@ -18,6 +20,7 @@ my (undef, $maj, $min) = Win32::GetOSVersion(); my $vista_or_later = $maj >= 6; my $tmpfile1 = tempfile(); +my $tmpfile2 = tempfile(); # test some of the win32 specific stat code, since we # don't depend on the CRT for some of it @@ -249,4 +252,75 @@ if (ok(mkdir($tmpfile1), "make a work directory")) { ok(!-e '"', qq(filename '"' shouldn't exist)); } +# https://github.com/Perl/perl5/issues/20204 +# Win32: stat/unlink fails on UNIX sockets +SKIP: +{ + use IO::Socket; + unlink $tmpfile1; + my $listen = IO::Socket::UNIX->new(Local => $tmpfile1, Listen => 0) + or skip "Cannot create unix socket", 1; + ok(-S $tmpfile1, "can stat a socket"); + ok(!-l $tmpfile1, "doesn't look like a symlink"); + unlink $tmpfile2; + if (system("mklink $tmpfile2 $tmpfile1") == 0) { + ok(-l $tmpfile2, "symlink to socket is a symlink (via lstat)"); + ok(-S $tmpfile2, "symlink to socket is also a socket (via stat)"); + unlink $tmpfile2; + } + close $listen; + unlink $tmpfile1; +} + +{ + # if a symlink chain leads to a socket, or loops, or is broken, + # CreateFileA() fails, so we do our own link following. + # The link leading to a socket is checked above, here check loops + # fail, and that we get ELOOP (which isn't what MSVC returns, but + # try to be better). + if (system("mklink $tmpfile1 $tmpfile2") == 0 + && system("mklink $tmpfile2 $tmpfile1") == 0) { + ok(!stat($tmpfile1), "looping symlink chain fails stat"); + is($!+0, &Errno::ELOOP, "check error set"); + ok(lstat($tmpfile1), "looping symlink chain passes lstat"); + + unlink $tmpfile2; + ok(!stat($tmpfile1), "broken symlink"); + is($!+0, &Errno::ENOENT, "check error set"); + ok(lstat($tmpfile1), "broken symlink chain passes lstat"); + } + unlink $tmpfile1, $tmpfile2; +} + +{ + # $tmpfile4 -> $tmpfile1/file1 -> ../$tmpfile2 -> abspath($tmpfile3) + # $tmpfile3 either doesn't exist, is a file, or is a socket + my ($tmpfile3, $tmpfile4) = (tempfile(), tempfile()); + ok(mkdir($tmpfile1), "make a directory"); + my $cwd = getcwd(); + if (system(qq(mklink $tmpfile4 $tmpfile1\\file1)) == 0 + && system(qq(mklink $tmpfile1\\file1 ..\\$tmpfile2)) == 0 + && system(qq(mklink $tmpfile2 "$cwd\\$tmpfile3")) == 0) { + ok(-l $tmpfile4, "yes, $tmpfile4 is a symlink"); + ok(!-e $tmpfile4, "but we can't stat it"); + + open my $fh, ">", $tmpfile3 or die $!; + close $fh; + ok(-f $tmpfile4, "now $tmpfile4 leads to a file"); + unlink $tmpfile3; + + SKIP: + { + my $listen = IO::Socket::UNIX->new(Local => $tmpfile3, Listen => 0) + or skip "Cannot create unix socket", 1; + ok(!-f $tmpfile4, "$tmpfile4 no longer leads to a file"); + ok(-S $tmpfile4, "now $tmpfile4 leads to a socket"); + ok(-S "$tmpfile1/file1", "$tmpfile1/file1 should lead to a socket"); + ok(-S $tmpfile2, "$tmpfile2 should lead to a socket"); + unlink $tmpfile3; + } + } + unlink $tmpfile2, $tmpfile4, "$tmpfile1/file1"; + rmdir $tmpfile1; +} done_testing(); |