jasonkercher 3 years ago
parent
commit
658a605c75

+ 28 - 0
core/os/os2/env_linux.odin

@@ -0,0 +1,28 @@
+//+private
+package os2
+
+_get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+	//TODO
+	return
+}
+
+_set_env :: proc(key, value: string) -> bool {
+	//TODO
+	return false
+}
+
+_unset_env :: proc(key: string) -> bool {
+	//TODO
+	return false
+}
+
+_clear_env :: proc() {
+	//TODO
+}
+
+_environ :: proc(allocator := context.allocator) -> []string {
+	//TODO
+	return nil
+}
+
+

+ 134 - 0
core/os/os2/errors_linux.odin

@@ -0,0 +1,134 @@
+//+private
+package os2
+
+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 */
+
+_error_string :: proc(errno: i32) -> string {
+	if errno == 0 {
+		return ""
+	}
+	return "Error"
+}

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

@@ -61,6 +61,10 @@ create :: proc(name: string, perm: File_Mode = 0) -> (Handle, Error) {
 	return open(name, {.Read, .Write, .Create}, perm)
 }
 
+opendir :: proc(name: string) -> (Handle, Error) {
+	return _opendir(name)
+}
+
 open :: proc(name: string, flags := File_Flags{.Read}, perm: File_Mode = 0) -> (Handle, Error) {
 	flags := flags
 	if .Write not_in flags {

+ 110 - 57
core/os/os2/file_linux.odin

@@ -3,6 +3,7 @@ package os2
 
 import "core:io"
 import "core:time"
+import "core:strings"
 import "core:sys/unix"
 
 
@@ -15,13 +16,64 @@ _ok_or_error :: proc(res: int) -> Error {
 	return res >= 0 ? nil : _get_platform_error(res)
 }
 
+_std_handle :: proc(kind: Std_Handle_Kind) -> Handle {
+	switch kind {
+	case .stdin:  return Handle(0)
+	case .stdout: return Handle(1)
+	case .stderr: return Handle(2)
+	}
+	unreachable()
+}
+
+_O_RDONLY    :: 0o0
+_O_WRONLY    :: 0o1
+_O_RDWR      :: 0o2
+_O_CREAT     :: 0o100
+_O_EXCL      :: 0o200
+_O_TRUNC     :: 0o1000
+_O_APPEND    :: 0o2000
+_O_NONBLOCK  :: 0o4000
+_O_LARGEFILE :: 0o100000
+_O_DIRECTORY :: 0o200000
+_O_SYNC      :: 0o4010000
+_O_CLOEXEC   :: 0o2000000
+
+_opendir :: proc(name: string) -> (Handle, Error) {
+	cstr := strings.clone_to_cstring(name, context.temp_allocator)
+
+	flags := _O_RDONLY|_O_NONBLOCK|_O_DIRECTORY|_O_LARGEFILE|_O_CLOEXEC
+
+	handle_i := unix.sys_open(cstr, flags)
+	if handle_i < 0 {
+		return INVALID_HANDLE, _get_platform_error(handle_i)
+	}
+
+	return Handle(handle_i), nil
+}
+
 _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Error) {
-	cstr := strings.clone_to_cstring(path, context.temp_allocator)
-	handle := Handle(unix.sys_open(cstr, int(flags), int(perm)))
-	if handle < 0 {
-		return Handle(-1), _get_platform_error(int(handle))
+	cstr := strings.clone_to_cstring(name, context.temp_allocator)
+
+	flags_i: int
+	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
+	}
+
+	flags_i |= (_O_APPEND * int(.Append in flags))
+	flags_i |= (_O_CREAT * int(.Create in flags))
+	flags_i |= (_O_EXCL * int(.Excl in flags))
+	flags_i |= (_O_SYNC * int(.Sync in flags))
+	flags_i |= (_O_TRUNC * int(.Trunc in flags))
+	flags_i |= (_O_CLOEXEC * int(.Close_On_Exec in flags))
+
+	handle_i := unix.sys_open(cstr, flags_i, int(perm))
+	if handle_i < 0 {
+		return INVALID_HANDLE, _get_platform_error(handle_i)
 	}
-	return handle, nil
+
+	return Handle(handle_i), nil
 }
 
 _close :: proc(fd: Handle) -> Error {
@@ -46,30 +98,27 @@ _read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
 	if len(p) == 0 {
 		return 0, nil
 	}
-	n = unix.sys_read(fd, &data[0], c.size_t(len(data)))
+	n = unix.sys_read(int(fd), &p[0], len(p))
 	if n < 0 {
-		return -1, unix.get_errno(n)
+		return -1, _get_platform_error(int(unix.get_errno(n)))
 	}
-	return bytes_read, nil
+	return n, nil
 }
 
 _read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
 	if offset < 0 {
 		return 0, .Invalid_Offset
 	}
-	
-	curr_offset, err := _seek(fd, 0, .Current)
-	if err != nil {
-		return 0, err
-	}
-	defer _seek(fd, curr_offset, .Start)
-	_seek(fd, offset, .Start)
 
-	b := p
+	b, offset := p, offset
 	for len(b) > 0 {
-		m := _read(fd, b) or_return
+		m := unix.sys_pread(int(fd), &b[0], len(b), offset)
+		if m < 0 {
+			return -1, _get_platform_error(m)
+		}
 		n += m
 		b = b[m:]
+		offset += i64(m)
 	}
 	return
 }
