Merge pull request #3939 from flysand7/os2-file-type

[os2]: Split file type from mode bits et other small fixes
This commit is contained in:
gingerBill
2024-07-19 11:37:54 +01:00
committed by GitHub
13 changed files with 137 additions and 120 deletions
+63 -13
View File
@@ -4,20 +4,57 @@ import "core:io"
import "core:time" import "core:time"
import "base:runtime" import "base:runtime"
/*
Type representing a file handle.
This struct represents an OS-specific file-handle, which can be one of
the following:
- File
- Directory
- Pipe
- Named pipe
- Block Device
- Character device
- Symlink
- Socket
See `File_Type` enum for more information on file types.
*/
File :: struct { File :: struct {
impl: rawptr, impl: rawptr,
stream: io.Stream, stream: io.Stream,
fstat: Fstat_Callback, fstat: Fstat_Callback,
} }
File_Mode :: distinct u32 /*
File_Mode_Dir :: File_Mode(1<<16) Type representing the type of a file handle.
File_Mode_Named_Pipe :: File_Mode(1<<17)
File_Mode_Device :: File_Mode(1<<18)
File_Mode_Char_Device :: File_Mode(1<<19)
File_Mode_Sym_Link :: File_Mode(1<<20)
File_Mode_Perm :: File_Mode(0o777) // Unix permision bits **Note(windows)**: Socket handles can not be distinguished from
files, as they are just a normal file handle that is being treated by
a special driver. Windows also makes no distinction between block and
character devices.
*/
File_Type :: enum {
// The type of a file could not be determined for the current platform.
Undetermined,
// Represents a regular file.
Regular,
// Represents a directory.
Directory,
// Represents a symbolic link.
Symlink,
// Represents a named pipe (FIFO).
Named_Pipe,
// Represents a socket.
// **Note(windows)**: Not returned on windows
Socket,
// Represents a block device.
// **Note(windows)**: On windows represents all devices.
Block_Device,
// Represents a character device.
// **Note(windows)**: Not returned on windows
Character_Device,
}
File_Flags :: distinct bit_set[File_Flag; uint] File_Flags :: distinct bit_set[File_Flag; uint]
File_Flag :: enum { File_Flag :: enum {
@@ -51,11 +88,11 @@ stderr: ^File = nil // OS-Specific
@(require_results) @(require_results)
create :: proc(name: string) -> (^File, Error) { create :: proc(name: string) -> (^File, Error) {
return open(name, {.Read, .Write, .Create}, File_Mode(0o777)) return open(name, {.Read, .Write, .Create}, 0o777)
} }
@(require_results) @(require_results)
open :: proc(name: string, flags := File_Flags{.Read}, perm := File_Mode(0o777)) -> (^File, Error) { open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File, Error) {
return _open(name, flags, perm) return _open(name, flags, perm)
} }
@@ -161,44 +198,56 @@ read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error)
chdir :: change_directory chdir :: change_directory
change_directory :: proc(name: string) -> Error { change_directory :: proc(name: string) -> Error {
return _chdir(name) return _chdir(name)
} }
chmod :: change_mode chmod :: change_mode
change_mode :: proc(name: string, mode: File_Mode) -> Error {
change_mode :: proc(name: string, mode: int) -> Error {
return _chmod(name, mode) return _chmod(name, mode)
} }
chown :: change_owner chown :: change_owner
change_owner :: proc(name: string, uid, gid: int) -> Error { change_owner :: proc(name: string, uid, gid: int) -> Error {
return _chown(name, uid, gid) return _chown(name, uid, gid)
} }
fchdir :: fchange_directory fchdir :: fchange_directory
fchange_directory :: proc(f: ^File) -> Error { fchange_directory :: proc(f: ^File) -> Error {
return _fchdir(f) return _fchdir(f)
} }
fchmod :: fchange_mode fchmod :: fchange_mode
fchange_mode :: proc(f: ^File, mode: File_Mode) -> Error {
fchange_mode :: proc(f: ^File, mode: int) -> Error {
return _fchmod(f, mode) return _fchmod(f, mode)
} }
fchown :: fchange_owner fchown :: fchange_owner
fchange_owner :: proc(f: ^File, uid, gid: int) -> Error { fchange_owner :: proc(f: ^File, uid, gid: int) -> Error {
return _fchown(f, uid, gid) return _fchown(f, uid, gid)
} }
lchown :: change_owner_do_not_follow_links lchown :: change_owner_do_not_follow_links
change_owner_do_not_follow_links :: proc(name: string, uid, gid: int) -> Error { change_owner_do_not_follow_links :: proc(name: string, uid, gid: int) -> Error {
return _lchown(name, uid, gid) return _lchown(name, uid, gid)
} }
chtimes :: change_times chtimes :: change_times
change_times :: proc(name: string, atime, mtime: time.Time) -> Error { change_times :: proc(name: string, atime, mtime: time.Time) -> Error {
return _chtimes(name, atime, mtime) return _chtimes(name, atime, mtime)
} }
fchtimes :: fchange_times fchtimes :: fchange_times
fchange_times :: proc(f: ^File, atime, mtime: time.Time) -> Error { fchange_times :: proc(f: ^File, atime, mtime: time.Time) -> Error {
return _fchtimes(f, atime, mtime) return _fchtimes(f, atime, mtime)
} }
@@ -214,6 +263,7 @@ is_file :: proc(path: string) -> bool {
} }
is_dir :: is_directory is_dir :: is_directory
@(require_results) @(require_results)
is_directory :: proc(path: string) -> bool { is_directory :: proc(path: string) -> bool {
return _is_dir(path) return _is_dir(path)
@@ -226,11 +276,11 @@ copy_file :: proc(dst_path, src_path: string) -> Error {
info := fstat(src, file_allocator()) or_return info := fstat(src, file_allocator()) or_return
defer file_info_delete(info, file_allocator()) defer file_info_delete(info, file_allocator())
if info.is_directory { if info.type == .Directory {
return .Invalid_File return .Invalid_File
} }
dst := open(dst_path, {.Read, .Write, .Create, .Trunc}, info.mode & File_Mode_Perm) or_return dst := open(dst_path, {.Read, .Write, .Create, .Trunc}, info.mode & 0o777) or_return
defer close(dst) defer close(dst)
_, err := io.copy(to_writer(dst), to_reader(src)) _, err := io.copy(to_writer(dst), to_reader(src))
+4 -5
View File
@@ -63,7 +63,7 @@ _file_allocator :: proc() -> runtime.Allocator {
return heap_allocator() return heap_allocator()
} }
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) { _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
TEMP_ALLOCATOR_GUARD() TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return name_cstr := temp_cstring(name) or_return
@@ -76,7 +76,6 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, er
case O_WRONLY: sys_flags += {.WRONLY} case O_WRONLY: sys_flags += {.WRONLY}
case O_RDWR: sys_flags += {.RDWR} case O_RDWR: sys_flags += {.RDWR}
} }
if .Append in flags { sys_flags += {.APPEND} } if .Append in flags { sys_flags += {.APPEND} }
if .Create in flags { sys_flags += {.CREAT} } if .Create in flags { sys_flags += {.CREAT} }
if .Excl in flags { sys_flags += {.EXCL} } if .Excl in flags { sys_flags += {.EXCL} }
@@ -84,7 +83,7 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, er
if .Trunc in flags { sys_flags += {.TRUNC} } if .Trunc in flags { sys_flags += {.TRUNC} }
if .Close_On_Exec in flags { sys_flags += {.CLOEXEC} } if .Close_On_Exec in flags { sys_flags += {.CLOEXEC} }
fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)(u32(perm))) fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)u32(perm))
if errno != .NONE { if errno != .NONE {
return nil, _get_platform_error(errno) return nil, _get_platform_error(errno)
} }
@@ -296,13 +295,13 @@ _fchdir :: proc(f: ^File) -> Error {
return _get_platform_error(linux.fchdir(impl.fd)) return _get_platform_error(linux.fchdir(impl.fd))
} }
_chmod :: proc(name: string, mode: File_Mode) -> Error { _chmod :: proc(name: string, mode: int) -> Error {
TEMP_ALLOCATOR_GUARD() TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return name_cstr := temp_cstring(name) or_return
return _get_platform_error(linux.chmod(name_cstr, transmute(linux.Mode)(u32(mode)))) return _get_platform_error(linux.chmod(name_cstr, transmute(linux.Mode)(u32(mode))))
} }
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error { _fchmod :: proc(f: ^File, mode: int) -> Error {
impl := (^File_Impl)(f.impl) impl := (^File_Impl)(f.impl)
return _get_platform_error(linux.fchmod(impl.fd, transmute(linux.Mode)(u32(mode)))) return _get_platform_error(linux.fchmod(impl.fd, transmute(linux.Mode)(u32(mode))))
} }
+1 -1
View File
@@ -138,7 +138,7 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d
} }
@(require_results) @(require_results)
write_entire_file :: proc(name: string, data: []byte, perm: File_Mode, truncate := true) -> Error { write_entire_file :: proc(name: string, data: []byte, perm: int, truncate := true) -> Error {
flags := O_WRONLY|O_CREATE flags := O_WRONLY|O_CREATE
if truncate { if truncate {
flags |= O_TRUNC flags |= O_TRUNC
+4 -7
View File
@@ -55,7 +55,7 @@ _handle :: proc(f: ^File) -> win32.HANDLE {
return win32.HANDLE(_fd(f)) return win32.HANDLE(_fd(f))
} }
_open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (handle: uintptr, err: Error) { _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: uintptr, err: Error) {
if len(name) == 0 { if len(name) == 0 {
err = .Not_Exist err = .Not_Exist
return return
@@ -122,7 +122,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (han
} }
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) { _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
flags := flags if flags != nil else {.Read} flags := flags if flags != nil else {.Read}
handle := _open_internal(name, flags, perm) or_return handle := _open_internal(name, flags, perm) or_return
return _new_file(handle, name), nil return _new_file(handle, name), nil
@@ -498,7 +498,6 @@ _rename :: proc(old_path, new_path: string) -> Error {
} }
_link :: proc(old_name, new_name: string) -> Error { _link :: proc(old_name, new_name: string) -> Error {
o := _fix_long_path(old_name) o := _fix_long_path(old_name)
n := _fix_long_path(new_name) n := _fix_long_path(new_name)
@@ -635,7 +634,7 @@ _fchdir :: proc(f: ^File) -> Error {
return nil return nil
} }
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error { _fchmod :: proc(f: ^File, mode: int) -> Error {
if f == nil || f.impl == nil { if f == nil || f.impl == nil {
return nil return nil
} }
@@ -670,7 +669,7 @@ _chdir :: proc(name: string) -> Error {
return nil return nil
} }
_chmod :: proc(name: string, mode: File_Mode) -> Error { _chmod :: proc(name: string, mode: int) -> Error {
f := open(name, {.Write}) or_return f := open(name, {.Write}) or_return
defer close(f) defer close(f)
return _fchmod(f, mode) return _fchmod(f, mode)
@@ -718,8 +717,6 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
return nil return nil
} }
_exists :: proc(path: string) -> bool { _exists :: proc(path: string) -> bool {
wpath := _fix_long_path(path) wpath := _fix_long_path(path)
attribs := win32.GetFileAttributesW(wpath) attribs := win32.GetFileAttributesW(wpath)
+6 -3
View File
@@ -12,12 +12,14 @@ is_path_separator :: proc(c: byte) -> bool {
} }
mkdir :: make_directory mkdir :: make_directory
make_directory :: proc(name: string, perm: File_Mode) -> Error {
make_directory :: proc(name: string, perm: int) -> Error {
return _mkdir(name, perm) return _mkdir(name, perm)
} }
mkdir_all :: make_directory_all mkdir_all :: make_directory_all
make_directory_all :: proc(path: string, perm: File_Mode) -> Error {
make_directory_all :: proc(path: string, perm: int) -> Error {
return _mkdir_all(path, perm) return _mkdir_all(path, perm)
} }
@@ -25,14 +27,15 @@ remove_all :: proc(path: string) -> Error {
return _remove_all(path) return _remove_all(path)
} }
getwd :: get_working_directory getwd :: get_working_directory
@(require_results) @(require_results)
get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _getwd(allocator) return _getwd(allocator)
} }
setwd :: set_working_directory setwd :: set_working_directory
set_working_directory :: proc(dir: string) -> (err: Error) { set_working_directory :: proc(dir: string) -> (err: Error) {
return _setwd(dir) return _setwd(dir)
} }
+5 -17
View File
@@ -15,19 +15,13 @@ _is_path_separator :: proc(c: byte) -> bool {
return c == '/' return c == '/'
} }
_mkdir :: proc(path: string, perm: File_Mode) -> Error { _mkdir :: proc(path: string, perm: int) -> Error {
// TODO: These modes would require mknod, however, that would also
// require additional arguments to this function..
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
return .Invalid_Argument
}
TEMP_ALLOCATOR_GUARD() TEMP_ALLOCATOR_GUARD()
path_cstr := temp_cstring(path) or_return path_cstr := temp_cstring(path) or_return
return _get_platform_error(linux.mkdir(path_cstr, transmute(linux.Mode)(u32(perm) & 0o777))) return _get_platform_error(linux.mkdir(path_cstr, transmute(linux.Mode)u32(perm)))
} }
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error { _mkdir_all :: proc(path: string, perm: int) -> Error {
mkdirat :: proc(dfd: linux.Fd, path: []u8, perm: int, has_created: ^bool) -> Error { mkdirat :: proc(dfd: linux.Fd, path: []u8, perm: int, has_created: ^bool) -> Error {
i: int i: int
for ; i < len(path) - 1 && path[i] != '/'; i += 1 {} for ; i < len(path) - 1 && path[i] != '/'; i += 1 {}
@@ -38,7 +32,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
new_dfd, errno := linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS) new_dfd, errno := linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
#partial switch errno { #partial switch errno {
case .ENOENT: case .ENOENT:
if errno = linux.mkdirat(dfd, cstring(&path[0]), transmute(linux.Mode)(u32(perm))); errno != .NONE { if errno = linux.mkdirat(dfd, cstring(&path[0]), transmute(linux.Mode)u32(perm)); errno != .NONE {
return _get_platform_error(errno) return _get_platform_error(errno)
} }
has_created^ = true has_created^ = true
@@ -58,12 +52,6 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
} }
unreachable() unreachable()
} }
// TODO
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
return .Invalid_Argument
}
TEMP_ALLOCATOR_GUARD() TEMP_ALLOCATOR_GUARD()
// need something we can edit, and use to generate cstrings // need something we can edit, and use to generate cstrings
path_bytes := make([]u8, len(path) + 1, temp_allocator()) path_bytes := make([]u8, len(path) + 1, temp_allocator())
@@ -85,7 +73,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
} }
has_created: bool has_created: bool
mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return mkdirat(dfd, path_bytes, perm, &has_created) or_return
if has_created { if has_created {
return nil return nil
} }
+6 -10
View File
@@ -12,14 +12,14 @@ _is_path_separator :: proc(c: byte) -> bool {
return c == '\\' || c == '/' return c == '\\' || c == '/'
} }
_mkdir :: proc(name: string, perm: File_Mode) -> Error { _mkdir :: proc(name: string, perm: int) -> Error {
if !win32.CreateDirectoryW(_fix_long_path(name), nil) { if !win32.CreateDirectoryW(_fix_long_path(name), nil) {
return _get_platform_error() return _get_platform_error()
} }
return nil return nil
} }
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error { _mkdir_all :: proc(path: string, perm: int) -> Error {
fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) { fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) {
if len(p) == len(`\\?\c:`) { if len(p) == len(`\\?\c:`) {
if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' { if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' {
@@ -33,9 +33,9 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
TEMP_ALLOCATOR_GUARD() TEMP_ALLOCATOR_GUARD()
dir, err := stat(path, temp_allocator()) dir_stat, err := stat(path, temp_allocator())
if err == nil { if err == nil {
if dir.is_directory { if dir_stat.type == .Directory {
return nil return nil
} }
return .Exist return .Exist
@@ -61,8 +61,8 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
err = mkdir(path, perm) err = mkdir(path, perm)
if err != nil { if err != nil {
dir1, err1 := lstat(path, temp_allocator()) new_dir_stat, err1 := lstat(path, temp_allocator())
if err1 == nil && dir1.is_directory { if err1 == nil && new_dir_stat.type == .Directory {
return nil return nil
} }
return err return err
@@ -85,7 +85,6 @@ _setwd :: proc(dir: string) -> (err: Error) {
return nil return nil
} }
can_use_long_paths: bool can_use_long_paths: bool
@(init) @(init)
@@ -96,7 +95,6 @@ init_long_path_support :: proc() {
can_use_long_paths = false can_use_long_paths = false
} }
_fix_long_path_slice :: proc(path: string) -> []u16 { _fix_long_path_slice :: proc(path: string) -> []u16 {
return win32.utf8_to_utf16(_fix_long_path_internal(path)) return win32.utf8_to_utf16(_fix_long_path_internal(path))
} }
@@ -105,7 +103,6 @@ _fix_long_path :: proc(path: string) -> win32.wstring {
return win32.utf8_to_wstring(_fix_long_path_internal(path)) return win32.utf8_to_wstring(_fix_long_path_internal(path))
} }
_fix_long_path_internal :: proc(path: string) -> string { _fix_long_path_internal :: proc(path: string) -> string {
if can_use_long_paths { if can_use_long_paths {
return path return path
@@ -162,5 +159,4 @@ _fix_long_path_internal :: proc(path: string) -> string {
} }
return string(path_buf[:w]) return string(path_buf[:w])
} }
+3 -2
View File
@@ -9,8 +9,8 @@ File_Info :: struct {
fullpath: string, fullpath: string,
name: string, name: string,
size: i64, size: i64,
mode: File_Mode, mode: int,
is_directory: bool, type: File_Type,
creation_time: time.Time, creation_time: time.Time,
modification_time: time.Time, modification_time: time.Time,
access_time: time.Time, access_time: time.Time,
@@ -43,6 +43,7 @@ stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
} }
lstat :: stat_do_not_follow_links lstat :: stat_do_not_follow_links
@(require_results) @(require_results)
stat_do_not_follow_links :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { stat_do_not_follow_links :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
return _lstat(name, allocator) return _lstat(name, allocator)
+13 -4
View File
@@ -17,20 +17,29 @@ _fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Inf
if errno != .NONE { if errno != .NONE {
return {}, _get_platform_error(errno) return {}, _get_platform_error(errno)
} }
type := File_Type.Regular
switch s.mode & linux.S_IFMT {
case linux.S_IFBLK: type = .Block_Device
case linux.S_IFCHR: type = .Character_Device
case linux.S_IFDIR: type = .Directory
case linux.S_IFIFO: type = .Named_Pipe
case linux.S_IFLNK: type = .Symlink
case linux.S_IFREG: type = .Regular
case linux.S_IFSOCK: type = .Socket
}
mode := int(s.mode) & 0o7777
// TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time // TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
fi := File_Info { fi := File_Info {
fullpath = _get_full_path(fd, allocator), fullpath = _get_full_path(fd, allocator),
name = "", name = "",
size = i64(s.size), size = i64(s.size),
mode = 0, mode = mode,
is_directory = linux.S_ISDIR(s.mode), type = type,
modification_time = time.Time {i64(s.mtime.time_sec) * i64(time.Second) + i64(s.mtime.time_nsec)}, modification_time = time.Time {i64(s.mtime.time_sec) * i64(time.Second) + i64(s.mtime.time_nsec)},
access_time = time.Time {i64(s.atime.time_sec) * i64(time.Second) + i64(s.atime.time_nsec)}, access_time = time.Time {i64(s.atime.time_sec) * i64(time.Second) + i64(s.atime.time_nsec)},
creation_time = time.Time{i64(s.ctime.time_sec) * i64(time.Second) + i64(s.ctime.time_nsec)}, // regular stat does not provide this creation_time = time.Time{i64(s.ctime.time_sec) * i64(time.Second) + i64(s.ctime.time_nsec)}, // regular stat does not provide this
} }
fi.creation_time = fi.modification_time fi.creation_time = fi.modification_time
fi.name = filepath.base(fi.fullpath) fi.name = filepath.base(fi.fullpath)
return fi, nil return fi, nil
} }
+27 -53
View File
@@ -19,28 +19,29 @@ _fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
h := _handle(f) h := _handle(f)
switch win32.GetFileType(h) { switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
fi: File_Info fi := File_Info {
fi.fullpath = path fullpath = path,
fi.name = basename(path) name = basename(path),
fi.mode |= file_type_mode(h) type = file_type(h),
}
return fi, nil return fi, nil
} }
return _file_info_from_get_file_information_by_handle(path, h, allocator) return _file_info_from_get_file_information_by_handle(path, h, allocator)
} }
_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { _stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS, allocator) return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS, allocator)
} }
_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { _lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT, allocator) return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT, allocator)
} }
_same_file :: proc(fi1, fi2: File_Info) -> bool { _same_file :: proc(fi1, fi2: File_Info) -> bool {
return fi1.fullpath == fi2.fullpath return fi1.fullpath == fi2.fullpath
} }
full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) { full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) {
name := name name := name
if name == "" { if name == "" {
@@ -62,7 +63,6 @@ full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path
return win32.utf16_to_utf8(buf[:n], allocator) return win32.utf16_to_utf8(buf[:n], allocator)
} }
internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) {
if len(name) == 0 { if len(name) == 0 {
return {}, .Not_Exist return {}, .Not_Exist
@@ -99,7 +99,6 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runt
return _file_info_from_get_file_information_by_handle(name, h, allocator) return _file_info_from_get_file_information_by_handle(name, h, allocator)
} }
_cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
buf := buf buf := buf
N := 0 N := 0
@@ -120,7 +119,6 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
return buf return buf
} }
_cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) { _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) {
if f == nil { if f == nil {
return "", nil return "", nil
@@ -159,7 +157,6 @@ _cleanpath_from_buf :: proc(buf: []u16, allocator: runtime.Allocator) -> (string
return win32.utf16_to_utf8(buf, allocator) return win32.utf16_to_utf8(buf, allocator)
} }
basename :: proc(name: string) -> (base: string) { basename :: proc(name: string) -> (base: string) {
name := name name := name
if len(name) > 3 && name[:3] == `\\?` { if len(name) > 3 && name[:3] == `\\?` {
@@ -185,83 +182,67 @@ basename :: proc(name: string) -> (base: string) {
return name return name
} }
file_type :: proc(h: win32.HANDLE) -> File_Type {
file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
switch win32.GetFileType(h) { switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE: case win32.FILE_TYPE_PIPE: return .Named_Pipe
return File_Mode_Named_Pipe case win32.FILE_TYPE_CHAR: return .Character_Device
case win32.FILE_TYPE_CHAR: case win32.FILE_TYPE_DISK: return .Regular
return File_Mode_Device | File_Mode_Char_Device
} }
return 0 return .Undetermined
} }
_file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (type: File_Type, mode: int) {
_file_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) {
if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 { if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
mode |= 0o444 mode |= 0o444
} else { } else {
mode |= 0o666 mode |= 0o666
} }
is_sym := false is_sym := false
if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
is_sym = false is_sym = false
} else { } else {
is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
} }
if is_sym { if is_sym {
mode |= File_Mode_Sym_Link type = .Symlink
} else { } else {
if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
mode |= 0o111 | File_Mode_Dir type = .Directory
mode |= 0o111
} }
if h != nil { if h != nil {
mode |= file_type_mode(h) type = file_type(h)
} }
} }
return return
} }
_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { _file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.mode |= _file_mode_from_file_attributes(d.dwFileAttributes, nil, 0) fi.type = type
fi.is_directory = fi.mode & File_Mode_Dir != 0 fi.mode |= mode
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
fi.fullpath, e = full_path_from_name(name, allocator) fi.fullpath, e = full_path_from_name(name, allocator)
fi.name = basename(fi.fullpath) fi.name = basename(fi.fullpath)
return return
} }
_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { _file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.mode |= _file_mode_from_file_attributes(d.dwFileAttributes, nil, 0) fi.type = type
fi.is_directory = fi.mode & File_Mode_Dir != 0 fi.mode |= mode
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
fi.fullpath, e = full_path_from_name(name, allocator) fi.fullpath, e = full_path_from_name(name, allocator)
fi.name = basename(fi.fullpath) fi.name = basename(fi.fullpath)
return return
} }
_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE, allocator: runtime.Allocator) -> (File_Info, Error) { _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE, allocator: runtime.Allocator) -> (File_Info, Error) {
d: win32.BY_HANDLE_FILE_INFORMATION d: win32.BY_HANDLE_FILE_INFORMATION
if !win32.GetFileInformationByHandle(h, &d) { if !win32.GetFileInformationByHandle(h, &d) {
@@ -278,25 +259,19 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA
// Indicate this is a symlink on FAT file systems // Indicate this is a symlink on FAT file systems
ti.ReparseTag = 0 ti.ReparseTag = 0
} }
fi: File_Info fi: File_Info
fi.fullpath = path fi.fullpath = path
fi.name = basename(path) fi.name = basename(path)
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.mode |= _file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag) fi.type = type
fi.is_directory = fi.mode & File_Mode_Dir != 0 fi.mode |= mode
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
return fi, nil return fi, nil
} }
reserved_names := [?]string{ reserved_names := [?]string{
"CON", "PRN", "AUX", "NUL", "CON", "PRN", "AUX", "NUL",
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
@@ -357,7 +332,6 @@ _volume_name_len :: proc(path: string) -> int {
return 0 return 0
} }
_is_abs :: proc(path: string) -> bool { _is_abs :: proc(path: string) -> bool {
if _is_reserved_name(path) { if _is_reserved_name(path) {
return true return true
+1 -1
View File
@@ -26,7 +26,7 @@ create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) {
attempts := 0 attempts := 0
for { for {
name := concatenate_strings_from_buffer(name_buf[:], prefix, random_string(rand_buf[:]), suffix) name := concatenate_strings_from_buffer(name_buf[:], prefix, random_string(rand_buf[:]), suffix)
f, err = open(name, {.Read, .Write, .Create, .Excl}, File_Mode(0o666)) f, err = open(name, {.Read, .Write, .Create, .Excl}, 0o666)
if err == .Exist { if err == .Exist {
close(f) close(f)
attempts += 1 attempts += 1
+1 -1
View File
@@ -244,7 +244,7 @@ Mode_Bits :: enum {
ISVTX = 9, // 0o0001000 ISVTX = 9, // 0o0001000
ISGID = 10, // 0o0002000 ISGID = 10, // 0o0002000
ISUID = 11, // 0o0004000 ISUID = 11, // 0o0004000
IFFIFO = 12, // 0o0010000 IFIFO = 12, // 0o0010000
IFCHR = 13, // 0o0020000 IFCHR = 13, // 0o0020000
IFDIR = 14, // 0o0040000 IFDIR = 14, // 0o0040000
IFREG = 15, // 0o0100000 IFREG = 15, // 0o0100000
+3 -3
View File
@@ -39,11 +39,11 @@ PRIO_MIN :: -20
SIGRTMIN :: Signal(32) SIGRTMIN :: Signal(32)
SIGRTMAX :: Signal(64) SIGRTMAX :: Signal(64)
S_IFMT :: Mode{.IFREG, .IFDIR, .IFCHR, .IFFIFO} S_IFMT :: Mode{.IFREG, .IFDIR, .IFCHR, .IFIFO}
S_IFSOCK :: Mode{.IFREG, .IFDIR} S_IFSOCK :: Mode{.IFREG, .IFDIR}
S_IFLNK :: Mode{.IFREG, .IFCHR} S_IFLNK :: Mode{.IFREG, .IFCHR}
S_IFBLK :: Mode{.IFDIR, .IFCHR} S_IFBLK :: Mode{.IFDIR, .IFCHR}
S_IFFIFO :: Mode{.IFFIFO} S_IFIFO :: Mode{.IFIFO}
S_IFCHR :: Mode{.IFCHR} S_IFCHR :: Mode{.IFCHR}
S_IFDIR :: Mode{.IFDIR} S_IFDIR :: Mode{.IFDIR}
S_IFREG :: Mode{.IFREG} S_IFREG :: Mode{.IFREG}
@@ -51,7 +51,7 @@ S_IFREG :: Mode{.IFREG}
/* /*
Checks the Mode bits to see if the file is a named pipe (FIFO). Checks the Mode bits to see if the file is a named pipe (FIFO).
*/ */
S_ISFIFO :: #force_inline proc "contextless" (m: Mode) -> bool {return (S_IFFIFO == (m & S_IFMT))} S_ISFIFO :: #force_inline proc "contextless" (m: Mode) -> bool {return (S_IFIFO == (m & S_IFMT))}
/* /*
Check the Mode bits to see if the file is a character device. Check the Mode bits to see if the file is a character device.