aboutsummaryrefslogtreecommitdiff
path: root/core/os/stat_posix.odin
blob: 9248607444eae3d810040d8dc81ac92d05eca47b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#+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
}