@@ -83,7 +132,7 @@ _write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
 	if len(p) == 0 {
 		return 0, nil
 	}
-	n = unix.sys_write(fd, &p[0], uint(len(p)))
+	n = unix.sys_write(int(fd), &p[0], uint(len(p)))
 	if n < 0 {
 		return -1, _get_platform_error(n)
 	}
@@ -94,19 +143,16 @@ _write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
 	if offset < 0 {
 		return 0, .Invalid_Offset
 	}
-	
-	curr_offset, err := _seek(fd, 0, .Current)
-	if err != nil {
-		return 0, err
-	}
-	defer _seek(fd, curr_offset, .Start)
-	_seek(fd, offset, .Start)
 
-	b := p
+	b, offset := p, offset
 	for len(b) > 0 {
-		m := _write(fd, b) or_return
+		m := unix.sys_pwrite(int(fd), &b[0], len(b), offset)
+		if m < 0 {
+			return -1, _get_platform_error(m)
+		}
 		n += m
 		b = b[m:]
+		offset += i64(m)
 	}
 	return
 }
@@ -117,11 +163,12 @@ _write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) {
 }
 
 _file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
-	s, err := _fstat(fd) or_return
-	if err != nil {
-		return 0, err
+	s: OS_Stat = ---
+	res := unix.sys_fstat(int(fd), &s)
+	if res < 0 {
+		return -1, _get_platform_error(res)
 	}
-	return max(s.size, 0), nil
+	return s.size, nil
 }
 
 _sync :: proc(fd: Handle) -> Error {
@@ -137,17 +184,25 @@ _truncate :: proc(fd: Handle, size: i64) -> Error {
 }
 
 _remove :: proc(name: string) -> Error {
-	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
-	if _is_dir(name) {
-		return _ok_or_error(unix.sys_rmdir(path_cstr))
+	name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
+
+	handle_i := unix.sys_open(name_cstr, int(File_Flags.Read))
+	if handle_i < 0 {
+		return _get_platform_error(handle_i)
 	}
-	return _ok_or_error(unix.sys_unlink(path_cstr))
+	defer unix.sys_close(handle_i)
+
+	/* TODO: THIS WILL NOT WORK */
+	if _is_dir(Handle(handle_i)) {
+		return _ok_or_error(unix.sys_rmdir(name_cstr))
+	}
+	return _ok_or_error(unix.sys_unlink(name_cstr))
 }
 
-_rename :: proc(old_path, new_path: string) -> Error {
-	old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
-	new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
-	return _ok_or_error(unix.sys_rename(old_path_cstr, new_path_cstr))
+_rename :: proc(old_name, new_name: string) -> Error {
+	old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator)
+	new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator)
+	return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_cstr))
 }
 
 _link :: proc(old_name, new_name: string) -> Error {
@@ -163,16 +218,16 @@ _symlink :: proc(old_name, new_name: string) -> Error {
 }
 
 _read_link :: proc(name: string, allocator := context.allocator) -> (string, Error) {
-	path_cstr := strings.clone_to_cstring(path)
-	defer delete(path_cstr)
+	name_cstr := strings.clone_to_cstring(name)
+	defer delete(name_cstr)
 
 	bufsz : uint = 256
 	buf := make([]byte, bufsz, allocator)
 	for {
-		rc := unix.sys_readlink(path_cstr, &(buf[0]), bufsz)
+		rc := unix.sys_readlink(name_cstr, &(buf[0]), bufsz)
 		if rc < 0 {
 			delete(buf)
-			return "", unix.get_errno(rc)
+			return "", _get_platform_error(int(unix.get_errno(rc)))
 		} else if rc == int(bufsz) {
 			bufsz *= 2
 			delete(buf)
@@ -183,9 +238,9 @@ _read_link :: proc(name: string, allocator := context.allocator) -> (string, Err
 	}
 }
 
-_unlink :: proc(path: string) -> Error {
-	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
-	return _ok_or_error(unix.sys_unlink(path_cstr))
+_unlink :: proc(name: string) -> Error {
+	name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
+	return _ok_or_error(unix.sys_unlink(name_cstr))
 }
 
 _chdir :: proc(fd: Handle) -> Error {
@@ -193,18 +248,16 @@ _chdir :: proc(fd: Handle) -> Error {
 }
 
 _chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
-	//TODO
-	return nil
+	return _ok_or_error(unix.sys_fchmod(int(fd), int(mode)))
 }
 
 _chown :: proc(fd: Handle, uid, gid: int) -> Error {
-	//TODO
-	return nil
+	return _ok_or_error(unix.sys_fchown(int(fd), uid, gid))
 }
 
 _lchown :: proc(name: string, uid, gid: int) -> Error {
-	//TODO
-	return nil
+	name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
+	return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid))
 }
 
 _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
@@ -212,14 +265,14 @@ _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
 	return nil
 }
 
-_exists :: proc(path: string) -> bool {
-	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
-	return unix.sys_access(path_cstr, F_OK) == 0
+_exists :: proc(name: string) -> bool {
+	name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
+	return unix.sys_access(name_cstr, F_OK) == 0
 }
 
 _is_file :: proc(fd: Handle) -> bool {
 	s: OS_Stat
-	res := unix.sys_fstat(int(fd), rawptr(&s))
+	res := unix.sys_fstat(int(fd), &s)
 	if res < 0 { // error
 		return false
 	}
@@ -228,7 +281,7 @@ _is_file :: proc(fd: Handle) -> bool {
 
 _is_dir :: proc(fd: Handle) -> bool {
 	s: OS_Stat
-	res := unix.sys_fstat(int(fd), rawptr(&s))
+	res := unix.sys_fstat(int(fd), &s)
 	if res < 0 { // error
 		return false
 	}

+ 5 - 0
core/os/os2/file_windows.odin

@@ -36,6 +36,11 @@ _std_handle :: proc(kind: Std_Handle_Kind) -> Handle {
 	unreachable()
 }
 
+_opendir :: proc(path: string) -> (handle: Handle, err: Error) {
+	return INVALID_HANDLE, .Invalid_Argument
+}
+
+
 _open :: proc(path: string, flags: File_Flags, perm: File_Mode) -> (handle: Handle, err: Error) {
 	handle = INVALID_HANDLE
 	if len(path) == 0 {

+ 27 - 0
core/os/os2/heap_linux.odin

@@ -0,0 +1,27 @@
+//+private
+package os2
+
+import "core:mem"
+
+heap_alloc :: proc(size: int) -> rawptr {
+	// TODO
+	return nil
+}
+
+heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+	// TODO
+	return nil
+}
+heap_free :: proc(ptr: rawptr) {
+	if ptr == nil {
+		return
+	}
+	// TODO
+}
+
+_heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
+                            size, alignment: int,
+                            old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
+	// TODO
+	return nil, nil
+}

+ 85 - 0
core/os/os2/path_linux.odin

@@ -0,0 +1,85 @@
+//+private
+package os2
+
+import "core:fmt"
+import "core:strings"
+import "core:sys/unix"
+import "core:path/filepath"
+
+_Path_Separator      :: '/'
+_Path_List_Separator :: ':'
+
+_is_path_separator :: proc(c: byte) -> bool {
+	return c == '/'
+}
+
+_mkdir :: proc(path: string, perm: File_Mode) -> Error {
+	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	//TODO file_mode
+	return _ok_or_error(unix.sys_mkdir(path_cstr))
+}
+
+_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
+	_mkdir_all_stat :: proc(path: string, s: ^OS_Stat, perm: File_Mode) -> Error {
+		if len(path) == 0 {
+			return nil
+		}
+	
+		path := path[len(path)-1] == '/' ? path[:len(path)-1] : path
+		dir, _ := filepath.split(path)
+
+		if len(dir) == 0 {
+			return _mkdir(path, perm)
+		}
+
+		dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator)
+		errno := int(unix.get_errno(unix.sys_stat(dir_cstr, s)))
+		switch errno {
+		case 0:
+			if !S_ISDIR(s.mode) {
+				return .Exist
+			}
+			return _mkdir(path, perm)
+		case ENOENT:
+			_mkdir_all_stat(dir, s, perm) or_return
+			return _mkdir(path, perm)
+		case:
+			return _get_platform_error(errno)
+		}
+		unreachable()
+	}
+	// OS_Stat is fat. Make one and re-use it.
+	s: OS_Stat = ---
+	return _mkdir_all_stat(path, &s, perm)
+}
+
+_remove_all :: proc(path: string) -> Error {
+	// TODO
+	return nil
+}
+
+_getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
+	// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
+	// an authoritative value for it across all systems.
+	// The largest value I could find was 4096, so might as well use the page size.
+	// NOTE(jason): Avoiding libc, so just use 4096 directly
+	PATH_MAX :: 4096
+	buf := make([dynamic]u8, PATH_MAX, allocator)
+	for {
+		#no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
+
+		if res >= 0 {
+			return strings.string_from_nul_terminated_ptr(&buf[0], len(buf)), nil
+		}
+		if errno := int(unix.get_errno(res)); errno != ERANGE {
+			return "", _get_platform_error(errno)
+		}
+		resize(&buf, len(buf)+PATH_MAX)
+	}
+	unreachable()
+}
+
+_setwd :: proc(dir: string) -> (err: Error) {
+	dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator)
+	return _ok_or_error(unix.sys_chdir(dir_cstr))
+}

+ 7 - 0
core/os/os2/pipe_linux.odin

@@ -0,0 +1,7 @@
+//+private
+package os2
+
+_pipe :: proc() -> (r, w: Handle, err: Error) {
+	return INVALID_HANDLE, INVALID_HANDLE, nil
+}
+

+ 16 - 6
core/os/os2/stat_linux.odin

@@ -2,7 +2,9 @@
 package os2
 
 import "core:time"
+import "core:strings"
 import "core:sys/unix"
+import "core:path/filepath"
 
 // File type
 S_IFMT   :: 0o170000 // Type of file mask
@@ -51,6 +53,12 @@ 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
 OS_Stat :: struct {
 	device_id:     u64, // ID of device containing file
@@ -75,19 +83,21 @@ OS_Stat :: struct {
 }
 
 _fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Error) {
+	return File_Info{}, nil
 }
 
 _stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
+	return File_Info{}, nil
 }
 
 _lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
