From e51bb4ef12ff6e5cf900f266f6da9d132b7167eb Mon Sep 17 00:00:00 2001 From: CiD- Date: Thu, 3 Mar 2022 10:16:36 -0500 Subject: [PATCH] os2 linux begin --- core/os/os2/file_linux.odin | 236 ++++++++++++++++++++++++++++++ core/os/os2/stat_linux.odin | 116 +++++++++++++++ core/sys/unix/syscalls_linux.odin | 138 ++++++++++++++++- 3 files changed, 486 insertions(+), 4 deletions(-) create mode 100644 core/os/os2/file_linux.odin create mode 100644 core/os/os2/stat_linux.odin diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin new file mode 100644 index 000000000..75a71b22b --- /dev/null +++ b/core/os/os2/file_linux.odin @@ -0,0 +1,236 @@ +//+private +package os2 + +import "core:io" +import "core:time" +import "core:sys/unix" + + +_get_platform_error :: proc(res: int) -> Error { + errno := unix.get_errno(res) + return Platform_Error{i32(errno)} +} + +_ok_or_error :: proc(res: int) -> Error { + return res >= 0 ? nil : _get_platform_error(res) +} + +_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)) + } + return handle, nil +} + +_close :: proc(fd: Handle) -> Error { + res := unix.sys_close(int(fd)) + return _ok_or_error(res) +} + +_name :: proc(fd: Handle, allocator := context.allocator) -> string { + //TODO + return "" +} + +_seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) { + res := unix.sys_lseek(int(fd), offset, int(whence)) + if res < 0 { + return -1, _get_platform_error(int(res)) + } + return res, nil +} + +_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))) + if n < 0 { + return -1, unix.get_errno(n) + } + return bytes_read, 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 + for len(b) > 0 { + m := _read(fd, b) or_return + n += m + b = b[m:] + } + return +} + +_read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) { + //TODO + return +} + +_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))) + if n < 0 { + return -1, _get_platform_error(n) + } + return int(n), nil +} + +_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 + for len(b) > 0 { + m := _write(fd, b) or_return + n += m + b = b[m:] + } + return +} + +_write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) { + //TODO + return +} + +_file_size :: proc(fd: Handle) -> (n: i64, err: Error) { + s, err := _fstat(fd) or_return + if err != nil { + return 0, err + } + return max(s.size, 0), nil +} + +_sync :: proc(fd: Handle) -> Error { + return _ok_or_error(unix.sys_fsync(int(fd))) +} + +_flush :: proc(fd: Handle) -> Error { + return _ok_or_error(unix.sys_fsync(int(fd))) +} + +_truncate :: proc(fd: Handle, size: i64) -> Error { + return _ok_or_error(unix.sys_ftruncate(int(fd), size)) +} + +_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)) + } + return _ok_or_error(unix.sys_unlink(path_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)) +} + +_link :: 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_link(old_name_cstr, new_name_cstr)) +} + +_symlink :: 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_symlink(old_name_cstr, new_name_cstr)) +} + +_read_link :: proc(name: string, allocator := context.allocator) -> (string, Error) { + path_cstr := strings.clone_to_cstring(path) + defer delete(path_cstr) + + bufsz : uint = 256 + buf := make([]byte, bufsz, allocator) + for { + rc := unix.sys_readlink(path_cstr, &(buf[0]), bufsz) + if rc < 0 { + delete(buf) + return "", unix.get_errno(rc) + } else if rc == int(bufsz) { + bufsz *= 2 + delete(buf) + buf = make([]byte, bufsz, allocator) + } else { + return strings.string_from_ptr(&buf[0], rc), nil + } + } +} + +_unlink :: proc(path: string) -> Error { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + return _ok_or_error(unix.sys_unlink(path_cstr)) +} + +_chdir :: proc(fd: Handle) -> Error { + return _ok_or_error(unix.sys_fchdir(int(fd))) +} + +_chmod :: proc(fd: Handle, mode: File_Mode) -> Error { + //TODO + return nil +} + +_chown :: proc(fd: Handle, uid, gid: int) -> Error { + //TODO + return nil +} + +_lchown :: proc(name: string, uid, gid: int) -> Error { + //TODO + return nil +} + +_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { + //TODO + 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 +} + +_is_file :: proc(fd: Handle) -> bool { + s: OS_Stat + res := unix.sys_fstat(int(fd), rawptr(&s)) + if res < 0 { // error + return false + } + return S_ISREG(s.mode) +} + +_is_dir :: proc(fd: Handle) -> bool { + s: OS_Stat + res := unix.sys_fstat(int(fd), rawptr(&s)) + if res < 0 { // error + return false + } + return S_ISDIR(s.mode) +} diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin new file mode 100644 index 000000000..7fce8fb9c --- /dev/null +++ b/core/os/os2/stat_linux.odin @@ -0,0 +1,116 @@ +//+private +package os2 + +import "core:time" +import "core:sys/unix" + +// 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 +OS_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(fd: Handle, allocator := context.allocator) -> (File_Info, Error) { +} + +_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { +} + +_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { + cstr := strings.clone_to_cstring(path) + defer delete(cstr) + + s: OS_Stat + result := unix.sys_lstat(cstr, &s) + if result < 0 { + return {}, unix.get_errno(result) + } + + fi := File_Info { + fullpath = "", + name = "", + size = s.size, + mode = 0, + is_dir = S_ISDIR(s.mode), + creation_time = nil, // linux does not track this + //TODO + modification_time = nil, + access_time = nil, + } + + return fi, nil +} + +_same_file :: proc(fi1, fi2: File_Info) -> bool { + return fi1.fullpath == fi2.fullpath +} + +_stat_internal :: proc(name: string) -> (s: OS_Stat, res: int) { + name_cstr = strings.clone_to_cstring(name, context.temp_allocator) + res = unix.sys_stat(name_cstr, &s) + return +} diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 0082c7261..243f8accc 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/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 @@ -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 == .i386 { +} else when ODIN_ARCH == "386" { 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 false /*ODIN_ARCH == .arm*/ { // TODO +} else when ODIN_ARCH == "arm" { SYS_restart_syscall : uintptr : 0 SYS_exit : uintptr : 1 SYS_fork : uintptr : 2 @@ -1516,6 +1516,10 @@ when ODIN_ARCH == .amd64 { #panic("Unsupported architecture") } +AT_FDCWD :: -100 +AT_REMOVEDIR :: uintptr(0x200) +AT_SYMLINK_NOFOLLOW :: uintptr(0x100) + sys_gettid :: proc "contextless" () -> int { return cast(int)intrinsics.syscall(SYS_gettid) } @@ -1523,3 +1527,129 @@ sys_gettid :: proc "contextless" () -> int { sys_getrandom :: proc "contextless" (buf: ^byte, buflen: int, flags: uint) -> int { return cast(int)intrinsics.syscall(SYS_getrandom, buf, cast(uintptr)(buflen), cast(uintptr)(flags)) } + +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))) + } else { // NOTE: arm64 does not have open + res := 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 { + return int(intrinsics.syscall(SYS_close, uintptr(fd))) +} + +sys_read :: proc(fd: int, buf: rawptr, size: uint) -> int { + return int(intrinsics.syscall(SYS_read, uintptr(fd), uintptr(buf), uintptr(size))) +} + +sys_write :: proc(fd: int, buf: rawptr, size: uint) -> int { + return int(intrinsics.syscall(SYS_write, uintptr(fd), uintptr(buf), uintptr(size))) +} + +sys_lseek :: proc(fd: int, offset: i64, whence: int) -> i64 { + 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 + } +} + +sys_stat :: proc(path: cstring, stat: rawptr) -> int { + when ODIN_ARCH == "amd64" { + return int(intrinsics.syscall(SYS_stat, uintptr(rawptr(path)), uintptr(stat))) + } 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)) + } +} + +sys_fstat :: proc(fd: int, stat: rawptr) -> int { + 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))) + } +} + +sys_lstat :: proc(path: cstring, stat: rawptr) -> int { + when ODIN_ARCH == "amd64" { + return int(intrinsics.syscall(SYS_lstat, uintptr(rawptr(path)), uintptr(stat))) + } 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)) + } +} + +sys_readlink :: proc(path: cstring, buf: rawptr, bufsiz: uint) -> int { + 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_access :: proc(path: cstring, mask: int) -> int { + 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))) + } +} + +sys_getcwd :: proc(buf: rawptr, size: uint) -> int { + return int(intrinsics.syscall(SYS_getcwd, uintptr(buf), uintptr(size))) +} + +sys_chdir :: proc(path: cstring) -> int { + return int(intrinsics.syscall(SYS_chdir, uintptr(rawptr(path)))) +} + +sys_rename :: proc(old, new: cstring) -> int { + 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_unlink :: proc(path: cstring) -> int { + 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))) + } +} + +sys_rmdir :: proc(path: cstring) -> int { + 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)) + } +} + +sys_mkdir :: proc(path: cstring, mode: u32 = 0o775) -> int { + 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 + +get_errno :: proc(res: int) -> i32 { + if res < 0 && res > -4096 { + return i32(-res) + } + return 0 +}