12 Win32::FsType() eq 'NTFS'
13 or skip_all("need NTFS");
15 my (undef, $maj, $min) = Win32::GetOSVersion();
17 my $vista_or_later = $maj >= 6;
19 my $tmpfile1 = tempfile();
21 # test some of the win32 specific stat code, since we
22 # don't depend on the CRT for some of it
24 ok(link($0, $tmpfile1), "make a link to test nlink");
27 open my $fh, "<", $0 or die;
30 ok(seek($fh, 0, SEEK_END), "seek to end");
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.
37 # I don't see anything we could reasonable populate it with either.
40 is("@st", "@fst", "check named stat vs handle stat");
42 ok($st[0], "we set dev by default now");
43 ok($st[1], "and ino");
45 # unlikely, but someone else might have linked to win32/stat.t
46 cmp_ok($st[3], '>', 1, "should be more than one link");
48 # we now populate all stat fields ourselves, so check what we can
49 is($st[7], $size, "we fetch size correctly");
51 cmp_ok($st[9], '<=', time(), "modification time before or on now");
52 ok(-f $0, "yes, we are a file");
53 ok(-d "win32", "and win32 is a directory");
55 ok(-p $p1, "a pipe is a pipe");
57 ok(-r $0, "we are readable");
58 ok(!-x $0, "but not executable");
59 ok(-e $0, "we exist");
61 ok(open(my $nul, ">", "nul"), "open nul");
62 ok(-c $nul, "nul is a character device");
67 # check we get nlinks etc for a directory
69 ok($st[0], "got dev for a directory");
70 ok($st[1], "got ino for a directory");
71 ok($st[3], "got nlink for a directory");
74 unlink($tmpfile1); # no more hard link
76 if (open my $fh, ">", "$tmpfile1.bat") {
77 ok(-x "$tmpfile1.bat", 'batch file is "executable"');
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"');
84 unlink "$tmpfile1.bat";
87 # mklink is available from Vista onwards
88 # this may only work in an admin shell
89 # MKLINK [[/D] | [/H] | [/J]] Link Target
90 if (system("mklink $tmpfile1 win32\\stat.t") == 0) {
91 ok(-l $tmpfile1, "lstat sees a symlink");
93 # check stat on file vs symlink
95 my @lst = stat $tmpfile1;
99 is("@st", "@lst", "check stat on file vs link");
101 # our hard link no longer exists, check that is reflected in nlink
102 is($st[3], $nlink-1, "check nlink updated");
107 # similarly for a directory
108 if (system("mklink /d $tmpfile1 win32") == 0) {
109 ok(-l $tmpfile1, "lstat sees a symlink on the directory symlink");
111 # check stat on directory vs symlink
113 my @lst = stat $tmpfile1;
115 $st[6] = $lst[6] = 0;
117 is("@st", "@lst", "check stat on dir vs link");
119 # for now at least, we need to rmdir symlinks to directories
123 # check a junction looks like a symlink
125 if (system("mklink /j $tmpfile1 win32") == 0) {
126 ok(-l $tmpfile1, "lstat sees a symlink on the directory junction");
131 # test interaction between stat and utime
132 if (ok(open(my $fh, ">", $tmpfile1), "make a work file")) {
136 my @st = stat $tmpfile1;
137 ok(@st, "stat our work file");
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.
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");
160 # same for a directory
161 if (ok(mkdir($tmpfile1), "make a work directory")) {
162 my @st = stat $tmpfile1;
163 ok(@st, "stat our work directory");
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");
178 # Other stat issues possibly fixed by the stat() re-work
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
185 # https://github.com/Perl/perl5/issues/8502 - filetest problem with STDIN/OUT on Windows
188 ok(-r *STDIN, "check stdin is readable");
189 ok(-w *STDOUT, "check stdout is writable");
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");
199 # https://github.com/Perl/perl5/issues/6080 - Last mod time from stat() can be wrong on Windows NT/2000/XP
202 # https://github.com/Perl/perl5/issues/4145 - Problem with filetest -x _ on Win2k AS Perl build 626
205 # https://github.com/Perl/perl5/issues/14687 - Function lstat behavior case differs between Windows and Unix #14687
208 local our $TODO = "... .... treated as .. by Win32 API";
209 ok(!-e ".....", "non-existing many dots shouldn't returned existence");
212 # https://github.com/Perl/perl5/issues/7410 - -e tests not reliable under Win32
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
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");
228 ok(!!-e " . . " == !!opendir(FOO, " . . "),
229 "these should be consistent");
232 # https://github.com/Perl/perl5/issues/12431 - Win32: -e '"' always returns true
235 ok(!-e '"', qq(filename '"' shouldn't exist));