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.
 | 
			
		||||
pub fn is_link(path string) bool {
 | 
			
		||||
	$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 {
 | 
			
		||||
		statbuf := C.stat{}
 | 
			
		||||
		if C.lstat(&char(path.str), &statbuf) != 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -784,26 +786,45 @@ pub fn real_path(fpath string) string {
 | 
			
		|||
	defer {
 | 
			
		||||
		unsafe { free(fullpath) }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mut res := ''
 | 
			
		||||
	$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)) }
 | 
			
		||||
		// TODO: check errors if path len is not enough
 | 
			
		||||
		ret := C.GetFullPathName(fpath.to_wide(), max_path_len, fullpath, 0)
 | 
			
		||||
		if ret == 0 {
 | 
			
		||||
			return fpath
 | 
			
		||||
		}
 | 
			
		||||
		res = unsafe { string_from_wide(fullpath) }
 | 
			
		||||
		//}
 | 
			
		||||
		// C.CloseHandle(file)		
 | 
			
		||||
	} $else {
 | 
			
		||||
		fullpath = vcalloc(max_path_len)
 | 
			
		||||
		ret := &char(C.realpath(&char(fpath.str), &char(fullpath)))
 | 
			
		||||
		if ret == 0 {
 | 
			
		||||
			return fpath
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mut res := ''
 | 
			
		||||
	$if windows {
 | 
			
		||||
		res = unsafe { string_from_wide(fullpath) }
 | 
			
		||||
	} $else {
 | 
			
		||||
		res = unsafe { fullpath.vstring() }
 | 
			
		||||
	}
 | 
			
		||||
	nres := normalize_drive_letter(res)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,11 +51,19 @@ fn C.uname(name voidptr) int
 | 
			
		|||
 | 
			
		||||
fn C.symlink(&char, &char) int
 | 
			
		||||
 | 
			
		||||
fn C.link(&char, &char) int
 | 
			
		||||
 | 
			
		||||
fn C.gethostname(&char, int) int
 | 
			
		||||
 | 
			
		||||
// NB: not available on Android fn C.getlogin_r(&char, int) int
 | 
			
		||||
fn C.getlogin() &char
 | 
			
		||||
 | 
			
		||||
fn C.getppid() int
 | 
			
		||||
 | 
			
		||||
fn C.getgid() int
 | 
			
		||||
 | 
			
		||||
fn C.getegid() int
 | 
			
		||||
 | 
			
		||||
pub fn uname() Uname {
 | 
			
		||||
	mut u := Uname{}
 | 
			
		||||
	utsize := sizeof(C.utsname)
 | 
			
		||||
| 
						 | 
				
			
			@ -279,6 +287,14 @@ pub fn symlink(origin string, target string) ?bool {
 | 
			
		|||
	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.
 | 
			
		||||
pub fn get_error_msg(code int) string {
 | 
			
		||||
	return posix_get_error_msg(code)
 | 
			
		||||
| 
						 | 
				
			
			@ -332,6 +348,11 @@ pub fn getpid() int {
 | 
			
		|||
	return C.getpid()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[inline]
 | 
			
		||||
pub fn getppid() int {
 | 
			
		||||
	return C.getppid()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[inline]
 | 
			
		||||
pub fn getuid() int {
 | 
			
		||||
	return C.getuid()
 | 
			
		||||
| 
						 | 
				
			
			@ -342,6 +363,16 @@ pub fn geteuid() int {
 | 
			
		|||
	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
 | 
			
		||||
pub fn posix_set_permission_bit(path_s string, mode u32, enable bool) {
 | 
			
		||||
	mut s := C.stat{}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -311,28 +311,62 @@ fn test_is_writable_folder() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
fn test_make_symlink_check_is_link_and_remove_symlink() {
 | 
			
		||||
	$if windows {
 | 
			
		||||
		// TODO
 | 
			
		||||
		assert true
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	folder := 'tfolder'
 | 
			
		||||
	symlink := 'tsymlink'
 | 
			
		||||
	// windows creates a directory symlink, so delete it with rmdir()
 | 
			
		||||
	$if windows {
 | 
			
		||||
		os.rmdir(symlink) or {}
 | 
			
		||||
	} $else {
 | 
			
		||||
		os.rm(symlink) or {}
 | 
			
		||||
	os.rm(folder) or {}
 | 
			
		||||
	}
 | 
			
		||||
	os.rmdir(folder) or {}
 | 
			
		||||
	os.mkdir(folder) or { panic(err) }
 | 
			
		||||
	folder_contents := os.ls(folder) or { panic(err) }
 | 
			
		||||
	assert folder_contents.len == 0
 | 
			
		||||
	os.system('ln -s $folder $symlink')
 | 
			
		||||
	os.symlink(folder, symlink) or { panic(err) }
 | 
			
		||||
	assert os.is_link(symlink)
 | 
			
		||||
	$if windows {
 | 
			
		||||
		os.rmdir(symlink) or { panic(err) }
 | 
			
		||||
	} $else {
 | 
			
		||||
		os.rm(symlink) or { panic(err) }
 | 
			
		||||
	os.rm(folder) or { panic(err) }
 | 
			
		||||
	}
 | 
			
		||||
	os.rmdir(folder) or { panic(err) }
 | 
			
		||||
	folder_exists := os.is_dir(folder)
 | 
			
		||||
	assert folder_exists == false
 | 
			
		||||
	symlink_exists := os.is_link(symlink)
 | 
			
		||||
	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() {
 | 
			
		||||
// pid := os.fork()
 | 
			
		||||
// if pid == 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -355,16 +389,17 @@ fn test_make_symlink_check_is_link_and_remove_symlink() {
 | 
			
		|||
// }
 | 
			
		||||
// }
 | 
			
		||||
fn test_symlink() {
 | 
			
		||||
	$if windows {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	os.mkdir('symlink') or { panic(err) }
 | 
			
		||||
	os.symlink('symlink', 'symlink2') or { panic(err) }
 | 
			
		||||
	assert os.exists('symlink2')
 | 
			
		||||
	// cleanup
 | 
			
		||||
	os.rm('symlink') or { panic(err) }
 | 
			
		||||
	os.rmdir('symlink') or { panic(err) }
 | 
			
		||||
	$if windows {
 | 
			
		||||
		os.rmdir('symlink2') or { panic(err) }
 | 
			
		||||
	} $else {
 | 
			
		||||
		os.rm('symlink2') or { panic(err) }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_is_executable_writable_readable() {
 | 
			
		||||
	file_name := 'rwxfile.exe'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,15 @@ import strings
 | 
			
		|||
#flag windows -l advapi32
 | 
			
		||||
#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 (
 | 
			
		||||
	path_separator = '\\'
 | 
			
		||||
	path_delimiter = ';'
 | 
			
		||||
| 
						 | 
				
			
			@ -138,7 +147,7 @@ pub fn mkdir(path string) ?bool {
 | 
			
		|||
	}
 | 
			
		||||
	apath := real_path(path)
 | 
			
		||||
	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())))
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
| 
						 | 
				
			
			@ -310,24 +319,49 @@ pub fn execute(cmd string) Result {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
 | 
			
		||||
fn C.CreateSymbolicLinkW(&u16, &u16, u32) int
 | 
			
		||||
 | 
			
		||||
pub fn symlink(symlink_path string, target_path string) ?bool {
 | 
			
		||||
pub fn symlink(origin string, target string) ?bool {
 | 
			
		||||
	// this is a temporary fix for TCC32 due to runtime error
 | 
			
		||||
	// TODO: patch TCC32
 | 
			
		||||
	$if x64 || x32 {
 | 
			
		||||
		mut flags := 0
 | 
			
		||||
	if is_dir(symlink_path) {
 | 
			
		||||
		flags |= 1
 | 
			
		||||
		if is_dir(origin) {
 | 
			
		||||
			flags ^= 1
 | 
			
		||||
		}
 | 
			
		||||
	flags |= 2 // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
 | 
			
		||||
	res := C.CreateSymbolicLinkW(symlink_path.to_wide(), target_path.to_wide(), flags)
 | 
			
		||||
	if res == 0 {
 | 
			
		||||
 | 
			
		||||
		flags ^= 2 // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
 | 
			
		||||
		res := C.CreateSymbolicLinkW(target.to_wide(), origin.to_wide(), flags)
 | 
			
		||||
 | 
			
		||||
		// 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(symlink_path) {
 | 
			
		||||
		if !exists(target) {
 | 
			
		||||
			return error('C.CreateSymbolicLinkW reported success, but symlink still does not exist')
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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())))
 | 
			
		||||
	}
 | 
			
		||||
	if !exists(target) {
 | 
			
		||||
		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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn (mut f File) close() {
 | 
			
		||||
	if !f.is_opened {
 | 
			
		||||
| 
						 | 
				
			
			@ -426,13 +460,36 @@ pub fn is_writable_folder(folder string) ?bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn C._getpid() int
 | 
			
		||||
 | 
			
		||||
[inline]
 | 
			
		||||
pub fn getpid() int {
 | 
			
		||||
	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) {
 | 
			
		||||
	// windows has no concept of a permission mask, so do nothing
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue