aboutsummaryrefslogtreecommitdiff
path: root/std/os/file.zig
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2018-08-25 21:57:28 -0400
committerAndrew Kelley <superjoe30@gmail.com>2018-08-25 21:57:28 -0400
commit7109035b78ee05302bbdaadc52013b430a030b69 (patch)
treeae6d7202dc75f2c799f5fbcad72ccf8b02a954a4 /std/os/file.zig
parent6cf248ec0824c746fc796905144c8077ccab99cf (diff)
parent526338b00fbe1cac19f64832176af3bdf2108a56 (diff)
downloadzig-7109035b78ee05302bbdaadc52013b430a030b69.tar.gz
zig-7109035b78ee05302bbdaadc52013b430a030b69.zip
Merge remote-tracking branch 'origin/master' into llvm7
Diffstat (limited to 'std/os/file.zig')
-rw-r--r--std/os/file.zig172
1 files changed, 111 insertions, 61 deletions
diff --git a/std/os/file.zig b/std/os/file.zig
index 6998ba00d1..1f5ce7cf9d 100644
--- a/std/os/file.zig
+++ b/std/os/file.zig
@@ -7,6 +7,7 @@ const assert = std.debug.assert;
const posix = os.posix;
const windows = os.windows;
const Os = builtin.Os;
+const windows_util = @import("windows/util.zig");
const is_posix = builtin.os != builtin.Os.windows;
const is_windows = builtin.os == builtin.Os.windows;
@@ -15,18 +16,39 @@ pub const File = struct {
/// The OS-specific file descriptor or file handle.
handle: os.FileHandle,
+ pub const Mode = switch (builtin.os) {
+ Os.windows => void,
+ else => u32,
+ };
+
+ pub const default_mode = switch (builtin.os) {
+ Os.windows => {},
+ else => 0o666,
+ };
+
pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
- /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
- /// Call close to clean up.
- pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File {
+ /// `openRead` except with a null terminated path
+ pub fn openReadC(path: [*]const u8) OpenError!File {
if (is_posix) {
const flags = posix.O_LARGEFILE | posix.O_RDONLY;
- const fd = try os.posixOpen(allocator, path, flags, 0);
+ const fd = try os.posixOpenC(path, flags, 0);
return openHandle(fd);
- } else if (is_windows) {
+ }
+ if (is_windows) {
+ return openRead(mem.toSliceConst(u8, path));
+ }
+ @compileError("Unsupported OS");
+ }
+
+ /// Call close to clean up.
+ pub fn openRead(path: []const u8) OpenError!File {
+ if (is_posix) {
+ const path_c = try os.toPosixPath(path);
+ return openReadC(&path_c);
+ }
+ if (is_windows) {
const handle = try os.windowsOpen(
- allocator,
path,
windows.GENERIC_READ,
windows.FILE_SHARE_READ,
@@ -34,28 +56,25 @@ pub const File = struct {
windows.FILE_ATTRIBUTE_NORMAL,
);
return openHandle(handle);
- } else {
- @compileError("TODO implement openRead for this OS");
}
+ @compileError("Unsupported OS");
}
- /// Calls `openWriteMode` with os.default_file_mode for the mode.
- pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {
- return openWriteMode(allocator, path, os.default_file_mode);
+ /// Calls `openWriteMode` with os.File.default_mode for the mode.
+ pub fn openWrite(path: []const u8) OpenError!File {
+ return openWriteMode(path, os.File.default_mode);
}
/// If the path does not exist it will be created.
/// If a file already exists in the destination it will be truncated.
- /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
/// Call close to clean up.
- pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {
+ pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File {
if (is_posix) {
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
- const fd = try os.posixOpen(allocator, path, flags, file_mode);
+ const fd = try os.posixOpen(path, flags, file_mode);
return openHandle(fd);
} else if (is_windows) {
const handle = try os.windowsOpen(
- allocator,
path,
windows.GENERIC_WRITE,
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
@@ -70,16 +89,14 @@ pub const File = struct {
/// If the path does not exist it will be created.
/// If a file already exists in the destination this returns OpenError.PathAlreadyExists
- /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
/// Call close to clean up.
- pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {
+ pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File {
if (is_posix) {
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
- const fd = try os.posixOpen(allocator, path, flags, file_mode);
+ const fd = try os.posixOpen(path, flags, file_mode);
return openHandle(fd);
} else if (is_windows) {
const handle = try os.windowsOpen(
- allocator,
path,
windows.GENERIC_WRITE,
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
@@ -98,23 +115,43 @@ pub const File = struct {
pub const AccessError = error{
PermissionDenied,
- NotFound,
+ FileNotFound,
NameTooLong,
- BadMode,
- BadPathName,
- Io,
+ InputOutput,
SystemResources,
- OutOfMemory,
+ BadPathName,
+
+ /// On Windows, file paths must be valid Unicode.
+ InvalidUtf8,
Unexpected,
};
- pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void {
- const path_with_null = try std.cstr.addNullByte(allocator, path);
- defer allocator.free(path_with_null);
+ /// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
+ /// Otherwise use `access` or `accessC`.
+ pub fn accessW(path: [*]const u16) AccessError!void {
+ if (os.windows.GetFileAttributesW(path) != os.windows.INVALID_FILE_ATTRIBUTES) {
+ return;
+ }
+
+ const err = windows.GetLastError();
+ switch (err) {
+ windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
+ windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
+ windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
+ else => return os.unexpectedErrorWindows(err),
+ }
+ }
+ /// Call if you have a UTF-8 encoded, null-terminated string.
+ /// Otherwise use `access` or `accessW`.
+ pub fn accessC(path: [*]const u8) AccessError!void {
+ if (is_windows) {
+ const path_w = try windows_util.cStrToPrefixedFileW(path);
+ return accessW(&path_w);
+ }
if (is_posix) {
- const result = posix.access(path_with_null.ptr, posix.F_OK);
+ const result = posix.access(path, posix.F_OK);
const err = posix.getErrno(result);
switch (err) {
0 => return,
@@ -122,32 +159,33 @@ pub const File = struct {
posix.EROFS => return error.PermissionDenied,
posix.ELOOP => return error.PermissionDenied,
posix.ETXTBSY => return error.PermissionDenied,
- posix.ENOTDIR => return error.NotFound,
- posix.ENOENT => return error.NotFound,
+ posix.ENOTDIR => return error.FileNotFound,
+ posix.ENOENT => return error.FileNotFound,
posix.ENAMETOOLONG => return error.NameTooLong,
posix.EINVAL => unreachable,
- posix.EFAULT => return error.BadPathName,
- posix.EIO => return error.Io,
+ posix.EFAULT => unreachable,
+ posix.EIO => return error.InputOutput,
posix.ENOMEM => return error.SystemResources,
else => return os.unexpectedErrorPosix(err),
}
- } else if (is_windows) {
- if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
- return;
- }
+ }
+ @compileError("Unsupported OS");
+ }
- const err = windows.GetLastError();
- switch (err) {
- windows.ERROR.FILE_NOT_FOUND,
- windows.ERROR.PATH_NOT_FOUND,
- => return error.NotFound,
- windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
- else => return os.unexpectedErrorWindows(err),
- }
- } else {
- @compileError("TODO implement access for this OS");
+ pub fn access(path: []const u8) AccessError!void {
+ if (is_windows) {
+ const path_w = try windows_util.sliceToPrefixedFileW(path);
+ return accessW(&path_w);
+ }
+ if (is_posix) {
+ var path_with_null: [posix.PATH_MAX]u8 = undefined;
+ if (path.len >= posix.PATH_MAX) return error.NameTooLong;
+ mem.copy(u8, path_with_null[0..], path);
+ path_with_null[path.len] = 0;
+ return accessC(&path_with_null);
}
+ @compileError("Unsupported OS");
}
/// Upon success, the stream is in an uninitialized state. To continue using it,
@@ -169,7 +207,9 @@ pub const File = struct {
const err = posix.getErrno(result);
if (err > 0) {
return switch (err) {
- posix.EBADF => error.BadFd,
+ // We do not make this an error code because if you get EBADF it's always a bug,
+ // since the fd could have been reused.
+ posix.EBADF => unreachable,
posix.EINVAL => error.Unseekable,
posix.EOVERFLOW => error.Unseekable,
posix.ESPIPE => error.Unseekable,
@@ -182,7 +222,7 @@ pub const File = struct {
if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) {
const err = windows.GetLastError();
return switch (err) {
- windows.ERROR.INVALID_PARAMETER => error.BadFd,
+ windows.ERROR.INVALID_PARAMETER => unreachable,
else => os.unexpectedErrorWindows(err),
};
}
@@ -199,7 +239,9 @@ pub const File = struct {
const err = posix.getErrno(result);
if (err > 0) {
return switch (err) {
- posix.EBADF => error.BadFd,
+ // We do not make this an error code because if you get EBADF it's always a bug,
+ // since the fd could have been reused.
+ posix.EBADF => unreachable,
posix.EINVAL => error.Unseekable,
posix.EOVERFLOW => error.Unseekable,
posix.ESPIPE => error.Unseekable,
@@ -213,7 +255,7 @@ pub const File = struct {
if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) {
const err = windows.GetLastError();
return switch (err) {
- windows.ERROR.INVALID_PARAMETER => error.BadFd,
+ windows.ERROR.INVALID_PARAMETER => unreachable,
else => os.unexpectedErrorWindows(err),
};
}
@@ -229,7 +271,9 @@ pub const File = struct {
const err = posix.getErrno(result);
if (err > 0) {
return switch (err) {
- posix.EBADF => error.BadFd,
+ // We do not make this an error code because if you get EBADF it's always a bug,
+ // since the fd could have been reused.
+ posix.EBADF => unreachable,
posix.EINVAL => error.Unseekable,
posix.EOVERFLOW => error.Unseekable,
posix.ESPIPE => error.Unseekable,
@@ -244,7 +288,7 @@ pub const File = struct {
if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {
const err = windows.GetLastError();
return switch (err) {
- windows.ERROR.INVALID_PARAMETER => error.BadFd,
+ windows.ERROR.INVALID_PARAMETER => unreachable,
else => os.unexpectedErrorWindows(err),
};
}
@@ -277,18 +321,19 @@ pub const File = struct {
}
pub const ModeError = error{
- BadFd,
SystemResources,
Unexpected,
};
- pub fn mode(self: *File) ModeError!os.FileMode {
+ pub fn mode(self: *File) ModeError!Mode {
if (is_posix) {
var stat: posix.Stat = undefined;
const err = posix.getErrno(posix.fstat(self.handle, &stat));
if (err > 0) {
return switch (err) {
- posix.EBADF => error.BadFd,
+ // We do not make this an error code because if you get EBADF it's always a bug,
+ // since the fd could have been reused.
+ posix.EBADF => unreachable,
posix.ENOMEM => error.SystemResources,
else => os.unexpectedErrorPosix(err),
};
@@ -296,7 +341,7 @@ pub const File = struct {
// TODO: we should be able to cast u16 to ModeError!u32, making this
// explicit cast not necessary
- return os.FileMode(stat.mode);
+ return Mode(stat.mode);
} else if (is_windows) {
return {};
} else {
@@ -305,9 +350,11 @@ pub const File = struct {
}
pub const ReadError = error{
- BadFd,
- Io,
+ FileClosed,
+ InputOutput,
IsDir,
+ WouldBlock,
+ SystemResources,
Unexpected,
};
@@ -323,9 +370,12 @@ pub const File = struct {
posix.EINTR => continue,
posix.EINVAL => unreachable,
posix.EFAULT => unreachable,
- posix.EBADF => return error.BadFd,
- posix.EIO => return error.Io,
+ posix.EAGAIN => return error.WouldBlock,
+ posix.EBADF => return error.FileClosed,
+ posix.EIO => return error.InputOutput,
posix.EISDIR => return error.IsDir,
+ posix.ENOBUFS => return error.SystemResources,
+ posix.ENOMEM => return error.SystemResources,
else => return os.unexpectedErrorPosix(read_err),
}
}
@@ -338,7 +388,7 @@ pub const File = struct {
while (index < buffer.len) {
const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));
var amt_read: windows.DWORD = undefined;
- if (windows.ReadFile(self.handle, @ptrCast(*c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) {
+ if (windows.ReadFile(self.handle, buffer.ptr + index, want_read_count, &amt_read, null) == 0) {
const err = windows.GetLastError();
return switch (err) {
windows.ERROR.OPERATION_ABORTED => continue,