Browse Source

Merge pull request #3822 from jasonKercher/os2-rebase

os2 linux round 2
gingerBill 1 year ago
parent
commit
0c8924ea85

+ 210 - 9
core/os/os2/env_linux.odin

@@ -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 {
-	//TODO
-	return false
+_set_env :: proc(key, v_new: string) -> bool {
+	if _org_env_begin == 0 {
+		_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
-	return false
+	if _org_env_begin == 0 {
+		_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
-	return nil
+	if _org_env_begin == 0 {
+		_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
+}

+ 16 - 0
core/os/os2/errors.odin

@@ -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
core/os/os2/errors_linux.odin

@@ -1,145 +1,165 @@
 //+private
 //+private
 package os2
 package os2
 
 
-import "core:sys/unix"
+import "core:sys/linux"
 
 
-EPERM          :: 1
-ENOENT         :: 2
-ESRCH          :: 3
-EINTR          :: 4
-EIO            :: 5
-ENXIO          :: 6
-EBADF          :: 9
-EAGAIN         :: 11
-ENOMEM         :: 12
-EACCES         :: 13
-EFAULT         :: 14
-EEXIST         :: 17
-ENODEV         :: 19
-ENOTDIR        :: 20
-EISDIR         :: 21
-EINVAL         :: 22
-ENFILE         :: 23
-EMFILE         :: 24
-ETXTBSY        :: 26
-EFBIG          :: 27
-ENOSPC         :: 28
-ESPIPE         :: 29
-EROFS          :: 30
-EPIPE          :: 32
-ERANGE         :: 34   /* Result too large */
-EDEADLK        :: 35   /* Resource deadlock would occur */
-ENAMETOOLONG   :: 36   /* File name too long */
-ENOLCK         :: 37   /* No record locks available */
-ENOSYS         :: 38   /* Invalid system call number */
-ENOTEMPTY      :: 39   /* Directory not empty */
-ELOOP          :: 40   /* Too many symbolic links encountered */
-EWOULDBLOCK    :: EAGAIN /* Operation would block */
-ENOMSG         :: 42   /* No message of desired type */
-EIDRM          :: 43   /* Identifier removed */
-ECHRNG         :: 44   /* Channel number out of range */
-EL2NSYNC       :: 45   /* Level 2 not synchronized */
-EL3HLT         :: 46   /* Level 3 halted */
-EL3RST         :: 47   /* Level 3 reset */
-ELNRNG         :: 48   /* Link number out of range */
-EUNATCH        :: 49   /* Protocol driver not attached */
-ENOCSI         :: 50   /* No CSI structure available */
-EL2HLT         :: 51   /* Level 2 halted */
-EBADE          :: 52   /* Invalid exchange */
-EBADR          :: 53   /* Invalid request descriptor */
-EXFULL         :: 54   /* Exchange full */
-ENOANO         :: 55   /* No anode */
-EBADRQC        :: 56   /* Invalid request code */
-EBADSLT        :: 57   /* Invalid slot */
-EDEADLOCK      :: EDEADLK
-EBFONT         :: 59   /* Bad font file format */
-ENOSTR         :: 60   /* Device not a stream */
-ENODATA        :: 61   /* No data available */
-ETIME          :: 62   /* Timer expired */
-ENOSR          :: 63   /* Out of streams resources */
-ENONET         :: 64   /* Machine is not on the network */
-ENOPKG         :: 65   /* Package not installed */
-EREMOTE        :: 66   /* Object is remote */
-ENOLINK        :: 67   /* Link has been severed */
-EADV           :: 68   /* Advertise error */
-ESRMNT         :: 69   /* Srmount error */
-ECOMM          :: 70   /* Communication error on send */
-EPROTO         :: 71   /* Protocol error */
-EMULTIHOP      :: 72   /* Multihop attempted */
-EDOTDOT        :: 73   /* RFS specific error */
-EBADMSG        :: 74   /* Not a data message */
-EOVERFLOW      :: 75   /* Value too large for defined data type */
-ENOTUNIQ       :: 76   /* Name not unique on network */
-EBADFD         :: 77   /* File descriptor in bad state */
-EREMCHG        :: 78   /* Remote address changed */
-ELIBACC        :: 79   /* Can not access a needed shared library */
-ELIBBAD        :: 80   /* Accessing a corrupted shared library */
-ELIBSCN        :: 81   /* .lib section in a.out corrupted */
-ELIBMAX        :: 82   /* Attempting to link in too many shared libraries */
-ELIBEXEC       :: 83   /* Cannot exec a shared library directly */
-EILSEQ         :: 84   /* Illegal byte sequence */
-ERESTART       :: 85   /* Interrupted system call should be restarted */
-ESTRPIPE       :: 86   /* Streams pipe error */
-EUSERS         :: 87   /* Too many users */
-ENOTSOCK       :: 88   /* Socket operation on non-socket */
-EDESTADDRREQ   :: 89   /* Destination address required */
-EMSGSIZE       :: 90   /* Message too long */
-EPROTOTYPE     :: 91   /* Protocol wrong type for socket */
-ENOPROTOOPT    :: 92   /* Protocol not available */
-EPROTONOSUPPORT:: 93   /* Protocol not supported */
-ESOCKTNOSUPPORT:: 94   /* Socket type not supported */
-EOPNOTSUPP     :: 95   /* Operation not supported on transport endpoint */
-EPFNOSUPPORT   :: 96   /* Protocol family not supported */
-EAFNOSUPPORT   :: 97   /* Address family not supported by protocol */
-EADDRINUSE     :: 98   /* Address already in use */
-EADDRNOTAVAIL  :: 99   /* Cannot assign requested address */
-ENETDOWN       :: 100  /* Network is down */
-ENETUNREACH    :: 101  /* Network is unreachable */
-ENETRESET      :: 102  /* Network dropped connection because of reset */
-ECONNABORTED   :: 103  /* Software caused connection abort */
-ECONNRESET     :: 104  /* Connection reset by peer */
-ENOBUFS        :: 105  /* No buffer space available */
-EISCONN        :: 106  /* Transport endpoint is already connected */
-ENOTCONN       :: 107  /* Transport endpoint is not connected */
-ESHUTDOWN      :: 108  /* Cannot send after transport endpoint shutdown */
-ETOOMANYREFS   :: 109  /* Too many references: cannot splice */
-ETIMEDOUT      :: 110  /* Connection timed out */
-ECONNREFUSED   :: 111  /* Connection refused */
-EHOSTDOWN      :: 112  /* Host is down */
-EHOSTUNREACH   :: 113  /* No route to host */
-EALREADY       :: 114  /* Operation already in progress */
-EINPROGRESS    :: 115  /* Operation now in progress */
-ESTALE         :: 116  /* Stale file handle */
-EUCLEAN        :: 117  /* Structure needs cleaning */
-ENOTNAM        :: 118  /* Not a XENIX named type file */
-ENAVAIL        :: 119  /* No XENIX semaphores available */
-EISNAM         :: 120  /* Is a named type file */
-EREMOTEIO      :: 121  /* Remote I/O error */
-EDQUOT         :: 122  /* Quota exceeded */
-ENOMEDIUM      :: 123  /* No medium found */
-EMEDIUMTYPE    :: 124  /* Wrong medium type */
-ECANCELED      :: 125  /* Operation Canceled */
-ENOKEY         :: 126  /* Required key not available */
-EKEYEXPIRED    :: 127  /* Key has expired */
-EKEYREVOKED    :: 128  /* Key has been revoked */
-EKEYREJECTED   :: 129  /* Key was rejected by service */
-EOWNERDEAD     :: 130  /* Owner died */
-ENOTRECOVERABLE:: 131  /* State not recoverable */
-ERFKILL        :: 132  /* Operation not possible due to RF-kill */
-EHWPOISON      :: 133  /* Memory page has hardware error */
-
-_get_platform_error :: proc(res: int) -> Error {
-	errno := unix.get_errno(res)
-	return Platform_Error(i32(errno))
+@(rodata)
+_errno_strings : [linux.Errno]string = {
+	.NONE            = "Success",
+	.EPERM           = "Operation not permitted",
+	.ENOENT          = "No such file or directory",
+	.ESRCH           = "No such process",
+	.EINTR           = "Interrupted system call",
+	.EIO             = "Input/output error",
+	.ENXIO           = "No such device or address",
+	.E2BIG           = "Argument list too long",
+	.ENOEXEC         = "Exec format error",
+	.EBADF           = "Bad file descriptor",
+	.ECHILD          = "No child processes",
+	.EAGAIN          = "Resource temporarily unavailable",
+	.ENOMEM          = "Cannot allocate memory",
+	.EACCES          = "Permission denied",
+	.EFAULT          = "Bad address",
+	.ENOTBLK         = "Block device required",
+	.EBUSY           = "Device or resource busy",
+	.EEXIST          = "File exists",
+	.EXDEV           = "Invalid cross-device link",
+	.ENODEV          = "No such device",
+	.ENOTDIR         = "Not a directory",
+	.EISDIR          = "Is a directory",
+	.EINVAL          = "Invalid argument",
+	.ENFILE          = "Too many open files in system",
+	.EMFILE          = "Too many open files",
+	.ENOTTY          = "Inappropriate ioctl for device",
+	.ETXTBSY         = "Text file busy",
+	.EFBIG           = "File too large",
+	.ENOSPC          = "No space left on device",
+	.ESPIPE          = "Illegal seek",
+	.EROFS           = "Read-only file system",
+	.EMLINK          = "Too many links",
+	.EPIPE           = "Broken pipe",
+	.EDOM            = "Numerical argument out of domain",
+	.ERANGE          = "Numerical result out of range",
+	.EDEADLK         = "Resource deadlock avoided",
+	.ENAMETOOLONG    = "File name too long",
+	.ENOLCK          = "No locks available",
+	.ENOSYS          = "Function not implemented",
+	.ENOTEMPTY       = "Directory not empty",
+	.ELOOP           = "Too many levels of symbolic links",
+	.EUNKNOWN_41     = "Unknown Error (41)",
+	.ENOMSG          = "No message of desired type",
+	.EIDRM           = "Identifier removed",
+	.ECHRNG          = "Channel number out of range",
+	.EL2NSYNC        = "Level 2 not synchronized",
+	.EL3HLT          = "Level 3 halted",
+	.EL3RST          = "Level 3 reset",
+	.ELNRNG          = "Link number out of range",
+	.EUNATCH         = "Protocol driver not attached",
+	.ENOCSI          = "No CSI structure available",
+	.EL2HLT          = "Level 2 halted",
+	.EBADE           = "Invalid exchange",
+	.EBADR           = "Invalid request descriptor",
+	.EXFULL          = "Exchange full",
+	.ENOANO          = "No anode",
+	.EBADRQC         = "Invalid request code",
+	.EBADSLT         = "Invalid slot",
+	.EUNKNOWN_58     = "Unknown Error (58)",
+	.EBFONT          = "Bad font file format",
+	.ENOSTR          = "Device not a stream",
+	.ENODATA         = "No data available",
+	.ETIME           = "Timer expired",
+	.ENOSR           = "Out of streams resources",
+	.ENONET          = "Machine is not on the network",
+	.ENOPKG          = "Package not installed",
+	.EREMOTE         = "Object is remote",
+	.ENOLINK         = "Link has been severed",
+	.EADV            = "Advertise error",
+	.ESRMNT          = "Srmount error",
+	.ECOMM           = "Communication error on send",
+	.EPROTO          = "Protocol error",
+	.EMULTIHOP       = "Multihop attempted",
+	.EDOTDOT         = "RFS specific error",
+	.EBADMSG         = "Bad message",
+	.EOVERFLOW       = "Value too large for defined data type",
+	.ENOTUNIQ        = "Name not unique on network",
+	.EBADFD          = "File descriptor in bad state",
+	.EREMCHG         = "Remote address changed",
+	.ELIBACC         = "Can not access a needed shared library",
+	.ELIBBAD         = "Accessing a corrupted shared library",
+	.ELIBSCN         = ".lib section in a.out corrupted",
+	.ELIBMAX         = "Attempting to link in too many shared libraries",
+	.ELIBEXEC        = "Cannot exec a shared library directly",
+	.EILSEQ          = "Invalid or incomplete multibyte or wide character",
+	.ERESTART        = "Interrupted system call should be restarted",
+	.ESTRPIPE        = "Streams pipe error",
+	.EUSERS          = "Too many users",
+	.ENOTSOCK        = "Socket operation on non-socket",
+	.EDESTADDRREQ    = "Destination address required",
+	.EMSGSIZE        = "Message too long",
+	.EPROTOTYPE      = "Protocol wrong type for socket",
+	.ENOPROTOOPT     = "Protocol not available",
+	.EPROTONOSUPPORT = "Protocol not supported",
+	.ESOCKTNOSUPPORT = "Socket type not supported",
+	.EOPNOTSUPP      = "Operation not supported",
+	.EPFNOSUPPORT    = "Protocol family not supported",
+	.EAFNOSUPPORT    = "Address family not supported by protocol",
+	.EADDRINUSE      = "Address already in use",
+	.EADDRNOTAVAIL   = "Cannot assign requested address",
+	.ENETDOWN        = "Network is down",
+	.ENETUNREACH     = "Network is unreachable",
+	.ENETRESET       = "Network dropped connection on reset",
+	.ECONNABORTED    = "Software caused connection abort",
+	.ECONNRESET      = "Connection reset by peer",
+	.ENOBUFS         = "No buffer space available",
+	.EISCONN         = "Transport endpoint is already connected",
+	.ENOTCONN        = "Transport endpoint is not connected",
+	.ESHUTDOWN       = "Cannot send after transport endpoint shutdown",
+	.ETOOMANYREFS    = "Too many references: cannot splice",
+	.ETIMEDOUT       = "Connection timed out",
+	.ECONNREFUSED    = "Connection refused",
+	.EHOSTDOWN       = "Host is down",
+	.EHOSTUNREACH    = "No route to host",
+	.EALREADY        = "Operation already in progress",
+	.EINPROGRESS     = "Operation now in progress",
+	.ESTALE          = "Stale file handle",
+	.EUCLEAN         = "Structure needs cleaning",
+	.ENOTNAM         = "Not a XENIX named type file",
+	.ENAVAIL         = "No XENIX semaphores available",
+	.EISNAM          = "Is a named type file",
+	.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",
 }
 }
 
 
-_ok_or_error :: proc(res: int) -> Error {
-	return res >= 0 ? nil : _get_platform_error(res)
+
+_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
+	}
+
+	return Platform_Error(i32(errno))
 }
 }
 
 
 _error_string :: proc(errno: i32) -> string {
 _error_string :: proc(errno: i32) -> string {
-	if errno == 0 {
-		return ""
+	if errno >= 0 && errno <= i32(max(linux.Errno)) {
+		return _errno_strings[linux.Errno(errno)]
 	}
 	}
-	return "Error"
+	return "Unknown Error"
 }
 }

+ 0 - 3
core/os/os2/file.odin

@@ -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))

