Files
Odin/core/os/stat_posix.odin
2026-02-09 16:27:53 +01:00

141 lines
3.4 KiB
Odin

#+private
#+build darwin, netbsd, freebsd, openbsd
package os
import "base:runtime"
import "core:sys/posix"
import "core:time"
internal_stat :: proc(stat: posix.stat_t, fullpath: string) -> (fi: File_Info) {
fi.fullpath = fullpath
_, fi.name = split_path(fi.fullpath)
fi.inode = u128(stat.st_ino)
fi.size = i64(stat.st_size)
fi.mode = transmute(Permissions)u32(transmute(posix._mode_t)(stat.st_mode - posix.S_IFMT))
fi.type = .Undetermined
switch {
case posix.S_ISBLK(stat.st_mode):
fi.type = .Block_Device
case posix.S_ISCHR(stat.st_mode):
fi.type = .Character_Device
case posix.S_ISDIR(stat.st_mode):
fi.type = .Directory
case posix.S_ISFIFO(stat.st_mode):
fi.type = .Named_Pipe
case posix.S_ISLNK(stat.st_mode):
fi.type = .Symlink
case posix.S_ISREG(stat.st_mode):
fi.type = .Regular
case posix.S_ISSOCK(stat.st_mode):
fi.type = .Socket
}
fi.creation_time = timespec_time(stat.st_birthtimespec)
fi.modification_time = timespec_time(stat.st_mtim)
fi.access_time = timespec_time(stat.st_atim)
timespec_time :: proc(t: posix.timespec) -> time.Time {
return time.Time{_nsec = i64(t.tv_sec) * 1e9 + i64(t.tv_nsec)}
}
return
}
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
if f == nil || f.impl == nil {
err = .Invalid_File
return
}
impl := (^File_Impl)(f.impl)
stat: posix.stat_t
if posix.fstat(impl.fd, &stat) != .OK {
err = _get_platform_error()
return
}
fullpath := clone_string(impl.name, allocator) or_return
return internal_stat(stat, fullpath), nil
}
_stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
if name == "" {
err = .Invalid_Path
return
}
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
cname := clone_to_cstring(name, temp_allocator) or_return
fd := posix.open(cname, {})
if fd == -1 {
err = _get_platform_error()
return
}
defer posix.close(fd)
fullpath := _posix_absolute_path(fd, name, allocator) or_return
stat: posix.stat_t
if posix.stat(fullpath, &stat) != .OK {
err = _get_platform_error()
return
}
return internal_stat(stat, string(fullpath)), nil
}
_lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
if name == "" {
err = .Invalid_Path
return
}
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
// NOTE: can't use realpath or open (+ fcntl F_GETPATH) here because it tries to resolve symlinks.
// NOTE: This might not be correct when given "/symlink/foo.txt",
// you would want that to resolve "/symlink", but not resolve "foo.txt".
fullpath := clean_path(name, temp_allocator) or_return
assert(len(fullpath) > 0)
switch {
case fullpath[0] == '/':
// nothing.
case fullpath == ".":
fullpath = getwd(temp_allocator) or_return
case len(fullpath) > 1 && fullpath[0] == '.' && fullpath[1] == '/':
fullpath = fullpath[2:]
fallthrough
case:
fullpath = concatenate({
getwd(temp_allocator) or_return,
"/",
fullpath,
}, temp_allocator) or_return
}
stat: posix.stat_t
c_fullpath := clone_to_cstring(fullpath, temp_allocator) or_return
if posix.lstat(c_fullpath, &stat) != .OK {
err = _get_platform_error()
return
}
fullpath = clone_string(fullpath, allocator) or_return
return internal_stat(stat, fullpath), nil
}
_same_file :: proc(fi1, fi2: File_Info) -> bool {
return fi1.fullpath == fi2.fullpath
}
_is_reserved_name :: proc(path: string) -> bool {
return false
}