From 345032f804752306f4d29fe0f298643c7ad23b30 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 21 Feb 2022 12:35:52 +0000 Subject: [PATCH 01/28] Get env stuff working on Windows --- core/os/os2/env.odin | 15 ++------ core/os/os2/env_windows.odin | 72 +++++++++++++++++++++-------------- core/os/os2/errors.odin | 56 ++++++++++++++++----------- core/os/os2/file.odin | 31 ++++++++++----- core/os/os2/file_windows.odin | 2 +- core/os/os2/user.odin | 22 +++++------ 6 files changed, 112 insertions(+), 86 deletions(-) diff --git a/core/os/os2/env.odin b/core/os/os2/env.odin index f25290a59..f1a3e40c7 100644 --- a/core/os/os2/env.odin +++ b/core/os/os2/env.odin @@ -1,20 +1,11 @@ package os2 -// get_env retrieves the value of the environment variable named by the key -// It returns the value, which will be empty if the variable is not present -// To distinguish between an empty value and an unset value, use lookup_env -// NOTE: the value will be allocated with the supplied allocator -get_env :: proc(key: string, allocator := context.allocator) -> string { - value, _ := lookup_env(key, allocator) - return value -} - -// lookup_env gets the value of the environment variable named by the key +// get_env gets the value of the environment variable named by the key // If the variable is found in the environment the value (which can be empty) is returned and the boolean is true // Otherwise the returned value will be empty and the boolean will be false // NOTE: the value will be allocated with the supplied allocator -lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { - return _lookup_env(key, allocator) +get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + return _get_env(key, allocator) } // set_env sets the value of the environment variable named by the key diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin index af04db858..a3b97375b 100644 --- a/core/os/os2/env_windows.odin +++ b/core/os/os2/env_windows.odin @@ -1,50 +1,56 @@ //+private package os2 +import "core:runtime" import "core:mem" import win32 "core:sys/windows" -_lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { +_get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { if key == "" { return } wkey := win32.utf8_to_wstring(key) - b := make([dynamic]u16, 100, context.temp_allocator) - for { - n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) - if n == 0 { - err := win32.GetLastError() - if err == win32.ERROR_ENVVAR_NOT_FOUND { - return "", false - } - } - if n <= u32(len(b)) { - value = win32.utf16_to_utf8(b[:n], allocator) - found = true - return - } - - resize(&b, len(b)*2) + // https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew + buf_len := win32.GetEnvironmentVariableW(wkey, nil, 0) + if buf_len == 0 { + return } + buf := make([dynamic]u16, buf_len, context.temp_allocator) + n := win32.GetEnvironmentVariableW(wkey, raw_data(buf), buf_len) + if n == 0 { + if win32.GetLastError() == win32.ERROR_ENVVAR_NOT_FOUND { + return "", false + } + value = "" + found = true + return + } + + value = win32.utf16_to_utf8(buf[:n], allocator) + found = true + return } _set_env :: proc(key, value: string) -> bool { k := win32.utf8_to_wstring(key) v := win32.utf8_to_wstring(value) + // https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew return bool(win32.SetEnvironmentVariableW(k, v)) } _unset_env :: proc(key: string) -> bool { k := win32.utf8_to_wstring(key) + + // https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew return bool(win32.SetEnvironmentVariableW(k, nil)) } _clear_env :: proc() { envs := environ(context.temp_allocator) for env in envs { - for j in 1.. Maybe(Path_Error) { +_mkdir :: proc(name: string, perm: File_Mode) -> Error { return nil } -_mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) { +_mkdir_all :: proc(path: string, perm: File_Mode) -> Error { // TODO(bill): _mkdir_all for windows return nil } -_remove_all :: proc(path: string) -> Maybe(Path_Error) { +_remove_all :: proc(path: string) -> Error { // TODO(bill): _remove_all for windows return nil } diff --git a/core/os/os2/pipe_windows.odin b/core/os/os2/pipe_windows.odin index 5570ca282..628b4c836 100644 --- a/core/os/os2/pipe_windows.odin +++ b/core/os/os2/pipe_windows.odin @@ -4,8 +4,12 @@ package os2 import win32 "core:sys/windows" _pipe :: proc() -> (r, w: Handle, err: Error) { + sa: win32.SECURITY_ATTRIBUTES + sa.nLength = size_of(win32.SECURITY_ATTRIBUTES) + sa.bInheritHandle = true + p: [2]win32.HANDLE - if !win32.CreatePipe(&p[0], &p[1], nil, 0) { + if !win32.CreatePipe(&p[0], &p[1], &sa, 0) { return 0, 0, Platform_Error{i32(win32.GetLastError())} } return Handle(p[0]), Handle(p[1]), nil diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index 19f1453ff..34d0de1c5 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -24,15 +24,15 @@ file_info_delete :: proc(fi: File_Info, allocator := context.allocator) { delete(fi.fullpath, allocator) } -fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { +fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Error) { return _fstat(fd, allocator) } -stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { +stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { return _stat(name, allocator) } -lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { +lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { return _lstat(name, allocator) } diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index f46a9435c..23ab8a44a 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -4,9 +4,9 @@ package os2 import "core:time" import win32 "core:sys/windows" -_fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { +_fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Error) { if fd == 0 { - return {}, Path_Error{err = .Invalid_Argument} + return {}, .Invalid_Argument } context.allocator = allocator @@ -27,10 +27,10 @@ _fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Maybe( return _file_info_from_get_file_information_by_handle(path, h) } -_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { +_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS) } -_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { +_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT) } _same_file :: proc(fi1, fi2: File_Info) -> bool { @@ -38,13 +38,7 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool { } - -_stat_errno :: proc(errno: win32.DWORD) -> Path_Error { - return Path_Error{err = Platform_Error{i32(errno)}} -} - - -full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Maybe(Path_Error)) { +full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Error) { context.allocator = allocator name := name @@ -57,7 +51,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) if n == 0 { delete(buf) - return "", _stat_errno(win32.GetLastError()) + return "", _get_platform_error() } if n <= u32(len(buf)) { return win32.utf16_to_utf8(buf[:n]), nil @@ -69,9 +63,9 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa } -internal_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Maybe(Path_Error)) { +internal_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Error) { if len(name) == 0 { - return {}, Path_Error{err = .Not_Exist} + return {}, .Not_Exist } context.allocator = allocator @@ -91,7 +85,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co fd: win32.WIN32_FIND_DATAW sh := win32.FindFirstFileW(wname, &fd) if sh == win32.INVALID_HANDLE_VALUE { - e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}} + e = _get_platform_error() return } win32.FindClose(sh) @@ -101,7 +95,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil) if h == win32.INVALID_HANDLE_VALUE { - e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}} + e = _get_platform_error() return } defer win32.CloseHandle(h) @@ -130,9 +124,9 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { } -_cleanpath_from_handle :: proc(fd: Handle) -> (string, Maybe(Path_Error)) { +_cleanpath_from_handle :: proc(fd: Handle) -> (string, Error) { if fd == 0 { - return "", Path_Error{err = .Invalid_Argument} + return "", .Invalid_Argument } h := win32.HANDLE(fd) @@ -143,7 +137,7 @@ _cleanpath_from_handle :: proc(fd: Handle) -> (string, Maybe(Path_Error)) { err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0) switch err { case win32.ERROR_PATH_NOT_FOUND, win32.ERROR_INVALID_PARAMETER: - return "", _stat_errno(err) + return "", Platform_Error{i32(err)} case win32.ERROR_NOT_ENOUGH_MEMORY: MAX_PATH = MAX_PATH*2 + 1 continue @@ -153,9 +147,9 @@ _cleanpath_from_handle :: proc(fd: Handle) -> (string, Maybe(Path_Error)) { return _cleanpath_from_buf(buf), nil } -_cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Maybe(Path_Error)) { +_cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Error) { if fd == 0 { - return nil, Path_Error{err = .Invalid_Argument} + return nil, .Invalid_Argument } h := win32.HANDLE(fd) @@ -166,7 +160,7 @@ _cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Maybe(Path_Error)) { err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0) switch err { case win32.ERROR_PATH_NOT_FOUND, win32.ERROR_INVALID_PARAMETER: - return nil, _stat_errno(err) + return nil, Platform_Error{i32(err)} case win32.ERROR_NOT_ENOUGH_MEMORY: MAX_PATH = MAX_PATH*2 + 1 continue @@ -251,7 +245,7 @@ _file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HA } -_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Maybe(Path_Error)) { +_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Error) { fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) fi.mode |= _file_mode_from_file_attributes(d.dwFileAttributes, nil, 0) @@ -268,7 +262,7 @@ _file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE } -_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Maybe(Path_Error)) { +_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Error) { fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) fi.mode |= _file_mode_from_file_attributes(d.dwFileAttributes, nil, 0) @@ -285,10 +279,10 @@ _file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string } -_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Maybe(Path_Error)) { +_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Error) { d: win32.BY_HANDLE_FILE_INFORMATION if !win32.GetFileInformationByHandle(h, &d) { - return {}, _stat_errno(win32.GetLastError()) + return {}, _get_platform_error() } @@ -296,7 +290,7 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) { err := win32.GetLastError() if err != win32.ERROR_INVALID_PARAMETER { - return {}, _stat_errno(err) + return {}, Platform_Error{i32(err)} } // Indicate this is a symlink on FAT file systems ti.ReparseTag = 0 diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 8c58fbd52..a530a402a 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -53,6 +53,7 @@ foreign kernel32 { LeaveCriticalSection :: proc(CriticalSection: ^CRITICAL_SECTION) --- DeleteCriticalSection :: proc(CriticalSection: ^CRITICAL_SECTION) --- + PathFileExistsW :: proc(lpPathName: LPCWSTR) -> BOOL --- RemoveDirectoryW :: proc(lpPathName: LPCWSTR) -> BOOL --- SetFileAttributesW :: proc(lpFileName: LPCWSTR, dwFileAttributes: DWORD) -> BOOL --- SetLastError :: proc(dwErrCode: DWORD) --- From 6630d703f82bf06849727ba2a62d38c56366c433 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 21 Feb 2022 13:42:29 +0000 Subject: [PATCH 03/28] Clean up ok or error handling --- core/os/os2/file_windows.odin | 71 ++++++++++------------------------- 1 file changed, 20 insertions(+), 51 deletions(-) diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index d0cf4505d..9fdbd9a5a 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -15,6 +15,10 @@ _get_platform_error :: proc() -> Error { return Platform_Error{i32(err)} } +_ok_or_error :: proc(ok: win32.BOOL) -> Error { + return nil if ok else _get_platform_error() +} + _std_handle :: proc(kind: Std_Handle_Kind) -> Handle { get_handle :: proc(h: win32.DWORD) -> Handle { fd := win32.GetStdHandle(h) @@ -88,17 +92,13 @@ _close :: proc(fd: Handle) -> Error { hnd := win32.HANDLE(fd) file_info: win32.BY_HANDLE_FILE_INFORMATION - if ok := win32.GetFileInformationByHandle(hnd, &file_info); !ok { - return _get_platform_error() - } + _ok_or_error(win32.GetFileInformationByHandle(hnd, &file_info)) or_return + if file_info.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { return nil } - if ok := win32.CloseHandle(hnd); !ok { - return _get_platform_error() - } - return nil + return _ok_or_error(win32.CloseHandle(hnd)) } _name :: proc(fd: Handle, allocator := context.allocator) -> string { @@ -145,10 +145,7 @@ _read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) { max_read := u32(min(BUF_SIZE, len(b)/4)) single_read_length: u32 - ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil) - if !ok { - err = _get_platform_error() - } + err = _ok_or_error(win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)) buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length]) src := buf8[:buf8_len] @@ -245,9 +242,7 @@ _pread :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { h := win32.HANDLE(fd) done: win32.DWORD - if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) { - _get_platform_error() or_return - } + _ok_or_error(win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o)) or_return return int(done), nil } @@ -267,9 +262,7 @@ _pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { h := win32.HANDLE(fd) done: win32.DWORD - if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) { - _get_platform_error() or_return - } + _ok_or_error(win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o)) or_return return int(done), nil } @@ -298,9 +291,7 @@ _write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) { _file_size :: proc(fd: Handle) -> (n: i64, err: Error) { length: win32.LARGE_INTEGER - if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) { - err = _get_platform_error() - } + err = _ok_or_error(win32.GetFileSizeEx(win32.HANDLE(fd), &length)) return i64(length), err } @@ -310,34 +301,24 @@ _sync :: proc(fd: Handle) -> Error { } _flush :: proc(fd: Handle) -> Error { - if !win32.FlushFileBuffers(win32.HANDLE(fd)) { - return _get_platform_error() - } - return nil + return _ok_or_error(win32.FlushFileBuffers(win32.HANDLE(fd))) } _truncate :: proc(fd: Handle, size: i64) -> Error { offset := seek(fd, size, .Start) or_return defer seek(fd, offset, .Start) - if !win32.SetEndOfFile(win32.HANDLE(fd)) { - return _get_platform_error() - } - return nil + return _ok_or_error(win32.SetEndOfFile(win32.HANDLE(fd))) } _remove :: proc(name: string) -> Error { p := win32.utf8_to_wstring(_fix_long_path(name)) - err, err1: Error - if !win32.DeleteFileW(p) { - err = _get_platform_error() - } + + err := _ok_or_error(win32.DeleteFileW(p)) if err == nil { return nil } - if !win32.RemoveDirectoryW(p) { - err1 = _get_platform_error() - } + err1 := _ok_or_error(win32.RemoveDirectoryW(p)) if err1 == nil { return nil } @@ -351,10 +332,7 @@ _remove :: proc(name: string) -> Error { err = err1 } else if a & win32.FILE_ATTRIBUTE_READONLY != 0 { if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) { - err = nil - if !win32.DeleteFileW(p) { - err = _get_platform_error() - } + err = _ok_or_error(win32.DeleteFileW(p)) } } } @@ -366,20 +344,14 @@ _remove :: proc(name: string) -> Error { _rename :: proc(old_path, new_path: string) -> Error { from := win32.utf8_to_wstring(old_path, context.temp_allocator) to := win32.utf8_to_wstring(new_path, context.temp_allocator) - if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) { - return _get_platform_error() - } - return nil + return _ok_or_error(win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING)) } _link :: proc(old_name, new_name: string) -> Error { n := win32.utf8_to_wstring(_fix_long_path(new_name)) o := win32.utf8_to_wstring(_fix_long_path(old_name)) - if !win32.CreateHardLinkW(n, o, nil) { - return _get_platform_error() - } - return nil + return _ok_or_error(win32.CreateHardLinkW(n, o, nil)) } _symlink :: proc(old_name, new_name: string) -> Error { @@ -392,10 +364,7 @@ _read_link :: proc(name: string) -> (string, Error) { _unlink :: proc(path: string) -> Error { wpath := win32.utf8_to_wstring(path, context.temp_allocator) - if !win32.DeleteFileW(wpath) { - return _get_platform_error() - } - return nil + return _ok_or_error(win32.DeleteFileW(wpath)) } From e51bb4ef12ff6e5cf900f266f6da9d132b7167eb Mon Sep 17 00:00:00 2001 From: CiD- Date: Thu, 3 Mar 2022 10:16:36 -0500 Subject: [PATCH 04/28] 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 +} From 658a605c75c32746f248dc8e8e367c6b5dae976e Mon Sep 17 00:00:00 2001 From: jasonkercher Date: Fri, 4 Mar 2022 17:11:53 -0500 Subject: [PATCH 05/28] compiles --- core/os/os2/env_linux.odin | 28 +++++ core/os/os2/errors_linux.odin | 134 +++++++++++++++++++++++ core/os/os2/file.odin | 4 + core/os/os2/file_linux.odin | 169 ++++++++++++++++++++---------- core/os/os2/file_windows.odin | 5 + core/os/os2/heap_linux.odin | 27 +++++ core/os/os2/path_linux.odin | 85 +++++++++++++++ core/os/os2/pipe_linux.odin | 7 ++ core/os/os2/stat_linux.odin | 22 ++-- core/os/os2/temp_file_linux.odin | 18 ++++ core/sys/unix/syscalls_linux.odin | 146 +++++++++++++++++++++----- 11 files changed, 557 insertions(+), 88 deletions(-) create mode 100644 core/os/os2/env_linux.odin create mode 100644 core/os/os2/errors_linux.odin create mode 100644 core/os/os2/heap_linux.odin create mode 100644 core/os/os2/path_linux.odin create mode 100644 core/os/os2/pipe_linux.odin create mode 100644 core/os/os2/temp_file_linux.odin diff --git a/core/os/os2/env_linux.odin b/core/os/os2/env_linux.odin new file mode 100644 index 000000000..1833ac4dc --- /dev/null +++ b/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 +} + + diff --git a/core/os/os2/errors_linux.odin b/core/os/os2/errors_linux.odin new file mode 100644 index 000000000..f074c7c86 --- /dev/null +++ b/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" +} diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index 707df37a2..09e1e8daf 100644 --- a/core/os/os2/file.odin +++ b/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 { diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 75a71b22b..72fbdcb56 100644 --- a/core/os/os2/file_linux.odin +++ b/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) } -_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)) +_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) } - return handle, nil + 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(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(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 } diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 9fdbd9a5a..dd33d8a53 100644 --- a/core/os/os2/file_windows.odin +++ b/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 { diff --git a/core/os/os2/heap_linux.odin b/core/os/os2/heap_linux.odin new file mode 100644 index 000000000..f617f8cc8 --- /dev/null +++ b/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 +} diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin new file mode 100644 index 000000000..b45d6e976 --- /dev/null +++ b/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)) +} diff --git a/core/os/os2/pipe_linux.odin b/core/os/os2/pipe_linux.odin new file mode 100644 index 000000000..0699c5720 --- /dev/null +++ b/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 +} + diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index 7fce8fb9c..c4cc5fe8d 100644 --- a/core/os/os2/stat_linux.odin +++ b/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 } diff --git a/core/os/os2/temp_file_linux.odin b/core/os/os2/temp_file_linux.odin new file mode 100644 index 000000000..d56bc34d3 --- /dev/null +++ b/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 "" +} diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 243f8accc..ccd8a75e6 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 @@ -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 { From 1f19610fd67b00b49cc9d726af2e8d9ac5f4807b Mon Sep 17 00:00:00 2001 From: jasonkercher Date: Mon, 7 Mar 2022 17:16:03 -0500 Subject: [PATCH 06/28] added _remove_all --- core/os/os2/file_linux.odin | 44 ++++++------- core/os/os2/path_linux.odin | 101 ++++++++++++++++++++++++++++-- core/sys/unix/syscalls_linux.odin | 8 +++ 3 files changed, 126 insertions(+), 27 deletions(-) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 72fbdcb56..a88515b0e 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -25,23 +25,23 @@ _std_handle :: proc(kind: Std_Handle_Kind) -> Handle { 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 +__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 + flags := __O_RDONLY|__O_NONBLOCK|__O_DIRECTORY|__O_LARGEFILE|__O_CLOEXEC handle_i := unix.sys_open(cstr, flags) if handle_i < 0 { @@ -56,17 +56,17 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Erro 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 + 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)) + 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 { diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index b45d6e976..31abf5bf8 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -1,7 +1,6 @@ //+private package os2 -import "core:fmt" import "core:strings" import "core:sys/unix" import "core:path/filepath" @@ -9,6 +8,8 @@ import "core:path/filepath" _Path_Separator :: '/' _Path_List_Separator :: ':' +DIRECTORY_FLAGS :: __O_RDONLY|__O_NONBLOCK|__O_DIRECTORY|__O_LARGEFILE|__O_CLOEXEC + _is_path_separator :: proc(c: byte) -> bool { return c == '/' } @@ -53,9 +54,99 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { return _mkdir_all_stat(path, &s, perm) } +dirent64 :: struct { + d_ino: u64, + d_off: u64, + d_reclen: u16, + d_type: u8, + d_name: [1]u8, +} + +DT_UNKNOWN :: 0 +DT_FIFO :: 1 +DT_CHR :: 2 +DT_DIR :: 4 +DT_BLK :: 6 +DT_REG :: 8 +DT_LNK :: 10 +DT_SOCK :: 12 +DT_WHT :: 14 + _remove_all :: proc(path: string) -> Error { - // TODO - return nil + _remove_all_dir :: proc(dfd: Handle) -> Error { + n := 64 + buf := make([]u8, n) + defer delete(buf) + + loop: for { + res := unix.sys_getdents64(int(dfd), &buf[0], n) + switch res { + case -22: //-EINVAL + n *= 2 + buf = make([]u8, n) + continue loop + case -4096..<0: + return _get_platform_error(res) + case 0: + break loop + } + + d: ^dirent64 + + for i := 0; i < res; i += int(d.d_reclen) { + description: string + d = (^dirent64)(rawptr(&buf[i])) + d_name_cstr := cstring(&d.d_name[0]) + + buf_len := uintptr(d.d_reclen) - offset_of(d.d_name) + + /* check for current directory (.) */ + #no_bounds_check if buf_len > 1 && d.d_name[0] == '.' && d.d_name[1] == 0 { + continue + } + + /* check for parent directory (..) */ + #no_bounds_check if buf_len > 2 && d.d_name[0] == '.' && d.d_name[1] == '.' && d.d_name[2] == 0 { + continue + } + + res: int + + switch d.d_type { + case DT_DIR: + handle_i := unix.sys_openat(int(dfd), d_name_cstr, DIRECTORY_FLAGS) + if handle_i < 0 { + return _get_platform_error(handle_i) + } + defer unix.sys_close(handle_i) + _remove_all_dir(Handle(handle_i)) or_return + res = unix.sys_unlinkat(int(dfd), d_name_cstr, int(unix.AT_REMOVEDIR)) + case: + res = unix.sys_unlinkat(int(dfd), d_name_cstr) + } + + if res < 0 { + return _get_platform_error(res) + } + } + } + return nil + } + + cstr := strings.clone_to_cstring(path, context.temp_allocator) + + handle_i := unix.sys_open(cstr, DIRECTORY_FLAGS) + switch handle_i { + case -ENOTDIR: + return _ok_or_error(unix.sys_unlink(cstr)) + case -4096..<0: + return _get_platform_error(handle_i) + } + + fd := Handle(handle_i) + defer close(fd) + _remove_all_dir(fd) or_return + return _ok_or_error(unix.sys_rmdir(cstr)) } _getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) { @@ -71,8 +162,8 @@ _getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) { 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) + if res != -ERANGE { + return "", _get_platform_error(res) } resize(&buf, len(buf)+PATH_MAX) } diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index ccd8a75e6..889dd3b90 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -1537,6 +1537,10 @@ sys_open :: proc(path: cstring, flags: int, mode: int = 0o000) -> int { } } +sys_openat :: proc(dfd: int, path: cstring, flags: int, mode: int = 0o000) -> int { + return int(intrinsics.syscall(SYS_openat, uintptr(dfd), uintptr(rawptr(path)), uintptr(flags), uintptr(mode))) +} + sys_close :: proc(fd: int) -> int { return int(intrinsics.syscall(SYS_close, uintptr(fd))) } @@ -1701,6 +1705,10 @@ sys_unlink :: proc(path: cstring) -> int { } } +sys_unlinkat :: proc(dfd: int, path: cstring, flag: int = 0) -> int { + return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), flag)) +} + sys_rmdir :: proc(path: cstring) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(SYS_rmdir, uintptr(rawptr(path)))) From 832003dd4b20b3581e22f57ffef72e67d6cb7146 Mon Sep 17 00:00:00 2001 From: CiD- Date: Tue, 8 Mar 2022 17:15:45 -0500 Subject: [PATCH 07/28] os2 tests --- core/os/os2/errors_linux.odin | 11 +++++++++++ core/os/os2/file.odin | 4 ---- core/os/os2/file_linux.odin | 29 +---------------------------- tests/core/Makefile | 7 +++++-- 4 files changed, 17 insertions(+), 34 deletions(-) diff --git a/core/os/os2/errors_linux.odin b/core/os/os2/errors_linux.odin index f074c7c86..d9056bd6b 100644 --- a/core/os/os2/errors_linux.odin +++ b/core/os/os2/errors_linux.odin @@ -1,6 +1,8 @@ //+private package os2 +import "core:sys/unix" + EPERM :: 1 ENOENT :: 2 ESRCH :: 3 @@ -126,6 +128,15 @@ 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)} +} + +_ok_or_error :: proc(res: int) -> Error { + return res >= 0 ? nil : _get_platform_error(res) +} + _error_string :: proc(errno: i32) -> string { if errno == 0 { return "" diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index 09e1e8daf..707df37a2 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -61,10 +61,6 @@ 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 { diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index a88515b0e..db0e2efa8 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -7,22 +7,8 @@ import "core:strings" 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) -} - _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() + return Handle(kind) } __O_RDONLY :: 0o0 @@ -38,19 +24,6 @@ __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(name, context.temp_allocator) diff --git a/tests/core/Makefile b/tests/core/Makefile index 1c2cee6bd..0c3e1e09a 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -1,7 +1,7 @@ ODIN=../../odin PYTHON=$(shell which python3) -all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test +all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test os2_test download_test_assets: $(PYTHON) download_assets.py @@ -22,4 +22,7 @@ crypto_test: $(ODIN) run crypto -out=crypto_hash -o:speed -no-bounds-check noise_test: - $(ODIN) run math/noise -out=test_noise \ No newline at end of file + $(ODIN) run math/noise -out=test_noise + +os2_test: + $(ODIN) run os2/test_os2.odin -out=test_os2 From bad295cf695e623591f737fb68556ea0a76a53ce Mon Sep 17 00:00:00 2001 From: CiD- Date: Thu, 10 Mar 2022 09:23:33 -0500 Subject: [PATCH 08/28] add test directory... --- tests/core/os2/test_os2.odin | 170 +++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 tests/core/os2/test_os2.odin diff --git a/tests/core/os2/test_os2.odin b/tests/core/os2/test_os2.odin new file mode 100644 index 000000000..7fa0dd20d --- /dev/null +++ b/tests/core/os2/test_os2.odin @@ -0,0 +1,170 @@ +package test_os2 + +import "core:fmt" +import "core:os/os2" +import "core:sys/unix" +import "core:testing" +import "core:intrinsics" + +TEST_count := 0 +TEST_fail := 0 + +when ODIN_TEST { + expect :: testing.expect + log :: testing.log +} else { + expect_value :: proc(t: ^testing.T, value, expected: $T, loc := #caller_location) where intrinsics.type_is_comparable(T) { + fmt.printf("[%v] ", loc) + TEST_count += 1 + ok := value == expected + if !ok { + fmt.printf("expected %v, got %v", expected, value) + TEST_fail += 1 + return + } + fmt.println(" PASS") + } + + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + fmt.printf("[%v] ", loc) + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.println(message) + return + } + fmt.println(" PASS") + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } +} + +main :: proc() +{ + t: testing.T + file_test(&t) + path_test(&t) + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) +} + +@private +_expect_no_error :: proc(t: ^testing.T, e: os2.Error, loc := #caller_location) { + expect(t, e == nil, "unexpected error", loc) +} + + +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 + +@test +file_test :: proc(t: ^testing.T) { + + /* Things to test: + * std_handle,create,open,close,name,seek,read,read_at,read_from,write,write_at, + * write_to,file_size,sync,flush,truncate,remove,rename,link,symlink,read_link, + * unlink,chdir,chmod,chown,lchown,chtimes,exists,is_file,is_dir + */ + + stdin := os2.std_handle(.stdin) + expect_value(t, stdin, 0) + stdout := os2.std_handle(.stdout) + expect_value(t, stdout, 1) + stderr := os2.std_handle(.stderr) + expect_value(t, stderr, 2) + + fd, err := os2.open("filethatdoesntexist.txt") + expect(t, err != nil, "missing error") + expect_value(t, fd, os2.INVALID_HANDLE) + + fd, err = os2.open("write.txt", {.Write, .Create, .Trunc}, 0o664) + _expect_no_error(t, err) + expect(t, fd != os2.INVALID_HANDLE, "unexpected handle") + + s1 := "hello" + b1 := transmute([]u8)s1 + + n: int + n, err = os2.write_at(fd, b1, 10) + _expect_no_error(t, err) + expect_value(t, n, 5) + + s2 := "abcdefghij" + b2 := transmute([]u8)s2 + + n, err = os2.write(fd, b2) + _expect_no_error(t, err) + expect_value(t, n, 10) + + _expect_no_error(t, os2.sync(fd)) + _expect_no_error(t, os2.close(fd)) + + fd, err = os2.open("write.txt") + _expect_no_error(t, err) + + buf: [32]u8 + + n, err = os2.read(fd, buf[:]) + _expect_no_error(t, err) + expect_value(t, n, 15) + expect_value(t, string(buf[:n]), "abcdefghijhello") + + n, err = os2.read_at(fd, buf[0:2], 1) + _expect_no_error(t, err) + expect_value(t, n, 2) + expect_value(t, string(buf[0:2]), "bc") + + _expect_no_error(t, os2.close(fd)) +} + +@test +path_test :: proc(t: ^testing.T) { + err: os2.Error + if os2.exists("a") { + err = os2.remove_all("a") + _expect_no_error(t, err) + } + + err = os2.mkdir_all("a/b/c/d", 0) + _expect_no_error(t, err) + + expect(t, os2.exists("a"), "directory does not exist") + + fd: os2.Handle + fd, err = os2.create("a/b/c/file.txt", 0o644) + _expect_no_error(t, err) + + err = os2.close(fd) + _expect_no_error(t, err) + + expect(t, unix.sys_access("a/b/c/file.txt", X_OK) < 0, "unexpected exec permission") + + err = os2.rename("a/b/c/file.txt", "a/b/file.txt") + _expect_no_error(t, err) + + expect(t, unix.sys_access("a/b/c/file.txt", F_OK) < 0, "unexpected exec permission") + + err = os2.symlink("b/c/d", "a/symlink_to_d") + _expect_no_error(t, err) + + symlink: string + symlink, err = os2.read_link("a/symlink_to_d") + _expect_no_error(t, err) + expect_value(t, symlink, "b/c/d") + + fd, err = os2.create("a/symlink_to_d/shnt.txt", 0o744) + _expect_no_error(t, err) + + err = os2.close(fd) + _expect_no_error(t, err) + + expect_value(t, unix.sys_access("a/b/c/d/shnt.txt", X_OK | R_OK | W_OK), 0) + + err = os2.remove_all("a") + _expect_no_error(t, err) + + expect(t, !os2.exists("a"), "directory a exists") +} From 0b61215f7bf056e3ae4b5619542481c2a1b3fdc0 Mon Sep 17 00:00:00 2001 From: Jason Kercher Date: Thu, 10 Mar 2022 11:12:06 -0500 Subject: [PATCH 09/28] getting tests to run --- core/os/os2/env_windows.odin | 4 ++-- core/os/os2/file_windows.odin | 5 ----- tests/core/Makefile | 32 ++++++++++++++++++++++++++++++++ tests/core/build.bat | 7 ++++++- tests/core/os2/test_os2.odin | 18 ++++++++++++++---- 5 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 tests/core/Makefile diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin index a3b97375b..1e1ffba4d 100644 --- a/core/os/os2/env_windows.odin +++ b/core/os/os2/env_windows.odin @@ -1,8 +1,8 @@ //+private package os2 -import "core:runtime" -import "core:mem" +//import "core:runtime" +//import "core:mem" import win32 "core:sys/windows" _get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index dd33d8a53..9fdbd9a5a 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -36,11 +36,6 @@ _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 { diff --git a/tests/core/Makefile b/tests/core/Makefile new file mode 100644 index 000000000..82bcae068 --- /dev/null +++ b/tests/core/Makefile @@ -0,0 +1,32 @@ +ODIN=../../odin +PYTHON=$(shell which python3) + +all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test os2_test + +download_test_assets: + $(PYTHON) download_assets.py + +image_test: + $(ODIN) run image/test_core_image.odin + +compress_test: + $(ODIN) run compress/test_core_compress.odin + +strings_test: + $(ODIN) run strings/test_core_strings.odin + +hash_test: + $(ODIN) run hash -out=test_hash -o:speed -no-bounds-check + +crypto_test: + $(ODIN) run crypto -out=crypto_hash -o:speed -no-bounds-check + +noise_test: + $(ODIN) run math/noise -out=test_noise + +os2_test: + $(ODIN) run os2/test_os2.odin -out=test_os2 + +encoding_test: + $(ODIN) run encoding/json -out=test_json + $(ODIN) run encoding/varint -out=test_varint diff --git a/tests/core/build.bat b/tests/core/build.bat index 0227ac6bb..8cf6486d3 100644 --- a/tests/core/build.bat +++ b/tests/core/build.bat @@ -41,4 +41,9 @@ echo --- echo --- echo Running core:math/noise tests echo --- -%PATH_TO_ODIN% run math/noise %COMMON% \ No newline at end of file +%PATH_TO_ODIN% run math/noise %COMMON% + +echo --- +echo Running core:os/os2 tests +echo --- +%PATH_TO_ODIN% run os2 %COMMON% diff --git a/tests/core/os2/test_os2.odin b/tests/core/os2/test_os2.odin index 7fa0dd20d..f8ef133a5 100644 --- a/tests/core/os2/test_os2.odin +++ b/tests/core/os2/test_os2.odin @@ -2,10 +2,14 @@ package test_os2 import "core:fmt" import "core:os/os2" -import "core:sys/unix" import "core:testing" import "core:intrinsics" +// really only want sys_access for more finite testing +when ODIN_OS == .Linux { + import "core:sys/unix" +} + TEST_count := 0 TEST_fail := 0 @@ -140,12 +144,16 @@ path_test :: proc(t: ^testing.T) { err = os2.close(fd) _expect_no_error(t, err) - expect(t, unix.sys_access("a/b/c/file.txt", X_OK) < 0, "unexpected exec permission") + when ODIN_OS == .Linux { + expect(t, unix.sys_access("a/b/c/file.txt", X_OK) < 0, "unexpected exec permission") + } err = os2.rename("a/b/c/file.txt", "a/b/file.txt") _expect_no_error(t, err) - expect(t, unix.sys_access("a/b/c/file.txt", F_OK) < 0, "unexpected exec permission") + when ODIN_OS == .Linux { + expect(t, unix.sys_access("a/b/c/file.txt", F_OK) < 0, "unexpected exec permission") + } err = os2.symlink("b/c/d", "a/symlink_to_d") _expect_no_error(t, err) @@ -161,7 +169,9 @@ path_test :: proc(t: ^testing.T) { err = os2.close(fd) _expect_no_error(t, err) - expect_value(t, unix.sys_access("a/b/c/d/shnt.txt", X_OK | R_OK | W_OK), 0) + when ODIN_OS == .Linux { + expect_value(t, unix.sys_access("a/b/c/d/shnt.txt", X_OK | R_OK | W_OK), 0) + } err = os2.remove_all("a") _expect_no_error(t, err) From e008b5a1602d669c865ac3ad42bc6f65b34d8012 Mon Sep 17 00:00:00 2001 From: "U-JSM\\jkercher" Date: Fri, 11 Mar 2022 10:47:59 -0500 Subject: [PATCH 10/28] build os2 test on windows --- tests/core/build.bat | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/core/build.bat b/tests/core/build.bat index 8cf6486d3..27b444e44 100644 --- a/tests/core/build.bat +++ b/tests/core/build.bat @@ -46,4 +46,5 @@ echo --- echo --- echo Running core:os/os2 tests echo --- -%PATH_TO_ODIN% run os2 %COMMON% +Rem Needed Shlwapi.lib for PathFileExistsW +%PATH_TO_ODIN% run os2 %COMMON% -extra-linker-flags:Shlwapi.lib From c293e88f2e31bfed896ddba701bdc2629497005a Mon Sep 17 00:00:00 2001 From: CiD- Date: Mon, 14 Mar 2022 13:34:06 -0400 Subject: [PATCH 11/28] commit to merge upstream/master --- core/os/os2/file_linux.odin | 43 +++++++------ core/os/os2/path_linux.odin | 72 ++++++++-------------- core/sys/unix/syscalls_linux.odin | 10 +++- tests/core/crypto_hash | Bin 621648 -> 0 bytes tests/core/os2/test_os2.odin | 96 +++++++++++++++++++++++------- 5 files changed, 129 insertions(+), 92 deletions(-) delete mode 100644 tests/core/crypto_hash diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index db0e2efa8..9030d265d 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -11,35 +11,35 @@ _std_handle :: proc(kind: Std_Handle_Kind) -> Handle { return Handle(kind) } -__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 +_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 _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Error) { 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 + 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)) + 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 { @@ -165,7 +165,6 @@ _remove :: proc(name: string) -> Error { } 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)) } diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index 31abf5bf8..b474ae207 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -8,7 +8,16 @@ import "core:path/filepath" _Path_Separator :: '/' _Path_List_Separator :: ':' -DIRECTORY_FLAGS :: __O_RDONLY|__O_NONBLOCK|__O_DIRECTORY|__O_LARGEFILE|__O_CLOEXEC +_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 _is_path_separator :: proc(c: byte) -> bool { return c == '/' @@ -16,42 +25,17 @@ _is_path_separator :: proc(c: byte) -> bool { _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)) + perm_i: int + if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 { + return .Invalid_Argument + } + + return _ok_or_error(unix.sys_mkdir(path_cstr, int(perm & 0o777))) } +// TODO _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) + return nil } dirent64 :: struct { @@ -62,17 +46,9 @@ dirent64 :: struct { d_name: [1]u8, } -DT_UNKNOWN :: 0 -DT_FIFO :: 1 -DT_CHR :: 2 -DT_DIR :: 4 -DT_BLK :: 6 -DT_REG :: 8 -DT_LNK :: 10 -DT_SOCK :: 12 -DT_WHT :: 14 - _remove_all :: proc(path: string) -> Error { + DT_DIR :: 4 + _remove_all_dir :: proc(dfd: Handle) -> Error { n := 64 buf := make([]u8, n) @@ -114,7 +90,7 @@ _remove_all :: proc(path: string) -> Error { switch d.d_type { case DT_DIR: - handle_i := unix.sys_openat(int(dfd), d_name_cstr, DIRECTORY_FLAGS) + handle_i := unix.sys_openat(int(dfd), d_name_cstr, _OPENDIR_FLAGS) if handle_i < 0 { return _get_platform_error(handle_i) } @@ -135,7 +111,7 @@ _remove_all :: proc(path: string) -> Error { cstr := strings.clone_to_cstring(path, context.temp_allocator) - handle_i := unix.sys_open(cstr, DIRECTORY_FLAGS) + handle_i := unix.sys_open(cstr, _OPENDIR_FLAGS) switch handle_i { case -ENOTDIR: return _ok_or_error(unix.sys_unlink(cstr)) @@ -149,7 +125,7 @@ _remove_all :: proc(path: string) -> Error { return _ok_or_error(unix.sys_rmdir(cstr)) } -_getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) { +_getwd :: proc(allocator := context.allocator) -> (string, 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. @@ -170,7 +146,7 @@ _getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) { unreachable() } -_setwd :: proc(dir: string) -> (err: Error) { +_setwd :: proc(dir: string) -> Error { dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator) return _ok_or_error(unix.sys_chdir(dir_cstr)) } diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 889dd3b90..926e69691 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -1717,7 +1717,7 @@ sys_rmdir :: proc(path: cstring) -> int { } } -sys_mkdir :: proc(path: cstring, mode: u32 = 0o775) -> int { +sys_mkdir :: proc(path: cstring, mode: int) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode))) } else { // NOTE: arm64 does not have mkdir @@ -1725,6 +1725,14 @@ sys_mkdir :: proc(path: cstring, mode: u32 = 0o775) -> int { } } +sys_mknod :: proc(path: cstring, mode: int, dev: int) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_mknod, uintptr(rawptr(path)), uintptr(mode), uintptr(dev))) + } else { // NOTE: arm64 does not have mknod + return int(intrinsics.syscall(SYS_mknodat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mode), uintptr(dev))) + } +} + 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))) diff --git a/tests/core/crypto_hash b/tests/core/crypto_hash deleted file mode 100644 index 18b85a1e8ed34fa9fa8659c7e2841b67c9b476c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 621648 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
ENDSC_# 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