+ 228 - 158
core/os/os2/file_linux.odin

@@ -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"
-
-INVALID_HANDLE :: -1
-
-_O_RDONLY    :: 0o00000000
-_O_WRONLY    :: 0o00000001
-_O_RDWR      :: 0o00000002
-_O_CREAT     :: 0o00000100
-_O_EXCL      :: 0o00000200
-_O_NOCTTY    :: 0o00000400
-_O_TRUNC     :: 0o00001000
-_O_APPEND    :: 0o00002000
-_O_NONBLOCK  :: 0o00004000
-_O_LARGEFILE :: 0o00100000
-_O_DIRECTORY :: 0o00200000
-_O_NOFOLLOW  :: 0o00400000
-_O_SYNC      :: 0o04010000
-_O_CLOEXEC   :: 0o02000000
-_O_PATH      :: 0o10000000
-
-_AT_FDCWD :: -100
-
-_CSTRING_NAME_HEAP_THRESHOLD :: 512
+import "base:runtime"
+import "core:sys/linux"
 
 
 _File :: struct {
 _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_WRONLY: flags_i = _O_WRONLY
-	case O_RDWR:   flags_i = _O_RDWR
+	case O_RDONLY:
+	case O_WRONLY: sys_flags += {.WRONLY}
+	case O_RDWR:   sys_flags += {.RDWR}
 	}
 	}
 
 
