os: fix os.is_link and os.symlink on windows, add new functions os.getppid, os.getgid, os.getegid (#10251)
							parent
							
								
									e4f6369cd1
								
							
						
					
					
						commit
						d6e462a6ca
					
				|  | @ -728,7 +728,9 @@ pub fn is_dir(path string) bool { | ||||||
| // is_link returns a boolean indicating whether `path` is a link.
 | // is_link returns a boolean indicating whether `path` is a link.
 | ||||||
| pub fn is_link(path string) bool { | pub fn is_link(path string) bool { | ||||||
| 	$if windows { | 	$if windows { | ||||||
| 		return false // TODO
 | 		path_ := path.replace('/', '\\') | ||||||
|  | 		attr := C.GetFileAttributesW(path_.to_wide()) | ||||||
|  | 		return int(attr) != int(C.INVALID_FILE_ATTRIBUTES) && (attr & 0x400) != 0 | ||||||
| 	} $else { | 	} $else { | ||||||
| 		statbuf := C.stat{} | 		statbuf := C.stat{} | ||||||
| 		if C.lstat(&char(path.str), &statbuf) != 0 { | 		if C.lstat(&char(path.str), &statbuf) != 0 { | ||||||
|  | @ -784,26 +786,45 @@ pub fn real_path(fpath string) string { | ||||||
| 	defer { | 	defer { | ||||||
| 		unsafe { free(fullpath) } | 		unsafe { free(fullpath) } | ||||||
| 	} | 	} | ||||||
| 
 | 	mut res := '' | ||||||
| 	$if windows { | 	$if windows { | ||||||
|  | 		/* | ||||||
|  | 		// GetFullPathName doesn't work with symbolic links
 | ||||||
|  | 		// TODO: TCC32 gets runtime error
 | ||||||
|  | 		max := 512 | ||||||
|  | 		size := max * 2 // max_path_len * sizeof(wchar_t)
 | ||||||
|  | 		// gets handle with GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
 | ||||||
|  | 		// use get_file_handle instead of C.CreateFile(fpath.to_wide(), 0x80000000, 1, 0, 3, 0x80, 0)
 | ||||||
|  | 		// try to open the file to get symbolic link path
 | ||||||
|  | 		file := get_file_handle(fpath) | ||||||
|  | 		if file != voidptr(-1) { | ||||||
|  | 			fullpath = unsafe { &u16(vcalloc(size)) } | ||||||
|  | 			// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew
 | ||||||
|  | 			final_len := C.GetFinalPathNameByHandleW(file, fullpath, size, 0) | ||||||
|  | 			if final_len < size { | ||||||
|  | 				ret := unsafe { string_from_wide2(fullpath, final_len) } | ||||||
|  | 				res = ret[4..] | ||||||
|  | 			} else { | ||||||
|  | 				eprintln('os.real_path() saw that the file path was too long') | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 		*/ | ||||||
|  | 		// if it is not a file get full path
 | ||||||
| 		fullpath = unsafe { &u16(vcalloc(max_path_len * 2)) } | 		fullpath = unsafe { &u16(vcalloc(max_path_len * 2)) } | ||||||
| 		// TODO: check errors if path len is not enough
 | 		// TODO: check errors if path len is not enough
 | ||||||
| 		ret := C.GetFullPathName(fpath.to_wide(), max_path_len, fullpath, 0) | 		ret := C.GetFullPathName(fpath.to_wide(), max_path_len, fullpath, 0) | ||||||
| 		if ret == 0 { | 		if ret == 0 { | ||||||
| 			return fpath | 			return fpath | ||||||
| 		} | 		} | ||||||
|  | 		res = unsafe { string_from_wide(fullpath) } | ||||||
|  | 		//}
 | ||||||
|  | 		// C.CloseHandle(file)		
 | ||||||
| 	} $else { | 	} $else { | ||||||
| 		fullpath = vcalloc(max_path_len) | 		fullpath = vcalloc(max_path_len) | ||||||
| 		ret := &char(C.realpath(&char(fpath.str), &char(fullpath))) | 		ret := &char(C.realpath(&char(fpath.str), &char(fullpath))) | ||||||
| 		if ret == 0 { | 		if ret == 0 { | ||||||
| 			return fpath | 			return fpath | ||||||
| 		} | 		} | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	mut res := '' |  | ||||||
| 	$if windows { |  | ||||||
| 		res = unsafe { string_from_wide(fullpath) } |  | ||||||
| 	} $else { |  | ||||||
| 		res = unsafe { fullpath.vstring() } | 		res = unsafe { fullpath.vstring() } | ||||||
| 	} | 	} | ||||||
| 	nres := normalize_drive_letter(res) | 	nres := normalize_drive_letter(res) | ||||||
|  |  | ||||||
|  | @ -51,11 +51,19 @@ fn C.uname(name voidptr) int | ||||||
| 
 | 
 | ||||||
| fn C.symlink(&char, &char) int | fn C.symlink(&char, &char) int | ||||||
| 
 | 
 | ||||||
|  | fn C.link(&char, &char) int | ||||||
|  | 
 | ||||||
| fn C.gethostname(&char, int) int | fn C.gethostname(&char, int) int | ||||||
| 
 | 
 | ||||||
| // NB: not available on Android fn C.getlogin_r(&char, int) int
 | // NB: not available on Android fn C.getlogin_r(&char, int) int
 | ||||||
| fn C.getlogin() &char | fn C.getlogin() &char | ||||||
| 
 | 
 | ||||||
|  | fn C.getppid() int | ||||||
|  | 
 | ||||||
|  | fn C.getgid() int | ||||||
|  | 
 | ||||||
|  | fn C.getegid() int | ||||||
|  | 
 | ||||||
| pub fn uname() Uname { | pub fn uname() Uname { | ||||||
| 	mut u := Uname{} | 	mut u := Uname{} | ||||||
| 	utsize := sizeof(C.utsname) | 	utsize := sizeof(C.utsname) | ||||||
|  | @ -279,6 +287,14 @@ pub fn symlink(origin string, target string) ?bool { | ||||||
| 	return error(posix_get_error_msg(C.errno)) | 	return error(posix_get_error_msg(C.errno)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | pub fn link(origin string, target string) ?bool { | ||||||
|  | 	res := C.link(&char(origin.str), &char(target.str)) | ||||||
|  | 	if res == 0 { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	return error(posix_get_error_msg(C.errno)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // get_error_msg return error code representation in string.
 | // get_error_msg return error code representation in string.
 | ||||||
| pub fn get_error_msg(code int) string { | pub fn get_error_msg(code int) string { | ||||||
| 	return posix_get_error_msg(code) | 	return posix_get_error_msg(code) | ||||||
|  | @ -332,6 +348,11 @@ pub fn getpid() int { | ||||||
| 	return C.getpid() | 	return C.getpid() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | [inline] | ||||||
|  | pub fn getppid() int { | ||||||
|  | 	return C.getppid() | ||||||
|  | } | ||||||
|  | 
 | ||||||
| [inline] | [inline] | ||||||
| pub fn getuid() int { | pub fn getuid() int { | ||||||
| 	return C.getuid() | 	return C.getuid() | ||||||
|  | @ -342,6 +363,16 @@ pub fn geteuid() int { | ||||||
| 	return C.geteuid() | 	return C.geteuid() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | [inline] | ||||||
|  | pub fn getgid() int { | ||||||
|  | 	return C.getgid() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [inline] | ||||||
|  | pub fn getegid() int { | ||||||
|  | 	return C.getegid() | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Turns the given bit on or off, depending on the `enable` parameter
 | // Turns the given bit on or off, depending on the `enable` parameter
 | ||||||
| pub fn posix_set_permission_bit(path_s string, mode u32, enable bool) { | pub fn posix_set_permission_bit(path_s string, mode u32, enable bool) { | ||||||
| 	mut s := C.stat{} | 	mut s := C.stat{} | ||||||
|  |  | ||||||
|  | @ -311,28 +311,62 @@ fn test_is_writable_folder() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_make_symlink_check_is_link_and_remove_symlink() { | fn test_make_symlink_check_is_link_and_remove_symlink() { | ||||||
| 	$if windows { |  | ||||||
| 		// TODO
 |  | ||||||
| 		assert true |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	folder := 'tfolder' | 	folder := 'tfolder' | ||||||
| 	symlink := 'tsymlink' | 	symlink := 'tsymlink' | ||||||
| 	os.rm(symlink) or {} | 	// windows creates a directory symlink, so delete it with rmdir()
 | ||||||
| 	os.rm(folder) or {} | 	$if windows { | ||||||
|  | 		os.rmdir(symlink) or {} | ||||||
|  | 	} $else { | ||||||
|  | 		os.rm(symlink) or {} | ||||||
|  | 	} | ||||||
|  | 	os.rmdir(folder) or {} | ||||||
| 	os.mkdir(folder) or { panic(err) } | 	os.mkdir(folder) or { panic(err) } | ||||||
| 	folder_contents := os.ls(folder) or { panic(err) } | 	folder_contents := os.ls(folder) or { panic(err) } | ||||||
| 	assert folder_contents.len == 0 | 	assert folder_contents.len == 0 | ||||||
| 	os.system('ln -s $folder $symlink') | 	os.symlink(folder, symlink) or { panic(err) } | ||||||
| 	assert os.is_link(symlink) | 	assert os.is_link(symlink) | ||||||
| 	os.rm(symlink) or { panic(err) } | 	$if windows { | ||||||
| 	os.rm(folder) or { panic(err) } | 		os.rmdir(symlink) or { panic(err) } | ||||||
|  | 	} $else { | ||||||
|  | 		os.rm(symlink) or { panic(err) } | ||||||
|  | 	} | ||||||
|  | 	os.rmdir(folder) or { panic(err) } | ||||||
| 	folder_exists := os.is_dir(folder) | 	folder_exists := os.is_dir(folder) | ||||||
| 	assert folder_exists == false | 	assert folder_exists == false | ||||||
| 	symlink_exists := os.is_link(symlink) | 	symlink_exists := os.is_link(symlink) | ||||||
| 	assert symlink_exists == false | 	assert symlink_exists == false | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn test_make_symlink_check_is_link_and_remove_symlink_with_file() { | ||||||
|  | 	file := 'tfile' | ||||||
|  | 	symlink := 'tsymlink' | ||||||
|  | 	os.rm(symlink) or {} | ||||||
|  | 	os.rm(file) or {} | ||||||
|  | 	mut f := os.create(file) or { panic(err) } | ||||||
|  | 	f.close() | ||||||
|  | 	os.symlink(file, symlink) or { panic(err) } | ||||||
|  | 	assert os.is_link(symlink) | ||||||
|  | 	os.rm(symlink) or { panic(err) } | ||||||
|  | 	os.rm(file) or { panic(err) } | ||||||
|  | 	symlink_exists := os.is_link(symlink) | ||||||
|  | 	assert symlink_exists == false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn test_make_hardlink_check_is_link_and_remove_hardlink_with_file() { | ||||||
|  | 	file := 'tfile' | ||||||
|  | 	symlink := 'tsymlink' | ||||||
|  | 	os.rm(symlink) or {} | ||||||
|  | 	os.rm(file) or {} | ||||||
|  | 	mut f := os.create(file) or { panic(err) } | ||||||
|  | 	f.close() | ||||||
|  | 	os.link(file, symlink) or { panic(err) } | ||||||
|  | 	assert os.exists(symlink) | ||||||
|  | 	os.rm(symlink) or { panic(err) } | ||||||
|  | 	os.rm(file) or { panic(err) } | ||||||
|  | 	symlink_exists := os.is_link(symlink) | ||||||
|  | 	assert symlink_exists == false | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // fn test_fork() {
 | // fn test_fork() {
 | ||||||
| // pid := os.fork()
 | // pid := os.fork()
 | ||||||
| // if pid == 0 {
 | // if pid == 0 {
 | ||||||
|  | @ -355,15 +389,16 @@ fn test_make_symlink_check_is_link_and_remove_symlink() { | ||||||
| // }
 | // }
 | ||||||
| // }
 | // }
 | ||||||
| fn test_symlink() { | fn test_symlink() { | ||||||
| 	$if windows { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	os.mkdir('symlink') or { panic(err) } | 	os.mkdir('symlink') or { panic(err) } | ||||||
| 	os.symlink('symlink', 'symlink2') or { panic(err) } | 	os.symlink('symlink', 'symlink2') or { panic(err) } | ||||||
| 	assert os.exists('symlink2') | 	assert os.exists('symlink2') | ||||||
| 	// cleanup
 | 	// cleanup
 | ||||||
| 	os.rm('symlink') or { panic(err) } | 	os.rmdir('symlink') or { panic(err) } | ||||||
| 	os.rm('symlink2') or { panic(err) } | 	$if windows { | ||||||
|  | 		os.rmdir('symlink2') or { panic(err) } | ||||||
|  | 	} $else { | ||||||
|  | 		os.rm('symlink2') or { panic(err) } | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_is_executable_writable_readable() { | fn test_is_executable_writable_readable() { | ||||||
|  |  | ||||||
|  | @ -5,6 +5,15 @@ import strings | ||||||
| #flag windows -l advapi32 | #flag windows -l advapi32 | ||||||
| #include <process.h> | #include <process.h> | ||||||
| 
 | 
 | ||||||
|  | // See https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
 | ||||||
|  | fn C.CreateSymbolicLinkW(&u16, &u16, u32) int | ||||||
|  | 
 | ||||||
|  | // See https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createhardlinkw
 | ||||||
|  | // TCC gets builder error
 | ||||||
|  | // fn C.CreateHardLinkW(&u16, &u16, C.SECURITY_ATTRIBUTES) int
 | ||||||
|  | 
 | ||||||
|  | fn C._getpid() int | ||||||
|  | 
 | ||||||
| pub const ( | pub const ( | ||||||
| 	path_separator = '\\' | 	path_separator = '\\' | ||||||
| 	path_delimiter = ';' | 	path_delimiter = ';' | ||||||
|  | @ -138,7 +147,7 @@ pub fn mkdir(path string) ?bool { | ||||||
| 	} | 	} | ||||||
| 	apath := real_path(path) | 	apath := real_path(path) | ||||||
| 	if !C.CreateDirectory(apath.to_wide(), 0) { | 	if !C.CreateDirectory(apath.to_wide(), 0) { | ||||||
| 		return error('mkdir failed for "$apath", because CreateDirectory returned ' + | 		return error('mkdir failed for "$apath", because CreateDirectory returned: ' + | ||||||
| 			get_error_msg(int(C.GetLastError()))) | 			get_error_msg(int(C.GetLastError()))) | ||||||
| 	} | 	} | ||||||
| 	return true | 	return true | ||||||
|  | @ -310,21 +319,46 @@ pub fn execute(cmd string) Result { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // See https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
 | pub fn symlink(origin string, target string) ?bool { | ||||||
| fn C.CreateSymbolicLinkW(&u16, &u16, u32) int | 	// this is a temporary fix for TCC32 due to runtime error
 | ||||||
|  | 	// TODO: patch TCC32
 | ||||||
|  | 	$if x64 || x32 { | ||||||
|  | 		mut flags := 0 | ||||||
|  | 		if is_dir(origin) { | ||||||
|  | 			flags ^= 1 | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| pub fn symlink(symlink_path string, target_path string) ?bool { | 		flags ^= 2 // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
 | ||||||
| 	mut flags := 0 | 		res := C.CreateSymbolicLinkW(target.to_wide(), origin.to_wide(), flags) | ||||||
| 	if is_dir(symlink_path) { | 
 | ||||||
| 		flags |= 1 | 		// 1 = success, != 1 failure => https://stackoverflow.com/questions/33010440/createsymboliclink-on-windows-10
 | ||||||
|  | 		if res != 1 { | ||||||
|  | 			return error(get_error_msg(int(C.GetLastError()))) | ||||||
|  | 		} | ||||||
|  | 		if !exists(target) { | ||||||
|  | 			return error('C.CreateSymbolicLinkW reported success, but symlink still does not exist') | ||||||
|  | 		} | ||||||
|  | 		return true | ||||||
| 	} | 	} | ||||||
| 	flags |= 2 // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
 | 	return false | ||||||
| 	res := C.CreateSymbolicLinkW(symlink_path.to_wide(), target_path.to_wide(), flags) | } | ||||||
| 	if res == 0 { | 
 | ||||||
|  | pub fn link(origin string, target string) ?bool { | ||||||
|  | 	/* | ||||||
|  | 	// TODO: TCC gets builder error
 | ||||||
|  | 	res := C.CreateHardLinkW(target.to_wide(), origin.to_wide(), C.NULL) | ||||||
|  | 	// 1 = success, != 1 failure => https://stackoverflow.com/questions/33010440/createsymboliclink-on-windows-10
 | ||||||
|  | 	if res != 1 { | ||||||
| 		return error(get_error_msg(int(C.GetLastError()))) | 		return error(get_error_msg(int(C.GetLastError()))) | ||||||
| 	} | 	} | ||||||
| 	if !exists(symlink_path) { | 	if !exists(target) { | ||||||
| 		return error('C.CreateSymbolicLinkW reported success, but symlink still does not exist') | 		return error('C.CreateHardLinkW reported success, but link still does not exist') | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | 	*/ | ||||||
|  | 	res := execute('fsutil hardlink create $target $origin') | ||||||
|  | 	if res.exit_code != 0 { | ||||||
|  | 		return error(res.output) | ||||||
| 	} | 	} | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
|  | @ -426,13 +460,36 @@ pub fn is_writable_folder(folder string) ?bool { | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn C._getpid() int |  | ||||||
| 
 |  | ||||||
| [inline] | [inline] | ||||||
| pub fn getpid() int { | pub fn getpid() int { | ||||||
| 	return C._getpid() | 	return C._getpid() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | [inline] | ||||||
|  | pub fn getppid() int { | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [inline] | ||||||
|  | pub fn getuid() int { | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [inline] | ||||||
|  | pub fn geteuid() int { | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [inline] | ||||||
|  | pub fn getgid() int { | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [inline] | ||||||
|  | pub fn getegid() int { | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| pub fn posix_set_permission_bit(path_s string, mode u32, enable bool) { | pub fn posix_set_permission_bit(path_s string, mode u32, enable bool) { | ||||||
| 	// windows has no concept of a permission mask, so do nothing
 | 	// windows has no concept of a permission mask, so do nothing
 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue