Browse Source

commit to merge upstream/master

CiD- 3 years ago
parent
commit
c293e88f2e

+ 21 - 22
core/os/os2/file_linux.odin

@@ -11,35 +11,35 @@ _std_handle :: proc(kind: Std_Handle_Kind) -> Handle {
 	return Handle(kind)
 	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) {
 _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Error) {
 	cstr := strings.clone_to_cstring(name, context.temp_allocator)
 	cstr := strings.clone_to_cstring(name, context.temp_allocator)
 
 
 	flags_i: int
 	flags_i: int
 	switch flags & O_RDONLY|O_WRONLY|O_RDWR {
 	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))
 	handle_i := unix.sys_open(cstr, flags_i, int(perm))
 	if handle_i < 0 {
 	if handle_i < 0 {
@@ -165,7 +165,6 @@ _remove :: proc(name: string) -> Error {
 	}
 	}
 	defer unix.sys_close(handle_i)
 	defer unix.sys_close(handle_i)
 
 
-	/* TODO: THIS WILL NOT WORK */
 	if _is_dir(Handle(handle_i)) {
 	if _is_dir(Handle(handle_i)) {
 		return _ok_or_error(unix.sys_rmdir(name_cstr))
 		return _ok_or_error(unix.sys_rmdir(name_cstr))
 	}
 	}

+ 24 - 48
core/os/os2/path_linux.odin

@@ -8,7 +8,16 @@ import "core:path/filepath"
 _Path_Separator      :: '/'
 _Path_Separator      :: '/'
 _Path_List_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 {
 _is_path_separator :: proc(c: byte) -> bool {
 	return c == '/'
 	return c == '/'
@@ -16,42 +25,17 @@ _is_path_separator :: proc(c: byte) -> bool {
 
 
 _mkdir :: proc(path: string, perm: File_Mode) -> Error {
 _mkdir :: proc(path: string, perm: File_Mode) -> Error {
 	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
 	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 :: 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 {
 dirent64 :: struct {
@@ -62,17 +46,9 @@ dirent64 :: struct {
 	d_name: [1]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 {
 _remove_all :: proc(path: string) -> Error {
+	DT_DIR :: 4
+
 	_remove_all_dir :: proc(dfd: Handle) -> Error {
 	_remove_all_dir :: proc(dfd: Handle) -> Error {
 		n := 64
 		n := 64
 		buf := make([]u8, n)
 		buf := make([]u8, n)
@@ -114,7 +90,7 @@ _remove_all :: proc(path: string) -> Error {
 
 
 				switch d.d_type {
 				switch d.d_type {
 				case DT_DIR:
 				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 {
 					if handle_i < 0 {
 						return _get_platform_error(handle_i)
 						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)
 	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 {
 	switch handle_i {
 	case -ENOTDIR:
 	case -ENOTDIR:
 		return _ok_or_error(unix.sys_unlink(cstr))
 		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))
 	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
 	// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
 	// an authoritative value for it across all systems.
 	// an authoritative value for it across all systems.
 	// The largest value I could find was 4096, so might as well use the page size.
 	// 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()
 	unreachable()
 }
 }
 
 
-_setwd :: proc(dir: string) -> (err: Error) {
+_setwd :: proc(dir: string) -> Error {
 	dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator)
 	dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator)
 	return _ok_or_error(unix.sys_chdir(dir_cstr))
 	return _ok_or_error(unix.sys_chdir(dir_cstr))
 }
 }

+ 9 - 1
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 {
 	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
 		return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
 	} else { // NOTE: arm64 does not have mkdir
 	} 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 {
 sys_truncate :: proc(path: cstring, length: i64) -> int {
 	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
 	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
 		return int(intrinsics.syscall(SYS_truncate, uintptr(rawptr(path)), uintptr(length)))
 		return int(intrinsics.syscall(SYS_truncate, uintptr(rawptr(path)), uintptr(length)))

BIN
tests/core/crypto_hash


+ 75 - 21
tests/core/os2/test_os2.odin

@@ -1,5 +1,6 @@
 package test_os2
 package test_os2
 
 
+import "core:os"
 import "core:fmt"
 import "core:fmt"
 import "core:os/os2"
 import "core:os/os2"
 import "core:testing"
 import "core:testing"
@@ -22,7 +23,7 @@ when ODIN_TEST {
 		TEST_count += 1
 		TEST_count += 1
 		ok := value == expected
 		ok := value == expected
 		if !ok {
 		if !ok {
-			fmt.printf("expected %v, got %v", expected, value)
+			fmt.printf("expected %v, got %v\n", expected, value)
 			TEST_fail += 1
 			TEST_fail += 1
 			return
 			return
 		}
 		}
@@ -45,12 +46,13 @@ when ODIN_TEST {
 	}
 	}
 }
 }
 
 
-main :: proc() 
+main :: proc()
 {
 {
 	t: testing.T
 	t: testing.T
 	file_test(&t)
 	file_test(&t)
 	path_test(&t)
 	path_test(&t)
 	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
 	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	os.exit(TEST_fail > 0 ? 1 : 0)
 }
 }
 
 
 @private
 @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
 X_OK :: 1 // Test for execute permission
 W_OK :: 2 // Test for write permission
 W_OK :: 2 // Test for write permission
 R_OK :: 4 // Test for read 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(t, err != nil, "missing error")
 	expect_value(t, fd, os2.INVALID_HANDLE)
 	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_no_error(t, err)
 	expect(t, fd != os2.INVALID_HANDLE, "unexpected handle")
 	expect(t, fd != os2.INVALID_HANDLE, "unexpected handle")
 
 
-	s1 := "hello"
-	b1 := transmute([]u8)s1
-
+	s := "hello"
 	n: int
 	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_no_error(t, err)
 	expect_value(t, n, 5)
 	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_no_error(t, err)
 	expect_value(t, n, 10)
 	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))
 	_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))
 	_expect_no_error(t, os2.close(fd))
 
 
-	fd, err = os2.open("write.txt")
+
+	fd, err = os2.open("file.txt")
 	_expect_no_error(t, err)
 	_expect_no_error(t, err)
 
 
 	buf: [32]u8
 	buf: [32]u8
-	
+
 	n, err = os2.read(fd, buf[:])
 	n, err = os2.read(fd, buf[:])
 	_expect_no_error(t, err)
 	_expect_no_error(t, err)
 	expect_value(t, n, 15)
 	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)
 	n, err = os2.read_at(fd, buf[0:2], 1)
 	_expect_no_error(t, err)
 	_expect_no_error(t, err)
 	expect_value(t, n, 2)
 	expect_value(t, n, 2)
 	expect_value(t, string(buf[0:2]), "bc")
 	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.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
 @test
@@ -131,7 +181,7 @@ path_test :: proc(t: ^testing.T) {
 		err = os2.remove_all("a")
 		err = os2.remove_all("a")
 		_expect_no_error(t, err)
 		_expect_no_error(t, err)
 	}
 	}
-	
+
 	err = os2.mkdir_all("a/b/c/d", 0)
 	err = os2.mkdir_all("a/b/c/d", 0)
 	_expect_no_error(t, err)
 	_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)
 	fd, err = os2.create("a/b/c/file.txt", 0o644)
 	_expect_no_error(t, err)
 	_expect_no_error(t, err)
 
 
-	err = os2.close(fd)
-	_expect_no_error(t, err)
 
 
 	when ODIN_OS == .Linux {
 	when ODIN_OS == .Linux {
 		expect(t, unix.sys_access("a/b/c/file.txt", X_OK) < 0, "unexpected exec permission")
 		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")
 	err = os2.rename("a/b/c/file.txt", "a/b/file.txt")
 	_expect_no_error(t, err)
 	_expect_no_error(t, err)
 
 
 	when ODIN_OS == .Linux {
 	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")
 	err = os2.symlink("b/c/d", "a/symlink_to_d")
 	_expect_no_error(t, err)
 	_expect_no_error(t, err)
-	
+
 	symlink: string
 	symlink: string
 	symlink, err = os2.read_link("a/symlink_to_d")
 	symlink, err = os2.read_link("a/symlink_to_d")
 	_expect_no_error(t, err)
 	_expect_no_error(t, err)
@@ -171,8 +223,10 @@ path_test :: proc(t: ^testing.T) {
 
 
 	when ODIN_OS == .Linux {
 	when ODIN_OS == .Linux {
 		expect_value(t, unix.sys_access("a/b/c/d/shnt.txt", X_OK | R_OK | W_OK), 0)
 		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")
 	err = os2.remove_all("a")
 	_expect_no_error(t, err)
 	_expect_no_error(t, err)