-	if .Append        in flags { flags_i |= _O_APPEND  }
-	if .Create        in flags { flags_i |= _O_CREAT   }
-	if .Excl          in flags { flags_i |= _O_EXCL    }
-	if .Sync          in flags { flags_i |= _O_SYNC    }
-	if .Trunc         in flags { flags_i |= _O_TRUNC   }
-	if .Close_On_Exec in flags { flags_i |= _O_CLOEXEC }
+	if .Append in flags        { sys_flags += {.APPEND} }
+	if .Create in flags        { sys_flags += {.CREAT} }
+	if .Excl in flags          { sys_flags += {.EXCL} }
+	if .Sync in flags          { sys_flags += {.DSYNC} }
+	if .Trunc in flags         { sys_flags += {.TRUNC} }
+	if .Close_On_Exec in flags { sys_flags += {.CLOEXEC} }
 
 
-	fd := unix.sys_open(name_cstr, flags_i, uint(perm))
-	if fd < 0 {
-		return nil, _get_platform_error(fd)
+	fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)(u32(perm)))
+	if errno != .NONE {
+		return nil, _get_platform_error(errno)
 	}
 	}
 
 
 	return _new_file(uintptr(fd), name), nil
 	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)
-	file.impl.allocator = file_allocator()
-	file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator)
-	file.stream = {
-		data = file,
-		procedure = _file_stream_proc,
-	}
+	_construct_file(file, fd, "")
 	return file
 	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 {
-		res := unix.sys_close(f.impl.fd)
-		_destroy(f)
-		return _ok_or_error(res)
+	if f == nil {
+		return nil
 	}
 	}
