mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Merge pull request #3822 from jasonKercher/os2-rebase
os2 linux round 2
This commit is contained in:
+210
-9
@@ -2,29 +2,230 @@
|
|||||||
package os2
|
package os2
|
||||||
|
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
import "core:sync"
|
||||||
|
import "core:slice"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
// TODO: IF NO_CRT:
|
||||||
|
// Override the libc environment functions' weak linkage to
|
||||||
|
// allow us to interact with 3rd party code that DOES link
|
||||||
|
// to libc. Otherwise, our environment can be out of sync.
|
||||||
|
// ELSE:
|
||||||
|
// Just use the libc.
|
||||||
|
|
||||||
|
NOT_FOUND :: -1
|
||||||
|
|
||||||
|
// the environment is a 0 delimited list of <key>=<value> strings
|
||||||
|
_env: [dynamic]string
|
||||||
|
|
||||||
|
_env_mutex: sync.Mutex
|
||||||
|
|
||||||
|
// We need to be able to figure out if the environment variable
|
||||||
|
// is contained in the original environment or not. This also
|
||||||
|
// serves as a flag to determine if we have built _env.
|
||||||
|
_org_env_begin: uintptr
|
||||||
|
_org_env_end: uintptr
|
||||||
|
|
||||||
|
// Returns value + index location into _env
|
||||||
|
// or -1 if not found
|
||||||
|
_lookup :: proc(key: string) -> (value: string, idx: int) {
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
defer sync.mutex_unlock(&_env_mutex)
|
||||||
|
|
||||||
|
for entry, i in _env {
|
||||||
|
if k, v := _kv_from_entry(entry); k == key {
|
||||||
|
return v, i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", -1
|
||||||
|
}
|
||||||
|
|
||||||
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||||
//TODO
|
if _org_env_begin == 0 {
|
||||||
|
_build_env()
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, idx := _lookup(key); idx != -1 {
|
||||||
|
found = true
|
||||||
|
value, _ = clone_string(v, allocator)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_set_env :: proc(key, value: string) -> bool {
|
_set_env :: proc(key, v_new: string) -> bool {
|
||||||
//TODO
|
if _org_env_begin == 0 {
|
||||||
return false
|
_build_env()
|
||||||
|
}
|
||||||
|
|
||||||
|
// all key values are stored as "key=value\x00"
|
||||||
|
kv_size := len(key) + len(v_new) + 2
|
||||||
|
if v_curr, idx := _lookup(key); idx != NOT_FOUND {
|
||||||
|
if v_curr == v_new {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
defer sync.mutex_unlock(&_env_mutex)
|
||||||
|
|
||||||
|
unordered_remove(&_env, idx)
|
||||||
|
|
||||||
|
if !_is_in_org_env(v_curr) {
|
||||||
|
// We allocated this key-value. Possibly resize and
|
||||||
|
// overwrite the value only. Otherwise, treat as if it
|
||||||
|
// wasn't in the environment in the first place.
|
||||||
|
k_addr, v_addr := _kv_addr_from_val(v_curr, key)
|
||||||
|
if len(v_new) > len(v_curr) {
|
||||||
|
k_addr = ([^]u8)(heap_resize(k_addr, kv_size))
|
||||||
|
if k_addr == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
v_addr = &k_addr[len(key) + 1]
|
||||||
|
}
|
||||||
|
intrinsics.mem_copy_non_overlapping(v_addr, raw_data(v_new), len(v_new))
|
||||||
|
v_addr[len(v_new)] = 0
|
||||||
|
|
||||||
|
append(&_env, string(k_addr[:kv_size]))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
k_addr := ([^]u8)(heap_alloc(kv_size))
|
||||||
|
if k_addr == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
intrinsics.mem_copy_non_overlapping(k_addr, raw_data(key), len(key))
|
||||||
|
k_addr[len(key)] = '='
|
||||||
|
|
||||||
|
val_slice := k_addr[len(key) + 1:]
|
||||||
|
intrinsics.mem_copy_non_overlapping(&val_slice[0], raw_data(v_new), len(v_new))
|
||||||
|
val_slice[len(v_new)] = 0
|
||||||
|
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
append(&_env, string(k_addr[:kv_size - 1]))
|
||||||
|
sync.mutex_unlock(&_env_mutex)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
_unset_env :: proc(key: string) -> bool {
|
_unset_env :: proc(key: string) -> bool {
|
||||||
//TODO
|
if _org_env_begin == 0 {
|
||||||
return false
|
_build_env()
|
||||||
|
}
|
||||||
|
|
||||||
|
v: string
|
||||||
|
i: int
|
||||||
|
if v, i = _lookup(key); i == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
unordered_remove(&_env, i)
|
||||||
|
sync.mutex_unlock(&_env_mutex)
|
||||||
|
|
||||||
|
if _is_in_org_env(v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got this far, the envrionment variable
|
||||||
|
// existed AND was allocated by us.
|
||||||
|
k_addr, _ := _kv_addr_from_val(v, key)
|
||||||
|
heap_free(k_addr)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
_clear_env :: proc() {
|
_clear_env :: proc() {
|
||||||
//TODO
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
defer sync.mutex_unlock(&_env_mutex)
|
||||||
|
|
||||||
|
for kv in _env {
|
||||||
|
if !_is_in_org_env(kv) {
|
||||||
|
heap_free(raw_data(kv))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clear(&_env)
|
||||||
|
|
||||||
|
// nothing resides in the original environment either
|
||||||
|
_org_env_begin = ~uintptr(0)
|
||||||
|
_org_env_end = ~uintptr(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||||
//TODO
|
if _org_env_begin == 0 {
|
||||||
return nil
|
_build_env()
|
||||||
|
}
|
||||||
|
env := make([]string, len(_env), allocator)
|
||||||
|
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
defer sync.mutex_unlock(&_env_mutex)
|
||||||
|
for entry, i in _env {
|
||||||
|
env[i], _ = clone_string(entry, allocator)
|
||||||
|
}
|
||||||
|
return env
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The entire environment is stored as 0 terminated strings,
|
||||||
|
// so there is no need to clone/free individual variables
|
||||||
|
export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
|
||||||
|
if _org_env_begin == 0 {
|
||||||
|
// The environment has not been modified, so we can just
|
||||||
|
// send the original environment
|
||||||
|
org_env := _get_original_env()
|
||||||
|
n: int
|
||||||
|
for ; org_env[n] != nil; n += 1 {}
|
||||||
|
return slice.clone(org_env[:n + 1], allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: already terminated by nil pointer via + 1
|
||||||
|
env := make([]cstring, len(_env) + 1, allocator)
|
||||||
|
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
defer sync.mutex_unlock(&_env_mutex)
|
||||||
|
for entry, i in _env {
|
||||||
|
env[i] = cstring(raw_data(entry))
|
||||||
|
}
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
|
_build_env :: proc() {
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
defer sync.mutex_unlock(&_env_mutex)
|
||||||
|
if _org_env_begin != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_env = make(type_of(_env), heap_allocator())
|
||||||
|
cstring_env := _get_original_env()
|
||||||
|
_org_env_begin = uintptr(rawptr(cstring_env[0]))
|
||||||
|
for i := 0; cstring_env[i] != nil; i += 1 {
|
||||||
|
bytes := ([^]u8)(cstring_env[i])
|
||||||
|
n := len(cstring_env[i])
|
||||||
|
_org_env_end = uintptr(&bytes[n])
|
||||||
|
append(&_env, string(bytes[:n]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_original_env :: #force_inline proc() -> [^]cstring {
|
||||||
|
// essentially &argv[argc] which should be a nil pointer!
|
||||||
|
#no_bounds_check env: [^]cstring = &runtime.args__[len(runtime.args__)]
|
||||||
|
assert(env[0] == nil)
|
||||||
|
return &env[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
_kv_from_entry :: #force_inline proc(entry: string) -> (k, v: string) {
|
||||||
|
eq_idx := strings.index_byte(entry, '=')
|
||||||
|
if eq_idx == -1 {
|
||||||
|
return entry, ""
|
||||||
|
}
|
||||||
|
return entry[:eq_idx], entry[eq_idx + 1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
_kv_addr_from_val :: #force_inline proc(val: string, key: string) -> ([^]u8, [^]u8) {
|
||||||
|
v_addr := raw_data(val)
|
||||||
|
k_addr := ([^]u8)(&v_addr[-(len(key) + 1)])
|
||||||
|
return k_addr, v_addr
|
||||||
|
}
|
||||||
|
|
||||||
|
_is_in_org_env :: #force_inline proc(env_data: string) -> bool {
|
||||||
|
addr := uintptr(raw_data(env_data))
|
||||||
|
return addr >= _org_env_begin && addr < _org_env_end
|
||||||
|
}
|
||||||
|
|||||||
@@ -99,3 +99,19 @@ error_string :: proc(ferr: Error) -> string {
|
|||||||
|
|
||||||
return "unknown error"
|
return "unknown error"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print_error :: proc(f: ^File, ferr: Error, msg: string) {
|
||||||
|
TEMP_ALLOCATOR_GUARD()
|
||||||
|
err_str := error_string(ferr)
|
||||||
|
|
||||||
|
// msg + ": " + err_str + '\n'
|
||||||
|
length := len(msg) + 2 + len(err_str) + 1
|
||||||
|
buf := make([]u8, length, temp_allocator())
|
||||||
|
|
||||||
|
copy(buf, msg)
|
||||||
|
buf[len(msg)] = ':'
|
||||||
|
buf[len(msg) + 1] = ' '
|
||||||
|
copy(buf[len(msg) + 2:], err_str)
|
||||||
|
buf[length - 1] = '\n'
|
||||||
|
write(f, buf)
|
||||||
|
}
|
||||||
|
|||||||
+154
-134
@@ -1,145 +1,165 @@
|
|||||||
//+private
|
//+private
|
||||||
package os2
|
package os2
|
||||||
|
|
||||||
import "core:sys/unix"
|
import "core:sys/linux"
|
||||||
|
|
||||||
EPERM :: 1
|
@(rodata)
|
||||||
ENOENT :: 2
|
_errno_strings : [linux.Errno]string = {
|
||||||
ESRCH :: 3
|
.NONE = "Success",
|
||||||
EINTR :: 4
|
.EPERM = "Operation not permitted",
|
||||||
EIO :: 5
|
.ENOENT = "No such file or directory",
|
||||||
ENXIO :: 6
|
.ESRCH = "No such process",
|
||||||
EBADF :: 9
|
.EINTR = "Interrupted system call",
|
||||||
EAGAIN :: 11
|
.EIO = "Input/output error",
|
||||||
ENOMEM :: 12
|
.ENXIO = "No such device or address",
|
||||||
EACCES :: 13
|
.E2BIG = "Argument list too long",
|
||||||
EFAULT :: 14
|
.ENOEXEC = "Exec format error",
|
||||||
EEXIST :: 17
|
.EBADF = "Bad file descriptor",
|
||||||
ENODEV :: 19
|
.ECHILD = "No child processes",
|
||||||
ENOTDIR :: 20
|
.EAGAIN = "Resource temporarily unavailable",
|
||||||
EISDIR :: 21
|
.ENOMEM = "Cannot allocate memory",
|
||||||
EINVAL :: 22
|
.EACCES = "Permission denied",
|
||||||
ENFILE :: 23
|
.EFAULT = "Bad address",
|
||||||
EMFILE :: 24
|
.ENOTBLK = "Block device required",
|
||||||
ETXTBSY :: 26
|
.EBUSY = "Device or resource busy",
|
||||||
EFBIG :: 27
|
.EEXIST = "File exists",
|
||||||
ENOSPC :: 28
|
.EXDEV = "Invalid cross-device link",
|
||||||
ESPIPE :: 29
|
.ENODEV = "No such device",
|
||||||
EROFS :: 30
|
.ENOTDIR = "Not a directory",
|
||||||
EPIPE :: 32
|
.EISDIR = "Is a directory",
|
||||||
ERANGE :: 34 /* Result too large */
|
.EINVAL = "Invalid argument",
|
||||||
EDEADLK :: 35 /* Resource deadlock would occur */
|
.ENFILE = "Too many open files in system",
|
||||||
ENAMETOOLONG :: 36 /* File name too long */
|
.EMFILE = "Too many open files",
|
||||||
ENOLCK :: 37 /* No record locks available */
|
.ENOTTY = "Inappropriate ioctl for device",
|
||||||
ENOSYS :: 38 /* Invalid system call number */
|
.ETXTBSY = "Text file busy",
|
||||||
ENOTEMPTY :: 39 /* Directory not empty */
|
.EFBIG = "File too large",
|
||||||
ELOOP :: 40 /* Too many symbolic links encountered */
|
.ENOSPC = "No space left on device",
|
||||||
EWOULDBLOCK :: EAGAIN /* Operation would block */
|
.ESPIPE = "Illegal seek",
|
||||||
ENOMSG :: 42 /* No message of desired type */
|
.EROFS = "Read-only file system",
|
||||||
EIDRM :: 43 /* Identifier removed */
|
.EMLINK = "Too many links",
|
||||||
ECHRNG :: 44 /* Channel number out of range */
|
.EPIPE = "Broken pipe",
|
||||||
EL2NSYNC :: 45 /* Level 2 not synchronized */
|
.EDOM = "Numerical argument out of domain",
|
||||||
EL3HLT :: 46 /* Level 3 halted */
|
.ERANGE = "Numerical result out of range",
|
||||||
EL3RST :: 47 /* Level 3 reset */
|
.EDEADLK = "Resource deadlock avoided",
|
||||||
ELNRNG :: 48 /* Link number out of range */
|
.ENAMETOOLONG = "File name too long",
|
||||||
EUNATCH :: 49 /* Protocol driver not attached */
|
.ENOLCK = "No locks available",
|
||||||
ENOCSI :: 50 /* No CSI structure available */
|
.ENOSYS = "Function not implemented",
|
||||||
EL2HLT :: 51 /* Level 2 halted */
|
.ENOTEMPTY = "Directory not empty",
|
||||||
EBADE :: 52 /* Invalid exchange */
|
.ELOOP = "Too many levels of symbolic links",
|
||||||
EBADR :: 53 /* Invalid request descriptor */
|
.EUNKNOWN_41 = "Unknown Error (41)",
|
||||||
EXFULL :: 54 /* Exchange full */
|
.ENOMSG = "No message of desired type",
|
||||||
ENOANO :: 55 /* No anode */
|
.EIDRM = "Identifier removed",
|
||||||
EBADRQC :: 56 /* Invalid request code */
|
.ECHRNG = "Channel number out of range",
|
||||||
EBADSLT :: 57 /* Invalid slot */
|
.EL2NSYNC = "Level 2 not synchronized",
|
||||||
EDEADLOCK :: EDEADLK
|
.EL3HLT = "Level 3 halted",
|
||||||
EBFONT :: 59 /* Bad font file format */
|
.EL3RST = "Level 3 reset",
|
||||||
ENOSTR :: 60 /* Device not a stream */
|
.ELNRNG = "Link number out of range",
|
||||||
ENODATA :: 61 /* No data available */
|
.EUNATCH = "Protocol driver not attached",
|
||||||
ETIME :: 62 /* Timer expired */
|
.ENOCSI = "No CSI structure available",
|
||||||
ENOSR :: 63 /* Out of streams resources */
|
.EL2HLT = "Level 2 halted",
|
||||||
ENONET :: 64 /* Machine is not on the network */
|
.EBADE = "Invalid exchange",
|
||||||
ENOPKG :: 65 /* Package not installed */
|
.EBADR = "Invalid request descriptor",
|
||||||
EREMOTE :: 66 /* Object is remote */
|
.EXFULL = "Exchange full",
|
||||||
ENOLINK :: 67 /* Link has been severed */
|
.ENOANO = "No anode",
|
||||||
EADV :: 68 /* Advertise error */
|
.EBADRQC = "Invalid request code",
|
||||||
ESRMNT :: 69 /* Srmount error */
|
.EBADSLT = "Invalid slot",
|
||||||
ECOMM :: 70 /* Communication error on send */
|
.EUNKNOWN_58 = "Unknown Error (58)",
|
||||||
EPROTO :: 71 /* Protocol error */
|
.EBFONT = "Bad font file format",
|
||||||
EMULTIHOP :: 72 /* Multihop attempted */
|
.ENOSTR = "Device not a stream",
|
||||||
EDOTDOT :: 73 /* RFS specific error */
|
.ENODATA = "No data available",
|
||||||
EBADMSG :: 74 /* Not a data message */
|
.ETIME = "Timer expired",
|
||||||
EOVERFLOW :: 75 /* Value too large for defined data type */
|
.ENOSR = "Out of streams resources",
|
||||||
ENOTUNIQ :: 76 /* Name not unique on network */
|
.ENONET = "Machine is not on the network",
|
||||||
EBADFD :: 77 /* File descriptor in bad state */
|
.ENOPKG = "Package not installed",
|
||||||
EREMCHG :: 78 /* Remote address changed */
|
.EREMOTE = "Object is remote",
|
||||||
ELIBACC :: 79 /* Can not access a needed shared library */
|
.ENOLINK = "Link has been severed",
|
||||||
ELIBBAD :: 80 /* Accessing a corrupted shared library */
|
.EADV = "Advertise error",
|
||||||
ELIBSCN :: 81 /* .lib section in a.out corrupted */
|
.ESRMNT = "Srmount error",
|
||||||
ELIBMAX :: 82 /* Attempting to link in too many shared libraries */
|
.ECOMM = "Communication error on send",
|
||||||
ELIBEXEC :: 83 /* Cannot exec a shared library directly */
|
.EPROTO = "Protocol error",
|
||||||
EILSEQ :: 84 /* Illegal byte sequence */
|
.EMULTIHOP = "Multihop attempted",
|
||||||
ERESTART :: 85 /* Interrupted system call should be restarted */
|
.EDOTDOT = "RFS specific error",
|
||||||
ESTRPIPE :: 86 /* Streams pipe error */
|
.EBADMSG = "Bad message",
|
||||||
EUSERS :: 87 /* Too many users */
|
.EOVERFLOW = "Value too large for defined data type",
|
||||||
ENOTSOCK :: 88 /* Socket operation on non-socket */
|
.ENOTUNIQ = "Name not unique on network",
|
||||||
EDESTADDRREQ :: 89 /* Destination address required */
|
.EBADFD = "File descriptor in bad state",
|
||||||
EMSGSIZE :: 90 /* Message too long */
|
.EREMCHG = "Remote address changed",
|
||||||
EPROTOTYPE :: 91 /* Protocol wrong type for socket */
|
.ELIBACC = "Can not access a needed shared library",
|
||||||
ENOPROTOOPT :: 92 /* Protocol not available */
|
.ELIBBAD = "Accessing a corrupted shared library",
|
||||||
EPROTONOSUPPORT:: 93 /* Protocol not supported */
|
.ELIBSCN = ".lib section in a.out corrupted",
|
||||||
ESOCKTNOSUPPORT:: 94 /* Socket type not supported */
|
.ELIBMAX = "Attempting to link in too many shared libraries",
|
||||||
EOPNOTSUPP :: 95 /* Operation not supported on transport endpoint */
|
.ELIBEXEC = "Cannot exec a shared library directly",
|
||||||
EPFNOSUPPORT :: 96 /* Protocol family not supported */
|
.EILSEQ = "Invalid or incomplete multibyte or wide character",
|
||||||
EAFNOSUPPORT :: 97 /* Address family not supported by protocol */
|
.ERESTART = "Interrupted system call should be restarted",
|
||||||
EADDRINUSE :: 98 /* Address already in use */
|
.ESTRPIPE = "Streams pipe error",
|
||||||
EADDRNOTAVAIL :: 99 /* Cannot assign requested address */
|
.EUSERS = "Too many users",
|
||||||
ENETDOWN :: 100 /* Network is down */
|
.ENOTSOCK = "Socket operation on non-socket",
|
||||||
ENETUNREACH :: 101 /* Network is unreachable */
|
.EDESTADDRREQ = "Destination address required",
|
||||||
ENETRESET :: 102 /* Network dropped connection because of reset */
|
.EMSGSIZE = "Message too long",
|
||||||
ECONNABORTED :: 103 /* Software caused connection abort */
|
.EPROTOTYPE = "Protocol wrong type for socket",
|
||||||
ECONNRESET :: 104 /* Connection reset by peer */
|
.ENOPROTOOPT = "Protocol not available",
|
||||||
ENOBUFS :: 105 /* No buffer space available */
|
.EPROTONOSUPPORT = "Protocol not supported",
|
||||||
EISCONN :: 106 /* Transport endpoint is already connected */
|
.ESOCKTNOSUPPORT = "Socket type not supported",
|
||||||
ENOTCONN :: 107 /* Transport endpoint is not connected */
|
.EOPNOTSUPP = "Operation not supported",
|
||||||
ESHUTDOWN :: 108 /* Cannot send after transport endpoint shutdown */
|
.EPFNOSUPPORT = "Protocol family not supported",
|
||||||
ETOOMANYREFS :: 109 /* Too many references: cannot splice */
|
.EAFNOSUPPORT = "Address family not supported by protocol",
|
||||||
ETIMEDOUT :: 110 /* Connection timed out */
|
.EADDRINUSE = "Address already in use",
|
||||||
ECONNREFUSED :: 111 /* Connection refused */
|
.EADDRNOTAVAIL = "Cannot assign requested address",
|
||||||
EHOSTDOWN :: 112 /* Host is down */
|
.ENETDOWN = "Network is down",
|
||||||
EHOSTUNREACH :: 113 /* No route to host */
|
.ENETUNREACH = "Network is unreachable",
|
||||||
EALREADY :: 114 /* Operation already in progress */
|
.ENETRESET = "Network dropped connection on reset",
|
||||||
EINPROGRESS :: 115 /* Operation now in progress */
|
.ECONNABORTED = "Software caused connection abort",
|
||||||
ESTALE :: 116 /* Stale file handle */
|
.ECONNRESET = "Connection reset by peer",
|
||||||
EUCLEAN :: 117 /* Structure needs cleaning */
|
.ENOBUFS = "No buffer space available",
|
||||||
ENOTNAM :: 118 /* Not a XENIX named type file */
|
.EISCONN = "Transport endpoint is already connected",
|
||||||
ENAVAIL :: 119 /* No XENIX semaphores available */
|
.ENOTCONN = "Transport endpoint is not connected",
|
||||||
EISNAM :: 120 /* Is a named type file */
|
.ESHUTDOWN = "Cannot send after transport endpoint shutdown",
|
||||||
EREMOTEIO :: 121 /* Remote I/O error */
|
.ETOOMANYREFS = "Too many references: cannot splice",
|
||||||
EDQUOT :: 122 /* Quota exceeded */
|
.ETIMEDOUT = "Connection timed out",
|
||||||
ENOMEDIUM :: 123 /* No medium found */
|
.ECONNREFUSED = "Connection refused",
|
||||||
EMEDIUMTYPE :: 124 /* Wrong medium type */
|
.EHOSTDOWN = "Host is down",
|
||||||
ECANCELED :: 125 /* Operation Canceled */
|
.EHOSTUNREACH = "No route to host",
|
||||||
ENOKEY :: 126 /* Required key not available */
|
.EALREADY = "Operation already in progress",
|
||||||
EKEYEXPIRED :: 127 /* Key has expired */
|
.EINPROGRESS = "Operation now in progress",
|
||||||
EKEYREVOKED :: 128 /* Key has been revoked */
|
.ESTALE = "Stale file handle",
|
||||||
EKEYREJECTED :: 129 /* Key was rejected by service */
|
.EUCLEAN = "Structure needs cleaning",
|
||||||
EOWNERDEAD :: 130 /* Owner died */
|
.ENOTNAM = "Not a XENIX named type file",
|
||||||
ENOTRECOVERABLE:: 131 /* State not recoverable */
|
.ENAVAIL = "No XENIX semaphores available",
|
||||||
ERFKILL :: 132 /* Operation not possible due to RF-kill */
|
.EISNAM = "Is a named type file",
|
||||||
EHWPOISON :: 133 /* Memory page has hardware error */
|
.EREMOTEIO = "Remote I/O error",
|
||||||
|
.EDQUOT = "Disk quota exceeded",
|
||||||
|
.ENOMEDIUM = "No medium found",
|
||||||
|
.EMEDIUMTYPE = "Wrong medium type",
|
||||||
|
.ECANCELED = "Operation canceled",
|
||||||
|
.ENOKEY = "Required key not available",
|
||||||
|
.EKEYEXPIRED = "Key has expired",
|
||||||
|
.EKEYREVOKED = "Key has been revoked",
|
||||||
|
.EKEYREJECTED = "Key was rejected by service",
|
||||||
|
.EOWNERDEAD = "Owner died",
|
||||||
|
.ENOTRECOVERABLE = "State not recoverable",
|
||||||
|
.ERFKILL = "Operation not possible due to RF-kill",
|
||||||
|
.EHWPOISON = "Memory page has hardware error",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_get_platform_error :: proc(errno: linux.Errno) -> Error {
|
||||||
|
#partial switch errno {
|
||||||
|
case .NONE:
|
||||||
|
return nil
|
||||||
|
case .EPERM:
|
||||||
|
return .Permission_Denied
|
||||||
|
case .EEXIST:
|
||||||
|
return .Exist
|
||||||
|
case .ENOENT:
|
||||||
|
return .Not_Exist
|
||||||
|
}
|
||||||
|
|
||||||
_get_platform_error :: proc(res: int) -> Error {
|
|
||||||
errno := unix.get_errno(res)
|
|
||||||
return Platform_Error(i32(errno))
|
return Platform_Error(i32(errno))
|
||||||
}
|
}
|
||||||
|
|
||||||
_ok_or_error :: proc(res: int) -> Error {
|
|
||||||
return res >= 0 ? nil : _get_platform_error(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
_error_string :: proc(errno: i32) -> string {
|
_error_string :: proc(errno: i32) -> string {
|
||||||
if errno == 0 {
|
if errno >= 0 && errno <= i32(max(linux.Errno)) {
|
||||||
return ""
|
return _errno_strings[linux.Errno(errno)]
|
||||||
}
|
}
|
||||||
return "Error"
|
return "Unknown Error"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,13 +45,10 @@ O_TRUNC :: File_Flags{.Trunc}
|
|||||||
O_SPARSE :: File_Flags{.Sparse}
|
O_SPARSE :: File_Flags{.Sparse}
|
||||||
O_CLOEXEC :: File_Flags{.Close_On_Exec}
|
O_CLOEXEC :: File_Flags{.Close_On_Exec}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
stdin: ^File = nil // OS-Specific
|
stdin: ^File = nil // OS-Specific
|
||||||
stdout: ^File = nil // OS-Specific
|
stdout: ^File = nil // OS-Specific
|
||||||
stderr: ^File = nil // OS-Specific
|
stderr: ^File = nil // OS-Specific
|
||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
create :: proc(name: string) -> (^File, Error) {
|
create :: proc(name: string) -> (^File, Error) {
|
||||||
return open(name, {.Read, .Write, .Create}, File_Mode(0o777))
|
return open(name, {.Read, .Write, .Create}, File_Mode(0o777))
|
||||||
|
|||||||
+231
-161
@@ -1,39 +1,64 @@
|
|||||||
//+private
|
//+private
|
||||||
package os2
|
package os2
|
||||||
|
|
||||||
import "base:runtime"
|
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
import "core:sys/unix"
|
import "base:runtime"
|
||||||
|
import "core:sys/linux"
|
||||||
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
|
|
||||||
|
|
||||||
_File :: struct {
|
_File :: struct {
|
||||||
name: string,
|
name: string,
|
||||||
fd: int,
|
fd: linux.Fd,
|
||||||
allocator: runtime.Allocator,
|
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
|
||||||
|
|
||||||
|
stdin = &_stdin
|
||||||
|
stdout = &_stdout
|
||||||
|
stderr = &_stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
_file_allocator :: proc() -> runtime.Allocator {
|
||||||
|
return heap_allocator()
|
||||||
|
}
|
||||||
|
|
||||||
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) {
|
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
@@ -41,40 +66,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
|
// Just default to using O_NOCTTY because needing to open a controlling
|
||||||
// terminal would be incredibly rare. This has no effect on files while
|
// terminal would be incredibly rare. This has no effect on files while
|
||||||
// allowing us to open serial devices.
|
// 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 {
|
switch flags & O_RDONLY|O_WRONLY|O_RDWR {
|
||||||
case O_RDONLY: flags_i = _O_RDONLY
|
case O_RDONLY:
|
||||||
case O_WRONLY: flags_i = _O_WRONLY
|
case O_WRONLY: sys_flags += {.WRONLY}
|
||||||
case O_RDWR: flags_i = _O_RDWR
|
case O_RDWR: sys_flags += {.RDWR}
|
||||||
}
|
}
|
||||||
|
|
||||||
if .Append in flags { flags_i |= _O_APPEND }
|
if .Append in flags { sys_flags += {.APPEND} }
|
||||||
if .Create in flags { flags_i |= _O_CREAT }
|
if .Create in flags { sys_flags += {.CREAT} }
|
||||||
if .Excl in flags { flags_i |= _O_EXCL }
|
if .Excl in flags { sys_flags += {.EXCL} }
|
||||||
if .Sync in flags { flags_i |= _O_SYNC }
|
if .Sync in flags { sys_flags += {.DSYNC} }
|
||||||
if .Trunc in flags { flags_i |= _O_TRUNC }
|
if .Trunc in flags { sys_flags += {.TRUNC} }
|
||||||
if .Close_On_Exec in flags { flags_i |= _O_CLOEXEC }
|
if .Close_On_Exec in flags { sys_flags += {.CLOEXEC} }
|
||||||
|
|
||||||
fd := unix.sys_open(name_cstr, flags_i, uint(perm))
|
fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)(u32(perm)))
|
||||||
if fd < 0 {
|
if errno != .NONE {
|
||||||
return nil, _get_platform_error(fd)
|
return nil, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
return _new_file(uintptr(fd), name), nil
|
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 := new(File, file_allocator())
|
||||||
file.impl.fd = int(fd)
|
_construct_file(file, 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,
|
|
||||||
}
|
|
||||||
return file
|
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 {
|
_destroy :: proc(f: ^File) -> Error {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -86,12 +119,15 @@ _destroy :: proc(f: ^File) -> Error {
|
|||||||
|
|
||||||
|
|
||||||
_close :: proc(f: ^File) -> Error {
|
_close :: proc(f: ^File) -> Error {
|
||||||
if f != nil {
|
if f == nil {
|
||||||
res := unix.sys_close(f.impl.fd)
|
return nil
|
||||||
_destroy(f)
|
|
||||||
return _ok_or_error(res)
|
|
||||||
}
|
}
|
||||||
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 {
|
_fd :: proc(f: ^File) -> uintptr {
|
||||||
@@ -106,20 +142,32 @@ _name :: proc(f: ^File) -> string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
|
_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
|
||||||
res := unix.sys_lseek(f.impl.fd, offset, int(whence))
|
n, errno := linux.lseek(f.impl.fd, offset, linux.Seek_Whence(whence))
|
||||||
if res < 0 {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(int(res))
|
return -1, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
return res, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_read :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
_read :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
||||||
if len(p) == 0 {
|
if len(p) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
n := unix.sys_read(f.impl.fd, &p[0], len(p))
|
n, errno := linux.read(f.impl.fd, p[:])
|
||||||
if n < 0 {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(n)
|
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 {
|
if n == 0 {
|
||||||
return 0, .EOF
|
return 0, .EOF
|
||||||
@@ -127,91 +175,67 @@ _read :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
|||||||
return i64(n), nil
|
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) {
|
_write :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
||||||
if len(p) == 0 {
|
if len(p) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
n := unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
|
n, errno := linux.write(f.impl.fd, p[:])
|
||||||
if n < 0 {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(n)
|
return -1, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
return i64(n), nil
|
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 {
|
if offset < 0 {
|
||||||
return 0, .Invalid_Offset
|
return 0, .Invalid_Offset
|
||||||
}
|
}
|
||||||
|
|
||||||
b, offset := p, offset
|
n, errno := linux.pwrite(f.impl.fd, p[:], offset)
|
||||||
for len(b) > 0 {
|
if errno != .NONE {
|
||||||
m := unix.sys_pwrite(f.impl.fd, &b[0], len(b), offset)
|
return -1, _get_platform_error(errno)
|
||||||
if m < 0 {
|
|
||||||
return -1, _get_platform_error(m)
|
|
||||||
}
|
|
||||||
n += i64(m)
|
|
||||||
b = b[m:]
|
|
||||||
offset += i64(m)
|
|
||||||
}
|
}
|
||||||
return
|
return i64(n), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||||
s: _Stat = ---
|
s: linux.Stat = ---
|
||||||
res := unix.sys_fstat(f.impl.fd, &s)
|
errno := linux.fstat(f.impl.fd, &s)
|
||||||
if res < 0 {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(res)
|
return -1, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
return s.size, nil
|
return i64(s.size), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_sync :: proc(f: ^File) -> Error {
|
_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 {
|
_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 {
|
_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 {
|
_remove :: proc(name: string) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
|
|
||||||
fd := unix.sys_open(name_cstr, int(File_Flags.Read))
|
fd, errno := linux.open(name_cstr, {.NOFOLLOW})
|
||||||
if fd < 0 {
|
#partial switch (errno) {
|
||||||
return _get_platform_error(fd)
|
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 _get_platform_error(linux.unlink(name_cstr))
|
||||||
return _ok_or_error(unix.sys_rmdir(name_cstr))
|
|
||||||
}
|
|
||||||
return _ok_or_error(unix.sys_unlink(name_cstr))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_rename :: proc(old_name, new_name: string) -> Error {
|
_rename :: proc(old_name, new_name: string) -> Error {
|
||||||
@@ -219,7 +243,7 @@ _rename :: proc(old_name, new_name: string) -> Error {
|
|||||||
old_name_cstr := temp_cstring(old_name) or_return
|
old_name_cstr := temp_cstring(old_name) or_return
|
||||||
new_name_cstr := temp_cstring(new_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 {
|
_link :: proc(old_name, new_name: string) -> Error {
|
||||||
@@ -227,148 +251,194 @@ _link :: proc(old_name, new_name: string) -> Error {
|
|||||||
old_name_cstr := temp_cstring(old_name) or_return
|
old_name_cstr := temp_cstring(old_name) or_return
|
||||||
new_name_cstr := temp_cstring(new_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 {
|
_symlink :: proc(old_name, new_name: string) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
old_name_cstr := temp_cstring(old_name) or_return
|
old_name_cstr := temp_cstring(old_name) or_return
|
||||||
new_name_cstr := temp_cstring(new_name) or_return
|
new_name_cstr := temp_cstring(new_name) or_return
|
||||||
|
return _get_platform_error(linux.symlink(old_name_cstr, new_name_cstr))
|
||||||
return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
|
_read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
|
||||||
bufsz : uint = 256
|
bufsz : uint = 256
|
||||||
buf := make([]byte, bufsz, allocator)
|
buf := make([]byte, bufsz, allocator)
|
||||||
for {
|
for {
|
||||||
rc := unix.sys_readlink(name_cstr, &buf[0], bufsz)
|
sz, errno := linux.readlink(name_cstr, buf[:])
|
||||||
if rc < 0 {
|
if errno != .NONE {
|
||||||
delete(buf)
|
delete(buf, allocator)
|
||||||
return "", _get_platform_error(rc)
|
return "", _get_platform_error(errno)
|
||||||
} else if rc == int(bufsz) {
|
} else if sz == int(bufsz) {
|
||||||
bufsz *= 2
|
bufsz *= 2
|
||||||
delete(buf)
|
delete(buf, allocator)
|
||||||
buf = make([]byte, bufsz, allocator)
|
buf = make([]byte, bufsz, allocator)
|
||||||
} else {
|
} 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()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
return _read_link_cstr(name_cstr, allocator)
|
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 {
|
_chdir :: proc(name: string) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
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 {
|
_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 {
|
_chmod :: proc(name: string, mode: File_Mode) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
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 {
|
_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
|
// NOTE: will throw error without super user priviledges
|
||||||
_chown :: proc(name: string, uid, gid: int) -> Error {
|
_chown :: proc(name: string, uid, gid: int) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
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
|
// NOTE: will throw error without super user priviledges
|
||||||
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
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
|
// NOTE: will throw error without super user priviledges
|
||||||
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
_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 {
|
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
times := [2]Unix_File_Time {
|
times := [2]linux.Time_Spec {
|
||||||
{ atime._nsec, 0 },
|
{
|
||||||
{ mtime._nsec, 0 },
|
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, ×, 0))
|
return _get_platform_error(linux.utimensat(linux.AT_FDCWD, name_cstr, ×[0], nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||||
times := [2]Unix_File_Time {
|
times := [2]linux.Time_Spec {
|
||||||
{ atime._nsec, 0 },
|
{
|
||||||
{ mtime._nsec, 0 },
|
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, ×, 0))
|
return _get_platform_error(linux.utimensat(f.impl.fd, nil, ×[0], nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
_exists :: proc(name: string) -> bool {
|
_exists :: proc(name: string) -> bool {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr, _ := temp_cstring(name)
|
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 {
|
_is_file :: proc(name: string) -> bool {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr, _ := temp_cstring(name)
|
name_cstr, _ := temp_cstring(name)
|
||||||
s: _Stat
|
s: linux.Stat
|
||||||
res := unix.sys_stat(name_cstr, &s)
|
if linux.stat(name_cstr, &s) != .NONE {
|
||||||
if res < 0 {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return S_ISREG(s.mode)
|
return linux.S_ISREG(s.mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
_is_file_fd :: proc(fd: int) -> bool {
|
_is_file_fd :: proc(fd: linux.Fd) -> bool {
|
||||||
s: _Stat
|
s: linux.Stat
|
||||||
res := unix.sys_fstat(fd, &s)
|
if linux.fstat(fd, &s) != .NONE {
|
||||||
if res < 0 { // error
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return S_ISREG(s.mode)
|
return linux.S_ISREG(s.mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
_is_dir :: proc(name: string) -> bool {
|
_is_dir :: proc(name: string) -> bool {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr, _ := temp_cstring(name)
|
name_cstr, _ := temp_cstring(name)
|
||||||
s: _Stat
|
s: linux.Stat
|
||||||
res := unix.sys_stat(name_cstr, &s)
|
if linux.stat(name_cstr, &s) != .NONE {
|
||||||
if res < 0 {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return S_ISDIR(s.mode)
|
return linux.S_ISDIR(s.mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
_is_dir_fd :: proc(fd: int) -> bool {
|
_is_dir_fd :: proc(fd: linux.Fd) -> bool {
|
||||||
s: _Stat
|
s: linux.Stat
|
||||||
res := unix.sys_fstat(fd, &s)
|
if linux.fstat(fd, &s) != .NONE {
|
||||||
if res < 0 { // error
|
|
||||||
return false
|
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")
|
@(private="package")
|
||||||
|
|||||||
+11
-17
@@ -1,7 +1,7 @@
|
|||||||
//+private
|
//+private
|
||||||
package os2
|
package os2
|
||||||
|
|
||||||
import "core:sys/unix"
|
import "core:sys/linux"
|
||||||
import "core:sync"
|
import "core:sync"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
@@ -97,9 +97,8 @@ CURRENTLY_ACTIVE :: (^^Region)(~uintptr(0))
|
|||||||
|
|
||||||
FREE_LIST_ENTRIES_PER_BLOCK :: BLOCK_SIZE / size_of(u16)
|
FREE_LIST_ENTRIES_PER_BLOCK :: BLOCK_SIZE / size_of(u16)
|
||||||
|
|
||||||
MMAP_FLAGS :: unix.MAP_ANONYMOUS | unix.MAP_PRIVATE
|
MMAP_FLAGS : linux.Map_Flags : {.ANONYMOUS, .PRIVATE}
|
||||||
MMAP_PROT :: unix.PROT_READ | unix.PROT_WRITE
|
MMAP_PROT : linux.Mem_Protection : {.READ, .WRITE}
|
||||||
|
|
||||||
|
|
||||||
@thread_local _local_region: ^Region
|
@thread_local _local_region: ^Region
|
||||||
global_regions: ^Region
|
global_regions: ^Region
|
||||||
@@ -324,11 +323,11 @@ heap_free :: proc(memory: rawptr) {
|
|||||||
// Regions
|
// Regions
|
||||||
//
|
//
|
||||||
_new_region :: proc() -> ^Region #no_bounds_check {
|
_new_region :: proc() -> ^Region #no_bounds_check {
|
||||||
res := unix.sys_mmap(nil, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
ptr, errno := linux.mmap(0, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
||||||
if res < 0 {
|
if errno != .NONE {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
new_region := (^Region)(uintptr(res))
|
new_region := (^Region)(ptr)
|
||||||
|
|
||||||
new_region.hdr.local_addr = CURRENTLY_ACTIVE
|
new_region.hdr.local_addr = CURRENTLY_ACTIVE
|
||||||
new_region.hdr.reset_addr = &_local_region
|
new_region.hdr.reset_addr = &_local_region
|
||||||
@@ -634,8 +633,8 @@ _region_free_list_remove :: proc(region: ^Region, free_idx: u16) #no_bounds_chec
|
|||||||
//
|
//
|
||||||
_direct_mmap_alloc :: proc(size: int) -> rawptr {
|
_direct_mmap_alloc :: proc(size: int) -> rawptr {
|
||||||
mmap_size := _round_up_to_nearest(size + BLOCK_SIZE, PAGE_SIZE)
|
mmap_size := _round_up_to_nearest(size + BLOCK_SIZE, PAGE_SIZE)
|
||||||
new_allocation := unix.sys_mmap(nil, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
new_allocation, errno := linux.mmap(0, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
||||||
if new_allocation < 0 && new_allocation > -4096 {
|
if errno != .NONE {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,13 +654,8 @@ _direct_mmap_resize :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr
|
|||||||
return mem.ptr_offset(alloc, 1)
|
return mem.ptr_offset(alloc, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
new_allocation := unix.sys_mremap(
|
new_allocation, errno := linux.mremap(alloc, uint(old_mmap_size), uint(new_mmap_size), {.MAYMOVE})
|
||||||
alloc,
|
if errno != .NONE {
|
||||||
uint(old_mmap_size),
|
|
||||||
uint(new_mmap_size),
|
|
||||||
unix.MREMAP_MAYMOVE,
|
|
||||||
)
|
|
||||||
if new_allocation < 0 && new_allocation > -4096 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,7 +696,7 @@ _direct_mmap_to_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawp
|
|||||||
_direct_mmap_free :: proc(alloc: ^Allocation_Header) {
|
_direct_mmap_free :: proc(alloc: ^Allocation_Header) {
|
||||||
requested := int(alloc.requested & REQUESTED_MASK)
|
requested := int(alloc.requested & REQUESTED_MASK)
|
||||||
mmap_size := _round_up_to_nearest(requested + BLOCK_SIZE, PAGE_SIZE)
|
mmap_size := _round_up_to_nearest(requested + BLOCK_SIZE, PAGE_SIZE)
|
||||||
unix.sys_munmap(alloc, uint(mmap_size))
|
linux.munmap(alloc, uint(mmap_size))
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
+70
-86
@@ -3,104 +3,89 @@ package os2
|
|||||||
|
|
||||||
import "core:strconv"
|
import "core:strconv"
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
import "core:sys/unix"
|
import "core:sys/linux"
|
||||||
|
|
||||||
_Path_Separator :: '/'
|
_Path_Separator :: '/'
|
||||||
_Path_Separator_String :: "/"
|
_Path_Separator_String :: "/"
|
||||||
_Path_List_Separator :: ':'
|
_Path_List_Separator :: ':'
|
||||||
|
|
||||||
_S_IFMT :: 0o170000 // Type of file mask
|
_OPENDIR_FLAGS : linux.Open_Flags : {.NONBLOCK, .DIRECTORY, .LARGEFILE, .CLOEXEC}
|
||||||
_S_IFIFO :: 0o010000 // Named pipe (fifo)
|
|
||||||
_S_IFCHR :: 0o020000 // Character special
|
|
||||||
_S_IFDIR :: 0o040000 // Directory
|
|
||||||
_S_IFBLK :: 0o060000 // Block special
|
|
||||||
_S_IFREG :: 0o100000 // Regular
|
|
||||||
_S_IFLNK :: 0o120000 // Symbolic link
|
|
||||||
_S_IFSOCK :: 0o140000 // Socket
|
|
||||||
|
|
||||||
_OPENDIR_FLAGS :: _O_RDONLY|_O_NONBLOCK|_O_DIRECTORY|_O_LARGEFILE|_O_CLOEXEC
|
|
||||||
|
|
||||||
_is_path_separator :: proc(c: byte) -> bool {
|
_is_path_separator :: proc(c: byte) -> bool {
|
||||||
return c == '/'
|
return c == '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
_mkdir :: proc(path: string, perm: File_Mode) -> Error {
|
_mkdir :: proc(path: string, perm: File_Mode) -> Error {
|
||||||
// NOTE: These modes would require sys_mknod, however, that would require
|
// TODO: These modes would require mknod, however, that would also
|
||||||
// additional arguments to this function.
|
// require additional arguments to this function..
|
||||||
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
||||||
return .Invalid_Argument
|
return .Invalid_Argument
|
||||||
}
|
}
|
||||||
|
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
path_cstr := temp_cstring(path) or_return
|
path_cstr := temp_cstring(path) or_return
|
||||||
return _ok_or_error(unix.sys_mkdir(path_cstr, uint(perm & 0o777)))
|
return _get_platform_error(linux.mkdir(path_cstr, transmute(linux.Mode)(u32(perm) & 0o777)))
|
||||||
}
|
}
|
||||||
|
|
||||||
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
|
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
|
||||||
_mkdirat :: proc(dfd: int, path: []u8, perm: int, has_created: ^bool) -> Error {
|
mkdirat :: proc(dfd: linux.Fd, path: []u8, perm: int, has_created: ^bool) -> Error {
|
||||||
if len(path) == 0 {
|
|
||||||
return _ok_or_error(unix.sys_close(dfd))
|
|
||||||
}
|
|
||||||
i: int
|
i: int
|
||||||
for /**/; i < len(path) - 1 && path[i] != '/'; i += 1 {}
|
for ; i < len(path) - 1 && path[i] != '/'; i += 1 {}
|
||||||
|
if i == 0 {
|
||||||
|
return _get_platform_error(linux.close(dfd))
|
||||||
|
}
|
||||||
path[i] = 0
|
path[i] = 0
|
||||||
new_dfd := unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
|
new_dfd, errno := linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
|
||||||
switch new_dfd {
|
#partial switch errno {
|
||||||
case -ENOENT:
|
case .ENOENT:
|
||||||
if res := unix.sys_mkdirat(dfd, cstring(&path[0]), uint(perm)); res < 0 {
|
if errno = linux.mkdirat(dfd, cstring(&path[0]), transmute(linux.Mode)(u32(perm))); errno != .NONE {
|
||||||
return _get_platform_error(res)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
has_created^ = true
|
has_created^ = true
|
||||||
if new_dfd = unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); new_dfd < 0 {
|
if new_dfd, errno = linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); errno != .NONE {
|
||||||
return _get_platform_error(new_dfd)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
case 0:
|
case .NONE:
|
||||||
if res := unix.sys_close(dfd); res < 0 {
|
if errno = linux.close(dfd); errno != .NONE {
|
||||||
return _get_platform_error(res)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
// skip consecutive '/'
|
// skip consecutive '/'
|
||||||
for i += 1; i < len(path) && path[i] == '/'; i += 1 {}
|
for i += 1; i < len(path) && path[i] == '/'; i += 1 {}
|
||||||
return _mkdirat(new_dfd, path[i:], perm, has_created)
|
return mkdirat(new_dfd, path[i:], perm, has_created)
|
||||||
case:
|
case:
|
||||||
return _get_platform_error(new_dfd)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
||||||
return .Invalid_Argument
|
return .Invalid_Argument
|
||||||
}
|
}
|
||||||
|
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
|
|
||||||
// need something we can edit, and use to generate cstrings
|
// need something we can edit, and use to generate cstrings
|
||||||
allocated: bool
|
path_bytes := make([]u8, len(path) + 1, temp_allocator())
|
||||||
path_bytes: []u8
|
|
||||||
if len(path) > _CSTRING_NAME_HEAP_THRESHOLD {
|
|
||||||
allocated = true
|
|
||||||
path_bytes = make([]u8, len(path) + 1)
|
|
||||||
} else {
|
|
||||||
path_bytes = make([]u8, len(path) + 1, temp_allocator())
|
|
||||||
}
|
|
||||||
|
|
||||||
// NULL terminate the byte slice to make it a valid cstring
|
// zero terminate the byte slice to make it a valid cstring
|
||||||
copy(path_bytes, path)
|
copy(path_bytes, path)
|
||||||
path_bytes[len(path)] = 0
|
path_bytes[len(path)] = 0
|
||||||
|
|
||||||
dfd: int
|
dfd: linux.Fd
|
||||||
|
errno: linux.Errno
|
||||||
if path_bytes[0] == '/' {
|
if path_bytes[0] == '/' {
|
||||||
dfd = unix.sys_open("/", _OPENDIR_FLAGS)
|
dfd, errno = linux.open("/", _OPENDIR_FLAGS)
|
||||||
path_bytes = path_bytes[1:]
|
path_bytes = path_bytes[1:]
|
||||||
} else {
|
} else {
|
||||||
dfd = unix.sys_open(".", _OPENDIR_FLAGS)
|
dfd, errno = linux.open(".", _OPENDIR_FLAGS)
|
||||||
}
|
}
|
||||||
if dfd < 0 {
|
if errno != .NONE {
|
||||||
return _get_platform_error(dfd)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
has_created: bool
|
has_created: bool
|
||||||
_mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return
|
mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return
|
||||||
if has_created {
|
if has_created {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -119,28 +104,28 @@ dirent64 :: struct {
|
|||||||
_remove_all :: proc(path: string) -> Error {
|
_remove_all :: proc(path: string) -> Error {
|
||||||
DT_DIR :: 4
|
DT_DIR :: 4
|
||||||
|
|
||||||
_remove_all_dir :: proc(dfd: int) -> Error {
|
remove_all_dir :: proc(dfd: linux.Fd) -> Error {
|
||||||
n := 64
|
n := 64
|
||||||
buf := make([]u8, n)
|
buf := make([]u8, n)
|
||||||
defer delete(buf)
|
defer delete(buf)
|
||||||
|
|
||||||
loop: for {
|
loop: for {
|
||||||
getdents_res := unix.sys_getdents64(dfd, &buf[0], n)
|
buflen, errno := linux.getdents(dfd, buf[:])
|
||||||
switch getdents_res {
|
#partial switch errno {
|
||||||
case -EINVAL:
|
case .EINVAL:
|
||||||
delete(buf)
|
delete(buf)
|
||||||
n *= 2
|
n *= 2
|
||||||
buf = make([]u8, n)
|
buf = make([]u8, n)
|
||||||
continue loop
|
continue loop
|
||||||
case -4096..<0:
|
case .NONE:
|
||||||
return _get_platform_error(getdents_res)
|
if buflen == 0 { break loop }
|
||||||
case 0:
|
case:
|
||||||
break loop
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
d: ^dirent64
|
d: ^dirent64
|
||||||
|
|
||||||
for i := 0; i < getdents_res; i += int(d.d_reclen) {
|
for i := 0; i < buflen; i += int(d.d_reclen) {
|
||||||
d = (^dirent64)(rawptr(&buf[i]))
|
d = (^dirent64)(rawptr(&buf[i]))
|
||||||
d_name_cstr := cstring(&d.d_name[0])
|
d_name_cstr := cstring(&d.d_name[0])
|
||||||
|
|
||||||
@@ -156,23 +141,22 @@ _remove_all :: proc(path: string) -> Error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
unlink_res: int
|
|
||||||
|
|
||||||
switch d.d_type {
|
switch d.d_type {
|
||||||
case DT_DIR:
|
case DT_DIR:
|
||||||
new_dfd := unix.sys_openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
|
new_dfd: linux.Fd
|
||||||
if new_dfd < 0 {
|
new_dfd, errno = linux.openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
|
||||||
return _get_platform_error(new_dfd)
|
if errno != .NONE {
|
||||||
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
defer unix.sys_close(new_dfd)
|
defer linux.close(new_dfd)
|
||||||
_remove_all_dir(new_dfd) or_return
|
remove_all_dir(new_dfd) or_return
|
||||||
unlink_res = unix.sys_unlinkat(dfd, d_name_cstr, int(unix.AT_REMOVEDIR))
|
errno = linux.unlinkat(dfd, d_name_cstr, {.REMOVEDIR})
|
||||||
case:
|
case:
|
||||||
unlink_res = unix.sys_unlinkat(dfd, d_name_cstr)
|
errno = linux.unlinkat(dfd, d_name_cstr, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if unlink_res < 0 {
|
if errno != .NONE {
|
||||||
return _get_platform_error(unlink_res)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,17 +166,19 @@ _remove_all :: proc(path: string) -> Error {
|
|||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
path_cstr := temp_cstring(path) or_return
|
path_cstr := temp_cstring(path) or_return
|
||||||
|
|
||||||
fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS)
|
fd, errno := linux.open(path_cstr, _OPENDIR_FLAGS)
|
||||||
switch fd {
|
#partial switch errno {
|
||||||
case -ENOTDIR:
|
case .NONE:
|
||||||
return _ok_or_error(unix.sys_unlink(path_cstr))
|
break
|
||||||
case -4096..<0:
|
case .ENOTDIR:
|
||||||
return _get_platform_error(fd)
|
return _get_platform_error(linux.unlink(path_cstr))
|
||||||
|
case:
|
||||||
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer unix.sys_close(fd)
|
defer linux.close(fd)
|
||||||
_remove_all_dir(fd) or_return
|
remove_all_dir(fd) or_return
|
||||||
return _ok_or_error(unix.sys_rmdir(path_cstr))
|
return _get_platform_error(linux.rmdir(path_cstr))
|
||||||
}
|
}
|
||||||
|
|
||||||
_getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
_getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
||||||
@@ -203,13 +189,12 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
|||||||
PATH_MAX :: 4096
|
PATH_MAX :: 4096
|
||||||
buf := make([dynamic]u8, PATH_MAX, allocator)
|
buf := make([dynamic]u8, PATH_MAX, allocator)
|
||||||
for {
|
for {
|
||||||
#no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
|
#no_bounds_check n, errno := linux.getcwd(buf[:])
|
||||||
|
if errno == .NONE {
|
||||||
if res >= 0 {
|
return string(buf[:n-1]), nil
|
||||||
return string_from_null_terminated_bytes(buf[:]), nil
|
|
||||||
}
|
}
|
||||||
if res != -ERANGE {
|
if errno != .ERANGE {
|
||||||
return "", _get_platform_error(res)
|
return "", _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
resize(&buf, len(buf)+PATH_MAX)
|
resize(&buf, len(buf)+PATH_MAX)
|
||||||
}
|
}
|
||||||
@@ -218,16 +203,16 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
|||||||
|
|
||||||
_setwd :: proc(dir: string) -> Error {
|
_setwd :: proc(dir: string) -> Error {
|
||||||
dir_cstr := temp_cstring(dir) or_return
|
dir_cstr := temp_cstring(dir) or_return
|
||||||
return _ok_or_error(unix.sys_chdir(dir_cstr))
|
return _get_platform_error(linux.chdir(dir_cstr))
|
||||||
}
|
}
|
||||||
|
|
||||||
_get_full_path :: proc(fd: int, allocator: runtime.Allocator) -> string {
|
_get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> string {
|
||||||
PROC_FD_PATH :: "/proc/self/fd/"
|
PROC_FD_PATH :: "/proc/self/fd/"
|
||||||
|
|
||||||
buf: [32]u8
|
buf: [32]u8
|
||||||
copy(buf[:], PROC_FD_PATH)
|
copy(buf[:], PROC_FD_PATH)
|
||||||
|
|
||||||
strconv.itoa(buf[len(PROC_FD_PATH):], fd)
|
strconv.itoa(buf[len(PROC_FD_PATH):], int(fd))
|
||||||
|
|
||||||
fullpath: string
|
fullpath: string
|
||||||
err: Error
|
err: Error
|
||||||
@@ -236,4 +221,3 @@ _get_full_path :: proc(fd: int, allocator: runtime.Allocator) -> string {
|
|||||||
}
|
}
|
||||||
return fullpath
|
return fullpath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
//+private
|
//+private
|
||||||
package os2
|
package os2
|
||||||
|
|
||||||
|
import "core:sys/linux"
|
||||||
|
|
||||||
_pipe :: proc() -> (r, w: ^File, err: Error) {
|
_pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||||
return nil, nil, nil
|
fds: [2]linux.Fd
|
||||||
|
errno := linux.pipe2(&fds, {.CLOEXEC})
|
||||||
|
if errno != .NONE {
|
||||||
|
return nil, nil,_get_platform_error(errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
r = _new_file(uintptr(fds[0]))
|
||||||
|
w = _new_file(uintptr(fds[1]))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-96
@@ -3,108 +3,32 @@ package os2
|
|||||||
|
|
||||||
import "core:time"
|
import "core:time"
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
import "core:sys/unix"
|
import "core:sys/linux"
|
||||||
import "core:path/filepath"
|
import "core:path/filepath"
|
||||||
|
|
||||||
// File type
|
|
||||||
S_IFMT :: 0o170000 // Type of file mask
|
|
||||||
S_IFIFO :: 0o010000 // Named pipe (fifo)
|
|
||||||
S_IFCHR :: 0o020000 // Character special
|
|
||||||
S_IFDIR :: 0o040000 // Directory
|
|
||||||
S_IFBLK :: 0o060000 // Block special
|
|
||||||
S_IFREG :: 0o100000 // Regular
|
|
||||||
S_IFLNK :: 0o120000 // Symbolic link
|
|
||||||
S_IFSOCK :: 0o140000 // Socket
|
|
||||||
|
|
||||||
// File mode
|
|
||||||
// Read, write, execute/search by owner
|
|
||||||
S_IRWXU :: 0o0700 // RWX mask for owner
|
|
||||||
S_IRUSR :: 0o0400 // R for owner
|
|
||||||
S_IWUSR :: 0o0200 // W for owner
|
|
||||||
S_IXUSR :: 0o0100 // X for owner
|
|
||||||
|
|
||||||
// Read, write, execute/search by group
|
|
||||||
S_IRWXG :: 0o0070 // RWX mask for group
|
|
||||||
S_IRGRP :: 0o0040 // R for group
|
|
||||||
S_IWGRP :: 0o0020 // W for group
|
|
||||||
S_IXGRP :: 0o0010 // X for group
|
|
||||||
|
|
||||||
// Read, write, execute/search by others
|
|
||||||
S_IRWXO :: 0o0007 // RWX mask for other
|
|
||||||
S_IROTH :: 0o0004 // R for other
|
|
||||||
S_IWOTH :: 0o0002 // W for other
|
|
||||||
S_IXOTH :: 0o0001 // X for other
|
|
||||||
|
|
||||||
S_ISUID :: 0o4000 // Set user id on execution
|
|
||||||
S_ISGID :: 0o2000 // Set group id on execution
|
|
||||||
S_ISVTX :: 0o1000 // Directory restrcted delete
|
|
||||||
|
|
||||||
|
|
||||||
S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
|
|
||||||
S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
|
|
||||||
S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
|
|
||||||
S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
|
|
||||||
S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
|
|
||||||
S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
|
|
||||||
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
|
||||||
|
|
||||||
F_OK :: 0 // Test for file existance
|
|
||||||
X_OK :: 1 // Test for execute permission
|
|
||||||
W_OK :: 2 // Test for write permission
|
|
||||||
R_OK :: 4 // Test for read permission
|
|
||||||
|
|
||||||
@private
|
|
||||||
Unix_File_Time :: struct {
|
|
||||||
seconds: i64,
|
|
||||||
nanoseconds: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
@private
|
|
||||||
_Stat :: struct {
|
|
||||||
device_id: u64, // ID of device containing file
|
|
||||||
serial: u64, // File serial number
|
|
||||||
nlink: u64, // Number of hard links
|
|
||||||
mode: u32, // Mode of the file
|
|
||||||
uid: u32, // User ID of the file's owner
|
|
||||||
gid: u32, // Group ID of the file's group
|
|
||||||
_padding: i32, // 32 bits of padding
|
|
||||||
rdev: u64, // Device ID, if device
|
|
||||||
size: i64, // Size of the file, in bytes
|
|
||||||
block_size: i64, // Optimal bllocksize for I/O
|
|
||||||
blocks: i64, // Number of 512-byte blocks allocated
|
|
||||||
|
|
||||||
last_access: Unix_File_Time, // Time of last access
|
|
||||||
modified: Unix_File_Time, // Time of last modification
|
|
||||||
status_change: Unix_File_Time, // Time of last status change
|
|
||||||
|
|
||||||
_reserve1,
|
|
||||||
_reserve2,
|
|
||||||
_reserve3: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||||
return _fstat_internal(f.impl.fd, allocator)
|
return _fstat_internal(f.impl.fd, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Error) {
|
_fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||||
s: _Stat
|
s: linux.Stat
|
||||||
result := unix.sys_fstat(fd, &s)
|
errno := linux.fstat(fd, &s)
|
||||||
if result < 0 {
|
if errno != .NONE {
|
||||||
return {}, _get_platform_error(result)
|
return {}, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
|
// TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
|
||||||
fi := File_Info {
|
fi := File_Info {
|
||||||
fullpath = _get_full_path(fd, allocator),
|
fullpath = _get_full_path(fd, allocator),
|
||||||
name = "",
|
name = "",
|
||||||
size = s.size,
|
size = i64(s.size),
|
||||||
mode = 0,
|
mode = 0,
|
||||||
is_directory = S_ISDIR(s.mode),
|
is_directory = linux.S_ISDIR(s.mode),
|
||||||
modification_time = time.Time {s.modified.seconds},
|
modification_time = time.Time {i64(s.mtime.time_sec) * i64(time.Second) + i64(s.mtime.time_nsec)},
|
||||||
access_time = time.Time {s.last_access.seconds},
|
access_time = time.Time {i64(s.atime.time_sec) * i64(time.Second) + i64(s.atime.time_nsec)},
|
||||||
creation_time = time.Time{0}, // regular stat does not provide this
|
creation_time = time.Time{i64(s.ctime.time_sec) * i64(time.Second) + i64(s.ctime.time_nsec)}, // regular stat does not provide this
|
||||||
}
|
}
|
||||||
|
fi.creation_time = fi.modification_time
|
||||||
|
|
||||||
fi.name = filepath.base(fi.fullpath)
|
fi.name = filepath.base(fi.fullpath)
|
||||||
return fi, nil
|
return fi, nil
|
||||||
@@ -115,11 +39,11 @@ _stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err
|
|||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
|
|
||||||
fd := unix.sys_open(name_cstr, _O_RDONLY)
|
fd, errno := linux.open(name_cstr, {})
|
||||||
if fd < 0 {
|
if errno != .NONE {
|
||||||
return {}, _get_platform_error(fd)
|
return {}, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
defer unix.sys_close(fd)
|
defer linux.close(fd)
|
||||||
return _fstat_internal(fd, allocator)
|
return _fstat_internal(fd, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,11 +51,11 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, er
|
|||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
|
|
||||||
fd := unix.sys_open(name_cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW)
|
fd, errno := linux.open(name_cstr, {.PATH, .NOFOLLOW})
|
||||||
if fd < 0 {
|
if errno != .NONE {
|
||||||
return {}, _get_platform_error(fd)
|
return {}, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
defer unix.sys_close(fd)
|
defer linux.close(fd)
|
||||||
return _fstat_internal(fd, allocator)
|
return _fstat_internal(fd, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+76
-39
@@ -48,6 +48,7 @@ Errno :: enum i32 {
|
|||||||
ENOSYS = 38,
|
ENOSYS = 38,
|
||||||
ENOTEMPTY = 39,
|
ENOTEMPTY = 39,
|
||||||
ELOOP = 40,
|
ELOOP = 40,
|
||||||
|
EUNKNOWN_41 = 41,
|
||||||
ENOMSG = 42,
|
ENOMSG = 42,
|
||||||
EIDRM = 43,
|
EIDRM = 43,
|
||||||
ECHRNG = 44,
|
ECHRNG = 44,
|
||||||
@@ -64,6 +65,7 @@ Errno :: enum i32 {
|
|||||||
ENOANO = 55,
|
ENOANO = 55,
|
||||||
EBADRQC = 56,
|
EBADRQC = 56,
|
||||||
EBADSLT = 57,
|
EBADSLT = 57,
|
||||||
|
EUNKNOWN_58 = 58,
|
||||||
EBFONT = 59,
|
EBFONT = 59,
|
||||||
ENOSTR = 60,
|
ENOSTR = 60,
|
||||||
ENODATA = 61,
|
ENODATA = 61,
|
||||||
@@ -150,44 +152,66 @@ Errno :: enum i32 {
|
|||||||
RDONLY flag is not present, because it has the value of 0, i.e. it is the
|
RDONLY flag is not present, because it has the value of 0, i.e. it is the
|
||||||
default, unless WRONLY or RDWR is specified.
|
default, unless WRONLY or RDWR is specified.
|
||||||
*/
|
*/
|
||||||
Open_Flags_Bits :: enum {
|
when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 {
|
||||||
WRONLY = 0,
|
Open_Flags_Bits :: enum {
|
||||||
RDWR = 1,
|
WRONLY = 0,
|
||||||
CREAT = 6,
|
RDWR = 1,
|
||||||
EXCL = 7,
|
CREAT = 6,
|
||||||
NOCTTY = 8,
|
EXCL = 7,
|
||||||
TRUNC = 9,
|
NOCTTY = 8,
|
||||||
APPEND = 10,
|
TRUNC = 9,
|
||||||
NONBLOCK = 11,
|
APPEND = 10,
|
||||||
DSYNC = 12,
|
NONBLOCK = 11,
|
||||||
ASYNC = 13,
|
DSYNC = 12,
|
||||||
DIRECT = 14,
|
ASYNC = 13,
|
||||||
LARGEFILE = 15,
|
DIRECT = 14,
|
||||||
DIRECTORY = 16,
|
LARGEFILE = 15,
|
||||||
NOFOLLOW = 17,
|
DIRECTORY = 16,
|
||||||
NOATIME = 18,
|
NOFOLLOW = 17,
|
||||||
CLOEXEC = 19,
|
NOATIME = 18,
|
||||||
PATH = 21,
|
CLOEXEC = 19,
|
||||||
}
|
PATH = 21,
|
||||||
|
}
|
||||||
|
// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
|
||||||
|
#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
|
||||||
|
|
||||||
// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
|
} else {
|
||||||
#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
|
Open_Flags_Bits :: enum {
|
||||||
#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
|
WRONLY = 0,
|
||||||
#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
|
RDWR = 1,
|
||||||
#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
|
CREAT = 6,
|
||||||
#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
|
EXCL = 7,
|
||||||
#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
|
NOCTTY = 8,
|
||||||
#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
|
TRUNC = 9,
|
||||||
#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
|
APPEND = 10,
|
||||||
#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
|
NONBLOCK = 11,
|
||||||
#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
|
DSYNC = 12,
|
||||||
#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
|
ASYNC = 13,
|
||||||
#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
|
DIRECTORY = 14,
|
||||||
#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
|
NOFOLLOW = 15,
|
||||||
#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
|
DIRECT = 16,
|
||||||
#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
|
LARGEFILE = 17,
|
||||||
#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
|
NOATIME = 18,
|
||||||
#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
|
CLOEXEC = 19,
|
||||||
|
PATH = 21,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Bits for FD_Flags bitset
|
Bits for FD_Flags bitset
|
||||||
@@ -867,7 +891,7 @@ Wait_Option :: enum {
|
|||||||
WSTOPPED = 1,
|
WSTOPPED = 1,
|
||||||
WEXITED = 2,
|
WEXITED = 2,
|
||||||
WCONTINUED = 3,
|
WCONTINUED = 3,
|
||||||
WNOWAIT = 24,
|
WNOWAIT = 24,
|
||||||
// // For processes created using clone
|
// // For processes created using clone
|
||||||
__WNOTHREAD = 29,
|
__WNOTHREAD = 29,
|
||||||
__WALL = 30,
|
__WALL = 30,
|
||||||
@@ -946,9 +970,22 @@ Sig_Stack_Flag :: enum i32 {
|
|||||||
AUTODISARM = 31,
|
AUTODISARM = 31,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sig_Action_Flag :: enum u32 {
|
||||||
|
NOCLDSTOP = 0,
|
||||||
|
NOCLDWAIT = 1,
|
||||||
|
SIGINFO = 2,
|
||||||
|
UNSUPPORTED = 10,
|
||||||
|
EXPOSE_TAGBITS = 11,
|
||||||
|
RESTORER = 26,
|
||||||
|
ONSTACK = 27,
|
||||||
|
RESTART = 28,
|
||||||
|
NODEFER = 30,
|
||||||
|
RESETHAND = 31,
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Type of socket to create
|
Type of socket to create
|
||||||
- For TCP you want to use SOCK_STREAM
|
- For TCP you want to use SOCK_STREAM
|
||||||
- For UDP you want to use SOCK_DGRAM
|
- For UDP you want to use SOCK_DGRAM
|
||||||
Also see `Protocol`
|
Also see `Protocol`
|
||||||
*/
|
*/
|
||||||
|
|||||||
+31
-16
@@ -69,7 +69,7 @@ close :: proc "contextless" (fd: Fd) -> (Errno) {
|
|||||||
stat :: proc "contextless" (filename: cstring, stat: ^Stat) -> (Errno) {
|
stat :: proc "contextless" (filename: cstring, stat: ^Stat) -> (Errno) {
|
||||||
when size_of(int) == 8 {
|
when size_of(int) == 8 {
|
||||||
when ODIN_ARCH == .arm64 {
|
when ODIN_ARCH == .arm64 {
|
||||||
ret := syscall(SYS_fstatat, AT_FDCWD, cast(rawptr) filename, stat)
|
ret := syscall(SYS_fstatat, AT_FDCWD, cast(rawptr) filename, stat, 0)
|
||||||
return Errno(-ret)
|
return Errno(-ret)
|
||||||
} else {
|
} else {
|
||||||
ret := syscall(SYS_stat, cast(rawptr) filename, stat)
|
ret := syscall(SYS_stat, cast(rawptr) filename, stat)
|
||||||
@@ -200,10 +200,25 @@ brk :: proc "contextless" (addr: uintptr) -> (Errno) {
|
|||||||
return Errno(-ret)
|
return Errno(-ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns from signal handlers on some archs.
|
||||||
|
*/
|
||||||
|
rt_sigreturn :: proc "c" () -> ! {
|
||||||
|
intrinsics.syscall(uintptr(SYS_rt_sigreturn))
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Alter an action taken by a process.
|
Alter an action taken by a process.
|
||||||
*/
|
*/
|
||||||
rt_sigaction :: proc "contextless" (sig: Signal, sigaction: ^Sig_Action, old_sigaction: ^Sig_Action) -> Errno {
|
rt_sigaction :: proc "contextless" (sig: Signal, sigaction: ^Sig_Action($T), old_sigaction: ^Sig_Action) -> Errno {
|
||||||
|
// NOTE(jason): It appears that the restorer is required for i386 and amd64
|
||||||
|
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||||
|
sigaction.flags += {.RESTORER}
|
||||||
|
}
|
||||||
|
if sigaction != nil && sigaction.restorer == nil && .RESTORER in sigaction.flags {
|
||||||
|
sigaction.restorer = rt_sigreturn
|
||||||
|
}
|
||||||
ret := syscall(SYS_rt_sigaction, sig, sigaction, old_sigaction, size_of(Sig_Set))
|
ret := syscall(SYS_rt_sigaction, sig, sigaction, old_sigaction, size_of(Sig_Set))
|
||||||
return Errno(-ret)
|
return Errno(-ret)
|
||||||
}
|
}
|
||||||
@@ -1123,7 +1138,7 @@ ftruncate :: proc "contextless" (fd: Fd, length: i64) -> (Errno) {
|
|||||||
ret := syscall(SYS_ftruncate64, fd, compat64_arg_pair(length))
|
ret := syscall(SYS_ftruncate64, fd, compat64_arg_pair(length))
|
||||||
return Errno(-ret)
|
return Errno(-ret)
|
||||||
} else {
|
} else {
|
||||||
ret := syscall(SYS_truncate, fd, compat64_arg_pair(length))
|
ret := syscall(SYS_ftruncate, fd, compat64_arg_pair(length))
|
||||||
return Errno(-ret)
|
return Errno(-ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1231,7 +1246,7 @@ creat :: proc "contextless" (name: cstring, mode: Mode) -> (Fd, Errno) {
|
|||||||
*/
|
*/
|
||||||
link :: proc "contextless" (target: cstring, linkpath: cstring) -> (Errno) {
|
link :: proc "contextless" (target: cstring, linkpath: cstring) -> (Errno) {
|
||||||
when ODIN_ARCH == .arm64 {
|
when ODIN_ARCH == .arm64 {
|
||||||
ret := syscall(SYS_linkat, AT_FDCWD, cast(rawptr) target, AT_FDCWD, cast(rawptr) linkpath)
|
ret := syscall(SYS_linkat, AT_FDCWD, cast(rawptr) target, AT_FDCWD, cast(rawptr) linkpath, 0)
|
||||||
return Errno(-ret)
|
return Errno(-ret)
|
||||||
} else {
|
} else {
|
||||||
ret := syscall(SYS_link, cast(rawptr) target, cast(rawptr) linkpath)
|
ret := syscall(SYS_link, cast(rawptr) target, cast(rawptr) linkpath)
|
||||||
@@ -1261,7 +1276,7 @@ unlink :: proc "contextless" (name: cstring) -> (Errno) {
|
|||||||
*/
|
*/
|
||||||
symlink :: proc "contextless" (target: cstring, linkpath: cstring) -> (Errno) {
|
symlink :: proc "contextless" (target: cstring, linkpath: cstring) -> (Errno) {
|
||||||
when ODIN_ARCH == .arm64 {
|
when ODIN_ARCH == .arm64 {
|
||||||
ret := syscall(SYS_symlinkat, AT_FDCWD, cast(rawptr) target, cast(rawptr) linkpath)
|
ret := syscall(SYS_symlinkat, cast(rawptr) target, AT_FDCWD, cast(rawptr) linkpath)
|
||||||
return Errno(-ret)
|
return Errno(-ret)
|
||||||
} else {
|
} else {
|
||||||
ret := syscall(SYS_symlink, cast(rawptr) target, cast(rawptr) linkpath)
|
ret := syscall(SYS_symlink, cast(rawptr) target, cast(rawptr) linkpath)
|
||||||
@@ -1291,7 +1306,7 @@ readlink :: proc "contextless" (name: cstring, buf: []u8) -> (int, Errno) {
|
|||||||
*/
|
*/
|
||||||
chmod :: proc "contextless" (name: cstring, mode: Mode) -> (Errno) {
|
chmod :: proc "contextless" (name: cstring, mode: Mode) -> (Errno) {
|
||||||
when ODIN_ARCH == .arm64 {
|
when ODIN_ARCH == .arm64 {
|
||||||
ret := syscall(SYS_fchmodat, cast(rawptr) name, transmute(u32) mode, 0)
|
ret := syscall(SYS_fchmodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode)
|
||||||
return Errno(-ret)
|
return Errno(-ret)
|
||||||
} else {
|
} else {
|
||||||
ret := syscall(SYS_chmod, cast(rawptr) name, transmute(u32) mode)
|
ret := syscall(SYS_chmod, cast(rawptr) name, transmute(u32) mode)
|
||||||
@@ -2476,8 +2491,8 @@ tgkill :: proc "contextless" (tgid, tid: Pid, sig: Signal) -> (Errno) {
|
|||||||
Wait on process, process group or pid file descriptor.
|
Wait on process, process group or pid file descriptor.
|
||||||
Available since Linux 2.6.10.
|
Available since Linux 2.6.10.
|
||||||
*/
|
*/
|
||||||
waitid :: proc "contextless" (id_type: Id_Type, id: Id, sig_info: ^Sig_Info, options: Wait_Options) -> (Errno) {
|
waitid :: proc "contextless" (id_type: Id_Type, id: Id, sig_info: ^Sig_Info, options: Wait_Options, rusage: ^RUsage) -> (Errno) {
|
||||||
ret := syscall(SYS_waitid, id_type, id, sig_info, transmute(i32) options)
|
ret := syscall(SYS_waitid, id_type, id, sig_info, transmute(i32) options, rusage)
|
||||||
return Errno(-ret)
|
return Errno(-ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2504,7 +2519,7 @@ waitid :: proc "contextless" (id_type: Id_Type, id: Id, sig_info: ^Sig_Info, opt
|
|||||||
Available since Linux 2.6.16.
|
Available since Linux 2.6.16.
|
||||||
*/
|
*/
|
||||||
openat :: proc "contextless" (fd: Fd, name: cstring, flags: Open_Flags, mode: Mode = {}) -> (Fd, Errno) {
|
openat :: proc "contextless" (fd: Fd, name: cstring, flags: Open_Flags, mode: Mode = {}) -> (Fd, Errno) {
|
||||||
ret := syscall(SYS_openat, fd, AT_FDCWD, transmute(uintptr) name, transmute(u32) mode)
|
ret := syscall(SYS_openat, fd, transmute(uintptr) name, transmute(u32) flags, transmute(u32) mode)
|
||||||
return errno_unwrap(ret, Fd)
|
return errno_unwrap(ret, Fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2583,8 +2598,8 @@ linkat :: proc "contextless" (target_dirfd: Fd, oldpath: cstring, link_dirfd: Fd
|
|||||||
Create a symbolic link at specified dirfd.
|
Create a symbolic link at specified dirfd.
|
||||||
Available since Linux 2.6.16.
|
Available since Linux 2.6.16.
|
||||||
*/
|
*/
|
||||||
symlinkat :: proc "contextless" (dirfd: Fd, target: cstring, linkpath: cstring) -> (Errno) {
|
symlinkat :: proc "contextless" (target: cstring, dirfd: Fd, linkpath: cstring) -> (Errno) {
|
||||||
ret := syscall(SYS_symlinkat, dirfd, cast(rawptr) target, cast(rawptr) linkpath)
|
ret := syscall(SYS_symlinkat, cast(rawptr) target, dirfd, cast(rawptr) linkpath)
|
||||||
return Errno(-ret)
|
return Errno(-ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2619,13 +2634,13 @@ faccessat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK) ->
|
|||||||
Wait for events on a file descriptor.
|
Wait for events on a file descriptor.
|
||||||
Available since Linux 2.6.16.
|
Available since Linux 2.6.16.
|
||||||
*/
|
*/
|
||||||
ppoll :: proc "contextless" (fds: []Poll_Fd, timeout: ^Time_Spec, sigmask: ^Sig_Set) -> (Errno) {
|
ppoll :: proc "contextless" (fds: []Poll_Fd, timeout: ^Time_Spec, sigmask: ^Sig_Set) -> (i32, Errno) {
|
||||||
when size_of(int) == 8 {
|
when size_of(int) == 8 {
|
||||||
ret := syscall(SYS_ppoll, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
|
ret := syscall(SYS_ppoll, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
|
||||||
return Errno(-ret)
|
return errno_unwrap(ret, i32)
|
||||||
} else {
|
} else {
|
||||||
ret := syscall(SYS_ppoll_time64, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
|
ret := syscall(SYS_ppoll_time64, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
|
||||||
return Errno(-ret)
|
return errno_unwrap(ret, i32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2808,8 +2823,8 @@ getrandom :: proc "contextless" (buf: []u8, flags: Get_Random_Flags) -> (int, Er
|
|||||||
Execute program relative to a directory file descriptor.
|
Execute program relative to a directory file descriptor.
|
||||||
Available since Linux 3.19.
|
Available since Linux 3.19.
|
||||||
*/
|
*/
|
||||||
execveat :: proc "contextless" (dirfd: Fd, name: cstring, argv: [^]cstring, envp: [^]cstring) -> (Errno) {
|
execveat :: proc "contextless" (dirfd: Fd, name: cstring, argv: [^]cstring, envp: [^]cstring, flags: FD_Flags = {}) -> (Errno) {
|
||||||
ret := syscall(SYS_execveat, dirfd, cast(rawptr) name, cast(rawptr) argv, cast(rawptr) envp)
|
ret := syscall(SYS_execveat, dirfd, cast(rawptr) name, cast(rawptr) argv, cast(rawptr) envp, transmute(i32) flags)
|
||||||
return Errno(-ret)
|
return Errno(-ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+76
-20
@@ -18,7 +18,7 @@ Gid :: distinct u32
|
|||||||
/*
|
/*
|
||||||
Type for Process IDs, Thread IDs, Thread group ID.
|
Type for Process IDs, Thread IDs, Thread group ID.
|
||||||
*/
|
*/
|
||||||
Pid :: distinct int
|
Pid :: distinct i32
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Type for any of: pid, pidfd, pgid.
|
Type for any of: pid, pidfd, pgid.
|
||||||
@@ -89,11 +89,11 @@ FD_Flags :: bit_set[FD_Flags_Bits; i32]
|
|||||||
Represents file's permission and status bits
|
Represents file's permission and status bits
|
||||||
**Example:**
|
**Example:**
|
||||||
When you're passing a value of this type the recommended usage is:
|
When you're passing a value of this type the recommended usage is:
|
||||||
|
|
||||||
```
|
```
|
||||||
linux.Mode{.S_IXOTH, .S_IROTH} | linux.S_IRWXU | linux.S_IRWXG
|
linux.Mode{.S_IXOTH, .S_IROTH} | linux.S_IRWXU | linux.S_IRWXG
|
||||||
```
|
```
|
||||||
|
|
||||||
This would generate a mode that has full permissions for the
|
This would generate a mode that has full permissions for the
|
||||||
file's owner and group, and only "read" and "execute" bits
|
file's owner and group, and only "read" and "execute" bits
|
||||||
for others.
|
for others.
|
||||||
@@ -151,9 +151,9 @@ when ODIN_ARCH == .amd64 {
|
|||||||
size: i64,
|
size: i64,
|
||||||
blksize: uint,
|
blksize: uint,
|
||||||
blocks: u64,
|
blocks: u64,
|
||||||
atim: Time_Spec,
|
atime: Time_Spec,
|
||||||
mtim: Time_Spec,
|
mtime: Time_Spec,
|
||||||
ctim: Time_Spec,
|
ctime: Time_Spec,
|
||||||
ino: Inode,
|
ino: Inode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -495,16 +495,15 @@ Pid_FD_Flags :: bit_set[Pid_FD_Flags_Bits; i32]
|
|||||||
// 1. Odin's bitfields start from 0, whereas signals start from 1
|
// 1. Odin's bitfields start from 0, whereas signals start from 1
|
||||||
// 2. It's unclear how bitfields act in terms of ABI (are they an array of ints or an array of longs?).
|
// 2. It's unclear how bitfields act in terms of ABI (are they an array of ints or an array of longs?).
|
||||||
// it makes a difference because ARM is big endian.
|
// it makes a difference because ARM is big endian.
|
||||||
@private _SIGSET_NWORDS :: (1024 / (8 * size_of(uint)))
|
@private _SIGSET_NWORDS :: (8 / size_of(uint))
|
||||||
Sig_Set :: [_SIGSET_NWORDS]uint
|
Sig_Set :: [_SIGSET_NWORDS]uint
|
||||||
|
|
||||||
@private SI_MAX_SIZE :: 128
|
@private SI_MAX_SIZE :: 128
|
||||||
@private SI_ARCH_PREAMBLE :: 3 * size_of(i32)
|
@private SI_ARCH_PREAMBLE :: 4 * size_of(i32)
|
||||||
@private SI_PAD_SIZE :: (SI_MAX_SIZE - SI_ARCH_PREAMBLE) / size_of(i32)
|
@private SI_PAD_SIZE :: SI_MAX_SIZE - SI_ARCH_PREAMBLE
|
||||||
@private SI_TIMER_PAD_SIZE :: size_of(Uid) - size_of(i32)
|
|
||||||
|
|
||||||
Sig_Handler_Fn :: #type proc "c" (sig: Signal)
|
Sig_Handler_Fn :: #type proc "c" (sig: Signal)
|
||||||
Sig_Restore_Fn :: #type proc "c" ()
|
Sig_Restore_Fn :: #type proc "c" () -> !
|
||||||
|
|
||||||
Sig_Info :: struct #packed {
|
Sig_Info :: struct #packed {
|
||||||
signo: Signal,
|
signo: Signal,
|
||||||
@@ -518,8 +517,9 @@ Sig_Info :: struct #packed {
|
|||||||
uid: Uid, /* sender's uid */
|
uid: Uid, /* sender's uid */
|
||||||
},
|
},
|
||||||
using _timer: struct {
|
using _timer: struct {
|
||||||
timerid: i32, /* timer id */
|
timerid: i32, /* timer id */
|
||||||
overrun: i32, /* overrun count */
|
overrun: i32, /* overrun count */
|
||||||
|
value: Sig_Val, /* timer value */
|
||||||
},
|
},
|
||||||
/* POSIX.1b signals */
|
/* POSIX.1b signals */
|
||||||
using _rt: struct {
|
using _rt: struct {
|
||||||
@@ -528,8 +528,8 @@ Sig_Info :: struct #packed {
|
|||||||
},
|
},
|
||||||
/* SIGCHLD */
|
/* SIGCHLD */
|
||||||
using _sigchld: struct {
|
using _sigchld: struct {
|
||||||
_pid1: Pid, /* which child */
|
_pid1: Pid, /* which child */
|
||||||
_uid1: Uid, /* sender's uid */
|
_uid1: Uid, /* sender's uid */
|
||||||
status: i32, /* exit code */
|
status: i32, /* exit code */
|
||||||
utime: uint,
|
utime: uint,
|
||||||
stime: uint, //clock_t
|
stime: uint, //clock_t
|
||||||
@@ -537,7 +537,24 @@ Sig_Info :: struct #packed {
|
|||||||
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
|
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
|
||||||
using _sigfault: struct {
|
using _sigfault: struct {
|
||||||
addr: rawptr, /* faulting insn/memory ref. */
|
addr: rawptr, /* faulting insn/memory ref. */
|
||||||
addr_lsb: i16, /* LSB of the reported address */
|
using _: struct #raw_union {
|
||||||
|
trapno: i32, /* Trap number that caused signal */
|
||||||
|
addr_lsb: i16, /* LSB of the reported address */
|
||||||
|
using _addr_bnd: struct {
|
||||||
|
_pad2: u64,
|
||||||
|
lower: rawptr, /* lower bound during fault */
|
||||||
|
upper: rawptr, /* upper bound during fault */
|
||||||
|
},
|
||||||
|
using _addr_pkey: struct {
|
||||||
|
_pad3: u64,
|
||||||
|
pkey: u32, /* protection key on PTE that faulted */
|
||||||
|
},
|
||||||
|
using _perf: struct {
|
||||||
|
perf_data: u64,
|
||||||
|
perf_type: u32,
|
||||||
|
perf_flags: u32,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
/* SIGPOLL */
|
/* SIGPOLL */
|
||||||
using _sigpoll: struct {
|
using _sigpoll: struct {
|
||||||
@@ -547,12 +564,43 @@ Sig_Info :: struct #packed {
|
|||||||
/* SIGSYS */
|
/* SIGSYS */
|
||||||
using _sigsys: struct {
|
using _sigsys: struct {
|
||||||
call_addr: rawptr, /* calling user insn */
|
call_addr: rawptr, /* calling user insn */
|
||||||
syscall: i32, /* triggering system call number */
|
syscall: i32, /* triggering system call number */
|
||||||
arch: u32, /* AUDIT_ARCH_* of syscall */
|
arch: u32, /* AUDIT_ARCH_* of syscall */
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#assert(size_of(Sig_Info) == 128)
|
||||||
|
when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
|
||||||
|
#assert(offset_of(Sig_Info, signo) == 0x00)
|
||||||
|
#assert(offset_of(Sig_Info, errno) == 0x04)
|
||||||
|
#assert(offset_of(Sig_Info, code) == 0x08)
|
||||||
|
#assert(offset_of(Sig_Info, pid) == 0x10)
|
||||||
|
#assert(offset_of(Sig_Info, uid) == 0x14)
|
||||||
|
#assert(offset_of(Sig_Info, timerid) == 0x10)
|
||||||
|
#assert(offset_of(Sig_Info, overrun) == 0x14)
|
||||||
|
#assert(offset_of(Sig_Info, value) == 0x18)
|
||||||
|
#assert(offset_of(Sig_Info, status) == 0x18)
|
||||||
|
#assert(offset_of(Sig_Info, utime) == 0x20)
|
||||||
|
#assert(offset_of(Sig_Info, stime) == 0x28)
|
||||||
|
#assert(offset_of(Sig_Info, addr) == 0x10)
|
||||||
|
#assert(offset_of(Sig_Info, addr_lsb) == 0x18)
|
||||||
|
#assert(offset_of(Sig_Info, trapno) == 0x18)
|
||||||
|
#assert(offset_of(Sig_Info, lower) == 0x20)
|
||||||
|
#assert(offset_of(Sig_Info, upper) == 0x28)
|
||||||
|
#assert(offset_of(Sig_Info, pkey) == 0x20)
|
||||||
|
#assert(offset_of(Sig_Info, perf_data) == 0x18)
|
||||||
|
#assert(offset_of(Sig_Info, perf_type) == 0x20)
|
||||||
|
#assert(offset_of(Sig_Info, perf_flags) == 0x24)
|
||||||
|
#assert(offset_of(Sig_Info, band) == 0x10)
|
||||||
|
#assert(offset_of(Sig_Info, fd) == 0x18)
|
||||||
|
#assert(offset_of(Sig_Info, call_addr) == 0x10)
|
||||||
|
#assert(offset_of(Sig_Info, syscall) == 0x18)
|
||||||
|
#assert(offset_of(Sig_Info, arch) == 0x1C)
|
||||||
|
} else {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
SIGEV_MAX_SIZE :: 64
|
SIGEV_MAX_SIZE :: 64
|
||||||
SIGEV_PAD_SIZE :: ((SIGEV_MAX_SIZE-size_of(i32)*2+size_of(Sig_Val))/size_of(i32))
|
SIGEV_PAD_SIZE :: ((SIGEV_MAX_SIZE-size_of(i32)*2+size_of(Sig_Val))/size_of(i32))
|
||||||
|
|
||||||
@@ -583,12 +631,20 @@ Sig_Stack :: struct {
|
|||||||
size: uintptr,
|
size: uintptr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sig_Action_Special :: enum uint {
|
||||||
|
SIG_DFL = 0,
|
||||||
|
SIG_IGN = 1,
|
||||||
|
SIG_ERR = ~uint(0),
|
||||||
|
}
|
||||||
|
|
||||||
|
Sig_Action_Flags :: bit_set[Sig_Action_Flag; uint]
|
||||||
Sig_Action :: struct($T: typeid) {
|
Sig_Action :: struct($T: typeid) {
|
||||||
using _u: struct #raw_union {
|
using _u: struct #raw_union {
|
||||||
handler: Sig_Handler_Fn,
|
handler: Sig_Handler_Fn,
|
||||||
sigaction: #type proc "c" (sig: Signal, si: ^Sig_Info, ctx: ^T),
|
sigaction: #type proc "c" (sig: Signal, si: ^Sig_Info, ctx: ^T),
|
||||||
|
special: Sig_Action_Special,
|
||||||
},
|
},
|
||||||
flags: uint,
|
flags: Sig_Action_Flags,
|
||||||
restorer: Sig_Restore_Fn,
|
restorer: Sig_Restore_Fn,
|
||||||
mask: Sig_Set,
|
mask: Sig_Set,
|
||||||
}
|
}
|
||||||
@@ -733,7 +789,7 @@ RLimit :: struct {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Structure representing how much of each resource got used.
|
Structure representing how much of each resource got used.
|
||||||
*/
|
*/
|
||||||
RUsage :: struct {
|
RUsage :: struct {
|
||||||
utime: Time_Val,
|
utime: Time_Val,
|
||||||
stime: Time_Val,
|
stime: Time_Val,
|
||||||
@@ -813,7 +869,7 @@ when size_of(int) == 8 || ODIN_ARCH == .i386 {
|
|||||||
cpid: Pid,
|
cpid: Pid,
|
||||||
lpid: Pid,
|
lpid: Pid,
|
||||||
nattach: uint,
|
nattach: uint,
|
||||||
_: [2]uint,
|
_: [2]uint,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user