diff --git a/vlib/os/os_c.v b/vlib/os/os_c.v index c241a48049..07d35b47cb 100644 --- a/vlib/os/os_c.v +++ b/vlib/os/os_c.v @@ -779,16 +779,35 @@ pub fn getwd() string { pub fn real_path(fpath string) string { mut res := '' $if windows { - // GetFullPathName doesn't work with symbolic links, - // so if it is not a file, get full path - mut fullpath := unsafe { &u16(vcalloc_noscan(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 { - unsafe { free(fullpath) } - return fpath.clone() + size := max_path_len * 2 + // gets handle with GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 + // use C.CreateFile(fpath.to_wide(), 0x80000000, 1, 0, 3, 0x80, 0) instead of get_file_handle + // try to open the file to get symbolic link path + file := C.CreateFile(fpath.to_wide(), 0x80000000, 1, 0, 3, 0x80, 0) + if file != voidptr(-1) { + mut fullpath := unsafe { &u16(vcalloc_noscan(size)) } + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew + final_len := C.GetFinalPathNameByHandleW(file, fullpath, size, 0) + C.CloseHandle(file) + if final_len < size { + rt := unsafe { string_from_wide2(fullpath, final_len) } + res = rt[4..] + } else { + unsafe { free(fullpath) } + eprintln('os.real_path() saw that the file path was too long') + return fpath.clone() + } + } else { + // if it is not a file C.CreateFile doesn't gets a file handle, use GetFullPath instead + mut fullpath := unsafe { &u16(vcalloc_noscan(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 { + unsafe { free(fullpath) } + return fpath.clone() + } + res = unsafe { string_from_wide(fullpath) } } - res = unsafe { string_from_wide(fullpath) } } $else { mut fullpath := vcalloc_noscan(max_path_len) ret := &char(C.realpath(&char(fpath.str), &char(fullpath))) diff --git a/vlib/os/os_test.v b/vlib/os/os_test.v index 2f57f165ee..56089daef1 100644 --- a/vlib/os/os_test.v +++ b/vlib/os/os_test.v @@ -342,6 +342,20 @@ fn test_realpath_does_not_absolutize_non_existing_relative_paths() { } } +fn test_realpath_absolutepath_symlink() { + file_name := 'tolink_file.txt' + symlink_name := 'symlink.txt' + mut f := os.create(file_name) or { panic(err) } + f.close() + assert os.symlink(file_name, symlink_name) or { panic(err) } + rpath := os.real_path(symlink_name) + println(rpath) + assert os.is_abs_path(rpath) + assert rpath.ends_with(file_name) + os.rm(symlink_name) or {} + os.rm(file_name) or {} +} + fn test_tmpdir() { t := os.temp_dir() assert t.len > 0 diff --git a/vlib/os/os_windows.c.v b/vlib/os/os_windows.c.v index d187aff61d..0c10519d9e 100644 --- a/vlib/os/os_windows.c.v +++ b/vlib/os/os_windows.c.v @@ -9,8 +9,7 @@ import strings 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.CreateHardLinkW(&u16, &u16, C.SECURITY_ATTRIBUTES) int fn C._getpid() int @@ -321,7 +320,7 @@ pub fn execute(cmd string) Result { pub fn symlink(origin string, target string) ?bool { // this is a temporary fix for TCC32 due to runtime error - // TODO: patch TCC32 + // TODO: find the cause why TCC32 for Windows does not work without the compiletime option $if x64 || x32 { mut flags := 0 if is_dir(origin) { @@ -344,8 +343,6 @@ pub fn symlink(origin string, target string) ?bool { } 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 { @@ -355,12 +352,6 @@ pub fn link(origin string, target string) ?bool { 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() {