-	return nil
+	errno := linux.close(f.impl.fd)
+	if errno == .EBADF { // avoid possible double free
+		return _get_platform_error(errno)
+	}
+	_destroy(f)
+	return _get_platform_error(errno)
 }
 }
 
 
 _fd :: proc(f: ^File) -> uintptr {
 _fd :: proc(f: ^File) -> uintptr {
@@ -106,112 +142,100 @@ _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))
-	if res < 0 {
-		return -1, _get_platform_error(int(res))
+	n, errno := linux.lseek(f.impl.fd, offset, linux.Seek_Whence(whence))
+	if errno != .NONE {
+		return -1, _get_platform_error(errno)
 	}
 	}
-	return res, nil
+	return n, nil
 }
 }
 
 
 _read :: proc(f: ^File, p: []byte) -> (i64, Error) {
 _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))
-	if n < 0 {
-		return -1, _get_platform_error(n)
+	n, errno := linux.read(f.impl.fd, p[:])
+	if errno != .NONE {
+		return -1, _get_platform_error(errno)
 	}
 	}
-	if n == 0 {
-		return 0, .EOF
-	}
-	return i64(n), nil
+	return i64(n), n == 0 ? io.Error.EOF : nil
 }
 }
 
 
-_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
+_read_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
-	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)
+	n, errno := linux.pread(f.impl.fd, p[:], offset)
+	if errno != .NONE {
+		return -1, _get_platform_error(errno)
 	}
 	}
