mirror of
https://github.com/vlang/v.git
synced 2025-09-13 14:32:26 +03:00
os: refactor to use os.stat and os.lstat instead of unsafe C calls (#20759)
This commit is contained in:
parent
3c0257af1b
commit
410bd9db71
6 changed files with 139 additions and 223 deletions
|
@ -60,75 +60,16 @@ pub fn (m FileMode) bitmask() u32 {
|
|||
|
||||
// inode returns the metadata of the file/inode, containing inode type, permission information, size and modification time.
|
||||
// it supports windows for regular files, but it doesn't matter if you use owner, group or others when checking permissions on windows.
|
||||
// if a symlink is targetted, it returns info on the link, not the target
|
||||
pub fn inode(path string) FileInfo {
|
||||
mut attr := C.stat{}
|
||||
$if windows {
|
||||
// TODO: replace this with a C.GetFileAttributesW call instead.
|
||||
// Use stat, lstat is not available on windows
|
||||
unsafe { C.stat(&char(path.str), &attr) }
|
||||
mut typ := FileType.regular
|
||||
if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFDIR) {
|
||||
typ = .directory
|
||||
}
|
||||
return FileInfo{
|
||||
typ: typ
|
||||
size: attr.st_size
|
||||
mtime: attr.st_mtime
|
||||
owner: FilePermission{
|
||||
read: (attr.st_mode & u32(C.S_IREAD)) != 0
|
||||
write: (attr.st_mode & u32(C.S_IWRITE)) != 0
|
||||
execute: (attr.st_mode & u32(C.S_IEXEC)) != 0
|
||||
}
|
||||
group: FilePermission{
|
||||
read: (attr.st_mode & u32(C.S_IREAD)) != 0
|
||||
write: (attr.st_mode & u32(C.S_IWRITE)) != 0
|
||||
execute: (attr.st_mode & u32(C.S_IEXEC)) != 0
|
||||
}
|
||||
others: FilePermission{
|
||||
read: (attr.st_mode & u32(C.S_IREAD)) != 0
|
||||
write: (attr.st_mode & u32(C.S_IWRITE)) != 0
|
||||
execute: (attr.st_mode & u32(C.S_IEXEC)) != 0
|
||||
}
|
||||
}
|
||||
} $else {
|
||||
// note, that we use lstat here on purpose, to know the information about
|
||||
// the potential symlinks themselves, not about the entities they point at
|
||||
unsafe { C.lstat(&char(path.str), &attr) }
|
||||
mut typ := FileType.unknown
|
||||
if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFREG) {
|
||||
typ = .regular
|
||||
} else if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFDIR) {
|
||||
typ = .directory
|
||||
} else if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFCHR) {
|
||||
typ = .character_device
|
||||
} else if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFBLK) {
|
||||
typ = .block_device
|
||||
} else if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFIFO) {
|
||||
typ = .fifo
|
||||
} else if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFLNK) {
|
||||
typ = .symbolic_link
|
||||
} else if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFSOCK) {
|
||||
typ = .socket
|
||||
}
|
||||
return FileInfo{
|
||||
typ: typ
|
||||
size: attr.st_size
|
||||
mtime: attr.st_mtime
|
||||
owner: FilePermission{
|
||||
read: (attr.st_mode & u32(C.S_IRUSR)) != 0
|
||||
write: (attr.st_mode & u32(C.S_IWUSR)) != 0
|
||||
execute: (attr.st_mode & u32(C.S_IXUSR)) != 0
|
||||
}
|
||||
group: FilePermission{
|
||||
read: (attr.st_mode & u32(C.S_IRGRP)) != 0
|
||||
write: (attr.st_mode & u32(C.S_IWGRP)) != 0
|
||||
execute: (attr.st_mode & u32(C.S_IXGRP)) != 0
|
||||
}
|
||||
others: FilePermission{
|
||||
read: (attr.st_mode & u32(C.S_IROTH)) != 0
|
||||
write: (attr.st_mode & u32(C.S_IWOTH)) != 0
|
||||
execute: (attr.st_mode & u32(C.S_IXOTH)) != 0
|
||||
}
|
||||
}
|
||||
attr := lstat(path) or { Stat{} }
|
||||
fm := attr.get_mode()
|
||||
return FileInfo{
|
||||
typ: fm.typ
|
||||
owner: fm.owner
|
||||
group: fm.group
|
||||
others: fm.others
|
||||
size: attr.size
|
||||
mtime: attr.mtime
|
||||
}
|
||||
}
|
||||
|
|
146
vlib/os/os.c.v
146
vlib/os/os.c.v
|
@ -169,52 +169,15 @@ pub fn truncate(path string, len u64) ! {
|
|||
}
|
||||
}
|
||||
|
||||
fn eprintln_unknown_file_size() {
|
||||
eprintln('os.file_size() Cannot determine file-size: ' + posix_get_error_msg(C.errno))
|
||||
}
|
||||
|
||||
// file_size returns the size of the file located in `path`.
|
||||
// If an error occurs it returns 0.
|
||||
// Note that use of this on symbolic links on Windows returns always 0.
|
||||
pub fn file_size(path string) u64 {
|
||||
mut s := C.stat{}
|
||||
unsafe {
|
||||
$if x64 {
|
||||
$if windows {
|
||||
mut swin := C.__stat64{}
|
||||
if C._wstat64(path.to_wide(), voidptr(&swin)) != 0 {
|
||||
eprintln_unknown_file_size()
|
||||
return 0
|
||||
}
|
||||
return swin.st_size
|
||||
} $else {
|
||||
if C.stat(&char(path.str), &s) != 0 {
|
||||
eprintln_unknown_file_size()
|
||||
return 0
|
||||
}
|
||||
return u64(s.st_size)
|
||||
}
|
||||
}
|
||||
$if x32 {
|
||||
$if debug {
|
||||
eprintln('Using os.file_size() on 32bit systems may not work on big files.')
|
||||
}
|
||||
$if windows {
|
||||
if C._wstat(path.to_wide(), voidptr(&s)) != 0 {
|
||||
eprintln_unknown_file_size()
|
||||
return 0
|
||||
}
|
||||
return u64(s.st_size)
|
||||
} $else {
|
||||
if C.stat(&char(path.str), &s) != 0 {
|
||||
eprintln_unknown_file_size()
|
||||
return 0
|
||||
}
|
||||
return u64(s.st_size)
|
||||
}
|
||||
}
|
||||
attr := stat(path) or {
|
||||
eprintln('os.file_size() Cannot determine file-size: ' + posix_get_error_msg(C.errno))
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
return attr.size
|
||||
}
|
||||
|
||||
// rename_dir renames the folder from `src` to `dst`.
|
||||
|
@ -291,11 +254,8 @@ pub fn cp(src string, dst string) ! {
|
|||
return error_with_code('cp: failed to write to ${dst}', int(-1))
|
||||
}
|
||||
}
|
||||
from_attr := C.stat{}
|
||||
unsafe {
|
||||
C.stat(&char(src.str), &from_attr)
|
||||
}
|
||||
if C.chmod(&char(dst.str), from_attr.st_mode) < 0 {
|
||||
from_attr := stat(src)!
|
||||
if C.chmod(&char(dst.str), from_attr.mode) < 0 {
|
||||
C.close(fp_to)
|
||||
C.close(fp_from)
|
||||
return error_with_code('failed to set permissions for ${dst}', int(-1))
|
||||
|
@ -460,13 +420,8 @@ pub fn is_executable(path string) bool {
|
|||
return exists(p) && (p.ends_with('.exe') || p.ends_with('.bat') || p.ends_with('.cmd'))
|
||||
}
|
||||
$if solaris {
|
||||
statbuf := C.stat{}
|
||||
unsafe {
|
||||
if C.stat(&char(path.str), &statbuf) != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return (int(statbuf.st_mode) & (s_ixusr | s_ixgrp | s_ixoth)) != 0
|
||||
attr := stat(path) or { return false }
|
||||
return (int(attr.mode) & (s_ixusr | s_ixgrp | s_ixoth)) != 0
|
||||
}
|
||||
return C.access(&char(path.str), x_ok) != -1
|
||||
}
|
||||
|
@ -783,46 +738,6 @@ pub fn executable() string {
|
|||
return executable_fallback()
|
||||
}
|
||||
|
||||
// is_dir returns a `bool` indicating whether the given `path` is a directory.
|
||||
pub fn is_dir(path string) bool {
|
||||
$if windows {
|
||||
w_path := path.replace('/', '\\')
|
||||
attr := C.GetFileAttributesW(w_path.to_wide())
|
||||
if attr == u32(C.INVALID_FILE_ATTRIBUTES) {
|
||||
return false
|
||||
}
|
||||
if int(attr) & C.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} $else {
|
||||
statbuf := C.stat{}
|
||||
if unsafe { C.stat(&char(path.str), &statbuf) } != 0 {
|
||||
return false
|
||||
}
|
||||
// ref: https://code.woboq.org/gcc/include/sys/stat.h.html
|
||||
val := int(statbuf.st_mode) & s_ifmt
|
||||
return val == s_ifdir
|
||||
}
|
||||
}
|
||||
|
||||
// is_link returns a boolean indicating whether `path` is a link.
|
||||
// Warning: `is_link()` is known to cause a TOCTOU vulnerability when used incorrectly
|
||||
// (for more information: https://github.com/vlang/v/blob/master/vlib/os/README.md)
|
||||
pub fn is_link(path string) bool {
|
||||
$if windows {
|
||||
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 {
|
||||
return false
|
||||
}
|
||||
return int(statbuf.st_mode) & s_ifmt == s_iflnk
|
||||
}
|
||||
}
|
||||
|
||||
struct PathKind {
|
||||
mut:
|
||||
is_file bool
|
||||
|
@ -830,41 +745,6 @@ mut:
|
|||
is_link bool
|
||||
}
|
||||
|
||||
fn kind_of_existing_path(path string) PathKind {
|
||||
mut res := PathKind{}
|
||||
$if windows {
|
||||
attr := C.GetFileAttributesW(path.to_wide())
|
||||
if attr != u32(C.INVALID_FILE_ATTRIBUTES) {
|
||||
if (int(attr) & C.FILE_ATTRIBUTE_NORMAL) != 0 {
|
||||
res.is_file = true
|
||||
}
|
||||
if (int(attr) & C.FILE_ATTRIBUTE_DIRECTORY) != 0 {
|
||||
res.is_dir = true
|
||||
}
|
||||
if (int(attr) & 0x400) != 0 {
|
||||
res.is_link = true
|
||||
}
|
||||
}
|
||||
} $else {
|
||||
statbuf := C.stat{}
|
||||
// ref: https://code.woboq.org/gcc/include/sys/stat.h.html
|
||||
res_stat := unsafe { C.lstat(&char(path.str), &statbuf) }
|
||||
if res_stat == 0 {
|
||||
kind := (int(statbuf.st_mode) & s_ifmt)
|
||||
if kind == s_ifreg {
|
||||
res.is_file = true
|
||||
}
|
||||
if kind == s_ifdir {
|
||||
res.is_dir = true
|
||||
}
|
||||
if kind == s_iflnk {
|
||||
res.is_link = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// chdir changes the current working directory to the new directory in `path`.
|
||||
pub fn chdir(path string) ! {
|
||||
ret := $if windows { C._wchdir(path.to_wide()) } $else { C.chdir(&char(path.str)) }
|
||||
|
@ -1005,12 +885,10 @@ pub fn wait() int {
|
|||
|
||||
// file_last_mod_unix returns the "last modified" time stamp of file in `path`.
|
||||
pub fn file_last_mod_unix(path string) i64 {
|
||||
attr := C.stat{}
|
||||
// # struct stat attr;
|
||||
unsafe { C.stat(&char(path.str), &attr) }
|
||||
// # stat(path.str, &attr);
|
||||
return i64(attr.st_mtime)
|
||||
// # return attr.st_mtime ;
|
||||
if attr := stat(path) {
|
||||
return attr.mtime
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// flush will flush the stdout buffer.
|
||||
|
|
23
vlib/os/os.v
23
vlib/os/os.v
|
@ -978,17 +978,18 @@ pub fn config_dir() !string {
|
|||
return error('Cannot find config directory')
|
||||
}
|
||||
|
||||
// Stat struct modeled on POSIX
|
||||
pub struct Stat {
|
||||
pub:
|
||||
dev u64
|
||||
inode u64
|
||||
mode u32
|
||||
nlink u64
|
||||
uid u32
|
||||
gid u32
|
||||
rdev u64
|
||||
size u64
|
||||
atime i64
|
||||
mtime i64
|
||||
ctime i64
|
||||
dev u64 // ID of device containing file
|
||||
inode u64 // Inode number
|
||||
mode u32 // File type and user/group/world permission bits
|
||||
nlink u64 // Number of hard links to file
|
||||
uid u32 // Owner user ID
|
||||
gid u32 // Owner group ID
|
||||
rdev u64 // Device ID (if special file)
|
||||
size u64 // Total size in bytes
|
||||
atime i64 // Last access (seconds since UNIX epoch)
|
||||
mtime i64 // Last modified (seconds since UNIX epoch)
|
||||
ctime i64 // Last status change (seconds since UNIX epoch)
|
||||
}
|
||||
|
|
|
@ -519,18 +519,15 @@ pub fn getegid() int {
|
|||
|
||||
// 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{}
|
||||
mut new_mode := u32(0)
|
||||
path := &char(path_s.str)
|
||||
unsafe {
|
||||
C.stat(path, &s)
|
||||
new_mode = s.st_mode
|
||||
if s := stat(path_s) {
|
||||
new_mode = s.mode
|
||||
}
|
||||
match enable {
|
||||
true { new_mode |= mode }
|
||||
false { new_mode &= (0o7777 - mode) }
|
||||
}
|
||||
C.chmod(path, int(new_mode))
|
||||
C.chmod(&char(path_s.str), int(new_mode))
|
||||
}
|
||||
|
||||
// get_long_path has no meaning for *nix, but has for windows, where `c:\folder\some~1` for example
|
||||
|
|
|
@ -2,9 +2,36 @@ module os
|
|||
|
||||
// stat returns a platform-agnostic Stat struct comparable to what is
|
||||
// available in other programming languages and fails with the POSIX
|
||||
// error if the stat call fails. Symlinks are followed and the resulting
|
||||
// Stat provided. (If this is not desired, use lstat().)
|
||||
pub fn stat(path string) !Stat {
|
||||
mut s := C.stat{}
|
||||
unsafe {
|
||||
res := C.stat(&char(path.str), &s)
|
||||
if res != 0 {
|
||||
return error_posix()
|
||||
}
|
||||
return Stat{
|
||||
dev: s.st_dev
|
||||
inode: s.st_ino
|
||||
nlink: s.st_nlink
|
||||
mode: s.st_mode
|
||||
uid: s.st_uid
|
||||
gid: s.st_gid
|
||||
rdev: s.st_rdev
|
||||
size: s.st_size
|
||||
atime: s.st_atime
|
||||
mtime: s.st_mtime
|
||||
ctime: s.st_ctime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lstat returns a platform-agnostic Stat struct comparable to what is
|
||||
// available in other programming languages and fails with the POSIX
|
||||
// error if the stat call fails. If a link is stat'd, the stat info
|
||||
// for the link is provided.
|
||||
pub fn stat(path string) !Stat {
|
||||
pub fn lstat(path string) !Stat {
|
||||
mut s := C.stat{}
|
||||
unsafe {
|
||||
res := C.lstat(&char(path.str), &s)
|
||||
|
@ -79,3 +106,27 @@ pub fn (st Stat) get_mode() FileMode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// is_dir returns a `bool` indicating whether the given `path` is a directory.
|
||||
pub fn is_dir(path string) bool {
|
||||
attr := stat(path) or { return false }
|
||||
return attr.get_filetype() == .directory
|
||||
}
|
||||
|
||||
// is_link returns a boolean indicating whether `path` is a link.
|
||||
// Warning: `is_link()` is known to cause a TOCTOU vulnerability when used incorrectly
|
||||
// (for more information: https://github.com/vlang/v/blob/master/vlib/os/README.md)
|
||||
pub fn is_link(path string) bool {
|
||||
attr := lstat(path) or { return false }
|
||||
return attr.get_filetype() == .symbolic_link
|
||||
}
|
||||
|
||||
// kind_of_existing_path identifies whether path is a file, directory, or link
|
||||
fn kind_of_existing_path(path string) PathKind {
|
||||
mut res := PathKind{}
|
||||
attr := lstat(path) or { return res }
|
||||
res.is_file = attr.get_filetype() == .regular
|
||||
res.is_dir = attr.get_filetype() == .directory
|
||||
res.is_link = attr.get_filetype() == .symbolic_link
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@ module os
|
|||
|
||||
// stat returns a platform-agnostic Stat struct comparable to what is
|
||||
// available in other programming languages and fails with the POSIX
|
||||
// error if the stat call fails. If a link is stat'd, the stat info
|
||||
// for the link is provided.
|
||||
// error if the stat call fails. In Windows, there is no lstat so
|
||||
// information on a link cannot be provided.
|
||||
// C._wstat64() can be used on 32- and 64-bit Windows per
|
||||
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions?view=msvc-170
|
||||
pub fn stat(path string) !Stat {
|
||||
mut s := C.__stat64{}
|
||||
unsafe {
|
||||
|
@ -27,6 +29,12 @@ pub fn stat(path string) !Stat {
|
|||
}
|
||||
}
|
||||
|
||||
// lstat is the same as stat() for Windows
|
||||
@[inline]
|
||||
pub fn lstat(path string) !Stat {
|
||||
return stat(path)
|
||||
}
|
||||
|
||||
// get_filetype returns the FileType from the Stat struct
|
||||
pub fn (st Stat) get_filetype() FileType {
|
||||
match st.mode & u32(C.S_IFMT) {
|
||||
|
@ -61,3 +69,43 @@ pub fn (st Stat) get_mode() FileMode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// is_dir returns a `bool` indicating whether the given `path` is a directory.
|
||||
pub fn is_dir(path string) bool {
|
||||
w_path := path.replace('/', '\\')
|
||||
attr := C.GetFileAttributesW(w_path.to_wide())
|
||||
if attr == u32(C.INVALID_FILE_ATTRIBUTES) {
|
||||
return false
|
||||
}
|
||||
if int(attr) & C.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// is_link returns a boolean indicating whether `path` is a link.
|
||||
// Warning: `is_link()` is known to cause a TOCTOU vulnerability when used incorrectly
|
||||
// (for more information: https://github.com/vlang/v/blob/master/vlib/os/README.md)
|
||||
pub fn is_link(path string) bool {
|
||||
path_ := path.replace('/', '\\')
|
||||
attr := C.GetFileAttributesW(path_.to_wide())
|
||||
return int(attr) != int(C.INVALID_FILE_ATTRIBUTES) && (attr & 0x400) != 0
|
||||
}
|
||||
|
||||
// kind_of_existing_path identifies whether path is a file, directory, or link
|
||||
fn kind_of_existing_path(path string) PathKind {
|
||||
mut res := PathKind{}
|
||||
attr := C.GetFileAttributesW(path.to_wide())
|
||||
if attr != u32(C.INVALID_FILE_ATTRIBUTES) {
|
||||
if (int(attr) & C.FILE_ATTRIBUTE_NORMAL) != 0 {
|
||||
res.is_file = true
|
||||
}
|
||||
if (int(attr) & C.FILE_ATTRIBUTE_DIRECTORY) != 0 {
|
||||
res.is_dir = true
|
||||
}
|
||||
if (int(attr) & 0x400) != 0 {
|
||||
res.is_link = true
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue