@@ -25,6 +25,9 @@ const supports_chdir = (native_os != .wasi);
25
25
// Filter to skip tests on platforms that don't support absolute paths
26
26
const supports_absolute_paths = (native_os != .wasi );
27
27
28
+ // Filter to skip tests on platforms that don't (yet) suppport fstat/fstatat
29
+ const supports_fstat = (native_os != .windows );
30
+
28
31
test "check WASI CWD" {
29
32
if (native_os == .wasi ) {
30
33
if (std .options .wasiCwd () != 3 ) {
@@ -299,6 +302,8 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {
299
302
}
300
303
301
304
test "link with relative paths" {
305
+ if (! supports_fstat ) return error .SkipZigTest ;
306
+
302
307
switch (native_os ) {
303
308
.wasi , .linux , .solaris , .illumos = > {},
304
309
else = > return error .SkipZigTest ,
@@ -341,6 +346,8 @@ test "link with relative paths" {
341
346
}
342
347
343
348
test "linkat with different directories" {
349
+ if (! supports_fstat ) return error .SkipZigTest ;
350
+
344
351
switch (native_os ) {
345
352
.wasi , .linux , .solaris , .illumos = > {},
346
353
else = > return error .SkipZigTest ,
@@ -382,9 +389,8 @@ test "linkat with different directories" {
382
389
}
383
390
}
384
391
385
- test "fstatat" {
386
- // enable when `fstat` and `fstatat` are implemented on Windows
387
- if (native_os == .windows ) return error .SkipZigTest ;
392
+ test "fstat(at) file" {
393
+ if (! supports_fstat ) return error .SkipZigTest ;
388
394
389
395
var tmp = tmpDir (.{});
390
396
defer tmp .cleanup ();
@@ -396,14 +402,66 @@ test "fstatat" {
396
402
// fetch file's info on the opened fd directly
397
403
const file = try tmp .dir .openFile ("file.txt" , .{});
398
404
const stat = try posix .fstat (file .handle );
399
- defer file .close ();
405
+ file .close ();
400
406
401
407
// now repeat but using `fstatat` instead
402
- const flags = posix .AT .SYMLINK_NOFOLLOW ;
403
- const statat = try posix .fstatat (tmp .dir .fd , "file.txt" , flags );
408
+ const statat = try posix .fstatat (tmp .dir .fd , "file.txt" , 0 );
404
409
try expectEqual (stat , statat );
405
410
}
406
411
412
+ test "fstat(at) symlink" {
413
+ if (! supports_fstat ) return error .SkipZigTest ;
414
+
415
+ var tmp = testing .tmpDir (.{});
416
+ defer tmp .cleanup ();
417
+
418
+ try tmp .dir .writeFile (.{ .sub_path = "target.txt" , .data = "irrelevant" });
419
+
420
+ const target = try tmp .dir .openFile ("target.txt" , .{});
421
+ const statTarget = try posix .fstat (target .handle );
422
+ target .close ();
423
+
424
+ // Set up symlink
425
+ try tmp .dir .symLink ("target.txt" , "sym.lnk" , .{});
426
+
427
+ // Openat (+follow) + fstat() the symlink
428
+ const linkFollowFd = try posix .openat (tmp .dir .fd , "sym.lnk" , .{}, default_mode );
429
+ defer posix .close (linkFollowFd );
430
+ const statLinkFollow = try posix .fstat (linkFollowFd );
431
+
432
+ // fstatat (with and without follow) the symlink
433
+ const statatLinkFollow = try posix .fstatat (tmp .dir .fd , "sym.lnk" , 0 );
434
+ const statatLinkNoFollow = try posix .fstatat (tmp .dir .fd , "sym.lnk" , posix .AT .SYMLINK_NOFOLLOW );
435
+
436
+ if (@hasField (posix .O , "PATH" )) {
437
+ // Can only openat() a symlink with NOFOLLOW if O.PATH is
438
+ // supported. Result should exactly match result from the
439
+ // no-follow fstatat() call.
440
+
441
+ const linkNoFollowFd = try posix .openat (tmp .dir .fd , "sym.lnk" , .{ .NOFOLLOW = true , .PATH = true }, default_mode );
442
+ defer posix .close (linkNoFollowFd );
443
+
444
+ const statLinkNoFollow = try posix .fstat (linkNoFollowFd );
445
+ try testing .expectEqual (statLinkNoFollow , statatLinkNoFollow );
446
+ }
447
+
448
+ // Link following should have followed the link
449
+ try testing .expectEqual (statTarget , statLinkFollow );
450
+ try testing .expectEqual (statTarget , statatLinkFollow );
451
+
452
+ // symlink and target are different:
453
+ try testing .expect (statTarget .ino != statatLinkNoFollow .ino );
454
+ try testing .expect (statTarget .mode != statatLinkNoFollow .mode );
455
+
456
+ // target is a regular, non-link file:
457
+ try testing .expect (posix .S .ISREG (statTarget .mode ));
458
+ try testing .expect (! posix .S .ISLNK (statTarget .mode ));
459
+
460
+ // symlink is a non-regular, link file:
461
+ try testing .expect (! posix .S .ISREG (statatLinkNoFollow .mode ));
462
+ try testing .expect (posix .S .ISLNK (statatLinkNoFollow .mode ));
463
+ }
464
+
407
465
test "readlinkat" {
408
466
var tmp = tmpDir (.{});
409
467
defer tmp .cleanup ();
@@ -1249,6 +1307,7 @@ fn expectMode(dir: posix.fd_t, file: []const u8, mode: posix.mode_t) !void {
1249
1307
1250
1308
test "fchmodat smoke test" {
1251
1309
if (! std .fs .has_executable_bit ) return error .SkipZigTest ;
1310
+ if (! supports_fstat ) return error .SkipZigTest ; // for expectMode()
1252
1311
1253
1312
var tmp = tmpDir (.{});
1254
1313
defer tmp .cleanup ();
0 commit comments