-	return
+	if n == 0 {
+		return 0, .EOF
+	}
+	return i64(n), nil
 }
 }
 
 
 _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)))
-	if n < 0 {
-		return -1, _get_platform_error(n)
+	n, errno := linux.write(f.impl.fd, p[:])
+	if errno != .NONE {
+		return -1, _get_platform_error(errno)
 	}
 	}
 	return i64(n), nil
 	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
-	for len(b) > 0 {
-		m := unix.sys_pwrite(f.impl.fd, &b[0], len(b), offset)
-		if m < 0 {
-			return -1, _get_platform_error(m)
-		}
-		n += i64(m)
-		b = b[m:]
-		offset += i64(m)
+	n, errno := linux.pwrite(f.impl.fd, p[:], offset)
+	if errno != .NONE {
+		return -1, _get_platform_error(errno)
 	}
 	}
-	return
+	return i64(n), nil
 }
 }
 
 
 _file_size :: proc(f: ^File) -> (n: i64, err: Error) {
 _file_size :: proc(f: ^File) -> (n: i64, err: Error) {
-	s: _Stat = ---
-	res := unix.sys_fstat(f.impl.fd, &s)
-	if res < 0 {
-		return -1, _get_platform_error(res)
+	s: linux.Stat = ---
+	errno := linux.fstat(f.impl.fd, &s)
+	if errno != .NONE {
+		return -1, _get_platform_error(errno)
 	}
 	}
-	return s.size, nil
+	return i64(s.size), nil
 }
 }
 
 
 _sync :: proc(f: ^File) -> Error {
 _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))
-	if fd < 0 {
-		return _get_platform_error(fd)
+	fd, errno := linux.open(name_cstr, {.NOFOLLOW})
+	#partial switch (errno) {
+	case .ELOOP: /* symlink */
+	case .NONE:
+		defer linux.close(fd)
+		if _is_dir_fd(fd) {
+			return _get_platform_error(linux.rmdir(name_cstr))
+		}
+	case:
+		return _get_platform_error(errno)
 	}
 	}
