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'
|
||||||
|
// windows creates a directory symlink, so delete it with rmdir()
|
||||||
|
$if windows {
|
||||||
|
os.rmdir(symlink) or {}
|
||||||
|
} $else {
|
||||||
os.rm(symlink) or {}
|
os.rm(symlink) or {}
|
||||||
os.rm(folder) 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)
|
||||||
|
$if windows {
|
||||||
|
os.rmdir(symlink) or { panic(err) }
|
||||||
|
} $else {
|
||||||
os.rm(symlink) or { panic(err) }
|
os.rm(symlink) or { panic(err) }
|
||||||
os.rm(folder) 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) }
|
||||||
|
$if windows {
|
||||||
|
os.rmdir('symlink2') or { panic(err) }
|
||||||
|
} $else {
|
||||||
os.rm('symlink2') or { panic(err) }
|
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,23 +319,48 @@ 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
|
||||||
pub fn symlink(symlink_path string, target_path string) ?bool {
|
$if x64 || x32 {
|
||||||
mut flags := 0
|
mut flags := 0
|
||||||
if is_dir(symlink_path) {
|
if is_dir(origin) {
|
||||||
flags |= 1
|
flags ^= 1
|
||||||
}
|
}
|
||||||
flags |= 2 // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
|
|
||||||
res := C.CreateSymbolicLinkW(symlink_path.to_wide(), target_path.to_wide(), flags)
|
flags ^= 2 // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
|
||||||
if res == 0 {
|
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())))
|
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.CreateSymbolicLinkW reported success, but symlink still does not exist')
|
||||||
}
|
}
|
||||||
return true
|
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() {
|
pub fn (mut f File) close() {
|
||||||
|
@ -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