-	cstr := strings.clone_to_cstring(path)
+	cstr := strings.clone_to_cstring(name)
 	defer delete(cstr)
 
 	s: OS_Stat
 	result := unix.sys_lstat(cstr, &s)
 	if result < 0 {
-		return {}, unix.get_errno(result)
+		return {}, _get_platform_error(int(unix.get_errno(result)))
 	}
 
 	fi := File_Info {
@@ -96,10 +106,10 @@ _lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Erro
 		size = s.size,
 		mode = 0,
 		is_dir = S_ISDIR(s.mode),
-		creation_time = nil, // linux does not track this
+		creation_time = time.Time{0}, // linux does not track this
 		//TODO
-		modification_time = nil,
-		access_time = nil,
+		modification_time = time.Time{0},
+		access_time = time.Time{0},
 	}
 	
 	return fi, nil
@@ -110,7 +120,7 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool {
 }
 
 _stat_internal :: proc(name: string) -> (s: OS_Stat, res: int) {
-	name_cstr = strings.clone_to_cstring(name, context.temp_allocator)
+	name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
 	res = unix.sys_stat(name_cstr, &s)
 	return
 }

+ 18 - 0
core/os/os2/temp_file_linux.odin

@@ -0,0 +1,18 @@
+//+private
+package os2
+
+
+_create_temp :: proc(dir, pattern: string) -> (Handle, Error) {
+	//TODO
+	return 0, nil
+}
+
+_mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) {
+	//TODO
+	return "", nil
+}
+
+_temp_dir :: proc(allocator := context.allocator) -> string {
+	//TODO
+	return ""
+}