-	defer unix.sys_close(fd)
 
 
-	if _is_dir_fd(fd) {
-		return _ok_or_error(unix.sys_rmdir(name_cstr))
-	}
-	return _ok_or_error(unix.sys_unlink(name_cstr))
+	return _get_platform_error(linux.unlink(name_cstr))
 }
 }
 
 
 _rename :: proc(old_name, new_name: string) -> Error {
 _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 _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr))
+	return _get_platform_error(linux.symlink(old_name_cstr, new_name_cstr))
 }
 }
 
 
 _read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
 _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)
-		if rc < 0 {
-			delete(buf)
-			return "", _get_platform_error(rc)
-		} else if rc == int(bufsz) {
+		sz, errno := linux.readlink(name_cstr, buf[:])
+		if errno != .NONE {
+			delete(buf, allocator)
+			return "", _get_platform_error(errno)
+		} else if sz == int(bufsz) {
 			bufsz *= 2
 			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 {
-		{ atime._nsec, 0 },
-		{ mtime._nsec, 0 },
+	times := [2]linux.Time_Spec {
+		{
+			uint(atime._nsec) / uint(time.Second),
+			uint(atime._nsec) % uint(time.Second),
+		},
+		{
+			uint(mtime._nsec) / uint(time.Second),
+			uint(mtime._nsec) % uint(time.Second),
+		},
 	}
 	}
-	return _ok_or_error(unix.sys_utimensat(_AT_FDCWD, name_cstr, &times, 0))
+	return _get_platform_error(linux.utimensat(linux.AT_FDCWD, name_cstr, &times[0], nil))
 }
 }
 
 
 _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
 _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
-	times := [2]Unix_File_Time {
-		{ atime._nsec, 0 },
-		{ mtime._nsec, 0 },
+	times := [2]linux.Time_Spec {
+		{
+			uint(atime._nsec) / uint(time.Second),
+			uint(atime._nsec) % uint(time.Second),
+		},
+		{
+			uint(mtime._nsec) / uint(time.Second),
+			uint(mtime._nsec) % uint(time.Second),
+		},
 	}
 	}
-	return _ok_or_error(unix.sys_utimensat(f.impl.fd, nil, &times, 0))
+	return _get_platform_error(linux.utimensat(f.impl.fd, nil, &times[0], nil))
 }
 }
 
 
 _exists :: proc(name: string) -> bool {
 _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
-	res := unix.sys_stat(name_cstr, &s)
-	if res < 0 {
+	s: linux.Stat
+	if linux.stat(name_cstr, &s) != .NONE {
 		return false
 		return false
 	}
 	}
-	return S_ISREG(s.mode)
+	return linux.S_ISREG(s.mode)
 }
 }
 
 