QPAbe;-=?`$DV#1JLS! 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 diff --git a/tests/core/os2/test_os2.odin b/tests/core/os2/test_os2.odin index f8ef133a5..a629063bd 100644 --- a/tests/core/os2/test_os2.odin +++ b/tests/core/os2/test_os2.odin @@ -1,5 +1,6 @@ package test_os2 +import "core:os" import "core:fmt" import "core:os/os2" import "core:testing" @@ -22,7 +23,7 @@ when ODIN_TEST { TEST_count += 1 ok := value == expected if !ok { - fmt.printf("expected %v, got %v", expected, value) + fmt.printf("expected %v, got %v\n", expected, value) TEST_fail += 1 return } @@ -45,12 +46,13 @@ when ODIN_TEST { } } -main :: proc() +main :: proc() { t: testing.T file_test(&t) path_test(&t) fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + os.exit(TEST_fail > 0 ? 1 : 0) } @private @@ -59,7 +61,7 @@ _expect_no_error :: proc(t: ^testing.T, e: os2.Error, loc := #caller_location) { } -F_OK :: 0 // Test for file existance +F_OK :: 0 // Test for file existence X_OK :: 1 // Test for execute permission W_OK :: 2 // Test for write permission R_OK :: 4 // Test for read permission @@ -84,44 +86,92 @@ file_test :: proc(t: ^testing.T) { expect(t, err != nil, "missing error") expect_value(t, fd, os2.INVALID_HANDLE) - fd, err = os2.open("write.txt", {.Write, .Create, .Trunc}, 0o664) + // NOTE: no executable permissions here + fd, err = os2.open("file.txt", {.Write, .Create, .Trunc}, 0o664) _expect_no_error(t, err) expect(t, fd != os2.INVALID_HANDLE, "unexpected handle") - s1 := "hello" - b1 := transmute([]u8)s1 - + s := "hello" n: int - n, err = os2.write_at(fd, b1, 10) + n, err = os2.write_at(fd, transmute([]u8)s, 10) _expect_no_error(t, err) expect_value(t, n, 5) - s2 := "abcdefghij" - b2 := transmute([]u8)s2 - - n, err = os2.write(fd, b2) + s = "abcdefghij" + n, err = os2.write(fd, transmute([]u8)s) _expect_no_error(t, err) expect_value(t, n, 10) + // seek to the "ll" in "hello" + n64: i64 + n64, err = os2.seek(fd, 12, .Start) + _expect_no_error(t, err) + expect_value(t, n64, 12) + + s = "11" + n, err = os2.write(fd, transmute([]u8)s) + _expect_no_error(t, err) + expect_value(t, n, 2) + + // seek to the "e" in "he11o" + n64, err = os2.seek(fd, -3, .Current) + _expect_no_error(t, err) + expect_value(t, n64, 11) + + s = "3" + n, err = os2.write(fd, transmute([]u8)s) + _expect_no_error(t, err) + expect_value(t, n, 1) + + // seek to the "o" in "h311o" + n64, err = os2.seek(fd, -1, .End) + _expect_no_error(t, err) + expect_value(t, n64, 14) + + s = "0" + n, err = os2.write(fd, transmute([]u8)s) + _expect_no_error(t, err) + expect_value(t, n, 1) + _expect_no_error(t, os2.sync(fd)) + + // Add executable permissions to current file (as well as read/write to all) + err = os2.chmod(fd, 0o766) + _expect_no_error(t, err) + + when ODIN_OS == .Linux { + expect(t, unix.sys_access("file.txt", X_OK) == 0, "expected exec permission") + } + + // NOTE: chown not possible without root user + //_expect_no_error(t, os2.chown(fd, 0, 0)) _expect_no_error(t, os2.close(fd)) - fd, err = os2.open("write.txt") + + fd, err = os2.open("file.txt") _expect_no_error(t, err) buf: [32]u8 - + n, err = os2.read(fd, buf[:]) _expect_no_error(t, err) expect_value(t, n, 15) - expect_value(t, string(buf[:n]), "abcdefghijhello") + expect_value(t, string(buf[:n]), "abcdefghijh3110") n, err = os2.read_at(fd, buf[0:2], 1) _expect_no_error(t, err) expect_value(t, n, 2) expect_value(t, string(buf[0:2]), "bc") + n64, err = os2.file_size(fd) + _expect_no_error(t, err) + expect_value(t, n64, 15) + _expect_no_error(t, os2.close(fd)) + + _expect_no_error(t, os2.remove("file.txt")) + _expect_no_error(t, os2.mkdir("empty dir", 0)) + _expect_no_error(t, os2.remove("empty dir")) } @test @@ -131,7 +181,7 @@ path_test :: proc(t: ^testing.T) { err = os2.remove_all("a") _expect_no_error(t, err) } - + err = os2.mkdir_all("a/b/c/d", 0) _expect_no_error(t, err) @@ -141,23 +191,25 @@ path_test :: proc(t: ^testing.T) { fd, err = os2.create("a/b/c/file.txt", 0o644) _expect_no_error(t, err) - err = os2.close(fd) - _expect_no_error(t, err) when ODIN_OS == .Linux { expect(t, unix.sys_access("a/b/c/file.txt", X_OK) < 0, "unexpected exec permission") + } else { + expect(t, os2.exists("a/b/c/file.txt"), "file does not exist") } err = os2.rename("a/b/c/file.txt", "a/b/file.txt") _expect_no_error(t, err) when ODIN_OS == .Linux { - expect(t, unix.sys_access("a/b/c/file.txt", F_OK) < 0, "unexpected exec permission") + expect(t, unix.sys_access("a/b/c/file.txt", F_OK) < 0, "unexpected file existence") + } else { + expect(t, !os2.exists("a/b/c/file.txt"), "unexpected file existence") } err = os2.symlink("b/c/d", "a/symlink_to_d") _expect_no_error(t, err) - + symlink: string symlink, err = os2.read_link("a/symlink_to_d") _expect_no_error(t, err) @@ -171,8 +223,10 @@ path_test :: proc(t: ^testing.T) { when ODIN_OS == .Linux { expect_value(t, unix.sys_access("a/b/c/d/shnt.txt", X_OK | R_OK | W_OK), 0) + } else { + expect(t, os2.exists("a/b/c/d/shnt.txt"), "file does not exist") } - + err = os2.remove_all("a") _expect_no_error(t, err) From b21e7e4518a82713910d959841a4a0eef88fc4a2 Mon Sep 17 00:00:00 2001 From: CiD- Date: Mon, 14 Mar 2022 15:44:34 -0400 Subject: [PATCH 12/28] rewrite mkdir_all --- core/os/os2/path_linux.odin | 63 +++++++++++++++++++++++++++++-- core/sys/unix/syscalls_linux.odin | 48 +++++++++++++++-------- tests/core/Makefile | 16 +++----- tests/core/os2/test_os2.odin | 9 +++-- 4 files changed, 101 insertions(+), 35 deletions(-) diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index b474ae207..e47bd36b0 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -24,18 +24,73 @@ _is_path_separator :: proc(c: byte) -> bool { } _mkdir :: proc(path: string, perm: File_Mode) -> Error { - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - perm_i: int if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 { return .Invalid_Argument } + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) return _ok_or_error(unix.sys_mkdir(path_cstr, int(perm & 0o777))) } -// TODO _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { - return nil + _mkdirat :: proc(dfd: Handle, path: []u8, perm: int, has_created: ^bool) -> Error { + if len(path) == 0 { + return nil + } + i: int + for /**/; i < len(path) - 1 && path[i] != '/'; i += 1 {} + path[i] = 0 + new_dfd := unix.sys_openat(int(dfd), cstring(&path[0]), _OPENDIR_FLAGS) + switch new_dfd { + case -ENOENT: + res := unix.sys_mkdirat(int(dfd), cstring(&path[0]), perm) + if res < 0 { + return _get_platform_error(res) + } + has_created^ = true + new_dfd = unix.sys_openat(int(dfd), cstring(&path[0]), _OPENDIR_FLAGS) + if new_dfd < 0 { + return _get_platform_error(new_dfd) + } + fallthrough + case 0: + unix.sys_close(int(dfd)) + // skip consecutive '/' + for i += 1; i < len(path) && path[i] == '/'; i += 1 {} + return _mkdirat(Handle(new_dfd), path[i:], perm, has_created) + case: + return _get_platform_error(new_dfd) + } + unreachable() + } + + if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 { + return .Invalid_Argument + } + + // need something we can edit, and use to generate cstrings + path_bytes := make([]u8, len(path) + 1, context.temp_allocator) + copy(path_bytes, path) + path_bytes[len(path)] = 0 + + dfd: int + if path_bytes[0] == '/' { + dfd = unix.sys_open("/", _OPENDIR_FLAGS) + path_bytes = path_bytes[1:] + } else { + dfd = unix.sys_open(".", _OPENDIR_FLAGS) + } + if dfd < 0 { + return _get_platform_error(dfd) + } + + has_created: bool + _mkdirat(Handle(dfd), path_bytes, int(perm & 0o777), &has_created) or_return + if has_created { + return nil + } + return .Exist + //return has_created ? nil : .Exist } dirent64 :: struct { diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 8cfb97076..8a1aadb9c 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -1518,7 +1518,7 @@ when ODIN_ARCH == .amd64 { #panic("Unsupported architecture") } -AT_FDCWD :: -100 +AT_FDCWD :: ~uintptr(99) AT_REMOVEDIR :: uintptr(0x200) AT_SYMLINK_FOLLOW :: uintptr(0x400) AT_SYMLINK_NOFOLLOW :: uintptr(0x100) @@ -1535,7 +1535,7 @@ sys_open :: proc(path: cstring, flags: int, mode: int = 0o000) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode))) } else { // NOTE: arm64 does not have open - return int(intrinsics.syscall(SYS_openat, uintptr(AT_FDCWD), uintptr(rawptr(path), uintptr(flags), uintptr(mode)))) + return int(intrinsics.syscall(SYS_openat, AT_FDCWD, uintptr(rawptr(path)), uintptr(flags), uintptr(mode))) } } @@ -1593,7 +1593,7 @@ sys_stat :: proc(path: cstring, stat: rawptr) -> int { } 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)) + return int(intrinsics.syscall(SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), 0)) } } @@ -1611,7 +1611,7 @@ sys_lstat :: proc(path: cstring, stat: rawptr) -> int { } 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)) + return int(intrinsics.syscall(SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW)) } } @@ -1619,7 +1619,7 @@ 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))) + return int(intrinsics.syscall(SYS_readlinkat, AT_FDCWD, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz))) } } @@ -1627,7 +1627,7 @@ 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)))) + return int(intrinsics.syscall(SYS_symlinkat, uintptr(rawptr(old_name)), AT_FDCWD, uintptr(rawptr(new_name)))) } } @@ -1635,7 +1635,7 @@ 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))) + return int(intrinsics.syscall(SYS_faccessat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mask))) } } @@ -1655,7 +1655,7 @@ 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))) + return int(intrinsics.syscall(SYS_fchmodat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode))) } } @@ -1667,7 +1667,7 @@ 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)) + return int(intrinsics.syscall(SYS_fchownat, AT_FDCWD, uintptr(rawptr(path)), uintptr(user), uintptr(group), 0)) } } @@ -1679,7 +1679,7 @@ 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)) + return int(intrinsics.syscall(SYS_fchownat, AT_FDCWD, uintptr(rawptr(path)), uintptr(user), uintptr(group), AT_SYMLINK_NOFOLLOW)) } } @@ -1687,7 +1687,7 @@ 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)))) + return int(intrinsics.syscall(SYS_renameat, AT_FDCWD, uintptr(rawptr(old)), uintptr(rawptr(new)))) } } @@ -1695,7 +1695,7 @@ 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)) + return int(intrinsics.syscall(SYS_linkat, AT_FDCWD, uintptr(rawptr(old_name)), AT_FDCWD, uintptr(rawptr(new_name)), AT_SYMLINK_FOLLOW)) } } @@ -1703,7 +1703,7 @@ 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))) + return int(intrinsics.syscall(SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), 0)) } } @@ -1715,7 +1715,7 @@ 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)) + return int(intrinsics.syscall(SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), AT_REMOVEDIR)) } } @@ -1723,18 +1723,26 @@ sys_mkdir :: proc(path: cstring, mode: int) -> 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))) + return int(intrinsics.syscall(SYS_mkdirat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode))) } } +sys_mkdirat :: proc(dfd: int, path: cstring, mode: int) -> int { + return int(intrinsics.syscall(SYS_mkdirat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode))) +} + sys_mknod :: proc(path: cstring, mode: int, dev: int) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(SYS_mknod, uintptr(rawptr(path)), uintptr(mode), uintptr(dev))) } else { // NOTE: arm64 does not have mknod - return int(intrinsics.syscall(SYS_mknodat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mode), uintptr(dev))) + return int(intrinsics.syscall(SYS_mknodat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode), uintptr(dev))) } } +sys_mknodat :: proc(dfd: int, path: cstring, mode: int, dev: int) -> int { + return int(intrinsics.syscall(SYS_mknodat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode), uintptr(dev))) +} + 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))) @@ -1763,6 +1771,14 @@ sys_getdents64 :: proc(fd: int, dirent: rawptr, count: int) -> int { return int(intrinsics.syscall(SYS_getdents64, uintptr(fd), uintptr(dirent), uintptr(count))) } +sys_fork :: proc() -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_fork)) + } else { + return int(intrinsics.syscall(SYS_clone, SIGCHLD)) + } +} + get_errno :: proc(res: int) -> i32 { if res < 0 && res > -4096 { return i32(-res) diff --git a/tests/core/Makefile b/tests/core/Makefile index 449efbb25..90b0df449 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -1,12 +1,8 @@ ODIN=../../odin PYTHON=$(shell which python3) -<<<<<<< HEAD -all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test os2_test -======= all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test \ - math_test linalg_glsl_math_test ->>>>>>> upstream/master + math_test linalg_glsl_math_test os2_test download_test_assets: $(PYTHON) download_assets.py @@ -29,18 +25,16 @@ crypto_test: noise_test: $(ODIN) run math/noise -out=test_noise -os2_test: - $(ODIN) run os2/test_os2.odin -out=test_os2 - encoding_test: $(ODIN) run encoding/json -out=test_json $(ODIN) run encoding/varint -out=test_varint -<<<<<<< HEAD -======= math_test: $(ODIN) run math/test_core_math.odin -out=test_core_math -collection:tests=.. linalg_glsl_math_test: $(ODIN) run math/linalg/glsl/test_linalg_glsl_math.odin -out=test_linalg_glsl_math -collection:tests=.. ->>>>>>> upstream/master + +os2_test: + $(ODIN) run os2/test_os2.odin -out=test_os2 + diff --git a/tests/core/os2/test_os2.odin b/tests/core/os2/test_os2.odin index a629063bd..0e0d3dcb5 100644 --- a/tests/core/os2/test_os2.odin +++ b/tests/core/os2/test_os2.odin @@ -15,8 +15,9 @@ TEST_count := 0 TEST_fail := 0 when ODIN_TEST { - expect :: testing.expect - log :: testing.log + expect_value :: testing.expect_value + expect :: testing.expect + log :: testing.log } else { expect_value :: proc(t: ^testing.T, value, expected: $T, loc := #caller_location) where intrinsics.type_is_comparable(T) { fmt.printf("[%v] ", loc) @@ -170,7 +171,7 @@ file_test :: proc(t: ^testing.T) { _expect_no_error(t, os2.close(fd)) _expect_no_error(t, os2.remove("file.txt")) - _expect_no_error(t, os2.mkdir("empty dir", 0)) + _expect_no_error(t, os2.mkdir("empty dir", 0o755)) _expect_no_error(t, os2.remove("empty dir")) } @@ -182,7 +183,7 @@ path_test :: proc(t: ^testing.T) { _expect_no_error(t, err) } - err = os2.mkdir_all("a/b/c/d", 0) + err = os2.mkdir_all("a/b/c/d", 0o755) _expect_no_error(t, err) expect(t, os2.exists("a"), "directory does not exist") From 4b1822ade8eda7cda798dc0d0b22e1f1d7040a8f Mon Sep 17 00:00:00 2001 From: CiD- Date: Mon, 14 Mar 2022 15:48:47 -0400 Subject: [PATCH 13/28] mkdir_all: close last open file --- core/os/os2/path_linux.odin | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index e47bd36b0..fa47f1883 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -35,7 +35,7 @@ _mkdir :: proc(path: string, perm: File_Mode) -> Error { _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { _mkdirat :: proc(dfd: Handle, path: []u8, perm: int, has_created: ^bool) -> Error { if len(path) == 0 { - return nil + return _ok_or_error(unix.sys_close(int(dfd))) } i: int for /**/; i < len(path) - 1 && path[i] != '/'; i += 1 {} @@ -43,18 +43,18 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { new_dfd := unix.sys_openat(int(dfd), cstring(&path[0]), _OPENDIR_FLAGS) switch new_dfd { case -ENOENT: - res := unix.sys_mkdirat(int(dfd), cstring(&path[0]), perm) - if res < 0 { + if res := unix.sys_mkdirat(int(dfd), cstring(&path[0]), perm); res < 0 { return _get_platform_error(res) } has_created^ = true - new_dfd = unix.sys_openat(int(dfd), cstring(&path[0]), _OPENDIR_FLAGS) - if new_dfd < 0 { + if new_dfd = unix.sys_openat(int(dfd), cstring(&path[0]), _OPENDIR_FLAGS); new_dfd < 0 { return _get_platform_error(new_dfd) } fallthrough case 0: - unix.sys_close(int(dfd)) + if res := unix.sys_close(int(dfd)) < 0; res < 0 { + return _get_platform_error(res) + } // skip consecutive '/' for i += 1; i < len(path) && path[i] == '/'; i += 1 {} return _mkdirat(Handle(new_dfd), path[i:], perm, has_created) From 6d6e840bc26a92bee1eb257d081bbc3695d741a4 Mon Sep 17 00:00:00 2001 From: CiD- Date: Mon, 14 Mar 2022 15:56:41 -0400 Subject: [PATCH 14/28] mkdir_all: WHOOPS --- core/os/os2/path_linux.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index fa47f1883..85c39916f 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -52,7 +52,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { } fallthrough case 0: - if res := unix.sys_close(int(dfd)) < 0; res < 0 { + if res := unix.sys_close(int(dfd)); res < 0 { return _get_platform_error(res) } // skip consecutive '/' From 36c22393a4e3026bc84a5ee8d2230d1f6f78b83c Mon Sep 17 00:00:00 2001 From: CiD- Date: Tue, 15 Mar 2022 11:47:35 -0400 Subject: [PATCH 15/28] fix memory leak --- core/os/os2/path_linux.odin | 3 ++- tests/core/os2/test_os2.odin | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index 85c39916f..889f7e447 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -112,7 +112,8 @@ _remove_all :: proc(path: string) -> Error { loop: for { res := unix.sys_getdents64(int(dfd), &buf[0], n) switch res { - case -22: //-EINVAL + case -EINVAL: + delete(buf) n *= 2 buf = make([]u8, n) continue loop diff --git a/tests/core/os2/test_os2.odin b/tests/core/os2/test_os2.odin index 0e0d3dcb5..e52351f03 100644 --- a/tests/core/os2/test_os2.odin +++ b/tests/core/os2/test_os2.odin @@ -2,6 +2,7 @@ package test_os2 import "core:os" import "core:fmt" +import "core:mem" import "core:os/os2" import "core:testing" import "core:intrinsics" @@ -49,10 +50,22 @@ when ODIN_TEST { main :: proc() { + track: mem.Tracking_Allocator + mem.tracking_allocator_init(&track, context.allocator) + context.allocator = mem.tracking_allocator(&track) + t: testing.T file_test(&t) path_test(&t) fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + + for _, leak in track.allocation_map { + fmt.printf("%v leaked %v bytes\n", leak.location, leak.size) + } + for bad_free in track.bad_free_array { + fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory) + } + os.exit(TEST_fail > 0 ? 1 : 0) } @@ -215,6 +228,7 @@ path_test :: proc(t: ^testing.T) { symlink, err = os2.read_link("a/symlink_to_d") _expect_no_error(t, err) expect_value(t, symlink, "b/c/d") + delete(symlink) fd, err = os2.create("a/symlink_to_d/shnt.txt", 0o744) _expect_no_error(t, err) From e252d3bedf28e603ee3c33a4aebcfefbb5cfb66c Mon Sep 17 00:00:00 2001 From: CiD- Date: Wed, 23 Mar 2022 11:49:19 -0400 Subject: [PATCH 16/28] add os2.name --- core/os/os2/file_linux.odin | 27 +++++++++++++++++++++------ tests/core/os2/test_os2.odin | 25 ++++++++++++++++++++----- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 9030d265d..7040d0ed3 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -4,6 +4,7 @@ package os2 import "core:io" import "core:time" import "core:strings" +import "core:strconv" import "core:sys/unix" @@ -55,8 +56,20 @@ _close :: proc(fd: Handle) -> Error { } _name :: proc(fd: Handle, allocator := context.allocator) -> string { - //TODO - return "" + // NOTE: Not sure how portable this really is + PROC_FD_PATH :: "/proc/self/fd/" + + buf: [32]u8 + copy(buf[:], PROC_FD_PATH) + + strconv.itoa(buf[len(PROC_FD_PATH):], int(fd)) + + realpath: string + err: Error + if realpath, err = _read_link_cstr(cstring(&buf[0])); err != nil || realpath[0] != '/' { + return "" + } + return realpath } _seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) { @@ -189,10 +202,7 @@ _symlink :: proc(old_name, new_name: string) -> Error { return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr)) } -_read_link :: proc(name: string, allocator := context.allocator) -> (string, Error) { - name_cstr := strings.clone_to_cstring(name) - defer delete(name_cstr) - +_read_link_cstr :: proc(name_cstr: cstring, allocator := context.allocator) -> (string, Error) { bufsz : uint = 256 buf := make([]byte, bufsz, allocator) for { @@ -210,6 +220,11 @@ _read_link :: proc(name: string, allocator := context.allocator) -> (string, Err } } +_read_link :: proc(name: string, allocator := context.allocator) -> (string, Error) { + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + return _read_link_cstr(name_cstr, allocator) +} + _unlink :: proc(name: string) -> Error { name_cstr := strings.clone_to_cstring(name, context.temp_allocator) return _ok_or_error(unix.sys_unlink(name_cstr)) diff --git a/tests/core/os2/test_os2.odin b/tests/core/os2/test_os2.odin index e52351f03..c588e532e 100644 --- a/tests/core/os2/test_os2.odin +++ b/tests/core/os2/test_os2.odin @@ -1,9 +1,11 @@ package test_os2 +import "core:os/os2" + import "core:os" import "core:fmt" import "core:mem" -import "core:os/os2" +import "core:strings" import "core:testing" import "core:intrinsics" @@ -116,7 +118,7 @@ file_test :: proc(t: ^testing.T) { _expect_no_error(t, err) expect_value(t, n, 10) - // seek to the "ll" in "hello" + // seek FROM BEGINNING to the "ll" in "hello" n64: i64 n64, err = os2.seek(fd, 12, .Start) _expect_no_error(t, err) @@ -127,7 +129,7 @@ file_test :: proc(t: ^testing.T) { _expect_no_error(t, err) expect_value(t, n, 2) - // seek to the "e" in "he11o" + // seek BACK to the "e" in "he11o" n64, err = os2.seek(fd, -3, .Current) _expect_no_error(t, err) expect_value(t, n64, 11) @@ -137,7 +139,7 @@ file_test :: proc(t: ^testing.T) { _expect_no_error(t, err) expect_value(t, n, 1) - // seek to the "o" in "h311o" + // seek FROM THE END the "o" in "h311o" n64, err = os2.seek(fd, -1, .End) _expect_no_error(t, err) expect_value(t, n64, 14) @@ -157,11 +159,24 @@ file_test :: proc(t: ^testing.T) { expect(t, unix.sys_access("file.txt", X_OK) == 0, "expected exec permission") } + /* Build expected full path via cwd and known file name */ + parts: [2]string + parts[0], err = os2.getwd() + defer delete(parts[0]) + _expect_no_error(t, err) + + parts[1] = "/file.txt" + expected_full_path := strings.concatenate(parts[:]) + defer delete(expected_full_path) + + full_path := os2.name(fd) + defer delete(full_path) + expect_value(t, full_path, expected_full_path) + // NOTE: chown not possible without root user //_expect_no_error(t, os2.chown(fd, 0, 0)) _expect_no_error(t, os2.close(fd)) - fd, err = os2.open("file.txt") _expect_no_error(t, err) From 645661889137f6f4cb96b9671976dbc006345497 Mon Sep 17 00:00:00 2001 From: CiD- Date: Wed, 30 Mar 2022 16:54:29 -0400 Subject: [PATCH 17/28] finish up stat, lstat and fstat --- core/os/os2/file_linux.odin | 4 ++- core/os/os2/stat_linux.odin | 50 +++++++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 7040d0ed3..3202b9e16 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -22,8 +22,10 @@ _O_APPEND :: 0o2000 _O_NONBLOCK :: 0o4000 _O_LARGEFILE :: 0o100000 _O_DIRECTORY :: 0o200000 +_O_NOFOLLOW :: 0o400000 _O_SYNC :: 0o4010000 _O_CLOEXEC :: 0o2000000 +_O_PATH :: 0o10000000 _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Error) { cstr := strings.clone_to_cstring(name, context.temp_allocator) @@ -66,7 +68,7 @@ _name :: proc(fd: Handle, allocator := context.allocator) -> string { realpath: string err: Error - if realpath, err = _read_link_cstr(cstring(&buf[0])); err != nil || realpath[0] != '/' { + if realpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || realpath[0] != '/' { return "" } return realpath diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index c4cc5fe8d..d22f32d65 100644 --- a/core/os/os2/stat_linux.odin +++ b/core/os/os2/stat_linux.odin @@ -82,39 +82,51 @@ OS_Stat :: struct { _reserve3: i64, } + _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(name) - defer delete(cstr) - s: OS_Stat - result := unix.sys_lstat(cstr, &s) + result := unix.sys_fstat(int(fd), &s) if result < 0 { - return {}, _get_platform_error(int(unix.get_errno(result))) + return {}, _get_platform_error(result) } + // TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time fi := File_Info { - fullpath = "", + fullpath = _name(fd, allocator), name = "", size = s.size, mode = 0, is_dir = S_ISDIR(s.mode), - creation_time = time.Time{0}, // linux does not track this - //TODO - modification_time = time.Time{0}, - access_time = time.Time{0}, + 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 } - + + fi.name = filepath.base(fi.fullpath) return fi, nil } +// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath +_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { + cstr := strings.clone_to_cstring(name, context.temp_allocator) + fd := unix.sys_open(cstr, _O_RDONLY) + if fd < 0 { + return {}, _get_platform_error(fd) + } + defer unix.sys_close(fd) + return _fstat(Handle(fd), allocator) +} + +_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { + cstr := strings.clone_to_cstring(name, context.temp_allocator) + fd := unix.sys_open(cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW) + if fd < 0 { + return {}, _get_platform_error(fd) + } + defer unix.sys_close(fd) + return _fstat(Handle(fd), allocator) +} + _same_file :: proc(fi1, fi2: File_Info) -> bool { return fi1.fullpath == fi2.fullpath } From 88de3a1c0633761e2f73f4e576a0dfd2bb9c32bf Mon Sep 17 00:00:00 2001 From: CiD- Date: Fri, 1 Apr 2022 22:41:35 -0400 Subject: [PATCH 18/28] add _chtimes --- core/os/os2/file_linux.odin | 10 ++++++++-- core/sys/unix/syscalls_linux.odin | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 3202b9e16..89075e00c 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -27,6 +27,8 @@ _O_SYNC :: 0o4010000 _O_CLOEXEC :: 0o2000000 _O_PATH :: 0o10000000 +_AT_FDCWD :: -100 + _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Error) { cstr := strings.clone_to_cstring(name, context.temp_allocator) @@ -250,8 +252,12 @@ _lchown :: proc(name: string, uid, gid: int) -> Error { } _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { - //TODO - return nil + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + times := [2]Unix_File_Time { + { atime._nsec, 0 }, + { mtime._nsec, 0 }, + } + return _ok_or_error(unix.sys_utimensat(_AT_FDCWD, name_cstr, ×, 0)) } _exists :: proc(name: string) -> bool { diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 8a1aadb9c..6fc6dc594 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -1779,6 +1779,12 @@ sys_fork :: proc() -> int { } } +// NOTE: Unsure about if this works directly on 32 bit archs. It may need 32 bit version of the time struct. +// As of Linux 5.1, there is a utimensat_time64 function. Maybe use this in the future? +sys_utimensat :: proc(dfd: int, path: cstring, times: rawptr, flags: int) -> int { + return int(intrinsics.syscall(SYS_utimensat, uintptr(dfd), uintptr(rawptr(path)), uintptr(times), uintptr(flags))) +} + get_errno :: proc(res: int) -> i32 { if res < 0 && res > -4096 { return i32(-res) From aadb4db2113896011a9cbc661c2ccde893300cb1 Mon Sep 17 00:00:00 2001 From: CiD- Date: Wed, 6 Apr 2022 10:53:46 -0400 Subject: [PATCH 19/28] avoid temp_allocator on stupidly long paths --- core/os/os2/file_linux.odin | 86 +++++++++++++++++++++++++++++++------ core/os/os2/path_linux.odin | 37 +++++++++++++--- core/os/os2/stat_linux.odin | 20 ++++++--- 3 files changed, 117 insertions(+), 26 deletions(-) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 89075e00c..998fe8617 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -29,8 +29,13 @@ _O_PATH :: 0o10000000 _AT_FDCWD :: -100 +_CSTRING_NAME_HEAP_THRESHOLD :: 512 + _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Error) { - cstr := strings.clone_to_cstring(name, context.temp_allocator) + name_cstr, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } flags_i: int switch flags & O_RDONLY|O_WRONLY|O_RDWR { @@ -46,7 +51,7 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Erro 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)) + handle_i := unix.sys_open(name_cstr, flags_i, int(perm)) if handle_i < 0 { return INVALID_HANDLE, _get_platform_error(handle_i) } @@ -174,7 +179,10 @@ _truncate :: proc(fd: Handle, size: i64) -> Error { } _remove :: proc(name: string) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + name_cstr, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } handle_i := unix.sys_open(name_cstr, int(File_Flags.Read)) if handle_i < 0 { @@ -189,20 +197,41 @@ _remove :: proc(name: string) -> Error { } _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) + old_name_cstr, old_allocated := _name_to_cstring(old_name) + new_name_cstr, new_allocated := _name_to_cstring(new_name) + defer if old_allocated { + delete(old_name_cstr) + } + defer if new_allocated { + delete(new_name_cstr) + } + return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_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) + old_name_cstr, old_allocated := _name_to_cstring(old_name) + new_name_cstr, new_allocated := _name_to_cstring(new_name) + defer if old_allocated { + delete(old_name_cstr) + } + defer if new_allocated { + delete(new_name_cstr) + } + 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) + old_name_cstr, old_allocated := _name_to_cstring(old_name) + new_name_cstr, new_allocated := _name_to_cstring(new_name) + defer if old_allocated { + delete(old_name_cstr) + } + defer if new_allocated { + delete(new_name_cstr) + } + return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr)) } @@ -225,12 +254,18 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator := context.allocator) -> ( } _read_link :: proc(name: string, allocator := context.allocator) -> (string, Error) { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + name_cstr, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } return _read_link_cstr(name_cstr, allocator) } _unlink :: proc(name: string) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + name_cstr, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } return _ok_or_error(unix.sys_unlink(name_cstr)) } @@ -247,12 +282,18 @@ _chown :: proc(fd: Handle, uid, gid: int) -> Error { } _lchown :: proc(name: string, uid, gid: int) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + name_cstr, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid)) } _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + name_cstr, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } times := [2]Unix_File_Time { { atime._nsec, 0 }, { mtime._nsec, 0 }, @@ -261,7 +302,10 @@ _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { } _exists :: proc(name: string) -> bool { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + name_cstr, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } return unix.sys_access(name_cstr, F_OK) == 0 } @@ -282,3 +326,17 @@ _is_dir :: proc(fd: Handle) -> bool { } return S_ISDIR(s.mode) } + +// Ideally we want to use the temp_allocator. PATH_MAX on Linux is commonly +// defined as 512, however, it is well known that paths can exceed that limit. +// So, in theory you could have a path larger than the entire temp_allocator's +// buffer. Therefor any large paths will use context.allocator. +_name_to_cstring :: proc(path: string) -> (cpath: cstring, allocated: bool) { + if len(path) > _CSTRING_NAME_HEAP_THRESHOLD { + cpath = strings.clone_to_cstring(path) + allocated = true + return + } + cpath = strings.clone_to_cstring(path, context.temp_allocator) + return +} diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index 889f7e447..5dadb7608 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -24,11 +24,16 @@ _is_path_separator :: proc(c: byte) -> bool { } _mkdir :: proc(path: string, perm: File_Mode) -> Error { + // NOTE: These modes would require sys_mknod, however, that would require + // additional arguments to this function. if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 { return .Invalid_Argument } - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + path_cstr, allocated := _name_to_cstring(path) + defer if allocated { + delete(path_cstr) + } return _ok_or_error(unix.sys_mkdir(path_cstr, int(perm & 0o777))) } @@ -69,7 +74,19 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { } // need something we can edit, and use to generate cstrings - path_bytes := make([]u8, len(path) + 1, context.temp_allocator) + 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, context.temp_allocator) + } + defer if allocated { + delete(path_bytes) + } + + // NULL terminate the byte slice to make it a valid cstring copy(path_bytes, path) path_bytes[len(path)] = 0 @@ -165,12 +182,15 @@ _remove_all :: proc(path: string) -> Error { return nil } - cstr := strings.clone_to_cstring(path, context.temp_allocator) + path_cstr, allocated := _name_to_cstring(path) + defer if allocated { + delete(path_cstr) + } - handle_i := unix.sys_open(cstr, _OPENDIR_FLAGS) + handle_i := unix.sys_open(path_cstr, _OPENDIR_FLAGS) switch handle_i { case -ENOTDIR: - return _ok_or_error(unix.sys_unlink(cstr)) + return _ok_or_error(unix.sys_unlink(path_cstr)) case -4096..<0: return _get_platform_error(handle_i) } @@ -178,7 +198,7 @@ _remove_all :: proc(path: string) -> Error { fd := Handle(handle_i) defer close(fd) _remove_all_dir(fd) or_return - return _ok_or_error(unix.sys_rmdir(cstr)) + return _ok_or_error(unix.sys_rmdir(path_cstr)) } _getwd :: proc(allocator := context.allocator) -> (string, Error) { @@ -203,6 +223,9 @@ _getwd :: proc(allocator := context.allocator) -> (string, Error) { } _setwd :: proc(dir: string) -> Error { - dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator) + dir_cstr, allocated := _name_to_cstring(dir) + defer if allocated { + delete(dir_cstr) + } return _ok_or_error(unix.sys_chdir(dir_cstr)) } diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index d22f32d65..9bfd900b6 100644 --- a/core/os/os2/stat_linux.odin +++ b/core/os/os2/stat_linux.odin @@ -108,8 +108,12 @@ _fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Error) // NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath _stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { - cstr := strings.clone_to_cstring(name, context.temp_allocator) - fd := unix.sys_open(cstr, _O_RDONLY) + name_cstr, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } + + fd := unix.sys_open(name_cstr, _O_RDONLY) if fd < 0 { return {}, _get_platform_error(fd) } @@ -118,8 +122,11 @@ _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(name, context.temp_allocator) - fd := unix.sys_open(cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW) + name_cstr, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } + fd := unix.sys_open(name_cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW) if fd < 0 { return {}, _get_platform_error(fd) } @@ -132,7 +139,10 @@ _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, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } res = unix.sys_stat(name_cstr, &s) return } From 9ae566adcc5e7e55039349eed2b4768739391ae8 Mon Sep 17 00:00:00 2001 From: CiD- Date: Fri, 8 Apr 2022 13:45:19 -0400 Subject: [PATCH 20/28] commit before fetching upstream/master --- core/os/os2/file_linux.odin | 8 +- core/sys/unix/syscalls_linux.odin | 125 +++++++++++++++++++++--------- 2 files changed, 91 insertions(+), 42 deletions(-) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 998fe8617..449eff1e2 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -331,12 +331,12 @@ _is_dir :: proc(fd: Handle) -> bool { // defined as 512, however, it is well known that paths can exceed that limit. // So, in theory you could have a path larger than the entire temp_allocator's // buffer. Therefor any large paths will use context.allocator. -_name_to_cstring :: proc(path: string) -> (cpath: cstring, allocated: bool) { - if len(path) > _CSTRING_NAME_HEAP_THRESHOLD { - cpath = strings.clone_to_cstring(path) +_name_to_cstring :: proc(name: string) -> (cname: cstring, allocated: bool) { + if len(name) > _CSTRING_NAME_HEAP_THRESHOLD { + cname = strings.clone_to_cstring(name) allocated = true return } - cpath = strings.clone_to_cstring(path, context.temp_allocator) + cname = strings.clone_to_cstring(name, context.temp_allocator) return } diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 6fc6dc594..e72bfcedf 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -1518,11 +1518,43 @@ when ODIN_ARCH == .amd64 { #panic("Unsupported architecture") } +// syscall related constants AT_FDCWD :: ~uintptr(99) AT_REMOVEDIR :: uintptr(0x200) AT_SYMLINK_FOLLOW :: uintptr(0x400) AT_SYMLINK_NOFOLLOW :: uintptr(0x100) +PROT_NONE :: 0x0 +PROT_READ :: 0x1 +PROT_WRITE :: 0x2 +PROT_EXEC :: 0x4 +PROT_GROWSDOWN :: 0x01000000 +PROT_GROWSUP :: 0x02000000 + +MAP_FIXED :: 0x1 +MAP_PRIVATE :: 0x2 +MAP_SHARED :: 0x4 +MAP_ANONYMOUS :: 0x20 + +MADV_NORMAL :: 0 +MADV_RANDOM :: 1 +MADV_SEQUENTIAL :: 2 +MADV_WILLNEED :: 3 +MADV_DONTNEED :: 4 +MADV_FREE :: 8 +MADV_REMOVE :: 9 +MADV_DONTFORK :: 10 +MADV_DOFORK :: 11 +MADV_MERGEABLE :: 12 +MADV_UNMERGEABLE :: 13 +MADV_HUGEPAGE :: 14 +MADV_NOHUGEPAGE :: 15 +MADV_DONTDUMP :: 16 +MADV_DODUMP :: 17 +MADV_WIPEONFORK :: 18 +MADV_KEEPONFORK :: 19 +MADV_HWPOISON :: 100 + sys_gettid :: proc "contextless" () -> int { return cast(int)intrinsics.syscall(SYS_gettid) } @@ -1531,7 +1563,7 @@ sys_getrandom :: proc "contextless" (buf: ^byte, buflen: int, flags: uint) -> in 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 { +sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode))) } else { // NOTE: arm64 does not have open @@ -1539,19 +1571,19 @@ sys_open :: proc(path: cstring, flags: int, mode: int = 0o000) -> int { } } -sys_openat :: proc(dfd: int, path: cstring, flags: int, mode: int = 0o000) -> int { +sys_openat :: proc "contextless" (dfd: int, path: cstring, flags: int, mode: int = 0o000) -> int { return int(intrinsics.syscall(SYS_openat, uintptr(dfd), uintptr(rawptr(path)), uintptr(flags), uintptr(mode))) } -sys_close :: proc(fd: int) -> int { +sys_close :: proc "contextless" (fd: int) -> int { return int(intrinsics.syscall(SYS_close, uintptr(fd))) } -sys_read :: proc(fd: int, buf: rawptr, size: uint) -> int { +sys_read :: proc "contextless" (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 { +sys_pread :: proc "contextless" (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 { @@ -1561,11 +1593,11 @@ sys_pread :: proc(fd: int, buf: rawptr, size: uint, offset: i64) -> int { } } -sys_write :: proc(fd: int, buf: rawptr, size: uint) -> int { +sys_write :: proc "contextless" (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 { +sys_pwrite :: proc "contextless" (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 { @@ -1575,7 +1607,7 @@ sys_pwrite :: proc(fd: int, buf: rawptr, size: uint, offset: i64) -> int { } } -sys_lseek :: proc(fd: int, offset: i64, whence: int) -> i64 { +sys_lseek :: proc "contextless" (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 { @@ -1587,7 +1619,7 @@ sys_lseek :: proc(fd: int, offset: i64, whence: int) -> i64 { } } -sys_stat :: proc(path: cstring, stat: rawptr) -> int { +sys_stat :: proc "contextless" (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 { @@ -1597,7 +1629,7 @@ sys_stat :: proc(path: cstring, stat: rawptr) -> int { } } -sys_fstat :: proc(fd: int, stat: rawptr) -> int { +sys_fstat :: proc "contextless" (fd: int, stat: rawptr) -> int { when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 { return int(intrinsics.syscall(SYS_fstat, uintptr(fd), uintptr(stat))) } else { @@ -1605,7 +1637,7 @@ sys_fstat :: proc(fd: int, stat: rawptr) -> int { } } -sys_lstat :: proc(path: cstring, stat: rawptr) -> int { +sys_lstat :: proc "contextless" (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 { @@ -1615,7 +1647,7 @@ sys_lstat :: proc(path: cstring, stat: rawptr) -> int { } } -sys_readlink :: proc(path: cstring, buf: rawptr, bufsiz: uint) -> int { +sys_readlink :: proc "contextless" (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 @@ -1623,7 +1655,7 @@ sys_readlink :: proc(path: cstring, buf: rawptr, bufsiz: uint) -> int { } } -sys_symlink :: proc(old_name: cstring, new_name: cstring) -> int { +sys_symlink :: proc "contextless" (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 @@ -1631,7 +1663,7 @@ sys_symlink :: proc(old_name: cstring, new_name: cstring) -> int { } } -sys_access :: proc(path: cstring, mask: int) -> int { +sys_access :: proc "contextless" (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 @@ -1639,19 +1671,19 @@ sys_access :: proc(path: cstring, mask: int) -> int { } } -sys_getcwd :: proc(buf: rawptr, size: uint) -> int { +sys_getcwd :: proc "contextless" (buf: rawptr, size: uint) -> int { return int(intrinsics.syscall(SYS_getcwd, uintptr(buf), uintptr(size))) } -sys_chdir :: proc(path: cstring) -> int { +sys_chdir :: proc "contextless" (path: cstring) -> int { return int(intrinsics.syscall(SYS_chdir, uintptr(rawptr(path)))) } -sys_fchdir :: proc(fd: int) -> int { +sys_fchdir :: proc "contextless" (fd: int) -> int { return int(intrinsics.syscall(SYS_fchdir, uintptr(fd))) } -sys_chmod :: proc(path: cstring, mode: int) -> int { +sys_chmod :: proc "contextless" (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 @@ -1659,11 +1691,11 @@ sys_chmod :: proc(path: cstring, mode: int) -> int { } } -sys_fchmod :: proc(fd: int, mode: int) -> int { +sys_fchmod :: proc "contextless" (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 { +sys_chown :: proc "contextless" (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 @@ -1671,11 +1703,11 @@ sys_chown :: proc(path: cstring, user: int, group: int) -> int { } } -sys_fchown :: proc(fd: int, user: int, group: int) -> int { +sys_fchown :: proc "contextless" (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 { +sys_lchown :: proc "contextless" (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 @@ -1683,7 +1715,7 @@ sys_lchown :: proc(path: cstring, user: int, group: int) -> int { } } -sys_rename :: proc(old, new: cstring) -> int { +sys_rename :: proc "contextless" (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 @@ -1691,7 +1723,7 @@ sys_rename :: proc(old, new: cstring) -> int { } } -sys_link :: proc(old_name: cstring, new_name: cstring) -> int { +sys_link :: proc "contextless" (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 @@ -1699,7 +1731,7 @@ sys_link :: proc(old_name: cstring, new_name: cstring) -> int { } } -sys_unlink :: proc(path: cstring) -> int { +sys_unlink :: proc "contextless" (path: cstring) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(SYS_unlink, uintptr(rawptr(path)))) } else { // NOTE: arm64 does not have unlink @@ -1707,11 +1739,11 @@ sys_unlink :: proc(path: cstring) -> int { } } -sys_unlinkat :: proc(dfd: int, path: cstring, flag: int = 0) -> int { +sys_unlinkat :: proc "contextless" (dfd: int, path: cstring, flag: int = 0) -> int { return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), flag)) } -sys_rmdir :: proc(path: cstring) -> int { +sys_rmdir :: proc "contextless" (path: cstring) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(SYS_rmdir, uintptr(rawptr(path)))) } else { // NOTE: arm64 does not have rmdir @@ -1719,7 +1751,7 @@ sys_rmdir :: proc(path: cstring) -> int { } } -sys_mkdir :: proc(path: cstring, mode: int) -> int { +sys_mkdir :: proc "contextless" (path: cstring, mode: int) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode))) } else { // NOTE: arm64 does not have mkdir @@ -1727,11 +1759,11 @@ sys_mkdir :: proc(path: cstring, mode: int) -> int { } } -sys_mkdirat :: proc(dfd: int, path: cstring, mode: int) -> int { +sys_mkdirat :: proc "contextless" (dfd: int, path: cstring, mode: int) -> int { return int(intrinsics.syscall(SYS_mkdirat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode))) } -sys_mknod :: proc(path: cstring, mode: int, dev: int) -> int { +sys_mknod :: proc "contextless" (path: cstring, mode: int, dev: int) -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(SYS_mknod, uintptr(rawptr(path)), uintptr(mode), uintptr(dev))) } else { // NOTE: arm64 does not have mknod @@ -1739,11 +1771,11 @@ sys_mknod :: proc(path: cstring, mode: int, dev: int) -> int { } } -sys_mknodat :: proc(dfd: int, path: cstring, mode: int, dev: int) -> int { +sys_mknodat :: proc "contextless" (dfd: int, path: cstring, mode: int, dev: int) -> int { return int(intrinsics.syscall(SYS_mknodat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode), uintptr(dev))) } -sys_truncate :: proc(path: cstring, length: i64) -> int { +sys_truncate :: proc "contextless" (path: cstring, length: i64) -> int { when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 { return int(intrinsics.syscall(SYS_truncate, uintptr(rawptr(path)), uintptr(length))) } else { @@ -1753,7 +1785,7 @@ sys_truncate :: proc(path: cstring, length: i64) -> int { } } -sys_ftruncate :: proc(fd: int, length: i64) -> int { +sys_ftruncate :: proc "contextless" (fd: int, length: i64) -> int { when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 { return int(intrinsics.syscall(SYS_ftruncate, uintptr(fd), uintptr(length))) } else { @@ -1763,15 +1795,15 @@ sys_ftruncate :: proc(fd: int, length: i64) -> int { } } -sys_fsync :: proc(fd: int) -> int { +sys_fsync :: proc "contextless" (fd: int) -> int { return int(intrinsics.syscall(SYS_fsync, uintptr(fd))) } -sys_getdents64 :: proc(fd: int, dirent: rawptr, count: int) -> int { +sys_getdents64 :: proc "contextless" (fd: int, dirent: rawptr, count: int) -> int { return int(intrinsics.syscall(SYS_getdents64, uintptr(fd), uintptr(dirent), uintptr(count))) } -sys_fork :: proc() -> int { +sys_fork :: proc "contextless" () -> int { when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(SYS_fork)) } else { @@ -1779,13 +1811,30 @@ sys_fork :: proc() -> int { } } +sys_mmap :: proc "contextless" (addr: rawptr, length: uint, prot, flags, fd: int, offset: uintptr) -> int { + return int(intrinsics.syscall(unix.SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)) +} + +sys_munmap :: proc "contextless" (addr: rawptr, length: uint) -> int { + return int(intrinsics.syscall(unix.SYS_munmap, uintptr(addr), uintptr(length))) +} + +sys_mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: int) -> int { + return int(intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uintptr(prot))) +} + +sys_madvise :: proc "contextless" (addr: rawptr, length: uint, advice: int) -> int { + return int(intrinsics.syscall(unix.SYS_madvise, uintptr(addr), uintptr(length), uintptr(advice))) +} + + // NOTE: Unsure about if this works directly on 32 bit archs. It may need 32 bit version of the time struct. // As of Linux 5.1, there is a utimensat_time64 function. Maybe use this in the future? -sys_utimensat :: proc(dfd: int, path: cstring, times: rawptr, flags: int) -> int { +sys_utimensat :: proc "contextless" (dfd: int, path: cstring, times: rawptr, flags: int) -> int { return int(intrinsics.syscall(SYS_utimensat, uintptr(dfd), uintptr(rawptr(path)), uintptr(times), uintptr(flags))) } -get_errno :: proc(res: int) -> i32 { +get_errno :: proc "contextless" (res: int) -> i32 { if res < 0 && res > -4096 { return i32(-res) } From 1a2c36e482315f87af92f29d36ad204789a129a5 Mon Sep 17 00:00:00 2001 From: CiD- Date: Fri, 8 Apr 2022 13:52:36 -0400 Subject: [PATCH 21/28] whoops --- core/sys/unix/syscalls_linux.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index e72bfcedf..8e0f7d89d 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -1812,19 +1812,19 @@ sys_fork :: proc "contextless" () -> int { } sys_mmap :: proc "contextless" (addr: rawptr, length: uint, prot, flags, fd: int, offset: uintptr) -> int { - return int(intrinsics.syscall(unix.SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)) + return int(intrinsics.syscall(SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)) } sys_munmap :: proc "contextless" (addr: rawptr, length: uint) -> int { - return int(intrinsics.syscall(unix.SYS_munmap, uintptr(addr), uintptr(length))) + return int(intrinsics.syscall(SYS_munmap, uintptr(addr), uintptr(length))) } sys_mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: int) -> int { - return int(intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uintptr(prot))) + return int(intrinsics.syscall(SYS_mprotect, uintptr(addr), uintptr(length), uintptr(prot))) } sys_madvise :: proc "contextless" (addr: rawptr, length: uint, advice: int) -> int { - return int(intrinsics.syscall(unix.SYS_madvise, uintptr(addr), uintptr(length), uintptr(advice))) + return int(intrinsics.syscall(SYS_madvise, uintptr(addr), uintptr(length), uintptr(advice))) } From 5bc81642748af7c9f1eb6847a97639620e2d41c6 Mon Sep 17 00:00:00 2001 From: CiD- Date: Tue, 26 Apr 2022 17:11:30 -0400 Subject: [PATCH 22/28] add mremap + flags --- core/sys/unix/syscalls_linux.odin | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 8e0f7d89d..630b4ef24 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -1524,18 +1524,26 @@ AT_REMOVEDIR :: uintptr(0x200) AT_SYMLINK_FOLLOW :: uintptr(0x400) AT_SYMLINK_NOFOLLOW :: uintptr(0x100) -PROT_NONE :: 0x0 -PROT_READ :: 0x1 -PROT_WRITE :: 0x2 -PROT_EXEC :: 0x4 +// mmap flags +PROT_NONE :: 0x0 +PROT_READ :: 0x1 +PROT_WRITE :: 0x2 +PROT_EXEC :: 0x4 PROT_GROWSDOWN :: 0x01000000 -PROT_GROWSUP :: 0x02000000 +PROT_GROWSUP :: 0x02000000 -MAP_FIXED :: 0x1 -MAP_PRIVATE :: 0x2 -MAP_SHARED :: 0x4 -MAP_ANONYMOUS :: 0x20 +MAP_FIXED :: 0x10 +MAP_SHARED :: 0x1 +MAP_PRIVATE :: 0x2 +MAP_SHARED_VALIDATE :: 0x3 +MAP_ANONYMOUS :: 0x20 +// mremap flags +MREMAP_MAYMOVE :: 1 +MREMAP_FIXED :: 2 +MREMAP_DONTUNMAP :: 4 + +// madvise flags MADV_NORMAL :: 0 MADV_RANDOM :: 1 MADV_SEQUENTIAL :: 2 @@ -1815,6 +1823,10 @@ sys_mmap :: proc "contextless" (addr: rawptr, length: uint, prot, flags, fd: int return int(intrinsics.syscall(SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)) } +sys_mremap :: proc "contextless" (addr: rawptr, old_length, new_length: uint, flags: int, new_addr: rawptr = nil) -> int { + return int(intrinsics.syscall(SYS_mremap, uintptr(addr), uintptr(old_length), uintptr(new_length), uintptr(flags), uintptr(new_addr))) +} + sys_munmap :: proc "contextless" (addr: rawptr, length: uint) -> int { return int(intrinsics.syscall(SYS_munmap, uintptr(addr), uintptr(length))) } From 7e0cc0af252810ea0177eabbb773695c33bd3544 Mon Sep 17 00:00:00 2001 From: jason Date: Wed, 4 May 2022 17:55:15 -0400 Subject: [PATCH 23/28] heap_linux.odin --- core/os/os2/heap_linux.odin | 719 +++++++++++++++++++++++++++++++++++- 1 file changed, 709 insertions(+), 10 deletions(-) diff --git a/core/os/os2/heap_linux.odin b/core/os/os2/heap_linux.odin index f617f8cc8..d72fab3ec 100644 --- a/core/os/os2/heap_linux.odin +++ b/core/os/os2/heap_linux.odin @@ -1,27 +1,726 @@ //+private package os2 +import "core:sys/unix" +import "core:sync" import "core:mem" -heap_alloc :: proc(size: int) -> rawptr { - // TODO - return nil +// NOTEs +// +// All allocations below DIRECT_MMAP_THRESHOLD exist inside of memory "Regions." A region +// consists of a Region_Header and the memory that will be divided into allocations to +// send to the user. The memory is an array of "Allocation_Headers" which are 8 bytes. +// Allocation_Headers are used to navigate the memory in the region. The "next" member of +// the Allocation_Header points to the next header, and the space between the headers +// can be used to send to the user. This space between is referred to as "blocks" in the +// code. The indexes in the header refer to these blocks instead of bytes. This allows us +// to index all the memory in the region with a u16. +// +// When an allocation request is made, it will use the first free block that can contain +// the entire block. If there is an excess number of blocks (as specified by the constant +// BLOCK_SEGMENT_THRESHOLD), this extra space will be segmented and left in the free_list. +// +// To keep the implementation simple, there can never exist 2 free blocks adjacent to each +// other. Any freeing will result in attempting to merge the blocks before and after the +// newly free'd blocks. +// +// Any request for size above the DIRECT_MMAP_THRESHOLD will result in the allocation +// getting its own individual mmap. Individual mmaps will still get an Allocation_Header +// that contains the size with the last bit set to 1 to indicate it is indeed a direct +// mmap allocation. + +// Why not brk? +// glibc's malloc utilizes a mix of the brk and mmap system calls. This implementation +// does *not* utilize the brk system call to avoid possible conflicts with foreign C +// code. Just because we aren't directly using libc, there is nothing stopping the user +// from doing it. + +// What's with all the #no_bounds_check? +// When memory is returned from mmap, it technically doesn't get written ... well ... anywhere +// until that region is written to by *you*. So, when a new region is created, we call mmap +// to get a pointer to some memory, and we claim that memory is a ^Region. Therefor, the +// region itself is never formally initialized by the compiler as this would result in writing +// zeros to memory that we can already assume are 0. This would also have the effect of +// actually commiting this data to memory whether it gets used or not. + + +// +// Some variables to play with +// + +// Minimum blocks used for any one allocation +MINIMUM_BLOCK_COUNT :: 2 + +// Number of extra blocks beyond the requested amount where we would segment. +// E.g. (blocks) |H0123456| 7 available +// |H01H0123| Ask for 2, now 4 available +BLOCK_SEGMENT_THRESHOLD :: 4 + +// Anything above this threshold will get its own memory map. Since regions +// are indexed by 16 bit integers, this value should not surpass max(u16) * 6 +DIRECT_MMAP_THRESHOLD_USER :: int(max(u16)) + +// The point at which we convert direct mmap to region. This should be a decent +// amount less than DIRECT_MMAP_THRESHOLD to avoid jumping in and out of regions. +MMAP_TO_REGION_SHRINK_THRESHOLD :: DIRECT_MMAP_THRESHOLD - PAGE_SIZE * 4 + +// free_list is dynamic and is initialized in the begining of the region memory +// when the region is initialized. Once resized, it can be moved anywhere. +FREE_LIST_DEFAULT_CAP :: 32 + + +// +// Other constants that should not be touched +// + +// This universally seems to be 4096 outside of uncommon archs. +PAGE_SIZE :: 4096 + +// just rounding up to nearest PAGE_SIZE +DIRECT_MMAP_THRESHOLD :: (DIRECT_MMAP_THRESHOLD_USER-1) + PAGE_SIZE - (DIRECT_MMAP_THRESHOLD_USER-1) % PAGE_SIZE + +// Regions must be big enough to hold DIRECT_MMAP_THRESHOLD - 1 as well +// as end right on a page boundary as to not waste space. +SIZE_OF_REGION :: DIRECT_MMAP_THRESHOLD + 4 * int(PAGE_SIZE) + +// size of user memory blocks +BLOCK_SIZE :: size_of(Allocation_Header) + +// number of allocation sections (call them blocks) of the region used for allocations +BLOCKS_PER_REGION :: u16((SIZE_OF_REGION - size_of(Region_Header)) / BLOCK_SIZE) + +// minimum amount of space that can used by any individual allocation (includes header) +MINIMUM_ALLOCATION :: (MINIMUM_BLOCK_COUNT * BLOCK_SIZE) + BLOCK_SIZE + +// This is used as a boolean value for Region_Header.local_addr. +CURRENTLY_ACTIVE :: (^^Region)(~uintptr(0)) + +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 + + +//@thread_local _local_region: ^Region +_local_region: ^Region +global_regions: ^Region + + +// There is no way of correctly setting the last bit of free_idx or +// the last bit of requested, so we can safely use it as a flag to +// determine if we are interacting with a direct mmap. +REQUESTED_MASK :: 0x7FFFFFFFFFFFFFFF +IS_DIRECT_MMAP :: 0x8000000000000000 + +// Special free_idx value that does not index the free_list. +NOT_FREE :: 0x7FFF +Allocation_Header :: struct #raw_union { + using _: struct { + // Block indicies + idx: u16, + prev: u16, + next: u16, + free_idx: u16, + }, + requested: u64, } -heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { - // TODO - return nil +Region_Header :: struct #align 16 { + next_region: ^Region, // points to next region in global_heap (linked list) + local_addr: ^^Region, // tracks region ownership via address of _local_region + reset_addr: ^^Region, // tracks old local addr for reset + free_list: []u16, + free_list_len: u16, + free_blocks: u16, // number of free blocks in region (includes headers) + last_used: u16, // farthest back block that has been used (need zeroing?) + _reserved: u16, } -heap_free :: proc(ptr: rawptr) { - if ptr == nil { + +Region :: struct { + hdr: Region_Header, + memory: [BLOCKS_PER_REGION]Allocation_Header, +} + +heap_alloc :: proc(size: int) -> rawptr { + if size >= DIRECT_MMAP_THRESHOLD { + return _direct_mmap_alloc(size) + } + + // atomically check if the local region has been stolen + if _local_region != nil { + res := sync.atomic_compare_exchange_strong_explicit( + &_local_region.hdr.local_addr, + &_local_region, + CURRENTLY_ACTIVE, + .Acquire, + .Relaxed, + ) + if res != &_local_region { + // At this point, the region has been stolen and res contains the unexpected value + expected := res + if res != CURRENTLY_ACTIVE { + expected = res + res = sync.atomic_compare_exchange_strong_explicit( + &_local_region.hdr.local_addr, + expected, + CURRENTLY_ACTIVE, + .Acquire, + .Relaxed, + ) + } + if res != expected { + _local_region = nil + } + } + } + + size := size + size = _round_up_to_nearest(size, BLOCK_SIZE) + blocks_needed := u16(max(MINIMUM_BLOCK_COUNT, size / BLOCK_SIZE)) + + // retrieve a region if new thread or stolen + if _local_region == nil { + _local_region, _ = _region_retrieve_with_space(blocks_needed) + if _local_region == nil { + return nil + } + } + defer sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release) + + // At this point we have a usable region. Let's find the user some memory + idx: u16 + local_region_idx := _region_get_local_idx() + back_idx := -1 + infinite: for { + for i := 0; i < int(_local_region.hdr.free_list_len); i += 1 { + idx = _local_region.hdr.free_list[i] + #no_bounds_check if _get_block_count(_local_region.memory[idx]) >= blocks_needed { + break infinite + } + } + sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release) + _local_region, back_idx = _region_retrieve_with_space(blocks_needed, local_region_idx, back_idx) + } + user_ptr, used := _region_get_block(_local_region, idx, blocks_needed) + _local_region.hdr.free_blocks -= (used + 1) + + // If this memory was ever used before, it now needs to be zero'd. + if idx < _local_region.hdr.last_used { + mem.zero(user_ptr, int(used) * BLOCK_SIZE) + } else { + _local_region.hdr.last_used = idx + used + } + + return user_ptr +} + +heap_resize :: proc(old_memory: rawptr, new_size: int) -> rawptr #no_bounds_check { + alloc := _get_allocation_header(old_memory) + if alloc.requested & IS_DIRECT_MMAP > 0 { + return _direct_mmap_resize(alloc, new_size) + } + + if new_size > DIRECT_MMAP_THRESHOLD { + return _direct_mmap_from_region(alloc, new_size) + } + + return _region_resize(alloc, new_size) +} + +heap_free :: proc(memory: rawptr) { + alloc := _get_allocation_header(memory) + if alloc.requested & IS_DIRECT_MMAP == IS_DIRECT_MMAP { + _direct_mmap_free(alloc) return } - // TODO + + assert(alloc.free_idx == NOT_FREE) + + _region_find_and_assign_local(alloc) + _region_local_free(alloc) + sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release) +} + +// +// Regions +// +_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 { + return nil + } + new_region := (^Region)(uintptr(res)) + + new_region.hdr.local_addr = CURRENTLY_ACTIVE + new_region.hdr.reset_addr = &_local_region + + free_list_blocks := _round_up_to_nearest(FREE_LIST_DEFAULT_CAP, FREE_LIST_ENTRIES_PER_BLOCK) + _region_assign_free_list(new_region, &new_region.memory[1], u16(free_list_blocks) * FREE_LIST_ENTRIES_PER_BLOCK) + + // + 2 to account for free_list's allocation header + first_user_block := len(new_region.hdr.free_list) / FREE_LIST_ENTRIES_PER_BLOCK + 2 + + // first allocation header (this is a free list) + new_region.memory[0].next = u16(first_user_block) + new_region.memory[0].free_idx = NOT_FREE + new_region.memory[first_user_block].idx = u16(first_user_block) + new_region.memory[first_user_block].next = BLOCKS_PER_REGION - 1 + + // add the first user block to the free list + new_region.hdr.free_list[0] = u16(first_user_block) + new_region.hdr.free_list_len = 1 + new_region.hdr.free_blocks = _get_block_count(new_region.memory[first_user_block]) + 1 + + for r := sync.atomic_compare_exchange_strong(&global_regions, nil, new_region); + r != nil; + r = sync.atomic_compare_exchange_strong(&r.hdr.next_region, nil, new_region) {} + + return new_region +} + +_region_resize :: proc(alloc: ^Allocation_Header, new_size: int, alloc_is_free_list: bool = false) -> rawptr #no_bounds_check { + assert(alloc.free_idx == NOT_FREE) + + old_memory := mem.ptr_offset(alloc, 1) + + old_block_count := _get_block_count(alloc^) + new_block_count := u16( + max(MINIMUM_BLOCK_COUNT, _round_up_to_nearest(new_size, BLOCK_SIZE) / BLOCK_SIZE), + ) + if new_block_count < old_block_count { + if new_block_count - old_block_count >= MINIMUM_BLOCK_COUNT { + _region_find_and_assign_local(alloc) + _region_segment(_local_region, alloc, new_block_count, alloc.free_idx) + new_block_count = _get_block_count(alloc^) + sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release) + } + // need to zero anything within the new block that that lies beyond new_size + extra_bytes := int(new_block_count * BLOCK_SIZE) - new_size + extra_bytes_ptr := mem.ptr_offset((^u8)(alloc), new_size + BLOCK_SIZE) + mem.zero(extra_bytes_ptr, extra_bytes) + return old_memory + } + + if !alloc_is_free_list { + _region_find_and_assign_local(alloc) + } + defer if !alloc_is_free_list { + sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release) + } + + // First, let's see if we can grow in place. + if alloc.next != BLOCKS_PER_REGION - 1 && _local_region.memory[alloc.next].free_idx != NOT_FREE { + next_alloc := _local_region.memory[alloc.next] + total_available := old_block_count + _get_block_count(next_alloc) + 1 + if total_available >= new_block_count { + alloc.next = next_alloc.next + _local_region.memory[alloc.next].prev = alloc.idx + if total_available - new_block_count > BLOCK_SEGMENT_THRESHOLD { + _region_segment(_local_region, alloc, new_block_count, next_alloc.free_idx) + } else { + _region_free_list_remove(_local_region, next_alloc.free_idx) + } + mem.zero(&_local_region.memory[next_alloc.idx], int(alloc.next - next_alloc.idx) * BLOCK_SIZE) + _local_region.hdr.last_used = max(alloc.next, _local_region.hdr.last_used) + _local_region.hdr.free_blocks -= (_get_block_count(alloc^) - old_block_count) + if alloc_is_free_list { + _region_assign_free_list(_local_region, old_memory, _get_block_count(alloc^)) + } + return old_memory + } + } + + // If we made it this far, we need to resize, copy, zero and free. + region_iter := _local_region + local_region_idx := _region_get_local_idx() + back_idx := -1 + idx: u16 + infinite: for { + for i := 0; i < len(region_iter.hdr.free_list); i += 1 { + idx = region_iter.hdr.free_list[i] + if _get_block_count(region_iter.memory[idx]) >= new_block_count { + break infinite + } + } + if region_iter != _local_region { + sync.atomic_store_explicit( + ®ion_iter.hdr.local_addr, + region_iter.hdr.reset_addr, + .Release, + ) + } + region_iter, back_idx = _region_retrieve_with_space(new_block_count, local_region_idx, back_idx) + } + if region_iter != _local_region { + sync.atomic_store_explicit( + ®ion_iter.hdr.local_addr, + region_iter.hdr.reset_addr, + .Release, + ) + } + + // copy from old memory + new_memory, used_blocks := _region_get_block(region_iter, idx, new_block_count) + mem.copy(new_memory, old_memory, int(old_block_count * BLOCK_SIZE)) + + // zero any new memory + addon_section := mem.ptr_offset((^Allocation_Header)(new_memory), old_block_count) + new_blocks := used_blocks - old_block_count + mem.zero(addon_section, int(new_blocks) * BLOCK_SIZE) + + region_iter.hdr.free_blocks -= (used_blocks + 1) + + // Set free_list before freeing. + if alloc_is_free_list { + _region_assign_free_list(_local_region, new_memory, used_blocks) + } + + // free old memory + _region_local_free(alloc) + return new_memory +} + +_region_local_free :: proc(alloc: ^Allocation_Header) #no_bounds_check { + alloc := alloc + add_to_free_list := true + + _local_region.hdr.free_blocks += _get_block_count(alloc^) + 1 + + // try to merge with prev + if alloc.idx > 0 && _local_region.memory[alloc.prev].free_idx != NOT_FREE { + _local_region.memory[alloc.prev].next = alloc.next + _local_region.memory[alloc.next].prev = alloc.prev + alloc = &_local_region.memory[alloc.prev] + add_to_free_list = false + } + + // try to merge with next + if alloc.next < BLOCKS_PER_REGION - 1 && _local_region.memory[alloc.next].free_idx != NOT_FREE { + old_next := alloc.next + alloc.next = _local_region.memory[old_next].next + _local_region.memory[alloc.next].prev = alloc.idx + + if add_to_free_list { + _local_region.hdr.free_list[_local_region.memory[old_next].free_idx] = alloc.idx + alloc.free_idx = _local_region.memory[old_next].free_idx + } else { + // NOTE: We have aleady merged with prev, and now merged with next. + // Now, we are actually going to remove from the free_list. + _region_free_list_remove(_local_region, _local_region.memory[old_next].free_idx) + } + add_to_free_list = false + } + + // This is the only place where anything is appended to the free list. + if add_to_free_list { + fl := _local_region.hdr.free_list + alloc.free_idx = _local_region.hdr.free_list_len + fl[alloc.free_idx] = alloc.idx + _local_region.hdr.free_list_len += 1 + if int(_local_region.hdr.free_list_len) == len(fl) { + free_alloc := _get_allocation_header(mem.raw_data(_local_region.hdr.free_list)) + _region_resize(free_alloc, len(fl) * 2 * size_of(fl[0]), true) + } + } +} + +_region_assign_free_list :: proc(region: ^Region, memory: rawptr, blocks: u16) { + raw_free_list := transmute(mem.Raw_Slice)region.hdr.free_list + raw_free_list.len = int(blocks) * FREE_LIST_ENTRIES_PER_BLOCK + raw_free_list.data = memory + region.hdr.free_list = transmute([]u16)(raw_free_list) +} + +_region_retrieve_with_space :: proc(blocks: u16, local_idx: int = -1, back_idx: int = -1) -> (^Region, int) { + r: ^Region + idx: int + for r = global_regions; r != nil; r = r.hdr.next_region { + if idx == local_idx || idx < back_idx || r.hdr.free_blocks < blocks { + idx += 1 + continue + } + idx += 1 + local_addr: ^^Region = sync.atomic_load(&r.hdr.local_addr) + if local_addr != CURRENTLY_ACTIVE { + res := sync.atomic_compare_exchange_strong_explicit( + &r.hdr.local_addr, + local_addr, + CURRENTLY_ACTIVE, + .Acquire, + .Relaxed, + ) + if res == local_addr { + r.hdr.reset_addr = local_addr + return r, idx + } + } + } + + return _new_region(), idx +} + +_region_retrieve_from_addr :: proc(addr: rawptr) -> ^Region { + r: ^Region + for r = global_regions; r != nil; r = r.hdr.next_region { + if _region_contains_mem(r, addr) { + return r + } + } + unreachable() +} + +_region_get_block :: proc(region: ^Region, idx, blocks_needed: u16) -> (rawptr, u16) #no_bounds_check { + alloc := ®ion.memory[idx] + + assert(alloc.free_idx != NOT_FREE) + assert(alloc.next > 0) + + block_count := _get_block_count(alloc^) + segmented_blocks: u16 + + if block_count - blocks_needed > BLOCK_SEGMENT_THRESHOLD { + _region_segment(region, alloc, blocks_needed, alloc.free_idx) + } else { + _region_free_list_remove(region, alloc.free_idx) + } + + alloc.free_idx = NOT_FREE + return mem.ptr_offset(alloc, 1), _get_block_count(alloc^) +} + +_region_segment :: proc(region: ^Region, alloc: ^Allocation_Header, blocks, new_free_idx: u16) #no_bounds_check { + old_next := alloc.next + alloc.next = alloc.idx + blocks + 1 + region.memory[old_next].prev = alloc.next + + // Initialize alloc.next allocation header here. + region.memory[alloc.next].prev = alloc.idx + region.memory[alloc.next].next = old_next + region.memory[alloc.next].idx = alloc.next + region.memory[alloc.next].free_idx = new_free_idx + + // Replace our original spot in the free_list with new segment. + region.hdr.free_list[new_free_idx] = alloc.next +} + +_region_get_local_idx :: proc() -> int { + idx: int + for r := global_regions; r != nil; r = r.hdr.next_region { + if r == _local_region { + return idx + } + idx += 1 + } + + return -1 +} + +_region_find_and_assign_local :: proc(alloc: ^Allocation_Header) { + // Find the region that contains this memory + if !_region_contains_mem(_local_region, alloc) { + _local_region = _region_retrieve_from_addr(alloc) + } + + // At this point, _local_region is set correctly. Spin until acquired + res: ^^Region + for res != &_local_region { + res = sync.atomic_compare_exchange_strong_explicit( + &_local_region.hdr.local_addr, + &_local_region, + CURRENTLY_ACTIVE, + .Acquire, + .Relaxed, + ) + } +} + +_region_contains_mem :: proc(r: ^Region, memory: rawptr) -> bool #no_bounds_check { + if r == nil { + return false + } + mem_int := uintptr(memory) + return mem_int >= uintptr(&r.memory[0]) && mem_int <= uintptr(&r.memory[BLOCKS_PER_REGION - 1]) +} + +_region_free_list_remove :: proc(region: ^Region, free_idx: u16) #no_bounds_check { + // pop, swap and update allocation hdr + if n := region.hdr.free_list_len - 1; free_idx != n { + region.hdr.free_list[free_idx] = region.hdr.free_list[n] + alloc_idx := region.hdr.free_list[free_idx] + region.memory[alloc_idx].free_idx = free_idx + } + region.hdr.free_list_len -= 1 +} + +// +// Direct mmap +// +_direct_mmap_alloc :: proc(size: int) -> rawptr { + 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 { + return nil + } + + alloc := (^Allocation_Header)(uintptr(new_allocation)) + alloc.requested = u64(size) // NOTE: requested = requested size + alloc.requested += IS_DIRECT_MMAP + return rawptr(mem.ptr_offset(alloc, 1)) +} + +_direct_mmap_resize :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr { + old_requested := int(alloc.requested & REQUESTED_MASK) + old_mmap_size := _round_up_to_nearest(old_requested + BLOCK_SIZE, PAGE_SIZE) + new_mmap_size := _round_up_to_nearest(new_size + BLOCK_SIZE, PAGE_SIZE) + if int(new_mmap_size) < MMAP_TO_REGION_SHRINK_THRESHOLD { + return _direct_mmap_to_region(alloc, old_mmap_size, new_mmap_size) + } else if old_requested == new_size { + 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 { + return nil + } + + new_header := (^Allocation_Header)(uintptr(new_allocation)) + new_header.requested = u64(new_size) + new_header.requested += IS_DIRECT_MMAP + + if new_mmap_size > old_mmap_size { + // new section may not be pointer aligned, so cast to ^u8 + new_section := mem.ptr_offset((^u8)(new_header), old_requested + BLOCK_SIZE) + mem.zero(new_section, new_mmap_size - old_mmap_size) + } + return mem.ptr_offset(new_header, 1) + +} + +_direct_mmap_from_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr { + new_memory := _direct_mmap_alloc(new_size) + if new_memory != nil { + old_memory := mem.ptr_offset(alloc, 1) + mem.copy(new_memory, old_memory, int(_get_block_count(alloc^)) * BLOCK_SIZE) + } + _region_find_and_assign_local(alloc) + _region_local_free(alloc) + sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release) + return new_memory +} + +_direct_mmap_to_region :: proc(alloc: ^Allocation_Header, old_size, new_size: int) -> rawptr { + new_memory := heap_alloc(new_size) + if new_memory != nil { + mem.copy(new_memory, mem.ptr_offset(alloc, -1), old_size) + _direct_mmap_free(alloc) + } + return new_memory +} + +_direct_mmap_free :: proc(alloc: ^Allocation_Header) { + requested := int(alloc.requested & REQUESTED_MASK) + mmap_size := _round_up_to_nearest(requested + BLOCK_SIZE, PAGE_SIZE) + unix.sys_munmap(alloc, uint(mmap_size)) +} + +// +// Util +// + +_get_block_count :: #force_inline proc(alloc: Allocation_Header) -> u16 { + return alloc.next - alloc.idx - 1 +} + +_get_allocation_header :: #force_inline proc(raw_mem: rawptr) -> ^Allocation_Header { + return mem.ptr_offset((^Allocation_Header)(raw_mem), -1) +} + +_round_up_to_nearest :: #force_inline proc(size, round: int) -> int { + return (size-1) + round - (size-1) % round } _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 + // + // NOTE(tetra, 2020-01-14): The heap doesn't respect alignment. + // Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert + // padding. We also store the original pointer returned by heap_alloc right before + // the pointer we return to the user. + // + + aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) { + a := max(alignment, align_of(rawptr)) + space := size + a - 1 + + allocated_mem: rawptr + if old_ptr != nil { + original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^ + allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr)) + } else { + allocated_mem = heap_alloc(space+size_of(rawptr)) + } + aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr))) + + ptr := uintptr(aligned_mem) + aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) + diff := int(aligned_ptr - ptr) + if (size + diff) > space { + return nil, .Out_Of_Memory + } + + aligned_mem = rawptr(aligned_ptr) + mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem + + return mem.byte_slice(aligned_mem, size), nil + } + + aligned_free :: proc(p: rawptr) { + if p != nil { + heap_free(mem.ptr_offset((^rawptr)(p), -1)^) + } + } + + aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) { + if p == nil { + return nil, nil + } + + return aligned_alloc(new_size, new_alignment, p) + } + + switch mode { + case .Alloc: + return aligned_alloc(size, alignment) + + case .Free: + aligned_free(old_memory) + + case .Free_All: + return nil, .Mode_Not_Implemented + + case .Resize: + if old_memory == nil { + return aligned_alloc(size, alignment) + } + return aligned_resize(old_memory, old_size, size, alignment) + + case .Query_Features: + set := (^mem.Allocator_Mode_Set)(old_memory) + if set != nil { + set^ = {.Alloc, .Free, .Resize, .Query_Features} + } + return nil, nil + + case .Query_Info: + return nil, .Mode_Not_Implemented + } + return nil, nil } + From bac96cf2ad8929c0ff06670fd40ed847fe997ba8 Mon Sep 17 00:00:00 2001 From: jason Date: Wed, 4 May 2022 18:32:14 -0400 Subject: [PATCH 24/28] fix mmap_to_region --- core/os/os2/heap_linux.odin | 169 ++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 86 deletions(-) diff --git a/core/os/os2/heap_linux.odin b/core/os/os2/heap_linux.odin index d72fab3ec..8706b51ef 100644 --- a/core/os/os2/heap_linux.odin +++ b/core/os/os2/heap_linux.odin @@ -141,6 +141,86 @@ Region :: struct { memory: [BLOCKS_PER_REGION]Allocation_Header, } +_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) { + // + // NOTE(tetra, 2020-01-14): The heap doesn't respect alignment. + // Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert + // padding. We also store the original pointer returned by heap_alloc right before + // the pointer we return to the user. + // + + aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) { + a := max(alignment, align_of(rawptr)) + space := size + a - 1 + + allocated_mem: rawptr + if old_ptr != nil { + original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^ + allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr)) + } else { + allocated_mem = heap_alloc(space+size_of(rawptr)) + } + aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr))) + + ptr := uintptr(aligned_mem) + aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) + diff := int(aligned_ptr - ptr) + if (size + diff) > space { + return nil, .Out_Of_Memory + } + + aligned_mem = rawptr(aligned_ptr) + mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem + + return mem.byte_slice(aligned_mem, size), nil + } + + aligned_free :: proc(p: rawptr) { + if p != nil { + heap_free(mem.ptr_offset((^rawptr)(p), -1)^) + } + } + + aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) { + if p == nil { + return nil, nil + } + + return aligned_alloc(new_size, new_alignment, p) + } + + switch mode { + case .Alloc: + return aligned_alloc(size, alignment) + + case .Free: + aligned_free(old_memory) + + case .Free_All: + return nil, .Mode_Not_Implemented + + case .Resize: + if old_memory == nil { + return aligned_alloc(size, alignment) + } + return aligned_resize(old_memory, old_size, size, alignment) + + case .Query_Features: + set := (^mem.Allocator_Mode_Set)(old_memory) + if set != nil { + set^ = {.Alloc, .Free, .Resize, .Query_Features} + } + return nil, nil + + case .Query_Info: + return nil, .Mode_Not_Implemented + } + + return nil, nil +} + heap_alloc :: proc(size: int) -> rawptr { if size >= DIRECT_MMAP_THRESHOLD { return _direct_mmap_alloc(size) @@ -476,8 +556,6 @@ _region_get_block :: proc(region: ^Region, idx, blocks_needed: u16) -> (rawptr, assert(alloc.next > 0) block_count := _get_block_count(alloc^) - segmented_blocks: u16 - if block_count - blocks_needed > BLOCK_SEGMENT_THRESHOLD { _region_segment(region, alloc, blocks_needed, alloc.free_idx) } else { @@ -573,7 +651,7 @@ _direct_mmap_resize :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr old_mmap_size := _round_up_to_nearest(old_requested + BLOCK_SIZE, PAGE_SIZE) new_mmap_size := _round_up_to_nearest(new_size + BLOCK_SIZE, PAGE_SIZE) if int(new_mmap_size) < MMAP_TO_REGION_SHRINK_THRESHOLD { - return _direct_mmap_to_region(alloc, old_mmap_size, new_mmap_size) + return _direct_mmap_to_region(alloc, new_size) } else if old_requested == new_size { return mem.ptr_offset(alloc, 1) } @@ -613,10 +691,10 @@ _direct_mmap_from_region :: proc(alloc: ^Allocation_Header, new_size: int) -> ra return new_memory } -_direct_mmap_to_region :: proc(alloc: ^Allocation_Header, old_size, new_size: int) -> rawptr { +_direct_mmap_to_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr { new_memory := heap_alloc(new_size) if new_memory != nil { - mem.copy(new_memory, mem.ptr_offset(alloc, -1), old_size) + mem.copy(new_memory, mem.ptr_offset(alloc, -1), new_size) _direct_mmap_free(alloc) } return new_memory @@ -643,84 +721,3 @@ _get_allocation_header :: #force_inline proc(raw_mem: rawptr) -> ^Allocation_Hea _round_up_to_nearest :: #force_inline proc(size, round: int) -> int { return (size-1) + round - (size-1) % round } - -_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) { - // - // NOTE(tetra, 2020-01-14): The heap doesn't respect alignment. - // Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert - // padding. We also store the original pointer returned by heap_alloc right before - // the pointer we return to the user. - // - - aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) { - a := max(alignment, align_of(rawptr)) - space := size + a - 1 - - allocated_mem: rawptr - if old_ptr != nil { - original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^ - allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr)) - } else { - allocated_mem = heap_alloc(space+size_of(rawptr)) - } - aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr))) - - ptr := uintptr(aligned_mem) - aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) - diff := int(aligned_ptr - ptr) - if (size + diff) > space { - return nil, .Out_Of_Memory - } - - aligned_mem = rawptr(aligned_ptr) - mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem - - return mem.byte_slice(aligned_mem, size), nil - } - - aligned_free :: proc(p: rawptr) { - if p != nil { - heap_free(mem.ptr_offset((^rawptr)(p), -1)^) - } - } - - aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) { - if p == nil { - return nil, nil - } - - return aligned_alloc(new_size, new_alignment, p) - } - - switch mode { - case .Alloc: - return aligned_alloc(size, alignment) - - case .Free: - aligned_free(old_memory) - - case .Free_All: - return nil, .Mode_Not_Implemented - - case .Resize: - if old_memory == nil { - return aligned_alloc(size, alignment) - } - return aligned_resize(old_memory, old_size, size, alignment) - - case .Query_Features: - set := (^mem.Allocator_Mode_Set)(old_memory) - if set != nil { - set^ = {.Alloc, .Free, .Resize, .Query_Features} - } - return nil, nil - - case .Query_Info: - return nil, .Mode_Not_Implemented - } - - return nil, nil -} - From 97d1a6787189d7630650612f44c393f7a635019a Mon Sep 17 00:00:00 2001 From: jason Date: Wed, 4 May 2022 18:45:39 -0400 Subject: [PATCH 25/28] make vet happy, thread_local heap --- core/os/os2/heap_linux.odin | 4 ++-- core/os/os2/path_linux.odin | 20 +++++++++----------- core/os/os2/stat_linux.odin | 1 - 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/core/os/os2/heap_linux.odin b/core/os/os2/heap_linux.odin index 8706b51ef..c470d2007 100644 --- a/core/os/os2/heap_linux.odin +++ b/core/os/os2/heap_linux.odin @@ -101,8 +101,8 @@ MMAP_FLAGS :: unix.MAP_ANONYMOUS | unix.MAP_PRIVATE MMAP_PROT :: unix.PROT_READ | unix.PROT_WRITE -//@thread_local _local_region: ^Region -_local_region: ^Region +@thread_local _local_region: ^Region +//_local_region: ^Region global_regions: ^Region diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index 5dadb7608..8e5aa35fe 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -3,7 +3,6 @@ package os2 import "core:strings" import "core:sys/unix" -import "core:path/filepath" _Path_Separator :: '/' _Path_List_Separator :: ':' @@ -127,23 +126,22 @@ _remove_all :: proc(path: string) -> Error { defer delete(buf) loop: for { - res := unix.sys_getdents64(int(dfd), &buf[0], n) - switch res { + getdents_res := unix.sys_getdents64(int(dfd), &buf[0], n) + switch getdents_res { case -EINVAL: delete(buf) n *= 2 buf = make([]u8, n) continue loop case -4096..<0: - return _get_platform_error(res) + return _get_platform_error(getdents_res) case 0: break loop } d: ^dirent64 - for i := 0; i < res; i += int(d.d_reclen) { - description: string + for i := 0; i < getdents_res; i += int(d.d_reclen) { d = (^dirent64)(rawptr(&buf[i])) d_name_cstr := cstring(&d.d_name[0]) @@ -159,7 +157,7 @@ _remove_all :: proc(path: string) -> Error { continue } - res: int + unlink_res: int switch d.d_type { case DT_DIR: @@ -169,13 +167,13 @@ _remove_all :: proc(path: string) -> Error { } defer unix.sys_close(handle_i) _remove_all_dir(Handle(handle_i)) or_return - res = unix.sys_unlinkat(int(dfd), d_name_cstr, int(unix.AT_REMOVEDIR)) + unlink_res = unix.sys_unlinkat(int(dfd), d_name_cstr, int(unix.AT_REMOVEDIR)) case: - res = unix.sys_unlinkat(int(dfd), d_name_cstr) + unlink_res = unix.sys_unlinkat(int(dfd), d_name_cstr) } - if res < 0 { - return _get_platform_error(res) + if unlink_res < 0 { + return _get_platform_error(unlink_res) } } } diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index 9bfd900b6..a52a84027 100644 --- a/core/os/os2/stat_linux.odin +++ b/core/os/os2/stat_linux.odin @@ -2,7 +2,6 @@ package os2 import "core:time" -import "core:strings" import "core:sys/unix" import "core:path/filepath" From d1499f3f78e1f65e164fcf68ade937aa46f8943b Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 16 May 2022 13:57:12 -0400 Subject: [PATCH 26/28] make -vet happy --- core/os/os2/file_linux.odin | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 8698ee54d..0f2e810f4 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -370,6 +370,9 @@ _is_file :: proc(name: string) -> bool { } s: _Stat res := unix.sys_stat(name_cstr, &s) + if res < 0 { + return false + } return S_ISREG(s.mode) } @@ -389,6 +392,9 @@ _is_dir :: proc(name: string) -> bool { } s: _Stat res := unix.sys_stat(name_cstr, &s) + if res < 0 { + return false + } return S_ISDIR(s.mode) } From 43432f92ec0780881284398dc151b73450ef6743 Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 16 May 2022 15:21:36 -0400 Subject: [PATCH 27/28] fix git dummy move --- core/sys/windows/kernel32.odin | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index b44aa0305..98b93ffb9 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -58,7 +58,6 @@ foreign kernel32 { LeaveCriticalSection :: proc(CriticalSection: ^CRITICAL_SECTION) --- DeleteCriticalSection :: proc(CriticalSection: ^CRITICAL_SECTION) --- - PathFileExistsW :: proc(lpPathName: LPCWSTR) -> BOOL --- RemoveDirectoryW :: proc(lpPathName: LPCWSTR) -> BOOL --- SetFileAttributesW :: proc(lpFileName: LPCWSTR, dwFileAttributes: DWORD) -> BOOL --- SetLastError :: proc(dwErrCode: DWORD) --- @@ -795,4 +794,4 @@ Control_Event :: enum DWORD { close = 2, logoff = 5, shutdown = 6, -} \ No newline at end of file +} From 5a6836ab99c91250dadb44617f4995c1598537fe Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 16 May 2022 15:28:56 -0400 Subject: [PATCH 28/28] match user.odin and env.odin to master --- core/os/os2/env.odin | 15 ++++++++++++--- core/os/os2/user.odin | 40 +++++++++++++++++++--------------------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/core/os/os2/env.odin b/core/os/os2/env.odin index f1a3e40c7..f25290a59 100644 --- a/core/os/os2/env.odin +++ b/core/os/os2/env.odin @@ -1,11 +1,20 @@ package os2 -// get_env gets the value of the environment variable named by the key +// get_env retrieves the value of the environment variable named by the key +// It returns the value, which will be empty if the variable is not present +// To distinguish between an empty value and an unset value, use lookup_env +// NOTE: the value will be allocated with the supplied allocator +get_env :: proc(key: string, allocator := context.allocator) -> string { + value, _ := lookup_env(key, allocator) + return value +} + +// lookup_env gets the value of the environment variable named by the key // If the variable is found in the environment the value (which can be empty) is returned and the boolean is true // Otherwise the returned value will be empty and the boolean will be false // NOTE: the value will be allocated with the supplied allocator -get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { - return _get_env(key, allocator) +lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + return _lookup_env(key, allocator) } // set_env sets the value of the environment variable named by the key diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index 14c0ce961..1fb653b85 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -4,58 +4,56 @@ import "core:strings" import "core:runtime" user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - found: bool #partial switch ODIN_OS { case .Windows: - dir, found = get_env("LocalAppData") - if found { + dir = get_env("LocalAppData") + if dir != "" { dir = strings.clone_safe(dir, allocator) or_return } case .Darwin: - dir, found = get_env("HOME") - if found { + dir = get_env("HOME") + if dir != "" { dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return } case: // All other UNIX systems - dir, found = get_env("XDG_CACHE_HOME") - if found { - dir, found = get_env("HOME") - if !found { + dir = get_env("XDG_CACHE_HOME") + if dir == "" { + dir = get_env("HOME") + if dir == "" { return } dir = strings.concatenate_safe({dir, "/.cache"}, allocator) or_return } } - if !found || dir == "" { + if dir == "" { err = .Invalid_Path } return } user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - found: bool #partial switch ODIN_OS { case .Windows: - dir, found = get_env("AppData") - if found { + dir = get_env("AppData") + if dir != "" { dir = strings.clone_safe(dir, allocator) or_return } case .Darwin: - dir, found = get_env("HOME") - if found { + dir = get_env("HOME") + if dir != "" { dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return } case: // All other UNIX systems - dir, found = get_env("XDG_CACHE_HOME") - if !found { - dir, found = get_env("HOME") - if !found { + dir = get_env("XDG_CACHE_HOME") + if dir == "" { + dir = get_env("HOME") + if dir == "" { return } dir = strings.concatenate_safe({dir, "/.config"}, allocator) or_return } } - if !found || dir == "" { + if dir == "" { err = .Invalid_Path } return @@ -67,7 +65,7 @@ user_home_dir :: proc() -> (dir: string, err: Error) { case .Windows: env = "USERPROFILE" } - if v, found := get_env(env); found { + if v := get_env(env); v != "" { return v, nil } return "", .Invalid_Path