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 }