-_is_file_fd :: proc(fd: int) -> bool {
-	s: _Stat
-	res := unix.sys_fstat(fd, &s)
-	if res < 0 { // error
+_is_file_fd :: proc(fd: linux.Fd) -> bool {
+	s: linux.Stat
+	if linux.fstat(fd, &s) != .NONE {
 		return false
 		return 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
-	res := unix.sys_stat(name_cstr, &s)
-	if res < 0 {
+	s: linux.Stat
+	if linux.stat(name_cstr, &s) != .NONE {
 		return false
 		return false
 	}
 	}
-	return S_ISDIR(s.mode)
+	return linux.S_ISDIR(s.mode)
 }
 }
 
 
-_is_dir_fd :: proc(fd: int) -> bool {
-	s: _Stat
-	res := unix.sys_fstat(fd, &s)
-	if res < 0 { // error
+_is_dir_fd :: proc(fd: linux.Fd) -> bool {
+	s: linux.Stat
+	if linux.fstat(fd, &s) != .NONE {
 		return false
 		return 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
core/os/os2/heap_linux.odin

@@ -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_PROT :: unix.PROT_READ | unix.PROT_WRITE
-
+MMAP_FLAGS : linux.Map_Flags      : {.ANONYMOUS, .PRIVATE}
+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)
-	if res < 0 {
+	ptr, errno := linux.mmap(0, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 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)
-	if new_allocation < 0 && new_allocation > -4096 {
+	new_allocation, errno := linux.mmap(0, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0)
+	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(
-		alloc,
-		uint(old_mmap_size),
-		uint(new_mmap_size),
-		unix.MREMAP_MAYMOVE,
-	)
-	if new_allocation < 0 && new_allocation > -4096 {
+	new_allocation, errno := linux.mremap(alloc, uint(old_mmap_size), uint(new_mmap_size), {.MAYMOVE})
+	if errno != .NONE {
 		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
core/os/os2/path_linux.odin

@@ -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
-_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
+_OPENDIR_FLAGS : linux.Open_Flags : {.NONBLOCK, .DIRECTORY, .LARGEFILE, .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
-	//       additional arguments to this function.
+	// TODO: These modes would require mknod, however, that would also
+	//       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 {
-		if len(path) == 0 {
-			return _ok_or_error(unix.sys_close(dfd))
-		}
+	mkdirat :: proc(dfd: linux.Fd, path: []u8, perm: int, has_created: ^bool) -> Error {
 		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)
-		switch new_dfd {
-		case -ENOENT:
-			if res := unix.sys_mkdirat(dfd, cstring(&path[0]), uint(perm)); res < 0 {
-				return _get_platform_error(res)
+		new_dfd, errno := linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
+		#partial switch errno {
+		case .ENOENT:
+			if errno = linux.mkdirat(dfd, cstring(&path[0]), transmute(linux.Mode)(u32(perm))); errno != .NONE {
+				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 {
-				return _get_platform_error(new_dfd)
+			if new_dfd, errno = linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); errno != .NONE {
+				return _get_platform_error(errno)
 			}
 			}
 			fallthrough
 			fallthrough
-		case 0:
-			if res := unix.sys_close(dfd); res < 0 {
-				return _get_platform_error(res)
+		case .NONE:
+			if errno = linux.close(dfd); errno != .NONE {
+				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: []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())
-	}
+	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 {
-		return _get_platform_error(dfd)
+	if errno != .NONE {
+		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)
-			switch getdents_res {
-			case -EINVAL:
+			buflen, errno := linux.getdents(dfd, buf[:])
+			#partial switch errno {
+			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:
-				return _get_platform_error(getdents_res)
-			case 0:
-				break loop
+			case .NONE:
+				if buflen == 0 { break loop }
+			case:
+				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)
-					if new_dfd < 0 {
-						return _get_platform_error(new_dfd)
+					new_dfd: linux.Fd
+					new_dfd, errno = linux.openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
+					if errno != .NONE {
+						return _get_platform_error(errno)
 					}
 					}
-					defer unix.sys_close(new_dfd)
-					_remove_all_dir(new_dfd) or_return
-					unlink_res = unix.sys_unlinkat(dfd, d_name_cstr, int(unix.AT_REMOVEDIR))
+					defer linux.close(new_dfd)
+					remove_all_dir(new_dfd) or_return
+					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 {
-					return _get_platform_error(unlink_res)
+				if errno != .NONE {
+					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)
-	switch fd {
-	case -ENOTDIR:
-		return _ok_or_error(unix.sys_unlink(path_cstr))
-	case -4096..<0:
-		return _get_platform_error(fd)
+	fd, errno := linux.open(path_cstr, _OPENDIR_FLAGS)
+	#partial switch errno {
+	case .NONE:
+		break
+	case .ENOTDIR:
+		return _get_platform_error(linux.unlink(path_cstr))
+	case:
+		return _get_platform_error(errno)
 	}
 	}
 
 
-	defer unix.sys_close(fd)
-	_remove_all_dir(fd) or_return
-	return _ok_or_error(unix.sys_rmdir(path_cstr))
+	defer linux.close(fd)
+	remove_all_dir(fd) or_return
+	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)))
-
-		if res >= 0 {
-			return string_from_null_terminated_bytes(buf[:]), nil
+		#no_bounds_check n, errno := linux.getcwd(buf[:])
+		if errno == .NONE {
+			return string(buf[:n-1]), nil
 		}
 		}
-		if res != -ERANGE {
-			return "", _get_platform_error(res)
+		if errno != .ERANGE {
+			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
 }
 }
-

+ 11 - 1
core/os/os2/pipe_linux.odin

@@ -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
core/os/os2/stat_linux.odin

@@ -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) {
-	s: _Stat
-	result := unix.sys_fstat(fd, &s)
-	if result < 0 {
-		return {}, _get_platform_error(result)
+_fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Info, Error) {
+	s: linux.Stat
+	errno := linux.fstat(fd, &s)
+	if errno != .NONE {
+		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),
-		modification_time = time.Time {s.modified.seconds},
-		access_time = time.Time {s.last_access.seconds},
-		creation_time = time.Time{0}, // regular stat does not provide this
+		is_directory = linux.S_ISDIR(s.mode),
+		modification_time = time.Time {i64(s.mtime.time_sec) * i64(time.Second) + i64(s.mtime.time_nsec)},
+		access_time = time.Time {i64(s.atime.time_sec) * i64(time.Second) + i64(s.atime.time_nsec)},
+		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)
-	if fd < 0 {
-		return {}, _get_platform_error(fd)
+	fd, errno := linux.open(name_cstr, {})
+	if errno != .NONE {
+		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)
-	if fd < 0 {
-		return {}, _get_platform_error(fd)
+	fd, errno := linux.open(name_cstr, {.PATH, .NOFOLLOW})
+	if errno != .NONE {
+		return {}, _get_platform_error(errno)
 	}
 	}
-	defer unix.sys_close(fd)
+	defer linux.close(fd)
 	return _fstat_internal(fd, allocator)
 	return _fstat_internal(fd, allocator)
 }
 }
 
 

+ 77 - 40
core/sys/linux/bits.odin

@@ -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 {
-	WRONLY    = 0,
-	RDWR      = 1,
-	CREAT     = 6,
-	EXCL      = 7,
-	NOCTTY    = 8,
-	TRUNC     = 9,
-	APPEND    = 10,
-	NONBLOCK  = 11,
-	DSYNC     = 12,
-	ASYNC     = 13,
-	DIRECT    = 14,
-	LARGEFILE = 15,
-	DIRECTORY = 16,
-	NOFOLLOW  = 17,
-	NOATIME   = 18,
-	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)
+when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 {
+	Open_Flags_Bits :: enum {
+		WRONLY    = 0,
+		RDWR      = 1,
+		CREAT     = 6,
+		EXCL      = 7,
+		NOCTTY    = 8,
+		TRUNC     = 9,
+		APPEND    = 10,
+		NONBLOCK  = 11,
+		DSYNC     = 12,
+		ASYNC     = 13,
+		DIRECT    = 14,
+		LARGEFILE = 15,
+		DIRECTORY = 16,
+		NOFOLLOW  = 17,
+		NOATIME   = 18,
+		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)
+
+} else {
+	Open_Flags_Bits :: enum {
+		WRONLY    = 0,
+		RDWR      = 1,
+		CREAT     = 6,
+		EXCL      = 7,
+		NOCTTY    = 8,
+		TRUNC     = 9,
+		APPEND    = 10,
+		NONBLOCK  = 11,
+		DSYNC     = 12,
+		ASYNC     = 13,
+		DIRECTORY = 14,
+		NOFOLLOW  = 15,
+		DIRECT    = 16,
+		LARGEFILE = 17,
+		NOATIME   = 18,
+		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
core/sys/linux/sys.odin

@@ -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) {
-	ret := syscall(SYS_waitid, id_type, id, sig_info, transmute(i32) options)
+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, 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) {
-	ret := syscall(SYS_symlinkat, dirfd, cast(rawptr) target, cast(rawptr) linkpath)
+symlinkat :: proc "contextless" (target: cstring, dirfd: Fd, linkpath: cstring) -> (Errno) {
+	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) {
-	ret := syscall(SYS_execveat, dirfd, cast(rawptr) name, cast(rawptr) argv, cast(rawptr) envp)
+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, transmute(i32) flags)
 	return Errno(-ret)
 	return Errno(-ret)
 }
 }
 
 

+ 76 - 20
core/sys/linux/types.odin

@@ -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,
-		mtim:       Time_Spec,
-		ctim:       Time_Spec,
+		atime:      Time_Spec,
+		mtime:      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_PAD_SIZE       :: (SI_MAX_SIZE - SI_ARCH_PREAMBLE) / size_of(i32)
-@private SI_TIMER_PAD_SIZE :: size_of(Uid) - size_of(i32)
+@private SI_ARCH_PREAMBLE  :: 4 * size_of(i32)
+@private SI_PAD_SIZE       :: SI_MAX_SIZE - SI_ARCH_PREAMBLE
 
 
 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 */
-			_uid1: Uid,      /* sender's uid */
+			_pid1: Pid,  /* which child */
+			_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 */
-			arch: u32,      /* AUDIT_ARCH_* of syscall */
+			syscall: i32,      /* triggering system call number */
+			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,
 	}
 	}
 }
 }