2017-05-01 17:12:38 +00:00
|
|
|
const builtin = @import("builtin");
|
|
|
|
const Os = builtin.Os;
|
2017-03-26 10:39:28 +00:00
|
|
|
pub const windows = @import("windows.zig");
|
|
|
|
pub const darwin = @import("darwin.zig");
|
|
|
|
pub const linux = @import("linux.zig");
|
2017-05-01 17:12:38 +00:00
|
|
|
pub const posix = switch(builtin.os) {
|
2017-03-26 10:39:28 +00:00
|
|
|
Os.linux => linux,
|
|
|
|
Os.darwin, Os.macosx, Os.ios => darwin,
|
|
|
|
Os.windows => windows,
|
2016-09-12 04:01:06 +00:00
|
|
|
else => @compileError("Unsupported OS"),
|
|
|
|
};
|
2017-04-02 23:14:23 +00:00
|
|
|
|
2017-04-03 08:58:19 +00:00
|
|
|
pub const max_noalloc_path_len = 1024;
|
2017-04-04 04:17:24 +00:00
|
|
|
pub const ChildProcess = @import("child_process.zig").ChildProcess;
|
2017-04-17 10:45:44 +00:00
|
|
|
pub const path = @import("path.zig");
|
2017-04-03 08:58:19 +00:00
|
|
|
|
2017-05-01 17:12:38 +00:00
|
|
|
pub const line_sep = switch (builtin.os) {
|
2017-04-19 05:13:15 +00:00
|
|
|
Os.windows => "\r\n",
|
|
|
|
else => "\n",
|
|
|
|
};
|
|
|
|
|
2017-04-20 06:26:36 +00:00
|
|
|
pub const page_size = 4 * 1024;
|
|
|
|
|
2017-04-02 22:19:59 +00:00
|
|
|
const debug = @import("../debug.zig");
|
|
|
|
const assert = debug.assert;
|
2017-03-26 10:39:28 +00:00
|
|
|
|
2016-02-28 05:06:46 +00:00
|
|
|
const errno = @import("errno.zig");
|
2017-03-26 10:39:28 +00:00
|
|
|
const linking_libc = @import("../target.zig").linking_libc;
|
|
|
|
const c = @import("../c/index.zig");
|
2016-02-04 08:00:54 +00:00
|
|
|
|
2017-04-02 22:19:59 +00:00
|
|
|
const mem = @import("../mem.zig");
|
|
|
|
const Allocator = mem.Allocator;
|
|
|
|
|
2017-04-04 05:52:20 +00:00
|
|
|
const BufMap = @import("../buf_map.zig").BufMap;
|
2017-04-03 22:11:57 +00:00
|
|
|
const cstr = @import("../cstr.zig");
|
2017-04-02 22:19:59 +00:00
|
|
|
|
2017-04-17 10:45:44 +00:00
|
|
|
const io = @import("../io.zig");
|
2017-04-17 23:08:41 +00:00
|
|
|
const base64 = @import("../base64.zig");
|
2017-05-04 18:05:06 +00:00
|
|
|
const ArrayList = @import("../array_list.zig").ArrayList;
|
2017-04-17 10:45:44 +00:00
|
|
|
|
2016-12-22 04:34:14 +00:00
|
|
|
error Unexpected;
|
2017-04-17 06:58:42 +00:00
|
|
|
error SystemResources;
|
2017-04-02 22:19:59 +00:00
|
|
|
error AccessDenied;
|
|
|
|
error InvalidExe;
|
|
|
|
error FileSystem;
|
|
|
|
error IsDir;
|
|
|
|
error FileNotFound;
|
|
|
|
error FileBusy;
|
2017-04-17 23:08:41 +00:00
|
|
|
error PathAlreadyExists;
|
2017-04-17 06:58:42 +00:00
|
|
|
error SymLinkLoop;
|
|
|
|
error ReadOnlyFileSystem;
|
2017-04-17 23:08:41 +00:00
|
|
|
error LinkQuotaExceeded;
|
|
|
|
error RenameAcrossMountPoints;
|
2017-04-20 06:26:36 +00:00
|
|
|
error DirNotEmpty;
|
2017-05-01 23:16:48 +00:00
|
|
|
error WouldBlock;
|
2016-02-04 08:00:54 +00:00
|
|
|
|
2017-03-23 06:59:58 +00:00
|
|
|
/// Fills `buf` with random bytes. If linking against libc, this calls the
|
|
|
|
/// appropriate OS-specific library call. Otherwise it uses the zig standard
|
|
|
|
/// library implementation.
|
2016-08-18 03:11:04 +00:00
|
|
|
pub fn getRandomBytes(buf: []u8) -> %void {
|
2016-09-12 04:01:06 +00:00
|
|
|
while (true) {
|
2017-05-01 17:12:38 +00:00
|
|
|
const err = switch (builtin.os) {
|
2017-03-23 06:59:58 +00:00
|
|
|
Os.linux => {
|
|
|
|
if (linking_libc) {
|
|
|
|
if (c.getrandom(buf.ptr, buf.len, 0) == -1) *c._errno() else 0
|
|
|
|
} else {
|
|
|
|
posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Os.darwin, Os.macosx, Os.ios => {
|
|
|
|
if (linking_libc) {
|
|
|
|
if (posix.getrandom(buf.ptr, buf.len) == -1) *c._errno() else 0
|
|
|
|
} else {
|
|
|
|
posix.getErrno(posix.getrandom(buf.ptr, buf.len))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Os.windows => {
|
|
|
|
var hCryptProv: windows.HCRYPTPROV = undefined;
|
|
|
|
if (!windows.CryptAcquireContext(&hCryptProv, null, null, windows.PROV_RSA_FULL, 0)) {
|
|
|
|
return error.Unexpected;
|
|
|
|
}
|
|
|
|
defer _ = windows.CryptReleaseContext(hCryptProv, 0);
|
|
|
|
|
|
|
|
if (!windows.CryptGenRandom(hCryptProv, windows.DWORD(buf.len), buf.ptr)) {
|
|
|
|
return error.Unexpected;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
},
|
|
|
|
else => @compileError("Unsupported OS"),
|
2016-09-12 04:01:06 +00:00
|
|
|
};
|
|
|
|
if (err > 0) {
|
|
|
|
return switch (err) {
|
2017-03-26 08:58:48 +00:00
|
|
|
errno.EINVAL => unreachable,
|
|
|
|
errno.EFAULT => unreachable,
|
2016-09-12 04:01:06 +00:00
|
|
|
errno.EINTR => continue,
|
|
|
|
else => error.Unexpected,
|
2016-02-14 05:59:49 +00:00
|
|
|
}
|
2016-09-12 04:01:06 +00:00
|
|
|
}
|
|
|
|
return;
|
2016-02-04 08:00:54 +00:00
|
|
|
}
|
|
|
|
}
|
2016-04-18 23:42:56 +00:00
|
|
|
|
2017-03-23 06:59:58 +00:00
|
|
|
/// Raises a signal in the current kernel thread, ending its execution.
|
|
|
|
/// If linking against libc, this calls the abort() libc function. Otherwise
|
|
|
|
/// it uses the zig standard library implementation.
|
2017-03-26 08:58:48 +00:00
|
|
|
pub coldcc fn abort() -> noreturn {
|
2017-03-23 06:59:58 +00:00
|
|
|
if (linking_libc) {
|
|
|
|
c.abort();
|
|
|
|
}
|
2017-05-01 17:12:38 +00:00
|
|
|
switch (builtin.os) {
|
2017-04-03 00:44:04 +00:00
|
|
|
Os.linux, Os.darwin, Os.macosx, Os.ios => {
|
2017-03-23 06:59:58 +00:00
|
|
|
_ = posix.raise(posix.SIGABRT);
|
|
|
|
_ = posix.raise(posix.SIGKILL);
|
2016-08-18 03:11:04 +00:00
|
|
|
while (true) {}
|
|
|
|
},
|
2017-03-23 06:59:58 +00:00
|
|
|
else => @compileError("Unsupported OS"),
|
2016-08-18 03:11:04 +00:00
|
|
|
}
|
2016-04-18 23:42:56 +00:00
|
|
|
}
|
2017-04-02 22:19:59 +00:00
|
|
|
|
2017-04-03 08:58:19 +00:00
|
|
|
/// Calls POSIX close, and keeps trying if it gets interrupted.
|
|
|
|
pub fn posixClose(fd: i32) {
|
2017-04-02 22:19:59 +00:00
|
|
|
while (true) {
|
|
|
|
const err = posix.getErrno(posix.close(fd));
|
|
|
|
if (err == errno.EINTR) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-01 23:16:48 +00:00
|
|
|
error WouldBlock;
|
|
|
|
error FileClosed;
|
|
|
|
error DestinationAddressRequired;
|
|
|
|
error FileSystem;
|
|
|
|
|
2017-04-03 08:58:19 +00:00
|
|
|
/// Calls POSIX write, and keeps trying if it gets interrupted.
|
|
|
|
pub fn posixWrite(fd: i32, bytes: []const u8) -> %void {
|
|
|
|
while (true) {
|
|
|
|
const write_ret = posix.write(fd, bytes.ptr, bytes.len);
|
|
|
|
const write_err = posix.getErrno(write_ret);
|
|
|
|
if (write_err > 0) {
|
|
|
|
return switch (write_err) {
|
|
|
|
errno.EINTR => continue,
|
2017-05-01 23:16:48 +00:00
|
|
|
errno.EINVAL, errno.EFAULT => unreachable,
|
|
|
|
errno.EAGAIN => error.WouldBlock,
|
|
|
|
errno.EBADF => error.FileClosed,
|
|
|
|
errno.EDESTADDRREQ => error.DestinationAddressRequired,
|
2017-04-03 08:58:19 +00:00
|
|
|
errno.EDQUOT => error.DiskQuota,
|
|
|
|
errno.EFBIG => error.FileTooBig,
|
2017-05-01 23:16:48 +00:00
|
|
|
errno.EIO => error.FileSystem,
|
2017-04-03 08:58:19 +00:00
|
|
|
errno.ENOSPC => error.NoSpaceLeft,
|
2017-05-01 23:16:48 +00:00
|
|
|
errno.EPERM => error.AccessDenied,
|
2017-04-03 08:58:19 +00:00
|
|
|
errno.EPIPE => error.PipeFail,
|
|
|
|
else => error.Unexpected,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-17 10:45:44 +00:00
|
|
|
/// ::file_path may need to be copied in memory to add a null terminating byte. In this case
|
2017-04-03 08:58:19 +00:00
|
|
|
/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed
|
|
|
|
/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned.
|
|
|
|
/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
|
|
|
|
/// Calls POSIX open, keeps trying if it gets interrupted, and translates
|
|
|
|
/// the return value into zig errors.
|
2017-04-17 10:45:44 +00:00
|
|
|
pub fn posixOpen(file_path: []const u8, flags: usize, perm: usize, allocator: ?&Allocator) -> %i32 {
|
2017-04-03 08:58:19 +00:00
|
|
|
var stack_buf: [max_noalloc_path_len]u8 = undefined;
|
|
|
|
var path0: []u8 = undefined;
|
|
|
|
var need_free = false;
|
|
|
|
|
2017-04-17 10:45:44 +00:00
|
|
|
if (file_path.len < stack_buf.len) {
|
|
|
|
path0 = stack_buf[0...file_path.len + 1];
|
2017-05-03 21:23:11 +00:00
|
|
|
} else if (allocator) |a| {
|
2017-04-17 10:45:44 +00:00
|
|
|
path0 = %return a.alloc(u8, file_path.len + 1);
|
2017-04-03 08:58:19 +00:00
|
|
|
need_free = true;
|
|
|
|
} else {
|
|
|
|
return error.NameTooLong;
|
|
|
|
}
|
|
|
|
defer if (need_free) {
|
|
|
|
(??allocator).free(path0);
|
|
|
|
};
|
2017-04-17 10:45:44 +00:00
|
|
|
mem.copy(u8, path0, file_path);
|
|
|
|
path0[file_path.len] = 0;
|
2017-04-03 08:58:19 +00:00
|
|
|
|
2017-04-02 22:19:59 +00:00
|
|
|
while (true) {
|
2017-04-03 08:58:19 +00:00
|
|
|
const result = posix.open(path0.ptr, flags, perm);
|
2017-04-02 22:19:59 +00:00
|
|
|
const err = posix.getErrno(result);
|
|
|
|
if (err > 0) {
|
|
|
|
return switch (err) {
|
|
|
|
errno.EINTR => continue,
|
|
|
|
|
|
|
|
errno.EFAULT => unreachable,
|
|
|
|
errno.EINVAL => unreachable,
|
2017-05-01 23:16:48 +00:00
|
|
|
errno.EACCES => error.AccessDenied,
|
2017-04-02 22:19:59 +00:00
|
|
|
errno.EFBIG, errno.EOVERFLOW => error.FileTooBig,
|
|
|
|
errno.EISDIR => error.IsDir,
|
|
|
|
errno.ELOOP => error.SymLinkLoop,
|
|
|
|
errno.EMFILE => error.ProcessFdQuotaExceeded,
|
|
|
|
errno.ENAMETOOLONG => error.NameTooLong,
|
|
|
|
errno.ENFILE => error.SystemFdQuotaExceeded,
|
|
|
|
errno.ENODEV => error.NoDevice,
|
|
|
|
errno.ENOENT => error.PathNotFound,
|
2017-04-17 06:58:42 +00:00
|
|
|
errno.ENOMEM => error.SystemResources,
|
2017-04-02 22:19:59 +00:00
|
|
|
errno.ENOSPC => error.NoSpaceLeft,
|
|
|
|
errno.ENOTDIR => error.NotDir,
|
2017-05-01 23:16:48 +00:00
|
|
|
errno.EPERM => error.AccessDenied,
|
2017-04-02 22:19:59 +00:00
|
|
|
else => error.Unexpected,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return i32(result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-04 04:17:24 +00:00
|
|
|
pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void {
|
2017-04-02 22:19:59 +00:00
|
|
|
while (true) {
|
|
|
|
const err = posix.getErrno(posix.dup2(old_fd, new_fd));
|
|
|
|
if (err > 0) {
|
|
|
|
return switch (err) {
|
|
|
|
errno.EBUSY, errno.EINTR => continue,
|
2017-04-17 23:08:41 +00:00
|
|
|
errno.EMFILE => error.ProcessFdQuotaExceeded,
|
2017-04-02 22:19:59 +00:00
|
|
|
errno.EINVAL => unreachable,
|
|
|
|
else => error.Unexpected,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-03 00:44:04 +00:00
|
|
|
/// This function must allocate memory to add a null terminating bytes on path and each arg.
|
|
|
|
/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
|
|
|
|
/// pointers after the args and after the environment variables.
|
2017-04-05 21:55:50 +00:00
|
|
|
/// Also make the first arg equal to exe_path.
|
|
|
|
/// This function also uses the PATH environment variable to get the full path to the executable.
|
|
|
|
pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &const BufMap,
|
|
|
|
allocator: &Allocator) -> %void
|
2017-04-04 04:17:24 +00:00
|
|
|
{
|
2017-04-21 05:56:12 +00:00
|
|
|
const argv_buf = %return allocator.alloc(?&u8, argv.len + 2);
|
|
|
|
mem.set(?&u8, argv_buf, null);
|
2017-04-03 00:44:04 +00:00
|
|
|
defer {
|
2017-04-04 10:47:42 +00:00
|
|
|
for (argv_buf) |arg| {
|
2017-05-03 21:23:11 +00:00
|
|
|
const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break;
|
2017-04-03 00:44:04 +00:00
|
|
|
allocator.free(arg_buf);
|
|
|
|
}
|
|
|
|
allocator.free(argv_buf);
|
|
|
|
}
|
|
|
|
{
|
2017-04-05 21:55:50 +00:00
|
|
|
// Add exe_path to the first argument.
|
|
|
|
const arg_buf = %return allocator.alloc(u8, exe_path.len + 1);
|
|
|
|
@memcpy(&arg_buf[0], exe_path.ptr, exe_path.len);
|
|
|
|
arg_buf[exe_path.len] = 0;
|
2017-04-03 00:44:04 +00:00
|
|
|
|
|
|
|
argv_buf[0] = arg_buf.ptr;
|
|
|
|
}
|
|
|
|
for (argv) |arg, i| {
|
|
|
|
const arg_buf = %return allocator.alloc(u8, arg.len + 1);
|
|
|
|
@memcpy(&arg_buf[0], arg.ptr, arg.len);
|
|
|
|
arg_buf[arg.len] = 0;
|
|
|
|
|
|
|
|
argv_buf[i + 1] = arg_buf.ptr;
|
|
|
|
}
|
|
|
|
argv_buf[argv.len + 1] = null;
|
|
|
|
|
2017-04-03 22:11:57 +00:00
|
|
|
const envp_count = env_map.count();
|
2017-04-21 05:56:12 +00:00
|
|
|
const envp_buf = %return allocator.alloc(?&u8, envp_count + 1);
|
|
|
|
mem.set(?&u8, envp_buf, null);
|
2017-04-03 00:44:04 +00:00
|
|
|
defer {
|
2017-04-04 10:47:42 +00:00
|
|
|
for (envp_buf) |env| {
|
2017-05-03 21:23:11 +00:00
|
|
|
const env_buf = if (env) |ptr| cstr.toSlice(ptr) else break;
|
2017-04-03 00:44:04 +00:00
|
|
|
allocator.free(env_buf);
|
|
|
|
}
|
|
|
|
allocator.free(envp_buf);
|
|
|
|
}
|
2017-04-03 22:11:57 +00:00
|
|
|
{
|
|
|
|
var it = env_map.iterator();
|
|
|
|
var i: usize = 0;
|
2017-05-04 14:37:19 +00:00
|
|
|
while (it.next()) |pair| : (i += 1) {
|
2017-04-03 22:11:57 +00:00
|
|
|
const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2);
|
|
|
|
@memcpy(&env_buf[0], pair.key.ptr, pair.key.len);
|
|
|
|
env_buf[pair.key.len] = '=';
|
|
|
|
@memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len);
|
|
|
|
env_buf[env_buf.len - 1] = 0;
|
|
|
|
|
|
|
|
envp_buf[i] = env_buf.ptr;
|
|
|
|
}
|
|
|
|
assert(i == envp_count);
|
2017-04-03 00:44:04 +00:00
|
|
|
}
|
2017-04-03 22:11:57 +00:00
|
|
|
envp_buf[envp_count] = null;
|
2017-04-03 00:44:04 +00:00
|
|
|
|
2017-04-05 21:55:50 +00:00
|
|
|
|
|
|
|
if (mem.indexOfScalar(u8, exe_path, '/') != null) {
|
|
|
|
// +1 for the null terminating byte
|
|
|
|
const path_buf = %return allocator.alloc(u8, exe_path.len + 1);
|
|
|
|
defer allocator.free(path_buf);
|
|
|
|
@memcpy(&path_buf[0], &exe_path[0], exe_path.len);
|
|
|
|
path_buf[exe_path.len] = 0;
|
|
|
|
return posixExecveErrnoToErr(posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)));
|
|
|
|
}
|
|
|
|
|
2017-04-08 21:45:22 +00:00
|
|
|
const PATH = getEnv("PATH") ?? "/usr/local/bin:/bin/:/usr/bin";
|
2017-04-05 21:55:50 +00:00
|
|
|
// PATH.len because it is >= the largest search_path
|
|
|
|
// +1 for the / to join the search path and exe_path
|
|
|
|
// +1 for the null terminating byte
|
|
|
|
const path_buf = %return allocator.alloc(u8, PATH.len + exe_path.len + 2);
|
|
|
|
defer allocator.free(path_buf);
|
|
|
|
var it = mem.split(PATH, ':');
|
|
|
|
var seen_eacces = false;
|
|
|
|
var err: usize = undefined;
|
2017-05-04 14:37:19 +00:00
|
|
|
while (it.next()) |search_path| {
|
2017-04-05 21:55:50 +00:00
|
|
|
mem.copy(u8, path_buf, search_path);
|
|
|
|
path_buf[search_path.len] = '/';
|
|
|
|
mem.copy(u8, path_buf[search_path.len + 1 ...], exe_path);
|
2017-04-21 17:12:30 +00:00
|
|
|
path_buf[search_path.len + exe_path.len + 1] = 0;
|
2017-04-05 21:55:50 +00:00
|
|
|
err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr));
|
|
|
|
assert(err > 0);
|
|
|
|
if (err == errno.EACCES) {
|
|
|
|
seen_eacces = true;
|
|
|
|
} else if (err != errno.ENOENT) {
|
|
|
|
return posixExecveErrnoToErr(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (seen_eacces) {
|
|
|
|
err = errno.EACCES;
|
|
|
|
}
|
|
|
|
return posixExecveErrnoToErr(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn posixExecveErrnoToErr(err: usize) -> error {
|
|
|
|
assert(err > 0);
|
|
|
|
return switch (err) {
|
|
|
|
errno.EFAULT => unreachable,
|
2017-04-17 06:58:42 +00:00
|
|
|
errno.E2BIG, errno.EMFILE, errno.ENAMETOOLONG, errno.ENFILE, errno.ENOMEM => error.SystemResources,
|
2017-04-05 21:55:50 +00:00
|
|
|
errno.EACCES, errno.EPERM => error.AccessDenied,
|
|
|
|
errno.EINVAL, errno.ENOEXEC => error.InvalidExe,
|
|
|
|
errno.EIO, errno.ELOOP => error.FileSystem,
|
|
|
|
errno.EISDIR => error.IsDir,
|
2017-04-19 05:13:15 +00:00
|
|
|
errno.ENOENT => error.FileNotFound,
|
|
|
|
errno.ENOTDIR => error.NotDir,
|
2017-04-05 21:55:50 +00:00
|
|
|
errno.ETXTBSY => error.FileBusy,
|
|
|
|
else => error.Unexpected,
|
|
|
|
};
|
2017-04-03 00:44:04 +00:00
|
|
|
}
|
|
|
|
|
2017-04-03 22:11:57 +00:00
|
|
|
pub var environ_raw: []&u8 = undefined;
|
|
|
|
|
2017-04-04 05:52:20 +00:00
|
|
|
pub fn getEnvMap(allocator: &Allocator) -> %BufMap {
|
|
|
|
var result = BufMap.init(allocator);
|
2017-04-03 22:11:57 +00:00
|
|
|
%defer result.deinit();
|
|
|
|
|
|
|
|
for (environ_raw) |ptr| {
|
|
|
|
var line_i: usize = 0;
|
2017-05-03 22:12:07 +00:00
|
|
|
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
|
2017-04-03 22:11:57 +00:00
|
|
|
const key = ptr[0...line_i];
|
|
|
|
|
|
|
|
var end_i: usize = line_i;
|
2017-05-03 22:12:07 +00:00
|
|
|
while (ptr[end_i] != 0) : (end_i += 1) {}
|
2017-04-03 22:11:57 +00:00
|
|
|
const value = ptr[line_i + 1...end_i];
|
|
|
|
|
|
|
|
%return result.set(key, value);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2017-04-02 23:14:23 +00:00
|
|
|
|
|
|
|
pub fn getEnv(key: []const u8) -> ?[]const u8 {
|
2017-04-03 22:11:57 +00:00
|
|
|
for (environ_raw) |ptr| {
|
|
|
|
var line_i: usize = 0;
|
2017-05-03 22:12:07 +00:00
|
|
|
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
|
2017-04-03 22:11:57 +00:00
|
|
|
const this_key = ptr[0...line_i];
|
|
|
|
if (!mem.eql(u8, key, this_key))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
var end_i: usize = line_i;
|
2017-05-03 22:12:07 +00:00
|
|
|
while (ptr[end_i] != 0) : (end_i += 1) {}
|
2017-04-03 22:11:57 +00:00
|
|
|
const this_value = ptr[line_i + 1...end_i];
|
|
|
|
|
|
|
|
return this_value;
|
2017-04-02 23:14:23 +00:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2017-04-03 22:11:57 +00:00
|
|
|
|
2017-04-04 04:17:24 +00:00
|
|
|
pub const args = struct {
|
|
|
|
pub var raw: []&u8 = undefined;
|
2017-04-03 22:11:57 +00:00
|
|
|
|
2017-04-04 04:17:24 +00:00
|
|
|
pub fn count() -> usize {
|
|
|
|
return raw.len;
|
|
|
|
}
|
|
|
|
pub fn at(i: usize) -> []const u8 {
|
|
|
|
const s = raw[i];
|
2017-04-21 05:56:12 +00:00
|
|
|
return cstr.toSlice(s);
|
2017-04-04 04:17:24 +00:00
|
|
|
}
|
|
|
|
};
|
2017-04-16 18:14:11 +00:00
|
|
|
|
|
|
|
/// Caller must free the returned memory.
|
|
|
|
pub fn getCwd(allocator: &Allocator) -> %[]u8 {
|
|
|
|
var buf = %return allocator.alloc(u8, 1024);
|
|
|
|
%defer allocator.free(buf);
|
|
|
|
while (true) {
|
|
|
|
const err = posix.getErrno(posix.getcwd(buf.ptr, buf.len));
|
|
|
|
if (err == errno.ERANGE) {
|
|
|
|
buf = %return allocator.realloc(u8, buf, buf.len * 2);
|
|
|
|
continue;
|
|
|
|
} else if (err > 0) {
|
|
|
|
return error.Unexpected;
|
|
|
|
}
|
|
|
|
|
2017-04-21 05:56:12 +00:00
|
|
|
return cstr.toSlice(buf.ptr);
|
2017-04-16 18:14:11 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-17 06:58:42 +00:00
|
|
|
|
|
|
|
pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {
|
|
|
|
const full_buf = %return allocator.alloc(u8, existing_path.len + new_path.len + 2);
|
|
|
|
defer allocator.free(full_buf);
|
|
|
|
|
|
|
|
const existing_buf = full_buf;
|
|
|
|
mem.copy(u8, existing_buf, existing_path);
|
|
|
|
existing_buf[existing_path.len] = 0;
|
|
|
|
|
|
|
|
const new_buf = full_buf[existing_path.len + 1...];
|
|
|
|
mem.copy(u8, new_buf, new_path);
|
|
|
|
new_buf[new_path.len] = 0;
|
|
|
|
|
|
|
|
const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));
|
|
|
|
if (err > 0) {
|
|
|
|
return switch (err) {
|
|
|
|
errno.EFAULT, errno.EINVAL => unreachable,
|
|
|
|
errno.EACCES, errno.EPERM => error.AccessDenied,
|
|
|
|
errno.EDQUOT => error.DiskQuota,
|
2017-04-17 23:08:41 +00:00
|
|
|
errno.EEXIST => error.PathAlreadyExists,
|
2017-04-17 06:58:42 +00:00
|
|
|
errno.EIO => error.FileSystem,
|
|
|
|
errno.ELOOP => error.SymLinkLoop,
|
|
|
|
errno.ENAMETOOLONG => error.NameTooLong,
|
2017-04-19 05:13:15 +00:00
|
|
|
errno.ENOENT => error.FileNotFound,
|
|
|
|
errno.ENOTDIR => error.NotDir,
|
2017-04-17 06:58:42 +00:00
|
|
|
errno.ENOMEM => error.SystemResources,
|
|
|
|
errno.ENOSPC => error.NoSpaceLeft,
|
|
|
|
errno.EROFS => error.ReadOnlyFileSystem,
|
|
|
|
else => error.Unexpected,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-17 23:08:41 +00:00
|
|
|
// here we replace the standard +/ with -_ so that it can be used in a file name
|
|
|
|
const b64_fs_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=";
|
|
|
|
|
|
|
|
pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {
|
2017-05-03 21:23:11 +00:00
|
|
|
if (symLink(allocator, existing_path, new_path)) {
|
2017-04-17 23:08:41 +00:00
|
|
|
return;
|
|
|
|
} else |err| {
|
|
|
|
if (err != error.PathAlreadyExists) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var rand_buf: [12]u8 = undefined;
|
|
|
|
const tmp_path = %return allocator.alloc(u8, new_path.len + base64.calcEncodedSize(rand_buf.len));
|
|
|
|
defer allocator.free(tmp_path);
|
|
|
|
mem.copy(u8, tmp_path[0...], new_path);
|
|
|
|
while (true) {
|
|
|
|
%return getRandomBytes(rand_buf[0...]);
|
|
|
|
_ = base64.encodeWithAlphabet(tmp_path[new_path.len...], rand_buf, b64_fs_alphabet);
|
2017-05-03 21:23:11 +00:00
|
|
|
if (symLink(allocator, existing_path, tmp_path)) {
|
2017-04-17 23:08:41 +00:00
|
|
|
return rename(allocator, tmp_path, new_path);
|
|
|
|
} else |err| {
|
|
|
|
if (err == error.PathAlreadyExists) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-04-17 10:45:44 +00:00
|
|
|
pub fn deleteFile(allocator: &Allocator, file_path: []const u8) -> %void {
|
|
|
|
const buf = %return allocator.alloc(u8, file_path.len + 1);
|
2017-04-17 06:58:42 +00:00
|
|
|
defer allocator.free(buf);
|
|
|
|
|
2017-04-17 10:45:44 +00:00
|
|
|
mem.copy(u8, buf, file_path);
|
|
|
|
buf[file_path.len] = 0;
|
2017-04-17 06:58:42 +00:00
|
|
|
|
|
|
|
const err = posix.getErrno(posix.unlink(buf.ptr));
|
|
|
|
if (err > 0) {
|
|
|
|
return switch (err) {
|
|
|
|
errno.EACCES, errno.EPERM => error.AccessDenied,
|
|
|
|
errno.EBUSY => error.FileBusy,
|
|
|
|
errno.EFAULT, errno.EINVAL => unreachable,
|
|
|
|
errno.EIO => error.FileSystem,
|
|
|
|
errno.EISDIR => error.IsDir,
|
|
|
|
errno.ELOOP => error.SymLinkLoop,
|
|
|
|
errno.ENAMETOOLONG => error.NameTooLong,
|
2017-04-19 05:13:15 +00:00
|
|
|
errno.ENOENT => error.FileNotFound,
|
|
|
|
errno.ENOTDIR => error.NotDir,
|
2017-04-17 06:58:42 +00:00
|
|
|
errno.ENOMEM => error.SystemResources,
|
|
|
|
errno.EROFS => error.ReadOnlyFileSystem,
|
|
|
|
else => error.Unexpected,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2017-04-17 10:45:44 +00:00
|
|
|
|
2017-05-01 02:09:44 +00:00
|
|
|
/// Calls ::copyFileMode with 0o666 for the mode.
|
2017-04-17 10:45:44 +00:00
|
|
|
pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) -> %void {
|
2017-05-01 02:09:44 +00:00
|
|
|
return copyFileMode(allocator, source_path, dest_path, 0o666);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO instead of accepting a mode argument, use the mode from fstat'ing the source path once open
|
|
|
|
/// Guaranteed to be atomic.
|
|
|
|
pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: usize) -> %void {
|
|
|
|
var rand_buf: [12]u8 = undefined;
|
|
|
|
const tmp_path = %return allocator.alloc(u8, dest_path.len + base64.calcEncodedSize(rand_buf.len));
|
|
|
|
defer allocator.free(tmp_path);
|
|
|
|
mem.copy(u8, tmp_path[0...], dest_path);
|
|
|
|
%return getRandomBytes(rand_buf[0...]);
|
|
|
|
_ = base64.encodeWithAlphabet(tmp_path[dest_path.len...], rand_buf, b64_fs_alphabet);
|
|
|
|
|
|
|
|
var out_stream = %return io.OutStream.openMode(tmp_path, mode, allocator);
|
|
|
|
defer out_stream.close();
|
|
|
|
%defer _ = deleteFile(allocator, tmp_path);
|
|
|
|
|
2017-04-17 10:45:44 +00:00
|
|
|
var in_stream = %return io.InStream.open(source_path, allocator);
|
|
|
|
defer in_stream.close();
|
|
|
|
|
|
|
|
const buf = out_stream.buffer[0...];
|
|
|
|
while (true) {
|
|
|
|
const amt = %return in_stream.read(buf);
|
|
|
|
out_stream.index = amt;
|
|
|
|
%return out_stream.flush();
|
|
|
|
if (amt != out_stream.buffer.len)
|
2017-05-01 02:09:44 +00:00
|
|
|
return rename(allocator, tmp_path, dest_path);
|
2017-04-17 10:45:44 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-17 23:08:41 +00:00
|
|
|
|
|
|
|
pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) -> %void {
|
|
|
|
const full_buf = %return allocator.alloc(u8, old_path.len + new_path.len + 2);
|
|
|
|
defer allocator.free(full_buf);
|
|
|
|
|
|
|
|
const old_buf = full_buf;
|
|
|
|
mem.copy(u8, old_buf, old_path);
|
|
|
|
old_buf[old_path.len] = 0;
|
|
|
|
|
|
|
|
const new_buf = full_buf[old_path.len + 1...];
|
|
|
|
mem.copy(u8, new_buf, new_path);
|
|
|
|
new_buf[new_path.len] = 0;
|
|
|
|
|
|
|
|
const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));
|
|
|
|
if (err > 0) {
|
|
|
|
return switch (err) {
|
|
|
|
errno.EACCES, errno.EPERM => error.AccessDenied,
|
|
|
|
errno.EBUSY => error.FileBusy,
|
|
|
|
errno.EDQUOT => error.DiskQuota,
|
|
|
|
errno.EFAULT, errno.EINVAL => unreachable,
|
|
|
|
errno.EISDIR => error.IsDir,
|
|
|
|
errno.ELOOP => error.SymLinkLoop,
|
|
|
|
errno.EMLINK => error.LinkQuotaExceeded,
|
|
|
|
errno.ENAMETOOLONG => error.NameTooLong,
|
2017-04-19 05:13:15 +00:00
|
|
|
errno.ENOENT => error.FileNotFound,
|
|
|
|
errno.ENOTDIR => error.NotDir,
|
2017-04-17 23:08:41 +00:00
|
|
|
errno.ENOMEM => error.SystemResources,
|
|
|
|
errno.ENOSPC => error.NoSpaceLeft,
|
|
|
|
errno.EEXIST, errno.ENOTEMPTY => error.PathAlreadyExists,
|
|
|
|
errno.EROFS => error.ReadOnlyFileSystem,
|
|
|
|
errno.EXDEV => error.RenameAcrossMountPoints,
|
|
|
|
else => error.Unexpected,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2017-04-19 05:13:15 +00:00
|
|
|
|
|
|
|
pub fn makeDir(allocator: &Allocator, dir_path: []const u8) -> %void {
|
|
|
|
const path_buf = %return allocator.alloc(u8, dir_path.len + 1);
|
|
|
|
defer allocator.free(path_buf);
|
|
|
|
|
|
|
|
mem.copy(u8, path_buf, dir_path);
|
|
|
|
path_buf[dir_path.len] = 0;
|
|
|
|
|
|
|
|
const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));
|
|
|
|
if (err > 0) {
|
|
|
|
return switch (err) {
|
|
|
|
errno.EACCES, errno.EPERM => error.AccessDenied,
|
|
|
|
errno.EDQUOT => error.DiskQuota,
|
|
|
|
errno.EEXIST => error.PathAlreadyExists,
|
|
|
|
errno.EFAULT => unreachable,
|
|
|
|
errno.ELOOP => error.SymLinkLoop,
|
|
|
|
errno.EMLINK => error.LinkQuotaExceeded,
|
|
|
|
errno.ENAMETOOLONG => error.NameTooLong,
|
|
|
|
errno.ENOENT => error.FileNotFound,
|
|
|
|
errno.ENOMEM => error.SystemResources,
|
|
|
|
errno.ENOSPC => error.NoSpaceLeft,
|
|
|
|
errno.ENOTDIR => error.NotDir,
|
|
|
|
errno.EROFS => error.ReadOnlyFileSystem,
|
|
|
|
else => error.Unexpected,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Calls makeDir recursively to make an entire path. Returns success if the path
|
|
|
|
/// already exists and is a directory.
|
|
|
|
pub fn makePath(allocator: &Allocator, full_path: []const u8) -> %void {
|
2017-04-21 05:56:12 +00:00
|
|
|
const resolved_path = %return path.resolve(allocator, full_path);
|
|
|
|
defer allocator.free(resolved_path);
|
2017-04-19 05:13:15 +00:00
|
|
|
|
2017-04-21 05:56:12 +00:00
|
|
|
var end_index: usize = resolved_path.len;
|
|
|
|
while (true) {
|
|
|
|
makeDir(allocator, resolved_path[0...end_index]) %% |err| {
|
|
|
|
if (err == error.PathAlreadyExists) {
|
|
|
|
// TODO stat the file and return an error if it's not a directory
|
|
|
|
// this is important because otherwise a dangling symlink
|
|
|
|
// could cause an infinite loop
|
|
|
|
if (end_index == resolved_path.len)
|
|
|
|
return;
|
|
|
|
} else if (err == error.FileNotFound) {
|
|
|
|
// march end_index backward until next path component
|
|
|
|
while (true) {
|
|
|
|
end_index -= 1;
|
|
|
|
if (resolved_path[end_index] == '/')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if (end_index == resolved_path.len)
|
|
|
|
return;
|
|
|
|
// march end_index forward until next path component
|
|
|
|
while (true) {
|
|
|
|
end_index += 1;
|
|
|
|
if (end_index == resolved_path.len or resolved_path[end_index] == '/')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-04-19 05:13:15 +00:00
|
|
|
}
|
2017-04-20 06:26:36 +00:00
|
|
|
|
|
|
|
/// Returns ::error.DirNotEmpty if the directory is not empty.
|
|
|
|
/// To delete a directory recursively, see ::deleteTree
|
|
|
|
pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) -> %void {
|
|
|
|
const path_buf = %return allocator.alloc(u8, dir_path.len + 1);
|
|
|
|
defer allocator.free(path_buf);
|
|
|
|
|
|
|
|
mem.copy(u8, path_buf, dir_path);
|
|
|
|
path_buf[dir_path.len] = 0;
|
|
|
|
|
|
|
|
const err = posix.getErrno(posix.rmdir(path_buf.ptr));
|
|
|
|
if (err > 0) {
|
|
|
|
return switch (err) {
|
|
|
|
errno.EACCES, errno.EPERM => error.AccessDenied,
|
|
|
|
errno.EBUSY => error.FileBusy,
|
|
|
|
errno.EFAULT, errno.EINVAL => unreachable,
|
|
|
|
errno.ELOOP => error.SymLinkLoop,
|
|
|
|
errno.ENAMETOOLONG => error.NameTooLong,
|
|
|
|
errno.ENOENT => error.FileNotFound,
|
|
|
|
errno.ENOMEM => error.SystemResources,
|
|
|
|
errno.ENOTDIR => error.NotDir,
|
|
|
|
errno.EEXIST, errno.ENOTEMPTY => error.DirNotEmpty,
|
|
|
|
errno.EROFS => error.ReadOnlyFileSystem,
|
|
|
|
else => error.Unexpected,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Whether ::full_path describes a symlink, file, or directory, this function
|
|
|
|
/// removes it. If it cannot be removed because it is a non-empty directory,
|
|
|
|
/// this function recursively removes its entries and then tries again.
|
|
|
|
// TODO non-recursive implementation
|
|
|
|
pub fn deleteTree(allocator: &Allocator, full_path: []const u8) -> %void {
|
|
|
|
start_over:
|
|
|
|
// First, try deleting the item as a file. This way we don't follow sym links.
|
2017-05-03 21:23:11 +00:00
|
|
|
if (deleteFile(allocator, full_path)) {
|
2017-04-20 06:26:36 +00:00
|
|
|
return;
|
|
|
|
} else |err| {
|
|
|
|
if (err == error.FileNotFound)
|
|
|
|
return;
|
|
|
|
if (err != error.IsDir)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
var dir = Dir.open(allocator, full_path) %% |err| {
|
|
|
|
if (err == error.FileNotFound)
|
|
|
|
return;
|
|
|
|
if (err == error.NotDir)
|
|
|
|
goto start_over;
|
|
|
|
return err;
|
|
|
|
};
|
|
|
|
defer dir.close();
|
|
|
|
|
2017-05-04 18:05:06 +00:00
|
|
|
var full_entry_buf = ArrayList(u8).init(allocator);
|
2017-04-20 06:26:36 +00:00
|
|
|
defer full_entry_buf.deinit();
|
|
|
|
|
2017-05-04 14:37:19 +00:00
|
|
|
while (%return dir.next()) |entry| {
|
2017-04-20 06:26:36 +00:00
|
|
|
%return full_entry_buf.resize(full_path.len + entry.name.len + 1);
|
|
|
|
const full_entry_path = full_entry_buf.toSlice();
|
|
|
|
mem.copy(u8, full_entry_path, full_path);
|
|
|
|
full_entry_path[full_path.len] = '/';
|
|
|
|
mem.copy(u8, full_entry_path[full_path.len + 1...], entry.name);
|
|
|
|
|
|
|
|
%return deleteTree(allocator, full_entry_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return deleteDir(allocator, full_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const Dir = struct {
|
|
|
|
fd: i32,
|
|
|
|
allocator: &Allocator,
|
|
|
|
buf: []u8,
|
|
|
|
index: usize,
|
|
|
|
end_index: usize,
|
|
|
|
|
|
|
|
const LinuxEntry = extern struct {
|
|
|
|
d_ino: usize,
|
|
|
|
d_off: usize,
|
|
|
|
d_reclen: u16,
|
|
|
|
d_name: u8, // field address is the address of first byte of name
|
|
|
|
};
|
|
|
|
|
|
|
|
pub const Entry = struct {
|
|
|
|
name: []const u8,
|
|
|
|
kind: Kind,
|
|
|
|
|
|
|
|
pub const Kind = enum {
|
|
|
|
BlockDevice,
|
|
|
|
CharacterDevice,
|
|
|
|
Directory,
|
|
|
|
NamedPipe,
|
|
|
|
SymLink,
|
|
|
|
File,
|
|
|
|
UnixDomainSocket,
|
|
|
|
Unknown,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn open(allocator: &Allocator, dir_path: []const u8) -> %Dir {
|
|
|
|
const fd = %return posixOpen(dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0, allocator);
|
|
|
|
return Dir {
|
|
|
|
.allocator = allocator,
|
|
|
|
.fd = fd,
|
|
|
|
.index = 0,
|
|
|
|
.end_index = 0,
|
|
|
|
.buf = []u8{},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn close(self: &Dir) {
|
|
|
|
self.allocator.free(self.buf);
|
|
|
|
posixClose(self.fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Memory such as file names referenced in this returned entry becomes invalid
|
|
|
|
/// with subsequent calls to next, as well as when this ::Dir is deinitialized.
|
|
|
|
pub fn next(self: &Dir) -> %?Entry {
|
|
|
|
start_over:
|
|
|
|
if (self.index >= self.end_index) {
|
|
|
|
if (self.buf.len == 0) {
|
|
|
|
self.buf = %return self.allocator.alloc(u8, 2); //page_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
|
|
|
|
const err = linux.getErrno(result);
|
|
|
|
if (err > 0) {
|
|
|
|
switch (err) {
|
|
|
|
errno.EBADF, errno.EFAULT, errno.ENOTDIR => unreachable,
|
|
|
|
errno.EINVAL => {
|
|
|
|
self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2);
|
|
|
|
continue;
|
|
|
|
},
|
|
|
|
else => return error.Unexpected,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (result == 0)
|
|
|
|
return null;
|
|
|
|
self.index = 0;
|
|
|
|
self.end_index = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-04-21 14:39:13 +00:00
|
|
|
const linux_entry = @ptrCast(&LinuxEntry, &self.buf[self.index]);
|
2017-04-20 06:26:36 +00:00
|
|
|
const next_index = self.index + linux_entry.d_reclen;
|
|
|
|
self.index = next_index;
|
|
|
|
|
2017-04-21 05:56:12 +00:00
|
|
|
const name = cstr.toSlice(&linux_entry.d_name);
|
2017-04-20 06:26:36 +00:00
|
|
|
|
|
|
|
// skip . and .. entries
|
|
|
|
if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
|
|
|
|
goto start_over;
|
|
|
|
}
|
|
|
|
|
|
|
|
const type_char = self.buf[next_index - 1];
|
|
|
|
const entry_kind = switch (type_char) {
|
|
|
|
posix.DT_BLK => Entry.Kind.BlockDevice,
|
|
|
|
posix.DT_CHR => Entry.Kind.CharacterDevice,
|
|
|
|
posix.DT_DIR => Entry.Kind.Directory,
|
|
|
|
posix.DT_FIFO => Entry.Kind.NamedPipe,
|
|
|
|
posix.DT_LNK => Entry.Kind.SymLink,
|
|
|
|
posix.DT_REG => Entry.Kind.File,
|
|
|
|
posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
|
|
|
|
else => Entry.Kind.Unknown,
|
|
|
|
};
|
|
|
|
return Entry {
|
|
|
|
.name = name,
|
|
|
|
.kind = entry_kind,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
2017-04-30 22:56:24 +00:00
|
|
|
|
|
|
|
pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) -> %void {
|
|
|
|
const path_buf = %return allocator.alloc(u8, dir_path.len + 1);
|
|
|
|
defer allocator.free(path_buf);
|
|
|
|
|
|
|
|
mem.copy(u8, path_buf, dir_path);
|
|
|
|
path_buf[dir_path.len] = 0;
|
|
|
|
|
|
|
|
const err = posix.getErrno(posix.chdir(path_buf.ptr));
|
|
|
|
if (err > 0) {
|
|
|
|
return switch (err) {
|
|
|
|
errno.EACCES => error.AccessDenied,
|
|
|
|
errno.EFAULT => unreachable,
|
|
|
|
errno.EIO => error.FileSystem,
|
|
|
|
errno.ELOOP => error.SymLinkLoop,
|
|
|
|
errno.ENAMETOOLONG => error.NameTooLong,
|
|
|
|
errno.ENOENT => error.FileNotFound,
|
|
|
|
errno.ENOMEM => error.SystemResources,
|
|
|
|
errno.ENOTDIR => error.NotDir,
|
|
|
|
else => error.Unexpected,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Read value of a symbolic link.
|
|
|
|
pub fn readLink(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
|
|
|
|
const path_buf = %return allocator.alloc(u8, pathname.len + 1);
|
|
|
|
defer allocator.free(path_buf);
|
|
|
|
|
|
|
|
mem.copy(u8, path_buf, pathname);
|
|
|
|
path_buf[pathname.len] = 0;
|
|
|
|
|
|
|
|
var result_buf = %return allocator.alloc(u8, 1024);
|
|
|
|
%defer allocator.free(result_buf);
|
|
|
|
while (true) {
|
|
|
|
const ret_val = posix.readlink(path_buf.ptr, result_buf.ptr, result_buf.len);
|
|
|
|
const err = posix.getErrno(ret_val);
|
|
|
|
if (err > 0) {
|
|
|
|
return switch (err) {
|
|
|
|
errno.EACCES => error.AccessDenied,
|
|
|
|
errno.EFAULT, errno.EINVAL => unreachable,
|
|
|
|
errno.EIO => error.FileSystem,
|
|
|
|
errno.ELOOP => error.SymLinkLoop,
|
|
|
|
errno.ENAMETOOLONG => error.NameTooLong,
|
|
|
|
errno.ENOENT => error.FileNotFound,
|
|
|
|
errno.ENOMEM => error.SystemResources,
|
|
|
|
errno.ENOTDIR => error.NotDir,
|
|
|
|
else => error.Unexpected,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (ret_val == result_buf.len) {
|
|
|
|
result_buf = %return allocator.realloc(u8, result_buf, result_buf.len * 2);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
return result_buf[0...ret_val];
|
|
|
|
}
|
|
|
|
}
|