diff options
| author | gingerBill <bill@gingerbill.org> | 2022-05-12 12:54:27 +0100 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2022-05-12 12:54:27 +0100 |
| commit | bb4f1084879975e2f6c76cda325d4d60154fae46 (patch) | |
| tree | 8d17df490d394a36c5b0b8fb157e4b8442dc4aaf | |
| parent | ccb38c3dc684ece829c7ca1066867cc5212533c3 (diff) | |
Update error handling for os2 on windows
| -rw-r--r-- | core/os/os2/env_windows.odin | 10 | ||||
| -rw-r--r-- | core/os/os2/errors.odin | 79 | ||||
| -rw-r--r-- | core/os/os2/errors_windows.odin | 46 | ||||
| -rw-r--r-- | core/os/os2/file_util.odin | 10 | ||||
| -rw-r--r-- | core/os/os2/file_windows.odin | 80 | ||||
| -rw-r--r-- | core/os/os2/path.odin | 4 | ||||
| -rw-r--r-- | core/os/os2/path_windows.odin | 57 | ||||
| -rw-r--r-- | core/os/os2/pipe_windows.odin | 2 | ||||
| -rw-r--r-- | core/os/os2/stat.odin | 11 | ||||
| -rw-r--r-- | core/os/os2/stat_windows.odin | 140 | ||||
| -rw-r--r-- | core/os/os2/temp_file.odin | 7 | ||||
| -rw-r--r-- | core/os/os2/temp_file_windows.odin | 28 | ||||
| -rw-r--r-- | core/os/os2/user.odin | 31 | ||||
| -rw-r--r-- | core/os/stat_unix.odin | 1 | ||||
| -rw-r--r-- | core/sys/windows/types.odin | 1 |
15 files changed, 313 insertions, 194 deletions
diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin index 2ce755203..52c6c0f55 100644 --- a/core/os/os2/env_windows.odin +++ b/core/os/os2/env_windows.odin @@ -2,8 +2,9 @@ package os2 import win32 "core:sys/windows" +import "core:runtime" -_lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { +_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) { if key == "" { return } @@ -17,7 +18,7 @@ _lookup_env :: proc(key: string, allocator := context.allocator) -> (value: stri } return "", true } - b := make([]u16, n+1, context.temp_allocator) + b := make([]u16, n+1, _temp_allocator()) n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) if n == 0 { @@ -25,6 +26,7 @@ _lookup_env :: proc(key: string, allocator := context.allocator) -> (value: stri if err == win32.ERROR_ENVVAR_NOT_FOUND { return "", false } + return "", false } value = win32.utf16_to_utf8(b[:n], allocator) @@ -45,7 +47,7 @@ _unset_env :: proc(key: string) -> bool { } _clear_env :: proc() { - envs := environ(context.temp_allocator) + envs := environ(_temp_allocator()) for env in envs { for j in 1..<len(env) { if env[j] == '=' { @@ -56,7 +58,7 @@ _clear_env :: proc() { } } -_environ :: proc(allocator := context.allocator) -> []string { +_environ :: proc(allocator: runtime.Allocator) -> []string { envs := win32.GetEnvironmentStringsW() if envs == nil { return nil diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin index b54b10cb2..f42f92eb9 100644 --- a/core/os/os2/errors.odin +++ b/core/os/os2/errors.odin @@ -1,9 +1,10 @@ package os2 import "core:io" +import "core:runtime" General_Error :: enum u32 { - Invalid_Argument, + None, Permission_Denied, Exist, @@ -18,13 +19,12 @@ General_Error :: enum u32 { Unsupported, } -Platform_Error :: struct { - err: i32, -} +Platform_Error :: enum i32 {None=0} -Error :: union { +Error :: union #shared_nil { General_Error, io.Error, + runtime.Allocator_Error, Platform_Error, } #assert(size_of(Error) == size_of(u64)) @@ -33,36 +33,55 @@ Error :: union { is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) { v := ferr.(Platform_Error) or_else {} - return v.err, v.err != 0 + return i32(v), i32(v) != 0 } error_string :: proc(ferr: Error) -> string { - switch ferr { - case nil: return "" - case .Invalid_Argument: return "invalid argument" - case .Permission_Denied: return "permission denied" - case .Exist: return "file already exists" - case .Not_Exist: return "file does not exist" - case .Closed: return "file already closed" - case .Timeout: return "i/o timeout" - case .EOF: return "eof" - case .Unexpected_EOF: return "unexpected eof" - case .Short_Write: return "short write" - case .Invalid_Write: return "invalid write result" - case .Short_Buffer: return "short buffer" - case .No_Progress: return "multiple read calls return no data or error" - case .Invalid_Whence: return "invalid whence" - case .Invalid_Offset: return "invalid offset" - case .Invalid_Unread: return "invalid unread" - case .Negative_Read: return "negative read" - case .Negative_Write: return "negative write" - case .Negative_Count: return "negative count" - case .Buffer_Full: return "buffer full" + if ferr == nil { + return "" } - - if errno, ok := is_platform_error(ferr); ok { - return _error_string(errno) + switch e in ferr { + case General_Error: + switch e { + case .None: return "" + case .Permission_Denied: return "permission denied" + case .Exist: return "file already exists" + case .Not_Exist: return "file does not exist" + case .Closed: return "file already closed" + case .Timeout: return "i/o timeout" + case .Invalid_File: return "invalid file" + case .Invalid_Path: return "invalid path" + case .Unsupported: return "unsupported" + } + case io.Error: + switch e { + case .None: return "" + case .EOF: return "eof" + case .Unexpected_EOF: return "unexpected eof" + case .Short_Write: return "short write" + case .Invalid_Write: return "invalid write result" + case .Short_Buffer: return "short buffer" + case .No_Progress: return "multiple read calls return no data or error" + case .Invalid_Whence: return "invalid whence" + case .Invalid_Offset: return "invalid offset" + case .Invalid_Unread: return "invalid unread" + case .Negative_Read: return "negative read" + case .Negative_Write: return "negative write" + case .Negative_Count: return "negative count" + case .Buffer_Full: return "buffer full" + case .Unknown, .Empty: // + } + case runtime.Allocator_Error: + switch e { + case .None: return "" + case .Out_Of_Memory: return "out of memory" + case .Invalid_Pointer: return "invalid allocator pointer" + case .Invalid_Argument: return "invalid allocator argument" + case .Mode_Not_Implemented: return "allocator mode not implemented" + } + case Platform_Error: + return _error_string(i32(e)) } return "unknown error" diff --git a/core/os/os2/errors_windows.odin b/core/os/os2/errors_windows.odin index f9dd3fdca..27c16e72e 100644 --- a/core/os/os2/errors_windows.odin +++ b/core/os/os2/errors_windows.odin @@ -12,3 +12,49 @@ _error_string :: proc(errno: i32) -> string { // FormatMessageW return "" } + +_get_platform_error :: proc() -> Error { + err := win32.GetLastError() + if err == 0 { + return nil + } + switch err { + case win32.ERROR_ACCESS_DENIED, win32.ERROR_SHARING_VIOLATION: + return .Permission_Denied + + case win32.ERROR_FILE_EXISTS, win32.ERROR_ALREADY_EXISTS: + return .Exist + + case win32.ERROR_FILE_NOT_FOUND, win32.ERROR_PATH_NOT_FOUND: + return .Not_Exist + + case win32.ERROR_NO_DATA: + return .Closed + + case win32.ERROR_TIMEOUT, win32.WAIT_TIMEOUT: + return .Timeout + + case win32.ERROR_NOT_SUPPORTED: + return .Unsupported + + case + win32.ERROR_BAD_ARGUMENTS, + win32.ERROR_INVALID_PARAMETER, + win32.ERROR_NOT_ENOUGH_MEMORY, + win32.ERROR_INVALID_HANDLE, + win32.ERROR_NO_MORE_FILES, + win32.ERROR_LOCK_VIOLATION, + win32.ERROR_HANDLE_EOF, + win32.ERROR_BROKEN_PIPE, + win32.ERROR_CALL_NOT_IMPLEMENTED, + win32.ERROR_INSUFFICIENT_BUFFER, + win32.ERROR_INVALID_NAME, + win32.ERROR_LOCK_FAILED, + win32.ERROR_ENVVAR_NOT_FOUND, + win32.ERROR_OPERATION_ABORTED, + win32.ERROR_IO_PENDING, + win32.ERROR_NO_UNICODE_TRANSLATION: + // fallthrough + } + return Platform_Error(err) +}
\ No newline at end of file diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index f82bf73d0..9f9064244 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -74,7 +74,7 @@ read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) { -read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byte, Error) { +read_entire_file :: proc(name: string, allocator := context.allocator) -> (data: []byte, err: Error) { f, ferr := open(name) if ferr != nil { return nil, ferr @@ -91,15 +91,17 @@ read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byt // TODO(bill): Is this correct logic? total: int - data := make([]byte, size, allocator) + data = make([]byte, size, allocator) or_return for { - n, err := read(f, data[total:]) + n: int + n, err = read(f, data[total:]) total += n if err != nil { if err == .EOF { err = nil } - return data[:total], err + data = data[:total] + return } } } diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 07117a15a..b9ebfe10e 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -3,6 +3,7 @@ package os2 import "core:io" import "core:mem" +import "core:sync" import "core:runtime" import "core:strings" import "core:time" @@ -19,6 +20,12 @@ _file_allocator :: proc() -> runtime.Allocator { return heap_allocator() } +_temp_allocator :: proc() -> runtime.Allocator { + // TODO(bill): make this not depend on the context allocator + return context.temp_allocator +} + + _File_Kind :: enum u8 { File, Console, @@ -30,20 +37,17 @@ _File :: struct { name: string, wname: win32.wstring, kind: _File_Kind, + + allocator: runtime.Allocator, + + rw_mutex: sync.RW_Mutex, // read write calls + p_mutex: sync.Mutex, // pread pwrite calls } _handle :: proc(f: ^File) -> win32.HANDLE { return win32.HANDLE(_fd(f)) } -_get_platform_error :: proc() -> Error { - err := i32(win32.GetLastError()) - if err != 0 { - return Platform_Error{err} - } - return nil -} - _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (handle: uintptr, err: Error) { if len(name) == 0 { err = .Not_Exist @@ -100,7 +104,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (han case 0: return uintptr(h), nil case: - return 0, Platform_Error{i32(e)} + return 0, Platform_Error(e) } } } @@ -123,11 +127,12 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File { if handle == INVALID_HANDLE { return nil } - context.allocator = _file_allocator() - f := new(File) + f := new(File, _file_allocator()) + + f.impl.allocator = _file_allocator() f.impl.fd = rawptr(fd) - f.impl.name = strings.clone(name, context.allocator) - f.impl.wname = win32.utf8_to_wstring(name, context.allocator) + f.impl.name = strings.clone(name, f.impl.allocator) + f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator) handle := _handle(f) kind := _File_Kind.File @@ -154,10 +159,10 @@ _destroy :: proc(f: ^File) -> Error { return nil } - context.allocator = _file_allocator() - free(f.impl.wname) - delete(f.impl.name) - free(f) + a := f.impl.allocator + free(f.impl.wname, a) + delete(f.impl.name, a) + free(f, a) return nil } @@ -185,6 +190,8 @@ _seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error return 0, .Invalid_File } + sync.guard(&f.impl.rw_mutex) + w: u32 switch whence { case .Start: w = win32.FILE_BEGIN @@ -207,6 +214,7 @@ _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { return 0, nil } + // TODO(bill): should this be moved to `_File` instead? BUF_SIZE :: 386 buf16: [BUF_SIZE]u16 buf8: [4*BUF_SIZE]u8 @@ -257,22 +265,27 @@ _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { total_read: int length := len(p) - to_read := min(win32.DWORD(length), MAX_RW) + sync.shared_guard(&f.impl.rw_mutex) // multiple readers - e: win32.BOOL - if f.impl.kind == .Console { - n, err := read_console(handle, p[total_read:][:to_read]) - total_read += n - if err != nil { - return int(total_read), err + if sync.guard(&f.impl.p_mutex) { + to_read := min(win32.DWORD(length), MAX_RW) + ok: win32.BOOL + if f.impl.kind == .Console { + n, err := read_console(handle, p[total_read:][:to_read]) + total_read += n + if err != nil { + return int(total_read), err + } + } else { + ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil) + } + + if single_read_length > 0 && ok { + total_read += int(single_read_length) + } else { + err = _get_platform_error() } - } else { - e = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil) - } - if single_read_length <= 0 || !e { - return int(total_read), _get_platform_error() } - total_read += int(single_read_length) return int(total_read), nil } @@ -303,6 +316,9 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { n = int(done) return } + + sync.guard(&f.impl.p_mutex) + p, offset := p, offset for len(p) > 0 { m := pread(f, p, offset) or_return @@ -329,6 +345,7 @@ _write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { handle := _handle(f) + sync.guard(&f.impl.rw_mutex) for total_write < length { remaining := length - total_write to_write := win32.DWORD(min(i32(remaining), MAX_RW)) @@ -369,6 +386,7 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { return } + sync.guard(&f.impl.p_mutex) p, offset := p, offset for len(p) > 0 { m := pwrite(f, p, offset) or_return @@ -531,7 +549,7 @@ _normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: st if n == 0 { return "", _get_platform_error() } - buf := make([]u16, n+1, context.temp_allocator) + buf := make([]u16, n+1, _temp_allocator()) n = win32.GetFinalPathNameByHandleW(handle, raw_data(buf), u32(len(buf)), win32.VOLUME_NAME_DOS) if n == 0 { return "", _get_platform_error() diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin index eca8b518f..c27015862 100644 --- a/core/os/os2/path.odin +++ b/core/os/os2/path.odin @@ -1,5 +1,7 @@ package os2 +import "core:runtime" + Path_Separator :: _Path_Separator // OS-Specific Path_List_Separator :: _Path_List_Separator // OS-Specific @@ -21,7 +23,7 @@ remove_all :: proc(path: string) -> Error { -getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) { +getwd :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _getwd(allocator) } setwd :: proc(dir: string) -> (err: Error) { diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin index 0e77db4fd..2dc667822 100644 --- a/core/os/os2/path_windows.odin +++ b/core/os/os2/path_windows.odin @@ -2,6 +2,8 @@ package os2 import win32 "core:sys/windows" +import "core:runtime" +import "core:strings" _Path_Separator :: '\\' _Path_List_Separator :: ';' @@ -11,11 +13,58 @@ _is_path_separator :: proc(c: byte) -> bool { } _mkdir :: proc(name: string, perm: File_Mode) -> Error { + if !win32.CreateDirectoryW(_fix_long_path(name), nil) { + return _get_platform_error() + } return nil } _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { - // TODO(bill): _mkdir_all for windows + fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) { + if len(p) == len(`\\?\c:`) { + if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' { + s = strings.concatenate_safe({p, `\`}, _file_allocator()) or_return + allocated = true + return + } + } + return p, false, nil + } + + dir, err := stat(path, _temp_allocator()) + if err == nil { + if dir.is_dir { + return nil + } + return .Exist + } + + i := len(path) + for i > 0 && is_path_separator(path[i-1]) { + i -= 1 + } + + j := i + for j > 0 && !is_path_separator(path[j-1]) { + j -= 1 + } + + if j > 1 { + new_path, allocated := fix_root_directory(path[:j-1]) or_return + defer if allocated { + delete(new_path, _file_allocator()) + } + mkdir_all(new_path, perm) or_return + } + + err = mkdir(path, perm) + if err != nil { + dir1, err1 := lstat(path, _temp_allocator()) + if err1 == nil && dir1.is_dir { + return nil + } + return err + } return nil } @@ -24,11 +73,13 @@ _remove_all :: proc(path: string) -> Error { return nil } -_getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) { +_getwd :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + // TODO(bill) return "", nil } _setwd :: proc(dir: string) -> (err: Error) { + // TODO(bill) return nil } @@ -75,7 +126,7 @@ _fix_long_path_internal :: proc(path: string) -> string { } PREFIX :: `\\?` - path_buf := make([]byte, len(PREFIX)+len(path)+1, context.temp_allocator) + path_buf := make([]byte, len(PREFIX)+len(path)+1, _temp_allocator()) copy(path_buf, PREFIX) n := len(path) r, w := 0, len(PREFIX) diff --git a/core/os/os2/pipe_windows.odin b/core/os/os2/pipe_windows.odin index ddb54f80c..bab8b44f5 100644 --- a/core/os/os2/pipe_windows.odin +++ b/core/os/os2/pipe_windows.odin @@ -6,7 +6,7 @@ import win32 "core:sys/windows" _pipe :: proc() -> (r, w: ^File, err: Error) { p: [2]win32.HANDLE if !win32.CreatePipe(&p[0], &p[1], nil, 0) { - return nil, nil, Platform_Error{i32(win32.GetLastError())} + return nil, nil, _get_platform_error() } return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil } diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index 63f5a17e8..24a01fb0a 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -1,6 +1,7 @@ package os2 import "core:time" +import "core:runtime" File_Info :: struct { fullpath: string, @@ -13,26 +14,26 @@ File_Info :: struct { access_time: time.Time, } -file_info_slice_delete :: proc(infos: []File_Info, allocator := context.allocator) { +file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.Allocator) { for i := len(infos)-1; i >= 0; i -= 1 { file_info_delete(infos[i], allocator) } delete(infos, allocator) } -file_info_delete :: proc(fi: File_Info, allocator := context.allocator) { +file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) { delete(fi.fullpath, allocator) } -fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) { +fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { return _fstat(f, allocator) } -stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { +stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { return _stat(name, allocator) } -lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { +lstat :: proc(name: string, allocator: runtime.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 a79e5aae2..603343a18 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -1,22 +1,22 @@ //+private package os2 +import "core:runtime" import "core:time" import "core:strings" import win32 "core:sys/windows" -_fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) { +_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { if f == nil || f.impl.fd == nil { - return {}, .Invalid_Argument + return {}, nil } - context.allocator = allocator - path, err := _cleanpath_from_handle(f) + path, err := _cleanpath_from_handle(f, allocator) if err != nil { return {}, err } - h := win32.HANDLE(f.impl.fd) + h := _handle(f) switch win32.GetFileType(h) { case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: fi: File_Info @@ -26,13 +26,13 @@ _fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) { return fi, nil } - return _file_info_from_get_file_information_by_handle(path, h) + return _file_info_from_get_file_information_by_handle(path, h, allocator) } -_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { - return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS) +_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { + return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS, allocator) } -_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) +_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { + return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT, allocator) } _same_file :: proc(fi1, fi2: File_Info) -> bool { return fi1.fullpath == fi2.fullpath @@ -40,50 +40,38 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool { -_stat_errno :: proc(errno: win32.DWORD) -> Error { - return Platform_Error{i32(errno)} -} - -full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Error) { - context.allocator = allocator - +full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) { name := name if name == "" { name = "." } - p := win32.utf8_to_utf16(name, context.temp_allocator) - buf := make([dynamic]u16, 100) - for { - n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) - if n == 0 { - delete(buf) - return "", _stat_errno(win32.GetLastError()) - } - if n <= u32(len(buf)) { - return win32.utf16_to_utf8(buf[:n]), nil - } - resize(&buf, len(buf)*2) - } + p := win32.utf8_to_utf16(name, _temp_allocator()) - return + n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil) + if n == 0 { + return "", _get_platform_error() + } + buf := make([]u16, n+1, _temp_allocator()) + n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) + if n == 0 { + return "", _get_platform_error() + } + return win32.utf16_to_utf8(buf[:n], allocator), nil } -internal_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Error) { +internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { if len(name) == 0 { return {}, .Not_Exist } - context.allocator = allocator - - wname := _fix_long_path(name) fa: win32.WIN32_FILE_ATTRIBUTE_DATA ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa) if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { // Not a symlink - return _file_info_from_win32_file_attribute_data(&fa, name) + return _file_info_from_win32_file_attribute_data(&fa, name, allocator) } err := 0 if ok else win32.GetLastError() @@ -97,7 +85,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co } win32.FindClose(sh) - return _file_info_from_win32_find_data(&fd, name) + return _file_info_from_win32_find_data(&fd, name, allocator) } h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil) @@ -106,7 +94,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co return } defer win32.CloseHandle(h) - return _file_info_from_get_file_information_by_handle(name, h) + return _file_info_from_get_file_information_by_handle(name, h, allocator) } @@ -131,56 +119,40 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { } -_cleanpath_from_handle :: proc(f: ^File) -> (string, Error) { +_cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) { if f == nil || f.impl.fd == nil { - return "", .Invalid_Argument - } - h := win32.HANDLE(f.impl.fd) - - MAX_PATH := win32.DWORD(260) + 1 - buf: []u16 - for { - buf = make([]u16, MAX_PATH, context.temp_allocator) - 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) - case win32.ERROR_NOT_ENOUGH_MEMORY: - MAX_PATH = MAX_PATH*2 + 1 - continue - } - break + return "", nil + } + h := _handle(f) + + n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) + if n == 0 { + return "", _get_platform_error() } - return _cleanpath_from_buf(buf), nil + buf := make([]u16, max(n, 260)+1, _temp_allocator()) + n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0) + return _cleanpath_from_buf(buf[:n], allocator), nil } _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) { if f == nil || f.impl.fd == nil { - return nil, .Invalid_Argument - } - h := win32.HANDLE(f.impl.fd) - - MAX_PATH := win32.DWORD(260) + 1 - buf: []u16 - for { - buf = make([]u16, MAX_PATH, context.temp_allocator) - 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) - case win32.ERROR_NOT_ENOUGH_MEMORY: - MAX_PATH = MAX_PATH*2 + 1 - continue - } - break + return nil, nil + } + h := _handle(f) + + n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) + if n == 0 { + return nil, _get_platform_error() } - return _cleanpath_strip_prefix(buf), nil + buf := make([]u16, max(n, 260)+1, _temp_allocator()) + n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0) + return _cleanpath_strip_prefix(buf[:n]), nil } -_cleanpath_from_buf :: proc(buf: []u16) -> string { +_cleanpath_from_buf :: proc(buf: []u16, allocator: runtime.Allocator) -> string { buf := buf buf = _cleanpath_strip_prefix(buf) - return win32.utf16_to_utf8(buf, context.allocator) + return win32.utf16_to_utf8(buf, allocator) } @@ -252,7 +224,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: Error) { +_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string, allocator: runtime.Allocator) -> (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) @@ -262,14 +234,14 @@ _file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) - fi.fullpath, e = full_path_from_name(name) + fi.fullpath, e = full_path_from_name(name, allocator) fi.name = basename(fi.fullpath) return } -_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Error) { +_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string, allocator: runtime.Allocator) -> (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) @@ -279,17 +251,17 @@ _file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) - fi.fullpath, e = full_path_from_name(name) + fi.fullpath, e = full_path_from_name(name, allocator) fi.name = basename(fi.fullpath) return } -_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Error) { +_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE, allocator: runtime.Allocator) -> (File_Info, Error) { d: win32.BY_HANDLE_FILE_INFORMATION if !win32.GetFileInformationByHandle(h, &d) { - return {}, _stat_errno(win32.GetLastError()) + return {}, _get_platform_error() } @@ -297,7 +269,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(err) } // Indicate this is a symlink on FAT file systems ti.ReparseTag = 0 diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index 90131699d..faf176de1 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -1,14 +1,15 @@ package os2 +import "core:runtime" create_temp :: proc(dir, pattern: string) -> (^File, Error) { return _create_temp(dir, pattern) } -mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) { - return _mkdir_temp(dir, pattern) +mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { + return _mkdir_temp(dir, pattern, allocator) } -temp_dir :: proc(allocator := context.allocator) -> string { +temp_dir :: proc(allocator: runtime.Allocator) -> string { return _temp_dir(allocator) } diff --git a/core/os/os2/temp_file_windows.odin b/core/os/os2/temp_file_windows.odin index 17967393a..a7587988b 100644 --- a/core/os/os2/temp_file_windows.odin +++ b/core/os/os2/temp_file_windows.odin @@ -1,29 +1,29 @@ //+private package os2 +import "core:runtime" import win32 "core:sys/windows" _create_temp :: proc(dir, pattern: string) -> (^File, Error) { return nil, nil } -_mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) { +_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { return "", nil } -_temp_dir :: proc(allocator := context.allocator) -> string { - b := make([dynamic]u16, u32(win32.MAX_PATH), context.temp_allocator) - for { - n := win32.GetTempPathW(u32(len(b)), raw_data(b)) - if n > u32(len(b)) { - resize(&b, int(n)) - continue - } - if n == 3 && b[1] == ':' && b[2] == '\\' { +_temp_dir :: proc(allocator: runtime.Allocator) -> string { + n := win32.GetTempPathW(0, nil) + if n == 0 { + return "" + } + b := make([]u16, max(win32.MAX_PATH, n), _temp_allocator()) + n = win32.GetTempPathW(u32(len(b)), raw_data(b)) + + if n == 3 && b[1] == ':' && b[2] == '\\' { - } else if n > 0 && b[n-1] == '\\' { - n -= 1 - } - return win32.utf16_to_utf8(b[:n], allocator) + } else if n > 0 && b[n-1] == '\\' { + n -= 1 } + return win32.utf16_to_utf8(b[:n], allocator) } diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index 976e61bb1..1fb653b85 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -1,18 +1,19 @@ package os2 import "core:strings" +import "core:runtime" -user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) { +user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Windows: dir = get_env("LocalAppData") if dir != "" { - dir = strings.clone(dir, allocator) + dir = strings.clone_safe(dir, allocator) or_return } case .Darwin: dir = get_env("HOME") if dir != "" { - dir = strings.concatenate({dir, "/Library/Caches"}, allocator) + dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME") @@ -21,24 +22,26 @@ user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defin if dir == "" { return } - dir = strings.concatenate({dir, "/.cache"}, allocator) + dir = strings.concatenate_safe({dir, "/.cache"}, allocator) or_return } } - is_defined = dir != "" + if dir == "" { + err = .Invalid_Path + } return } -user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) { +user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Windows: dir = get_env("AppData") if dir != "" { - dir = strings.clone(dir, allocator) + dir = strings.clone_safe(dir, allocator) or_return } case .Darwin: dir = get_env("HOME") if dir != "" { - dir = strings.concatenate({dir, "/Library/Application Support"}, allocator) + dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME") @@ -47,22 +50,24 @@ user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defi if dir == "" { return } - dir = strings.concatenate({dir, "/.config"}, allocator) + dir = strings.concatenate_safe({dir, "/.config"}, allocator) or_return } } - is_defined = dir != "" + if dir == "" { + err = .Invalid_Path + } return } -user_home_dir :: proc() -> (dir: string, is_defined: bool) { +user_home_dir :: proc() -> (dir: string, err: Error) { env := "HOME" #partial switch ODIN_OS { case .Windows: env = "USERPROFILE" } if v := get_env(env); v != "" { - return v, true + return v, nil } - return "", false + return "", .Invalid_Path } diff --git a/core/os/stat_unix.odin b/core/os/stat_unix.odin index 395d2e73e..dae7ab2fb 100644 --- a/core/os/stat_unix.odin +++ b/core/os/stat_unix.odin @@ -119,7 +119,6 @@ lstat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, e } stat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Errno) { - context.allocator = allocator s: OS_Stat diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 6770e7a95..4f594e22d 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -1140,6 +1140,7 @@ ERROR_BROKEN_PIPE: DWORD : 109 ERROR_CALL_NOT_IMPLEMENTED: DWORD : 120 ERROR_INSUFFICIENT_BUFFER: DWORD : 122 ERROR_INVALID_NAME: DWORD : 123 +ERROR_BAD_ARGUMENTS: DWORD: 160 ERROR_LOCK_FAILED: DWORD : 167 ERROR_ALREADY_EXISTS: DWORD : 183 ERROR_NO_DATA: DWORD : 232 |