1#!./perl 2 3BEGIN { 4 chdir 't' if -d 't'; 5 @INC = '../lib'; 6 require "./test.pl"; 7} 8 9use strict; 10use Fcntl ":seek"; 11 12Win32::FsType() eq 'NTFS' 13 or skip_all("need NTFS"); 14 15my (undef, $maj, $min) = Win32::GetOSVersion(); 16 17my $vista_or_later = $maj >= 6; 18 19my $tmpfile1 = tempfile(); 20 21# test some of the win32 specific stat code, since we 22# don't depend on the CRT for some of it 23 24ok(link($0, $tmpfile1), "make a link to test nlink"); 25 26my @st = stat $0; 27open my $fh, "<", $0 or die; 28my @fst = stat $fh; 29 30ok(seek($fh, 0, SEEK_END), "seek to end"); 31my $size = tell($fh); 32close $fh; 33 34# the ucrt stat() is inconsistent here, using an A=0 drive letter for stat() 35# and the fd for fstat(), I assume that's something backward compatible. 36# 37# I don't see anything we could reasonable populate it with either. 38$st[6] = $fst[6] = 0; 39 40is("@st", "@fst", "check named stat vs handle stat"); 41 42ok($st[0], "we set dev by default now"); 43ok($st[1], "and ino"); 44 45# unlikely, but someone else might have linked to win32/stat.t 46cmp_ok($st[3], '>', 1, "should be more than one link"); 47 48# we now populate all stat fields ourselves, so check what we can 49is($st[7], $size, "we fetch size correctly"); 50 51cmp_ok($st[9], '<=', time(), "modification time before or on now"); 52ok(-f $0, "yes, we are a file"); 53ok(-d "win32", "and win32 is a directory"); 54pipe(my ($p1, $p2)); 55ok(-p $p1, "a pipe is a pipe"); 56close $p1; close $p2; 57ok(-r $0, "we are readable"); 58ok(!-x $0, "but not executable"); 59ok(-e $0, "we exist"); 60 61ok(open(my $nul, ">", "nul"), "open nul"); 62ok(-c $nul, "nul is a character device"); 63close $nul; 64 65my $nlink = $st[3]; 66 67# check we get nlinks etc for a directory 68@st = stat("win32"); 69ok($st[0], "got dev for a directory"); 70ok($st[1], "got ino for a directory"); 71ok($st[3], "got nlink for a directory"); 72 73# symbolic links 74unlink($tmpfile1); # no more hard link 75 76if (open my $fh, ">", "$tmpfile1.bat") { 77 ok(-x "$tmpfile1.bat", 'batch file is "executable"'); 78 SKIP: { 79 skip "executable bit for handles needs vista or later", 1 80 unless $vista_or_later; 81 ok(-x $fh, 'batch file handle is "executable"'); 82 } 83 close $fh; 84 unlink "$tmpfile1.bat"; 85} 86 87# mklink is available from Vista onwards 88# this may only work in an admin shell 89# MKLINK [[/D] | [/H] | [/J]] Link Target 90if (system("mklink $tmpfile1 win32\\stat.t") == 0) { 91 ok(-l $tmpfile1, "lstat sees a symlink"); 92 93 # check stat on file vs symlink 94 @st = stat $0; 95 my @lst = stat $tmpfile1; 96 97 $st[6] = $lst[6] = 0; 98 99 is("@st", "@lst", "check stat on file vs link"); 100 101 # our hard link no longer exists, check that is reflected in nlink 102 is($st[3], $nlink-1, "check nlink updated"); 103 104 unlink($tmpfile1); 105} 106 107# similarly for a directory 108if (system("mklink /d $tmpfile1 win32") == 0) { 109 ok(-l $tmpfile1, "lstat sees a symlink on the directory symlink"); 110 111 # check stat on directory vs symlink 112 @st = stat "win32"; 113 my @lst = stat $tmpfile1; 114 115 $st[6] = $lst[6] = 0; 116 117 is("@st", "@lst", "check stat on dir vs link"); 118 119 # for now at least, we need to rmdir symlinks to directories 120 rmdir( $tmpfile1 ); 121} 122 123# check a junction looks like a symlink 124 125if (system("mklink /j $tmpfile1 win32") == 0) { 126 ok(-l $tmpfile1, "lstat sees a symlink on the directory junction"); 127 128 rmdir( $tmpfile1 ); 129} 130 131# test interaction between stat and utime 132if (ok(open(my $fh, ">", $tmpfile1), "make a work file")) { 133 # make our test file 134 close $fh; 135 136 my @st = stat $tmpfile1; 137 ok(@st, "stat our work file"); 138 139 # switch to the other half of the year, to flip from/to daylight 140 # savings time. It won't always do so, but it's close enough and 141 # avoids having to deal with working out exactly when it 142 # starts/ends (if it does), along with the hemisphere. 143 # 144 # By basing this on the current file times and using an offset 145 # that's the multiple of an hour we ensure the filesystem 146 # resolution supports the time we set. 147 my $moffset = 6 * 30 * 24 * 3600; 148 my $aoffset = $moffset - 24 * 3600;; 149 my $mymt = $st[9] - $moffset; 150 my $myat = $st[8] - $aoffset; 151 ok(utime($myat, $mymt, $tmpfile1), "set access and mod times"); 152 my @mst = stat $tmpfile1; 153 ok(@mst, "fetch stat after utime"); 154 is($mst[9], $mymt, "check mod time"); 155 is($mst[8], $myat, "check access time"); 156 157 unlink $tmpfile1; 158} 159 160# same for a directory 161if (ok(mkdir($tmpfile1), "make a work directory")) { 162 my @st = stat $tmpfile1; 163 ok(@st, "stat our work directory"); 164 165 my $moffset = 6 * 30 * 24 * 3600; 166 my $aoffset = $moffset - 24 * 3600;; 167 my $mymt = $st[9] - $moffset; 168 my $myat = $st[8] - $aoffset; 169 ok(utime($myat, $mymt, $tmpfile1), "set access and mod times"); 170 my @mst = stat $tmpfile1; 171 ok(@mst, "fetch stat after utime"); 172 is($mst[9], $mymt, "check mod time"); 173 is($mst[8], $myat, "check access time"); 174 175 rmdir $tmpfile1; 176} 177 178# Other stat issues possibly fixed by the stat() re-work 179 180# https://github.com/Perl/perl5/issues/9025 - win32 - file test operators don't work for //?/UNC/server/file filenames 181# can't really make a reliable regression test for this 182# reproduced original problem with a gcc build 183# confirmed fixed with a gcc build 184 185# https://github.com/Perl/perl5/issues/8502 - filetest problem with STDIN/OUT on Windows 186 187{ 188 ok(-r *STDIN, "check stdin is readable"); 189 ok(-w *STDOUT, "check stdout is writable"); 190 191 # CompareObjectHandles() could fix this, but requires Windows 10 192 local our $TODO = "dupped *STDIN and *STDOUT not read/write"; 193 open my $dupin, "<&STDIN" or die; 194 open my $dupout, ">&STDOUT" or die; 195 ok(-r $dupin, "check duplicated stdin is readable"); 196 ok(-w $dupout, "check duplicated stdout is writable"); 197} 198 199# https://github.com/Perl/perl5/issues/6080 - Last mod time from stat() can be wrong on Windows NT/2000/XP 200# tested already 201 202# https://github.com/Perl/perl5/issues/4145 - Problem with filetest -x _ on Win2k AS Perl build 626 203# tested already 204 205# https://github.com/Perl/perl5/issues/14687 - Function lstat behavior case differs between Windows and Unix #14687 206 207{ 208 local our $TODO = "... .... treated as .. by Win32 API"; 209 ok(!-e ".....", "non-existing many dots shouldn't returned existence"); 210} 211 212# https://github.com/Perl/perl5/issues/7410 - -e tests not reliable under Win32 213{ 214 # there's to issues here: 215 # 1) CreateFile() successfully opens " . . " when opened with backup 216 # semantics/directory 217 # 2) opendir(" . . ") becomes FindFirstFile(" . . /*") which fails 218 # 219 # So we end up with success for the first and failure for the second, 220 # making them inconsistent, there may be a Vista level fix for this, 221 # but if we expect -e " . . " to fail we need a more complex fix. 222 local our $TODO = "strange space handling by Windows"; 223 ok(!-e " ", "filename ' ' shouldn't exist"); 224 ok(!-e " . . ", "filename ' . . ' shouldn't exist"); 225 ok(!-e " .. ", "filename ' .. ' shouldn't exist"); 226 ok(!-e " . ", "filename ' . ' shouldn't exist"); 227 228 ok(!!-e " . . " == !!opendir(FOO, " . . "), 229 "these should be consistent"); 230} 231 232# https://github.com/Perl/perl5/issues/12431 - Win32: -e '"' always returns true 233 234{ 235 ok(!-e '"', qq(filename '"' shouldn't exist)); 236} 237 238done_testing(); 239