+ 122 - 24
core/sys/unix/syscalls_linux.odin

@@ -15,7 +15,7 @@ import "core:intrinsics"
 //  386: arch/x86/entry/syscalls/sycall_32.tbl
 //  arm: arch/arm/tools/syscall.tbl
 
-when ODIN_ARCH == "amd64" {
+when ODIN_ARCH == .amd64 {
 	SYS_read : uintptr : 0
 	SYS_write : uintptr : 1
 	SYS_open : uintptr : 2
@@ -33,8 +33,8 @@ when ODIN_ARCH == "amd64" {
 	SYS_rt_sigprocmask : uintptr : 14
 	SYS_rt_sigreturn : uintptr : 15
 	SYS_ioctl : uintptr : 16
-	SYS_pread : uintptr : 17
-	SYS_pwrite : uintptr : 18
+	SYS_pread64 : uintptr : 17
+	SYS_pwrite64 : uintptr : 18
 	SYS_readv : uintptr : 19
 	SYS_writev : uintptr : 20
 	SYS_access : uintptr : 21
@@ -374,7 +374,7 @@ when ODIN_ARCH == "amd64" {
 	SYS_landlock_add_rule : uintptr : 445
 	SYS_landlock_restrict_self : uintptr : 446
 	SYS_memfd_secret : uintptr : 447
-} else when ODIN_ARCH == "arm64" {
+} else when ODIN_ARCH == .arm64 {
 	SYS_io_setup : uintptr : 0
 	SYS_io_destroy : uintptr : 1
 	SYS_io_submit : uintptr : 2
@@ -675,7 +675,7 @@ when ODIN_ARCH == "amd64" {
 	SYS_landlock_create_ruleset : uintptr : 444
 	SYS_landlock_add_rule : uintptr : 445
 	SYS_landlock_restrict_self : uintptr : 446
-} else when ODIN_ARCH == "386" {
+} else when ODIN_ARCH == .i386 {
 	SYS_restart_syscall : uintptr : 0
 	SYS_exit : uintptr : 1
 	SYS_fork : uintptr : 2
@@ -1112,7 +1112,7 @@ when ODIN_ARCH == "amd64" {
 	SYS_landlock_add_rule : uintptr : 445
 	SYS_landlock_restrict_self : uintptr : 446
 	SYS_memfd_secret : uintptr : 447
-} else when ODIN_ARCH == "arm" {
+} else when ODIN_ARCH == .arm {
 	SYS_restart_syscall : uintptr : 0
 	SYS_exit : uintptr : 1
 	SYS_fork : uintptr : 2
@@ -1518,6 +1518,7 @@ when ODIN_ARCH == "amd64" {
 
 AT_FDCWD            :: -100
 AT_REMOVEDIR        :: uintptr(0x200)
+AT_SYMLINK_FOLLOW   :: uintptr(0x400)
 AT_SYMLINK_NOFOLLOW :: uintptr(0x100)
 
 sys_gettid :: proc "contextless" () -> int {
@@ -1529,12 +1530,11 @@ sys_getrandom :: proc "contextless" (buf: ^byte, buflen: int, flags: uint) -> in
 }
 
 sys_open :: proc(path: cstring, flags: int, mode: int = 0o000) -> int {
-	when ODIN_ARCH != "arm64" {
-		res := int(intrinsics.syscall(SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
+	when ODIN_ARCH != .arm64 {
+		return int(intrinsics.syscall(SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
 	} else { // NOTE: arm64 does not have open
-		res := int(intrinsics.syscall(SYS_openat, uintptr(AT_FDCWD), uintptr(rawptr(path), uintptr(flags), uintptr(mode))))
+		return int(intrinsics.syscall(SYS_openat, uintptr(AT_FDCWD), uintptr(rawptr(path), uintptr(flags), uintptr(mode))))
 	}
-	return -1 if res < 0 else res
 }
 
 sys_close :: proc(fd: int) -> int {
@@ -1545,26 +1545,46 @@ sys_read :: proc(fd: int, buf: rawptr, size: uint) -> int {
 	return int(intrinsics.syscall(SYS_read, uintptr(fd), uintptr(buf), uintptr(size)))
 }
 
+sys_pread :: proc(fd: int, buf: rawptr, size: uint, offset: i64) -> int {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+		return int(intrinsics.syscall(SYS_pread64, uintptr(fd), uintptr(buf), uintptr(size), uintptr(offset)))
+	} else {
+		low := uintptr(offset & 0xFFFFFFFF)
+		high := uintptr(offset >> 32)
+		return int(intrinsics.syscall(SYS_pread64, uintptr(fd), uintptr(buf), uintptr(size), high, low))
+	}
+}
+
 sys_write :: proc(fd: int, buf: rawptr, size: uint) -> int {
 	return int(intrinsics.syscall(SYS_write, uintptr(fd), uintptr(buf), uintptr(size)))
 }
 
+sys_pwrite :: proc(fd: int, buf: rawptr, size: uint, offset: i64) -> int {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+		return int(intrinsics.syscall(SYS_pwrite64, uintptr(fd), uintptr(buf), uintptr(size), uintptr(offset)))
+	} else {
+		low := uintptr(offset & 0xFFFFFFFF)
+		high := uintptr(offset >> 32)
+		return int(intrinsics.syscall(SYS_pwrite64, uintptr(fd), uintptr(buf), uintptr(size), high, low))
+	}
+}
+
 sys_lseek :: proc(fd: int, offset: i64, whence: int) -> i64 {
-	when ODIN_ARCH == "amd64" || ODIN_ARCH == "arm64" {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
 		return i64(intrinsics.syscall(SYS_lseek, uintptr(fd), uintptr(offset), uintptr(whence)))
 	} else {
 		low := uintptr(offset & 0xFFFFFFFF)
 		high := uintptr(offset >> 32)
 		result: i64
 		res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence)))
-		return -1 if res < 0 else result
+		return res if res < 0 else result
 	}
 }
 
 sys_stat :: proc(path: cstring, stat: rawptr) -> int {
-	when ODIN_ARCH == "amd64" {
+	when ODIN_ARCH == .amd64 {
 		return int(intrinsics.syscall(SYS_stat, uintptr(rawptr(path)), uintptr(stat)))
-	} else when ODIN_ARCH != "arm64" {
+	} else when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(SYS_stat64, uintptr(rawptr(path)), uintptr(stat)))
 	} else { // NOTE: arm64 does not have stat
 		return int(intrinsics.syscall(SYS_fstatat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(stat), 0))
@@ -1572,7 +1592,7 @@ sys_stat :: proc(path: cstring, stat: rawptr) -> int {
 }
 
 sys_fstat :: proc(fd: int, stat: rawptr) -> int {
-	when ODIN_ARCH == "amd64" || ODIN_ARCH == "arm64" {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
 		return int(intrinsics.syscall(SYS_fstat, uintptr(fd), uintptr(stat)))
 	} else {
 		return int(intrinsics.syscall(SYS_fstat64, uintptr(fd), uintptr(stat)))
@@ -1580,9 +1600,9 @@ sys_fstat :: proc(fd: int, stat: rawptr) -> int {
 }
 
 sys_lstat :: proc(path: cstring, stat: rawptr) -> int {
-	when ODIN_ARCH == "amd64" {
+	when ODIN_ARCH == .amd64 {
 		return int(intrinsics.syscall(SYS_lstat, uintptr(rawptr(path)), uintptr(stat)))
-	} else when ODIN_ARCH != "arm64" {
+	} else when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(SYS_lstat64, uintptr(rawptr(path)), uintptr(stat)))
 	} else { // NOTE: arm64 does not have any lstat
 		return int(intrinsics.syscall(SYS_fstatat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW))
@@ -1590,15 +1610,23 @@ sys_lstat :: proc(path: cstring, stat: rawptr) -> int {
 }
 
 sys_readlink :: proc(path: cstring, buf: rawptr, bufsiz: uint) -> int {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(SYS_readlink, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
 	} else { // NOTE: arm64 does not have readlink
 		return int(intrinsics.syscall(SYS_readlinkat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
 	}
 }
 
+sys_symlink :: proc(old_name: cstring, new_name: cstring) -> int {
+	when ODIN_ARCH != .arm64 {
+		return int(intrinsics.syscall(SYS_symlink, uintptr(rawptr(old_name)), uintptr(rawptr(new_name))))
+	} else { // NOTE: arm64 does not have symlink
+		return int(intrinsics.syscall(SYS_symlinkat, uintptr(rawptr(old_name)), uintptr(AT_FDCWD), uintptr(rawptr(new_name))))
+	}
+}
+
 sys_access :: proc(path: cstring, mask: int) -> int {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(SYS_access, uintptr(rawptr(path)), uintptr(mask)))
 	} else { // NOTE: arm64 does not have access
 		return int(intrinsics.syscall(SYS_faccessat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mask)))
@@ -1613,16 +1641,60 @@ sys_chdir :: proc(path: cstring) -> int {
 	return int(intrinsics.syscall(SYS_chdir, uintptr(rawptr(path))))
 }
 
+sys_fchdir :: proc(fd: int) -> int {
+	return int(intrinsics.syscall(SYS_fchdir, uintptr(fd)))
+}
+
+sys_chmod :: proc(path: cstring, mode: int) -> int {
+	when ODIN_ARCH != .arm64 {
+		return int(intrinsics.syscall(SYS_chmod, uintptr(rawptr(path)), uintptr(mode)))
+	} else { // NOTE: arm64 does not have chmod
+		return int(intrinsics.syscall(SYS_fchmodat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mode)))
+	}
+}
+
+sys_fchmod :: proc(fd: int, mode: int) -> int {
+	return int(intrinsics.syscall(SYS_fchmod, uintptr(fd), uintptr(mode)))
+}
+
+sys_chown :: proc(path: cstring, user: int, group: int) -> int {
+	when ODIN_ARCH != .arm64 {
+		return int(intrinsics.syscall(SYS_chown, uintptr(rawptr(path)), uintptr(user), uintptr(group)))
+	} else { // NOTE: arm64 does not have chown
+		return int(intrinsics.syscall(SYS_fchownat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(user), uintptr(group), 0))
+	}
+}
+
+sys_fchown :: proc(fd: int, user: int, group: int) -> int {
+	return int(intrinsics.syscall(SYS_fchown, uintptr(fd), uintptr(user), uintptr(group)))
+}
+
+sys_lchown :: proc(path: cstring, user: int, group: int) -> int {
+	when ODIN_ARCH != .arm64 {
+		return int(intrinsics.syscall(SYS_lchown, uintptr(rawptr(path)), uintptr(user), uintptr(group)))
+	} else { // NOTE: arm64 does not have lchown
+		return int(intrinsics.syscall(SYS_fchownat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(user), uintptr(group), AT_SYMLINK_NOFOLLOW))
+	}
+}
+
 sys_rename :: proc(old, new: cstring) -> int {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(SYS_rename, uintptr(rawptr(old)), uintptr(rawptr(new))))
 	} else { // NOTE: arm64 does not have rename
 		return int(intrinsics.syscall(SYS_renameat, uintptr(AT_FDCWD), uintptr(rawptr(old)), uintptr(rawptr(new))))
 	}
 }
 
+sys_link :: proc(old_name: cstring, new_name: cstring) -> int {
+	when ODIN_ARCH != .arm64 {
+		return int(intrinsics.syscall(SYS_link, uintptr(rawptr(old_name)), uintptr(rawptr(new_name))))
+	} else { // NOTE: arm64 does not have link
+		return int(intrinsics.syscall(SYS_linkat, uintptr(AT_FDCWD), uintptr(rawptr(old_name)), uintptr(AT_FDCWD), uintptr(rawptr(new_name)), AT_SYMLINK_FOLLOW))
+	}
+}
+
 sys_unlink :: proc(path: cstring) -> int {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(SYS_unlink, uintptr(rawptr(path))))
 	} else { // NOTE: arm64 does not have unlink
 		return int(intrinsics.syscall(SYS_unlinkat, uintptr(AT_FDCWD), uintptr(rawptr(path), 0)))
@@ -1630,7 +1702,7 @@ sys_unlink :: proc(path: cstring) -> int {
 }
 
 sys_rmdir :: proc(path: cstring) -> int {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(SYS_rmdir, uintptr(rawptr(path))))
 	} else { // NOTE: arm64 does not have rmdir
 		return int(intrinsics.syscall(SYS_unlinkat, uintptr(AT_FDCWD), uintptr(rawptr(path)), AT_REMOVEDIR))
@@ -1638,14 +1710,40 @@ sys_rmdir :: proc(path: cstring) -> int {
 }
 
 sys_mkdir :: proc(path: cstring, mode: u32 = 0o775) -> int {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
 	} else { // NOTE: arm64 does not have mkdir
 		return int(intrinsics.syscall(SYS_mkdirat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mode)))
 	}
 }
 
-//TODO: ftruncate, symlink, readlink, fchdir, fchmod, chown, fchown, lchown
+sys_truncate :: proc(path: cstring, length: i64) -> int {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+		return int(intrinsics.syscall(SYS_truncate, uintptr(rawptr(path)), uintptr(length)))
+	} else {
+		low := uintptr(length & 0xFFFFFFFF)
+		high := uintptr(length >> 32)
+		return int(intrinsics.syscall(SYS_truncate64, uintptr(rawptr(path)), high, low))
+	}
+}
+
+sys_ftruncate :: proc(fd: int, length: i64) -> int {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+		return int(intrinsics.syscall(SYS_ftruncate, uintptr(fd), uintptr(length)))
+	} else {
+		low := uintptr(length & 0xFFFFFFFF)
+		high := uintptr(length >> 32)
+		return int(intrinsics.syscall(SYS_ftruncate64, uintptr(fd), high, low))
+	}
+}
+
+sys_fsync :: proc(fd: int) -> int {
+	return int(intrinsics.syscall(SYS_fsync, uintptr(fd)))
+}
+
+sys_getdents64 :: proc(fd: int, dirent: rawptr, count: int) -> int {
+	return int(intrinsics.syscall(SYS_getdents64, uintptr(fd), uintptr(dirent), uintptr(count)))
+}
 
 get_errno :: proc(res: int) -> i32 {
 	if res < 0 && res > -4096 {