Browse Source

Merge pull request #5138 from laytan/os2-native-copy

os2: copy directory using walker & native copy setup and implementation on macos
gingerBill 3 months ago
parent
commit
ecd0655ba2

+ 40 - 13
core/os/os2/dir.odin

@@ -2,6 +2,7 @@ package os2
 
 import "base:runtime"
 import "core:slice"
+import "core:strings"
 
 read_dir :: read_directory
 
@@ -194,28 +195,54 @@ read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info,
 }
 
 // Recursively copies a directory to `dst` from `src`
-copy_directory :: proc(dst, src: string, dst_perm := 0o755) -> Error {
-	switch err := make_directory_all(dst, dst_perm); err {
-	case nil, .Exist:
-		// okay
-	case:
+copy_directory_all :: proc(dst, src: string, dst_perm := 0o755) -> Error {
+	when #defined(_copy_directory_all_native) {
+		return _copy_directory_all_native(dst, src, dst_perm)
+	} else {
+		return _copy_directory_all(dst, src, dst_perm)
+	}
+}
+
+@(private)
+_copy_directory_all :: proc(dst, src: string, dst_perm := 0o755) -> Error {
+	err := make_directory(dst, dst_perm)
+	if err != nil && err != .Exist {
 		return err
 	}
 
 	temp_allocator := TEMP_ALLOCATOR_GUARD({})
 
-	file_infos := read_all_directory_by_path(src, temp_allocator) or_return
-	for fi in file_infos {
-		temp_allocator_scope(temp_allocator)
+	abs_src := get_absolute_path(src, temp_allocator) or_return
+	abs_dst := get_absolute_path(dst, temp_allocator) or_return
+
+	dst_buf := make([dynamic]byte, 0, len(abs_dst) + 256, temp_allocator) or_return
+
+	w: Walker
+	walker_init_path(&w, src)
+	defer walker_destroy(&w)
 
-		dst_path := join_path({dst, fi.name}, temp_allocator) or_return
-		src_path := fi.fullpath
+	for info in walker_walk(&w) {
+		_ = walker_error(&w) or_break
 
-		if fi.type == .Directory {
-			copy_directory(dst_path, src_path) or_return
+		rel := strings.trim_prefix(info.fullpath, abs_src)
+
+		non_zero_resize(&dst_buf, 0)
+		reserve(&dst_buf, len(abs_dst) + len(Path_Separator_String) + len(rel)) or_return
+		append(&dst_buf, abs_dst)
+		append(&dst_buf, Path_Separator_String)
+		append(&dst_buf, rel)
+
+		if info.type == .Directory {
+			err = make_directory(string(dst_buf[:]), dst_perm)
+			if err != nil && err != .Exist {
+				return err
+			}
 		} else {
-			copy_file(dst_path, src_path) or_return
+			copy_file(string(dst_buf[:]), info.fullpath) or_return
 		}
 	}
+
+	_ = walker_error(&w) or_return
+
 	return nil
 }

+ 17 - 0
core/os/os2/dir_posix_darwin.odin

@@ -0,0 +1,17 @@
+#+private
+package os2
+
+import "core:sys/darwin"
+
+_copy_directory_all_native :: proc(dst, src: string, dst_perm := 0o755) -> (err: Error) {
+	temp_allocator := TEMP_ALLOCATOR_GUARD({})
+
+	csrc := clone_to_cstring(src, temp_allocator) or_return
+	cdst := clone_to_cstring(dst, temp_allocator) or_return
+
+	if darwin.copyfile(csrc, cdst, nil, darwin.COPYFILE_ALL + {.RECURSIVE}) < 0 {
+		err = _get_platform_error()
+	}
+
+	return
+}

+ 9 - 0
core/os/os2/file.odin

@@ -313,6 +313,15 @@ is_directory :: proc(path: string) -> bool {
 
 
 copy_file :: proc(dst_path, src_path: string) -> Error {
+	when #defined(_copy_file_native) {
+		return _copy_file_native(dst_path, src_path)
+	} else {
+		return _copy_file(dst_path, src_path)
+	}
+}
+
+@(private)
+_copy_file :: proc(dst_path, src_path: string) -> Error {
 	src := open(src_path) or_return
 	defer close(src)
 

+ 28 - 0
core/os/os2/file_posix_darwin.odin

@@ -3,6 +3,7 @@ package os2
 
 import "base:runtime"
 
+import "core:sys/darwin"
 import "core:sys/posix"
 
 _posix_absolute_path :: proc(fd: posix.FD, name: string, allocator: runtime.Allocator) -> (path: cstring, err: Error) {
@@ -16,3 +17,30 @@ _posix_absolute_path :: proc(fd: posix.FD, name: string, allocator: runtime.Allo
 
 	return clone_to_cstring(string(cstring(&buf[0])), allocator)
 }
+
+_copy_file_native :: proc(dst_path, src_path: string) -> (err: Error) {
+	temp_allocator := TEMP_ALLOCATOR_GUARD({})
+
+	csrc := clone_to_cstring(src_path, temp_allocator) or_return
+	cdst := clone_to_cstring(dst_path, temp_allocator) or_return
+
+	// Disallow directories, as specified by the generic implementation.
+
+	stat: posix.stat_t
+	if posix.stat(csrc, &stat) != .OK {
+		err = _get_platform_error()
+		return
+	}
+
+	if posix.S_ISDIR(stat.st_mode) {
+		err = .Invalid_File
+		return
+	}
+
+	ret := darwin.copyfile(csrc, cdst, nil, darwin.COPYFILE_ALL)
+	if ret < 0 {
+		err = _get_platform_error()
+	}
+
+	return
+}

+ 67 - 0
core/sys/darwin/copyfile.odin

@@ -0,0 +1,67 @@
+package darwin
+
+import "core:sys/posix"
+
+copyfile_state_t :: distinct rawptr
+
+copyfile_flags :: bit_set[enum {
+	ACL,
+	STAT,
+	XATTR,
+	DATA,
+
+	RECURSIVE = 15,
+
+	CHECK,
+	EXCL,
+	NOFOLLOW_SRC,
+	NOFOLLOW_DST,
+	MOVE,
+	UNLINK,
+	PACK,
+	UNPACK,
+
+	CLONE,
+	CLONE_FORCE,
+	RUN_IN_PLACE,
+	DATA_SPARSE,
+	PRESERVE_DST_TRACKED,
+	VERBOSE = 30,
+}; u32]
+
+COPYFILE_SECURITY :: copyfile_flags{.STAT, .ACL}
+COPYFILE_METADATA :: COPYFILE_SECURITY + copyfile_flags{.XATTR}
+COPYFILE_ALL      :: COPYFILE_METADATA + copyfile_flags{.DATA}
+
+COPYFILE_NOFOLLOW :: copyfile_flags{.NOFOLLOW_SRC, .NOFOLLOW_DST}
+
+copyfile_state_flag :: enum u32 {
+	SRC_FD = 1,
+	SRC_FILENAME,
+	DST_FD,
+	DST_FILENAME,
+	QUARANTINE,
+	STATUS_CB,
+	STATUS_CTX,
+	COPIED,
+	XATTRNAME,
+	WAS_CLONED,
+	SRC_BSIZE,
+	DST_BSIZE,
+	BSIZE,
+	FORBID_CROSS_MOUNT,
+	NOCPROTECT,
+	PRESERVE_SUID,
+	RECURSIVE_SRC_FTSENT,
+	FORBID_DST_EXISTING_SYMLINKS,
+}
+
+foreign system {
+	copyfile  :: proc(from, to: cstring,  state: copyfile_state_t, flags: copyfile_flags) -> i32 ---
+	fcopyfile :: proc(from, to: posix.FD, state: copyfile_state_t, flags: copyfile_flags) -> i32 ---
+
+	copyfile_state_alloc :: proc() -> copyfile_state_t ---
+	copyfile_state_free  :: proc(state: copyfile_state_t) -> posix.result ---
+	copyfile_state_get   :: proc(state: copyfile_state_t, flag: copyfile_state_flag, dst: rawptr) -> posix.result ---
+	copyfile_state_set   :: proc(state: copyfile_state_t, flag: copyfile_state_flag, src: rawptr) -> posix.result ---
+}

+ 1 - 0
core/sys/darwin/darwin.odin

@@ -3,6 +3,7 @@ package darwin
 
 import "core:c"
 
+@(export)
 foreign import system "system:System.framework"
 
 Bool :: b8

+ 0 - 2
core/sys/darwin/sync.odin

@@ -1,7 +1,5 @@
 package darwin
 
-foreign import system "system:System.framework"
-
 // #define OS_WAIT_ON_ADDR_AVAILABILITY \
 // 	__API_AVAILABLE(macos(14.4), ios(17.4), tvos(17.4), watchos(10.4))
 when ODIN_OS == .Darwin {

+ 0 - 10
core/sys/darwin/xnu_system_call_wrappers.odin

@@ -19,16 +19,6 @@ X_OK  :: c.int((1 << 0))  /* test for execute or search permission */
 W_OK  :: c.int((1 << 1))  /* test for write permission */
 R_OK  :: c.int((1 << 2))  /* test for read permission */
 
-/* copyfile flags */
-COPYFILE_ACL   :: (1 << 0)
-COPYFILE_STAT  :: (1 << 1)
-COPYFILE_XATTR :: (1 << 2)
-COPYFILE_DATA  :: (1 << 3)
-
-COPYFILE_SECURITY :: (COPYFILE_STAT | COPYFILE_ACL)
-COPYFILE_METADATA :: (COPYFILE_SECURITY | COPYFILE_XATTR)
-COPYFILE_ALL	  :: (COPYFILE_METADATA | COPYFILE_DATA)
-
 /* syslimits.h */
 PATH_MAX	:: 1024	/* max bytes in pathname */