convert all to use sys/linux over sys/unix; new implementations for pipe, process and env

This commit is contained in:
jason
2024-06-27 17:14:48 -04:00
parent f22754fc90
commit f24f72c280
12 changed files with 1269 additions and 543 deletions
+227 -161
View File
@@ -1,39 +1,60 @@
//+private
package os2
import "base:runtime"
import "core:io"
import "core:time"
import "core:sys/unix"
INVALID_HANDLE :: -1
_O_RDONLY :: 0o00000000
_O_WRONLY :: 0o00000001
_O_RDWR :: 0o00000002
_O_CREAT :: 0o00000100
_O_EXCL :: 0o00000200
_O_NOCTTY :: 0o00000400
_O_TRUNC :: 0o00001000
_O_APPEND :: 0o00002000
_O_NONBLOCK :: 0o00004000
_O_LARGEFILE :: 0o00100000
_O_DIRECTORY :: 0o00200000
_O_NOFOLLOW :: 0o00400000
_O_SYNC :: 0o04010000
_O_CLOEXEC :: 0o02000000
_O_PATH :: 0o10000000
_AT_FDCWD :: -100
_CSTRING_NAME_HEAP_THRESHOLD :: 512
import "base:runtime"
import "core:sys/linux"
_File :: struct {
name: string,
fd: int,
fd: linux.Fd,
allocator: runtime.Allocator,
}
_stdin : File = {
impl = {
name = "/proc/self/fd/0",
fd = 0,
allocator = _file_allocator(),
},
stream = {
procedure = _file_stream_proc,
},
}
_stdout : File = {
impl = {
name = "/proc/self/fd/1",
fd = 1,
allocator = _file_allocator(),
},
stream = {
procedure = _file_stream_proc,
},
}
_stderr : File = {
impl = {
name = "/proc/self/fd/2",
fd = 2,
allocator = _file_allocator(),
},
stream = {
procedure = _file_stream_proc,
},
}
@init
_standard_stream_init :: proc() {
// cannot define these manually because cyclic reference
_stdin.stream.data = &_stdin
_stdout.stream.data = &_stdout
_stderr.stream.data = &_stderr
}
_file_allocator :: proc() -> runtime.Allocator {
return heap_allocator()
}
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
@@ -41,40 +62,48 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, er
// Just default to using O_NOCTTY because needing to open a controlling
// terminal would be incredibly rare. This has no effect on files while
// allowing us to open serial devices.
flags_i: int = _O_NOCTTY
sys_flags: linux.Open_Flags = {.NOCTTY}
switch flags & O_RDONLY|O_WRONLY|O_RDWR {
case O_RDONLY: flags_i = _O_RDONLY
case O_WRONLY: flags_i = _O_WRONLY
case O_RDWR: flags_i = _O_RDWR
case O_RDONLY:
case O_WRONLY: sys_flags += {.WRONLY}
case O_RDWR: sys_flags += {.RDWR}
}
if .Append in flags { flags_i |= _O_APPEND }
if .Create in flags { flags_i |= _O_CREAT }
if .Excl in flags { flags_i |= _O_EXCL }
if .Sync in flags { flags_i |= _O_SYNC }
if .Trunc in flags { flags_i |= _O_TRUNC }
if .Close_On_Exec in flags { flags_i |= _O_CLOEXEC }
if .Append in flags { sys_flags += {.APPEND} }
if .Create in flags { sys_flags += {.CREAT} }
if .Excl in flags { sys_flags += {.EXCL} }
if .Sync in flags { sys_flags += {.DSYNC} }
if .Trunc in flags { sys_flags += {.TRUNC} }
if .Close_On_Exec in flags { sys_flags += {.CLOEXEC} }
fd := unix.sys_open(name_cstr, flags_i, uint(perm))
if fd < 0 {
return nil, _get_platform_error(fd)
fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)(u32(perm)))
if errno != .NONE {
return nil, _get_platform_error(errno)
}
return _new_file(uintptr(fd), name), nil
}
_new_file :: proc(fd: uintptr, _: string) -> ^File {
_new_file :: proc(fd: uintptr, _: string = "") -> ^File {
file := new(File, file_allocator())
file.impl.fd = int(fd)
file.impl.allocator = file_allocator()
file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator)
file.stream = {
data = file,
procedure = _file_stream_proc,
}
_construct_file(file, fd, "")
return file
}
_construct_file :: proc(file: ^File, fd: uintptr, _: string = "") {
file^ = {
impl = {
fd = linux.Fd(fd),
allocator = file_allocator(),
name = _get_full_path(file.impl.fd, file.impl.allocator),
},
stream = {
data = file,
procedure = _file_stream_proc,
},
}
}
_destroy :: proc(f: ^File) -> Error {
if f == nil {
return nil
@@ -86,12 +115,15 @@ _destroy :: proc(f: ^File) -> Error {
_close :: proc(f: ^File) -> Error {
if f != nil {
res := unix.sys_close(f.impl.fd)
_destroy(f)
return _ok_or_error(res)
if f == nil {
return nil
}
return nil
errno := linux.close(f.impl.fd)
if errno == .EBADF { // avoid possible double free
return _get_platform_error(errno)
}
_destroy(f)
return _get_platform_error(errno)
}
_fd :: proc(f: ^File) -> uintptr {
@@ -106,20 +138,32 @@ _name :: proc(f: ^File) -> string {
}
_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
res := unix.sys_lseek(f.impl.fd, offset, int(whence))
if res < 0 {
return -1, _get_platform_error(int(res))
n, errno := linux.lseek(f.impl.fd, offset, linux.Seek_Whence(whence))
if errno != .NONE {
return -1, _get_platform_error(errno)
}
return res, nil
return n, nil
}
_read :: proc(f: ^File, p: []byte) -> (i64, Error) {
if len(p) == 0 {
return 0, nil
}
n := unix.sys_read(f.impl.fd, &p[0], len(p))
if n < 0 {
return -1, _get_platform_error(n)
n, errno := linux.read(f.impl.fd, p[:])
if errno != .NONE {
return -1, _get_platform_error(errno)
}
return i64(n), n == 0 ? io.Error.EOF : nil
}
_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) {
if offset < 0 {
return 0, .Invalid_Offset
}
n, errno := linux.pread(f.impl.fd, p[:], offset)
if errno != .NONE {
return -1, _get_platform_error(errno)
}
if n == 0 {
return 0, .EOF
@@ -127,91 +171,67 @@ _read :: proc(f: ^File, p: []byte) -> (i64, Error) {
return i64(n), nil
}
_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
if offset < 0 {
return 0, .Invalid_Offset
}
b, offset := p, offset
for len(b) > 0 {
m := unix.sys_pread(f.impl.fd, &b[0], len(b), offset)
if m < 0 {
return -1, _get_platform_error(m)
}
if m == 0 {
return 0, .EOF
}
n += i64(m)
b = b[m:]
offset += i64(m)
}
return
}
_write :: proc(f: ^File, p: []byte) -> (i64, Error) {
if len(p) == 0 {
return 0, nil
}
n := unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
if n < 0 {
return -1, _get_platform_error(n)
n, errno := linux.write(f.impl.fd, p[:])
if errno != .NONE {
return -1, _get_platform_error(errno)
}
return i64(n), nil
}
_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) {
if offset < 0 {
return 0, .Invalid_Offset
}
b, offset := p, offset
for len(b) > 0 {
m := unix.sys_pwrite(f.impl.fd, &b[0], len(b), offset)
if m < 0 {
return -1, _get_platform_error(m)
}
n += i64(m)
b = b[m:]
offset += i64(m)
n, errno := linux.pwrite(f.impl.fd, p[:], offset)
if errno != .NONE {
return -1, _get_platform_error(errno)
}
return
return i64(n), nil
}
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
s: _Stat = ---
res := unix.sys_fstat(f.impl.fd, &s)
if res < 0 {
return -1, _get_platform_error(res)
s: linux.Stat = ---
errno := linux.fstat(f.impl.fd, &s)
if errno != .NONE {
return -1, _get_platform_error(errno)
}
return s.size, nil
return i64(s.size), nil
}
_sync :: proc(f: ^File) -> Error {
return _ok_or_error(unix.sys_fsync(f.impl.fd))
return _get_platform_error(linux.fsync(f.impl.fd))
}
_flush :: proc(f: ^File) -> Error {
return _ok_or_error(unix.sys_fsync(f.impl.fd))
return _get_platform_error(linux.fsync(f.impl.fd))
}
_truncate :: proc(f: ^File, size: i64) -> Error {
return _ok_or_error(unix.sys_ftruncate(f.impl.fd, size))
return _get_platform_error(linux.ftruncate(f.impl.fd, size))
}
_remove :: proc(name: string) -> Error {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
fd := unix.sys_open(name_cstr, int(File_Flags.Read))
if fd < 0 {
return _get_platform_error(fd)
fd, errno := linux.open(name_cstr, {.NOFOLLOW})
#partial switch (errno) {
case .ELOOP: /* symlink */
case .NONE:
defer linux.close(fd)
if _is_dir_fd(fd) {
return _get_platform_error(linux.rmdir(name_cstr))
}
case:
return _get_platform_error(errno)
}
defer unix.sys_close(fd)
if _is_dir_fd(fd) {
return _ok_or_error(unix.sys_rmdir(name_cstr))
}
return _ok_or_error(unix.sys_unlink(name_cstr))
return _get_platform_error(linux.unlink(name_cstr))
}
_rename :: proc(old_name, new_name: string) -> Error {
@@ -219,7 +239,7 @@ _rename :: proc(old_name, new_name: string) -> Error {
old_name_cstr := temp_cstring(old_name) or_return
new_name_cstr := temp_cstring(new_name) or_return
return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_cstr))
return _get_platform_error(linux.rename(old_name_cstr, new_name_cstr))
}
_link :: proc(old_name, new_name: string) -> Error {
@@ -227,148 +247,194 @@ _link :: proc(old_name, new_name: string) -> Error {
old_name_cstr := temp_cstring(old_name) or_return
new_name_cstr := temp_cstring(new_name) or_return
return _ok_or_error(unix.sys_link(old_name_cstr, new_name_cstr))
return _get_platform_error(linux.link(old_name_cstr, new_name_cstr))
}
_symlink :: proc(old_name, new_name: string) -> Error {
TEMP_ALLOCATOR_GUARD()
old_name_cstr := temp_cstring(old_name) or_return
new_name_cstr := temp_cstring(new_name) or_return
return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr))
return _get_platform_error(linux.symlink(old_name_cstr, new_name_cstr))
}
_read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
bufsz : uint = 256
buf := make([]byte, bufsz, allocator)
for {
rc := unix.sys_readlink(name_cstr, &buf[0], bufsz)
if rc < 0 {
delete(buf)
return "", _get_platform_error(rc)
} else if rc == int(bufsz) {
sz, errno := linux.readlink(name_cstr, buf[:])
if errno != .NONE {
delete(buf, allocator)
return "", _get_platform_error(errno)
} else if sz == int(bufsz) {
bufsz *= 2
delete(buf)
delete(buf, allocator)
buf = make([]byte, bufsz, allocator)
} else {
return string(buf[:rc]), nil
return string(buf[:sz]), nil
}
}
}
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) {
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, e: Error) {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
return _read_link_cstr(name_cstr, allocator)
}
_unlink :: proc(name: string) -> Error {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
return _ok_or_error(unix.sys_unlink(name_cstr))
}
_chdir :: proc(name: string) -> Error {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
return _ok_or_error(unix.sys_chdir(name_cstr))
return _get_platform_error(linux.chdir(name_cstr))
}
_fchdir :: proc(f: ^File) -> Error {
return _ok_or_error(unix.sys_fchdir(f.impl.fd))
return _get_platform_error(linux.fchdir(f.impl.fd))
}
_chmod :: proc(name: string, mode: File_Mode) -> Error {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
return _ok_or_error(unix.sys_chmod(name_cstr, uint(mode)))
return _get_platform_error(linux.chmod(name_cstr, transmute(linux.Mode)(u32(mode))))
}
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
return _ok_or_error(unix.sys_fchmod(f.impl.fd, uint(mode)))
return _get_platform_error(linux.fchmod(f.impl.fd, transmute(linux.Mode)(u32(mode))))
}
// NOTE: will throw error without super user priviledges
_chown :: proc(name: string, uid, gid: int) -> Error {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
return _ok_or_error(unix.sys_chown(name_cstr, uid, gid))
return _get_platform_error(linux.chown(name_cstr, linux.Uid(uid), linux.Gid(gid)))
}
// NOTE: will throw error without super user priviledges
_lchown :: proc(name: string, uid, gid: int) -> Error {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid))
return _get_platform_error(linux.lchown(name_cstr, linux.Uid(uid), linux.Gid(gid)))
}
// NOTE: will throw error without super user priviledges
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
return _ok_or_error(unix.sys_fchown(f.impl.fd, uid, gid))
return _get_platform_error(linux.fchown(f.impl.fd, linux.Uid(uid), linux.Gid(gid)))
}
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
times := [2]Unix_File_Time {
{ atime._nsec, 0 },
{ mtime._nsec, 0 },
times := [2]linux.Time_Spec {
{
uint(atime._nsec) / uint(time.Second),
uint(atime._nsec) % uint(time.Second),
},
{
uint(mtime._nsec) / uint(time.Second),
uint(mtime._nsec) % uint(time.Second),
},
}
return _ok_or_error(unix.sys_utimensat(_AT_FDCWD, name_cstr, &times, 0))
return _get_platform_error(linux.utimensat(linux.AT_FDCWD, name_cstr, &times[0], nil))
}
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
times := [2]Unix_File_Time {
{ atime._nsec, 0 },
{ mtime._nsec, 0 },
times := [2]linux.Time_Spec {
{
uint(atime._nsec) / uint(time.Second),
uint(atime._nsec) % uint(time.Second),
},
{
uint(mtime._nsec) / uint(time.Second),
uint(mtime._nsec) % uint(time.Second),
},
}
return _ok_or_error(unix.sys_utimensat(f.impl.fd, nil, &times, 0))
return _get_platform_error(linux.utimensat(f.impl.fd, nil, &times[0], nil))
}
_exists :: proc(name: string) -> bool {
TEMP_ALLOCATOR_GUARD()
name_cstr, _ := temp_cstring(name)
return unix.sys_access(name_cstr, F_OK) == 0
res, errno := linux.access(name_cstr, linux.F_OK)
return !res && errno == .NONE
}
_is_file :: proc(name: string) -> bool {
TEMP_ALLOCATOR_GUARD()
name_cstr, _ := temp_cstring(name)
s: _Stat
res := unix.sys_stat(name_cstr, &s)
if res < 0 {
s: linux.Stat
if linux.stat(name_cstr, &s) != .NONE {
return false
}
return S_ISREG(s.mode)
return linux.S_ISREG(s.mode)
}
_is_file_fd :: proc(fd: int) -> bool {
s: _Stat
res := unix.sys_fstat(fd, &s)
if res < 0 { // error
_is_file_fd :: proc(fd: linux.Fd) -> bool {
s: linux.Stat
if linux.fstat(fd, &s) != .NONE {
return false
}
return S_ISREG(s.mode)
return linux.S_ISREG(s.mode)
}
_is_dir :: proc(name: string) -> bool {
TEMP_ALLOCATOR_GUARD()
name_cstr, _ := temp_cstring(name)
s: _Stat
res := unix.sys_stat(name_cstr, &s)
if res < 0 {
s: linux.Stat
if linux.stat(name_cstr, &s) != .NONE {
return false
}
return S_ISDIR(s.mode)
return linux.S_ISDIR(s.mode)
}
_is_dir_fd :: proc(fd: int) -> bool {
s: _Stat
res := unix.sys_fstat(fd, &s)
if res < 0 { // error
_is_dir_fd :: proc(fd: linux.Fd) -> bool {
s: linux.Stat
if linux.fstat(fd, &s) != .NONE {
return false
}
return S_ISDIR(s.mode)
return linux.S_ISDIR(s.mode)
}
/* Certain files in the Linux file system are not actual
* files (e.g. everything in /proc/). Therefore, the
* read_entire_file procs fail to actually read anything
* since these "files" stat to a size of 0. Here, we just
* read until there is nothing left.
*/
_read_entire_pseudo_file :: proc { _read_entire_pseudo_file_string, _read_entire_pseudo_file_cstring }
_read_entire_pseudo_file_string :: proc(name: string, allocator: runtime.Allocator) -> (b: []u8, e: Error) {
name_cstr := clone_to_cstring(name, allocator) or_return
defer delete(name, allocator)
return _read_entire_pseudo_file_cstring(name_cstr, allocator)
}
_read_entire_pseudo_file_cstring :: proc(name: cstring, allocator: runtime.Allocator) -> ([]u8, Error) {
fd, errno := linux.open(name, {})
if errno != .NONE {
return nil, _get_platform_error(errno)
}
defer linux.close(fd)
BUF_SIZE_STEP :: 128
contents := make([dynamic]u8, 0, BUF_SIZE_STEP, allocator)
n: int
i: int
for {
resize(&contents, i + BUF_SIZE_STEP)
n, errno = linux.read(fd, contents[i:i+BUF_SIZE_STEP])
if errno != .NONE {
delete(contents)
return nil, _get_platform_error(errno)
}
if n < BUF_SIZE_STEP {
break
}
i += BUF_SIZE_STEP
}
resize(&contents, i + n)
return contents[:], nil
}
@(private="package")