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 18b85a1e8..000000000 Binary files a/tests/core/crypto_hash and /dev/null differ 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)