Add prelim `openW` and `openatW`
Added POSIX functions targeting Windows pass `open` and `openat` smoke tests.master
parent
a694f575ad
commit
4d9eff4bdb
|
@ -364,6 +364,7 @@ pub const ChildProcess = struct {
|
|||
error.FileTooBig => unreachable,
|
||||
error.DeviceBusy => unreachable,
|
||||
error.FileLocksNotSupported => unreachable,
|
||||
error.BadPathName => unreachable, // Windows-only
|
||||
else => |e| return e,
|
||||
}
|
||||
else
|
||||
|
|
|
@ -1041,6 +1041,9 @@ pub const OpenError = error{
|
|||
|
||||
/// The underlying filesystem does not support file locks
|
||||
FileLocksNotSupported,
|
||||
|
||||
BadPathName,
|
||||
InvalidUtf8,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||
|
@ -1092,18 +1095,65 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t
|
|||
}
|
||||
}
|
||||
|
||||
fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions {
|
||||
const w = windows;
|
||||
|
||||
var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE;
|
||||
if (flags & O_RDWR != 0) {
|
||||
access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
|
||||
} else if (flags & O_WRONLY != 0) {
|
||||
access_mask |= w.GENERIC_WRITE;
|
||||
} else {
|
||||
access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
|
||||
}
|
||||
|
||||
const open_dir: bool = flags & O_DIRECTORY != 0;
|
||||
const follow_symlinks: bool = flags & O_NOFOLLOW == 0;
|
||||
|
||||
const creation: w.ULONG = blk: {
|
||||
if (flags & O_CREAT != 0) {
|
||||
if (flags & O_EXCL != 0) {
|
||||
break :blk w.FILE_CREATE;
|
||||
}
|
||||
}
|
||||
break :blk w.FILE_OPEN;
|
||||
};
|
||||
|
||||
return .{
|
||||
.access_mask = access_mask,
|
||||
.io_mode = .blocking,
|
||||
.creation = creation,
|
||||
.open_dir = open_dir,
|
||||
.follow_symlinks = follow_symlinks,
|
||||
};
|
||||
}
|
||||
|
||||
/// Windows-only. The path parameter is
|
||||
/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
|
||||
/// Translates the POSIX open API call to a Windows API call.
|
||||
pub fn openW(file_path_w: []const u16, flags: u32, perm: usize) OpenError!fd_t {
|
||||
@compileError("TODO implement openW for windows");
|
||||
/// TODO currently, this function does not handle all flag combinations
|
||||
/// or makes use of perm argument.
|
||||
pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t {
|
||||
var options = openOptionsFromFlags(flags);
|
||||
options.dir = std.fs.cwd().fd;
|
||||
return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
|
||||
error.WouldBlock => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||
/// `file_path` is relative to the open directory handle `dir_fd`.
|
||||
/// See also `openatC`.
|
||||
/// TODO support windows
|
||||
pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("use openatWasi instead");
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return openatW(dir_fd, file_path_w.span(), flags, mode);
|
||||
}
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return openatZ(dir_fd, &file_path_c, flags, mode);
|
||||
}
|
||||
|
@ -1145,8 +1195,11 @@ pub const openatC = @compileError("deprecated: renamed to openatZ");
|
|||
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||
/// `file_path` is relative to the open directory handle `dir_fd`.
|
||||
/// See also `openat`.
|
||||
/// TODO support windows
|
||||
pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
|
||||
return openatW(dir_fd, file_path_w.span(), flags, mode);
|
||||
}
|
||||
while (true) {
|
||||
const rc = system.openat(dir_fd, file_path, flags, mode);
|
||||
switch (errno(rc)) {
|
||||
|
@ -1177,6 +1230,20 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)
|
|||
}
|
||||
}
|
||||
|
||||
/// Windows-only. Similar to `openat` but with pathname argument null-terminated
|
||||
/// WTF16 encoded.
|
||||
/// TODO currently, this function does not handle all flag combinations
|
||||
/// or makes use of perm argument.
|
||||
pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t {
|
||||
var options = openOptionsFromFlags(flags);
|
||||
options.dir = dir_fd;
|
||||
return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
|
||||
error.WouldBlock => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
|
||||
while (true) {
|
||||
switch (errno(system.dup2(old_fd, new_fd))) {
|
||||
|
|
|
@ -237,3 +237,28 @@ pub const IPPROTO_TCP = ws2_32.IPPROTO_TCP;
|
|||
pub const IPPROTO_UDP = ws2_32.IPPROTO_UDP;
|
||||
pub const IPPROTO_ICMPV6 = ws2_32.IPPROTO_ICMPV6;
|
||||
pub const IPPROTO_RM = ws2_32.IPPROTO_RM;
|
||||
|
||||
pub const O_RDONLY = 0o0;
|
||||
pub const O_WRONLY = 0o1;
|
||||
pub const O_RDWR = 0o2;
|
||||
|
||||
pub const O_CREAT = 0o100;
|
||||
pub const O_EXCL = 0o200;
|
||||
pub const O_NOCTTY = 0o400;
|
||||
pub const O_TRUNC = 0o1000;
|
||||
pub const O_APPEND = 0o2000;
|
||||
pub const O_NONBLOCK = 0o4000;
|
||||
pub const O_DSYNC = 0o10000;
|
||||
pub const O_SYNC = 0o4010000;
|
||||
pub const O_RSYNC = 0o4010000;
|
||||
pub const O_DIRECTORY = 0o200000;
|
||||
pub const O_NOFOLLOW = 0o400000;
|
||||
pub const O_CLOEXEC = 0o2000000;
|
||||
|
||||
pub const O_ASYNC = 0o20000;
|
||||
pub const O_DIRECT = 0o40000;
|
||||
pub const O_LARGEFILE = 0;
|
||||
pub const O_NOATIME = 0o1000000;
|
||||
pub const O_PATH = 0o10000000;
|
||||
pub const O_TMPFILE = 0o20200000;
|
||||
pub const O_NDELAY = O_NONBLOCK;
|
|
@ -21,7 +21,6 @@ const Dir = std.fs.Dir;
|
|||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
|
||||
test "open smoke test" {
|
||||
if (builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
// TODO verify file attributes using `fstat`
|
||||
|
@ -40,41 +39,41 @@ test "open smoke test" {
|
|||
|
||||
var file_path: []u8 = undefined;
|
||||
var fd: os.fd_t = undefined;
|
||||
const mode: os.mode_t = if (builtin.os.tag == .windows) 0 else 0o666;
|
||||
|
||||
// Create some file using `open`.
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
fd = try os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666);
|
||||
fd = try os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try this again with the same flags. This op should fail with error.PathAlreadyExists.
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
expectError(error.PathAlreadyExists, os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666));
|
||||
expectError(error.PathAlreadyExists, os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, mode));
|
||||
|
||||
// Try opening without `O_EXCL` flag.
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
fd = try os.open(file_path, os.O_RDWR | os.O_CREAT, 0o666);
|
||||
fd = try os.open(file_path, os.O_RDWR | os.O_CREAT, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try opening as a directory which should fail.
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
expectError(error.NotDir, os.open(file_path, os.O_RDWR | os.O_DIRECTORY, 0o666));
|
||||
expectError(error.NotDir, os.open(file_path, os.O_RDWR | os.O_DIRECTORY, mode));
|
||||
|
||||
// Create some directory
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
|
||||
try os.mkdir(file_path, 0o666);
|
||||
try os.mkdir(file_path, mode);
|
||||
|
||||
// Open dir using `open`
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
|
||||
fd = try os.open(file_path, os.O_RDONLY | os.O_DIRECTORY, 0o666);
|
||||
fd = try os.open(file_path, os.O_RDONLY | os.O_DIRECTORY, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try opening as file which should fail.
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
|
||||
expectError(error.IsDir, os.open(file_path, os.O_RDWR, 0o666));
|
||||
expectError(error.IsDir, os.open(file_path, os.O_RDWR, mode));
|
||||
}
|
||||
|
||||
test "openat smoke test" {
|
||||
if (builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
// TODO verify file attributes using `fstatat`
|
||||
|
@ -83,30 +82,31 @@ test "openat smoke test" {
|
|||
defer tmp.cleanup();
|
||||
|
||||
var fd: os.fd_t = undefined;
|
||||
const mode: os.mode_t = if (builtin.os.tag == .windows) 0 else 0o666;
|
||||
|
||||
// Create some file using `openat`.
|
||||
fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666);
|
||||
fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try this again with the same flags. This op should fail with error.PathAlreadyExists.
|
||||
expectError(error.PathAlreadyExists, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666));
|
||||
expectError(error.PathAlreadyExists, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, mode));
|
||||
|
||||
// Try opening without `O_EXCL` flag.
|
||||
fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT, 0o666);
|
||||
fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try opening as a directory which should fail.
|
||||
expectError(error.NotDir, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_DIRECTORY, 0o666));
|
||||
expectError(error.NotDir, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_DIRECTORY, mode));
|
||||
|
||||
// Create some directory
|
||||
try os.mkdirat(tmp.dir.fd, "some_dir", 0o666);
|
||||
try os.mkdirat(tmp.dir.fd, "some_dir", mode);
|
||||
|
||||
// Open dir using `open`
|
||||
fd = try os.openat(tmp.dir.fd, "some_dir", os.O_RDONLY | os.O_DIRECTORY, 0o666);
|
||||
fd = try os.openat(tmp.dir.fd, "some_dir", os.O_RDONLY | os.O_DIRECTORY, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try opening as file which should fail.
|
||||
expectError(error.IsDir, os.openat(tmp.dir.fd, "some_dir", os.O_RDWR, 0o666));
|
||||
expectError(error.IsDir, os.openat(tmp.dir.fd, "some_dir", os.O_RDWR, mode));
|
||||
}
|
||||
|
||||
test "symlink with relative paths" {
|
||||
|
|
|
@ -27,6 +27,7 @@ pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
|
|||
|
||||
pub const OpenError = error{
|
||||
IsDir,
|
||||
NotDir,
|
||||
FileNotFound,
|
||||
NoDevice,
|
||||
AccessDenied,
|
||||
|
@ -125,7 +126,8 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
|
|||
.PIPE_BUSY => return error.PipeBusy,
|
||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
|
||||
.FILE_IS_A_DIRECTORY => if (options.open_dir) unreachable else return error.IsDir,
|
||||
.FILE_IS_A_DIRECTORY => return error.IsDir,
|
||||
.NOT_A_DIRECTORY => return error.NotDir,
|
||||
else => return unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
|
@ -609,6 +611,7 @@ pub fn CreateSymbolicLink(
|
|||
.open_dir = is_directory,
|
||||
}) catch |err| switch (err) {
|
||||
error.IsDir => return error.PathAlreadyExists,
|
||||
error.NotDir => unreachable,
|
||||
error.WouldBlock => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
else => |e| return e,
|
||||
|
|
Loading…
Reference in New Issue