Commit f5d0664e78
Changed files (1)
lib
std
fs
lib/std/fs/test.zig
@@ -156,23 +156,80 @@ fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !vo
try testing.expectEqualStrings(target_path, given);
}
-test "stat on a symlink returns Kind.sym_link" {
+test "File.stat on a File that is a symlink returns Kind.sym_link" {
+ // This test requires getting a file descriptor of a symlink which
+ // is not possible on all targets
+ switch (builtin.target.os.tag) {
+ .windows, .linux => {},
+ else => return error.SkipZigTest,
+ }
+
try testWithAllSupportedPathTypes(struct {
fn impl(ctx: *TestContext) !void {
const dir_target_path = try ctx.transformPath("subdir");
try ctx.dir.makeDir(dir_target_path);
- // TODO: Also test a symlink to a file.
- // There's currently no way to avoid following symlinks when opening files.
- // https://github.com/ziglang/zig/issues/18327
-
ctx.dir.symLink(dir_target_path, "symlink", .{ .is_directory = true }) catch |err| switch (err) {
// Symlink requires admin privileges on windows, so this test can legitimately fail.
error.AccessDenied => return error.SkipZigTest,
else => return err,
};
- var symlink = try ctx.dir.openDir("symlink", .{ .no_follow = true });
+ var symlink = switch (builtin.target.os.tag) {
+ .windows => windows_symlink: {
+ const w = std.os.windows;
+
+ const sub_path_w = try std.os.windows.cStrToPrefixedFileW(ctx.dir.fd, "symlink");
+
+ var result = Dir{
+ .fd = undefined,
+ };
+
+ const path_len_bytes = @as(u16, @intCast(sub_path_w.span().len * 2));
+ var nt_name = w.UNICODE_STRING{
+ .Length = path_len_bytes,
+ .MaximumLength = path_len_bytes,
+ .Buffer = @constCast(&sub_path_w.data),
+ };
+ var attr = w.OBJECT_ATTRIBUTES{
+ .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
+ .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else ctx.dir.fd,
+ .Attributes = 0,
+ .ObjectName = &nt_name,
+ .SecurityDescriptor = null,
+ .SecurityQualityOfService = null,
+ };
+ var io: w.IO_STATUS_BLOCK = undefined;
+ const rc = w.ntdll.NtCreateFile(
+ &result.fd,
+ w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE,
+ &attr,
+ &io,
+ null,
+ w.FILE_ATTRIBUTE_NORMAL,
+ w.FILE_SHARE_READ | w.FILE_SHARE_WRITE,
+ w.FILE_OPEN,
+ // FILE_OPEN_REPARSE_POINT is the important thing here
+ w.FILE_OPEN_REPARSE_POINT | w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT,
+ null,
+ 0,
+ );
+
+ switch (rc) {
+ .SUCCESS => break :windows_symlink result,
+ else => return w.unexpectedStatus(rc),
+ }
+ },
+ .linux => linux_symlink: {
+ const sub_path_c = try os.toPosixPath("symlink");
+ // the O_NOFOLLOW | O_PATH combination can obtain a fd to a symlink
+ // note that if O_DIRECTORY is set, then this will error with ENOTDIR
+ const flags = os.O.NOFOLLOW | os.O.PATH | os.O.RDONLY | os.O.CLOEXEC;
+ const fd = try os.openatZ(ctx.dir.fd, &sub_path_c, flags, 0);
+ break :linux_symlink Dir{ .fd = fd };
+ },
+ else => unreachable,
+ };
defer symlink.close();
const stat = try symlink.stat();