1-- The rest of POSIX's file system in Landin types: reading and writing at
2-- an offset, sizes and synchronizing, renaming, hard and symbolic links,
3-- permission bits, owners and file times, advisory locks of both kinds
4-- the systems have, and the working directory's path. Each path is a
5-- host string, terminated beneath the caller as `open_at`'s is, and each
6-- call relative to a directory takes the directory as `at`, or
7-- `working_directory`. Written once here, over the `system_*` routines
8-- linux.ldn and libc.ldn give.
9
10import core/text
11
12-- The largest offset a call takes, C's `off_t` being signed.
13largest_offset: u64 = 0x7FFFFFFFFFFFFFFF
14
15--- Read at most `lenof into` bytes of `source`, starting `offset` bytes
16--- into the file, POSIX's `pread`: the count read, zero at or past the
17--- file's end, or the errno, `espipe` for a pipe or a socket and `einval`
18--- for an offset past what the system counts. The file's position, which
19--- `read` and `write` use, does not move.
20public link(symbol: "_landin_posix_read_at") read_at:
21 (source: descriptor, offset: u64, into: []mut u8)
22 -> (count: usize) ! errno =
23 fail einval when offset > largest_offset
24 count = try system_read_at(source, offset, into)
25end read_at
26
27--- Write at most `lenof bytes` bytes to `to`, starting `offset` bytes
28--- into the file, POSIX's `pwrite`: the count accepted, which may be
29--- short, or the errno. A write past the end extends the file, and the
30--- bytes between its old end and `offset` read as zeros, a hole that need
31--- take no room on the device. The position does not move. On a file
32--- opened `append`, Linux writes at the end whatever `offset` says, and
33--- Darwin and FreeBSD write at `offset`: do not write at an offset to a
34--- file opened to append.
35public link(symbol: "_landin_posix_write_at") write_at:
36 (to: descriptor, offset: u64, bytes: []u8) -> (count: usize) ! errno =
37 fail einval when offset > largest_offset
38 count = try system_write_at(to, offset, bytes)
39end write_at
40
41--- Give the file open as `opened`, which must be open for writing, the
42--- size `size`, POSIX's `ftruncate`: what lies beyond it is cut off, and a
43--- larger size extends the file with zeros. `einval` for a size past what
44--- the system counts or a file not open for writing.
45public link(symbol: "_landin_posix_truncate") truncate:
46 (opened: descriptor, size: u64) -> none ! errno =
47 fail einval when size > largest_offset
48 try system_truncate(opened, size)
49end truncate
50
51--- Hand what the system holds of the file open as `opened` to its device
52--- before returning, POSIX's `fsync`: its data and its status, or with
53--- `data_only` its data and only so much of its status as reading the data
54--- back needs, its size among it, `fdatasync`. On Darwin, whose `fsync`
55--- leaves what the device caches in the device, both ask the device to
56--- write that too, `F_FULLFSYNC`, and fall back to `fsync` on a file system
57--- that refuses it.
58public link(symbol: "_landin_posix_synchronize") synchronize:
59 (opened: descriptor, data_only: bool) -> none ! errno =
60 try system_synchronize(opened, data_only)
61end synchronize
62
63--- Give the file `old` names, relative to `old_at`, the name `new`,
64--- relative to `new_at`, POSIX's `renameat`, in one step: no process sees
65--- both names or neither. A file already named `new` is replaced in the
66--- same step, a directory only by a directory and only when it is empty.
67--- The errno: `enoent` when `old` or `new`'s parent is missing, `exdev`
68--- when the two are on different file systems, which a rename never
69--- crosses, `enotempty` or `eexist` for a directory that is not empty,
70--- `eisdir` for a file onto a directory and `enotdir` the other way, and
71--- `einval` for a directory into itself.
72public link(symbol: "_landin_posix_rename_at") rename_at:
73 (old_at: descriptor, old: text.host_string, new_at: descriptor,
74 new: text.host_string) -> none ! errno =
75 mut old_room: path_room = zeroed
76 old_path: ptr u8 = try terminate(old, old_room[0..<lenof old_room])
77 mut new_room: path_room = zeroed
78 new_path: ptr u8 = try terminate(new, new_room[0..<lenof new_room])
79 try system_rename(old_at, old_path, new_at, new_path)
80end rename_at
81
82--- Give the file `old` names, relative to `old_at`, a second name `new`,
83--- relative to `new_at`, POSIX's `linkat`, a hard link: both names are one
84--- file. A final symbolic link of `old` is linked itself, not followed.
85--- The errno: `eexist` when `new` is taken, `exdev` across file systems,
86--- `eperm` for a directory, which no system links, and `emlink` for a file
87--- with as many names as it may have.
88public link(symbol: "_landin_posix_link_at") link_at:
89 (old_at: descriptor, old: text.host_string, new_at: descriptor,
90 new: text.host_string) -> none ! errno =
91 mut old_room: path_room = zeroed
92 old_path: ptr u8 = try terminate(old, old_room[0..<lenof old_room])
93 mut new_room: path_room = zeroed
94 new_path: ptr u8 = try terminate(new, new_room[0..<lenof new_room])
95 try system_link(old_at, old_path, new_at, new_path)
96end link_at
97
98--- Make `path`, relative to `at`, a symbolic link whose target is
99--- `target`, POSIX's `symlinkat`: a path the system follows when it meets
100--- the link, relative to the link's own directory unless it begins with
101--- `/`, which need not name anything. `eexist` when `path` is taken.
102public link(symbol: "_landin_posix_symbolic_link_at") symbolic_link_at:
103 (target: text.host_string, at: descriptor, path: text.host_string)
104 -> none ! errno =
105 mut target_room: path_room = zeroed
106 pointed: ptr u8 = try terminate(target, target_room[0..<lenof target_room])
107 mut room: path_room = zeroed
108 terminated: ptr u8 = try terminate(path, room[0..<lenof room])
109 try system_symbolic_link(pointed, at, terminated)
110end symbolic_link_at
111
112--- The target of the symbolic link `path` names, relative to `at`,
113--- POSIX's `readlinkat`, copied into `into` and lent from it: `einval` when
114--- `path` is no symbolic link, and `erange` when the target fills `into`,
115--- since a target as long as the buffer may have been cut.
116public link(symbol: "_landin_posix_read_link_at") read_link_at:
117 (at: descriptor, path: text.host_string, into: []mut u8)
118 -> (target: text.host_string from into) ! errno =
119 mut room: path_room = zeroed
120 terminated: ptr u8 = try terminate(path, room[0..<lenof room])
121 fail erange when lenof into == 0
122 count: usize = try system_read_link(at, terminated, into)
123 fail erange when count >= lenof into
124 target = text.host_over(into[0..<count])
125end read_link_at
126
127--- Set the permission bits of the file `path` names, relative to `at`, to
128--- `permissions`, POSIX's `fchmodat`, following a final symbolic link,
129--- whose own bits no system gives a meaning: `einval` for bits beyond
130--- `0o7777`, and `eperm` when the process neither owns the file nor may
131--- override that.
132public link(symbol: "_landin_posix_change_mode_at") change_mode_at:
133 (at: descriptor, path: text.host_string, permissions: u32)
134 -> none ! errno =
135 fail einval when permissions > 0o7777
136 mut room: path_room = zeroed
137 terminated: ptr u8 = try terminate(path, room[0..<lenof room])
138 try system_change_mode_at(at, terminated, permissions)
139end change_mode_at
140
141--- Set the permission bits of the file open as `opened`, POSIX's
142--- `fchmod`, as `change_mode_at` sets a path's.
143public link(symbol: "_landin_posix_change_mode") change_mode:
144 (opened: descriptor, permissions: u32) -> none ! errno =
145 fail einval when permissions > 0o7777
146 try system_change_mode(opened, permissions)
147end change_mode
148
149--- Set the owner and group of the file `path` names, relative to `at`,
150--- POSIX's `fchownat`, `unchanged_user` and `unchanged_group` keeping
151--- either; with `follow` false a final symbolic link's own. `eperm`
152--- unless the process may: only a privileged one gives a file away, and
153--- an owner may give it only to a group the owner is in. A system may
154--- clear the set-user-ID and set-group-ID bits as it changes the owner.
155public link(symbol: "_landin_posix_change_owner_at") change_owner_at:
156 (at: descriptor, path: text.host_string, user: user_id, group: group_id,
157 follow: bool) -> none ! errno =
158 mut room: path_room = zeroed
159 terminated: ptr u8 = try terminate(path, room[0..<lenof room])
160 try system_change_owner_at(at, terminated, user, group, follow)
161end change_owner_at
162
163--- Set the owner and group of the file open as `opened`, POSIX's
164--- `fchown`, as `change_owner_at` sets a path's.
165public link(symbol: "_landin_posix_change_owner") change_owner:
166 (opened: descriptor, user: user_id, group: group_id) -> none ! errno =
167 try system_change_owner(opened, user, group)
168end change_owner
169
170--- Set when the file `path` names, relative to `at`, was last read and
171--- last written, POSIX's `utimensat`, to `accessed` and `modified`; with
172--- `follow` false a final symbolic link's own. The system sets the time
173--- it was last changed to now, which nothing else can. `einval` for
174--- nanoseconds of a second or more; a file system keeping coarser times
175--- rounds them down.
176public link(symbol: "_landin_posix_set_times_at") set_times_at:
177 (at: descriptor, path: text.host_string, accessed: time_value,
178 modified: time_value, follow: bool) -> none ! errno =
179 fail einval when accessed.nanoseconds >= 1000000000
180 or modified.nanoseconds >= 1000000000
181 mut room: path_room = zeroed
182 terminated: ptr u8 = try terminate(path, room[0..<lenof room])
183 try system_set_times_at(at, terminated, accessed, modified, follow)
184end set_times_at
185
186--- Set the two times of the file open as `opened`, POSIX's `futimens`, as
187--- `set_times_at` sets a path's.
188public link(symbol: "_landin_posix_set_times") set_times:
189 (opened: descriptor, accessed: time_value, modified: time_value)
190 -> none ! errno =
191 fail einval when accessed.nanoseconds >= 1000000000
192 or modified.nanoseconds >= 1000000000
193 try system_set_times(opened, accessed, modified)
194end set_times
195
196--- The status of the file open as `opened`, POSIX's `fstat`, as
197--- `status_at` gives a path's.
198public link(symbol: "_landin_posix_status_of") status_of:
199 (opened: descriptor) -> (result: status) ! errno =
200 result = try system_status_of(opened)
201end status_of
202
203--- The working directory's absolute path, POSIX's `getcwd`, copied into
204--- `into` and lent from it: `erange` when it does not fit with the NUL
205--- the system adds, and `enoent` when the directory has been removed.
206--- The working directory is the process's, so this module never changes
207--- it: beside threads a change would move every relative path at once,
208--- and a path relative to a directory open as `at` serves instead.
209public link(symbol: "_landin_posix_current_directory") current_directory:
210 (into: []mut u8) -> (path: text.host_string from into) ! errno =
211 fail erange when lenof into == 0
212 count: usize = try system_current_directory(into)
213 fail erange when count > lenof into
214 path = text.host_over(into[0..<count])
215end current_directory
216
217--- A shared lock: any number of holders, none of them exclusive.
218public shared_lock: atom
219--- An exclusive lock: one holder, and no shared one beside it.
220public exclusive_lock: atom
221--- What a lock is: `shared_lock` or `exclusive_lock`.
222public lock_kind: type = shared_lock | exclusive_lock
223
224--- Take a lock of `kind` on the whole file open as `opened`, waiting for
225--- it when `wait` is true, BSD's `flock`, which Linux, Darwin and FreeBSD
226--- all give: or the errno, `eagain` when another holds a lock in the way
227--- and `wait` is false, `eintr` when a signal came while waiting. The
228--- lock is advisory, binding only programs that ask for it, and belongs to
229--- the open file `opened` is a descriptor of: a second `open_at` of the
230--- same file in the same process is another holder, which it stands in
231--- the way of, while a duplicate of `opened`, and a child that inherited
232--- it, shares it. It lasts until `unlock`, or until every descriptor of
233--- that open file is closed. Taking it again changes its kind, and the
234--- change is not atomic: another holder may come between. Use one kind of
235--- lock on a file, `lock` or `lock_range`, since how the two see each
236--- other differs between systems.
237public link(symbol: "_landin_posix_lock") lock:
238 (opened: descriptor, kind: lock_kind, wait: bool) -> none ! errno =
239 try system_lock(opened, kind, wait)
240end lock
241
242--- Give up the lock `lock` took on the file open as `opened`.
243public link(symbol: "_landin_posix_unlock") unlock:
244 (opened: descriptor) -> none ! errno =
245 try system_unlock(opened)
246end unlock
247
248--- Take a lock of `kind` on `length` bytes of the file open as `opened`
249--- from `start`, a `length` of zero reaching however far the file ever
250--- will, POSIX's record lock, `fcntl`'s `F_SETLK`, or `F_SETLKW` with
251--- `wait`: or the errno, `eagain` or `eacces`, as the system chooses, when
252--- another process holds one in the way and `wait` is false, `edeadlk`
253--- when waiting would deadlock, and `ebadf` for a shared lock on a file not
254--- open for reading or an exclusive one on a file not open for writing.
255---
256--- A record lock belongs to the process, not to a descriptor: the process
257--- never stands in its own way, through any descriptor, and a lock over
258--- one it already holds replaces that part of it. Closing any descriptor
259--- of the file, even one opened elsewhere in the program for a moment,
260--- gives up every record lock the process holds on it. A child does not
261--- inherit one. `lock` has none of these hazards and no byte ranges.
262public link(symbol: "_landin_posix_lock_range") lock_range:
263 (opened: descriptor, kind: lock_kind, start: u64, length: u64,
264 wait: bool) -> none ! errno =
265 fail einval when start > largest_offset or length > largest_offset
266 try system_lock_range(opened, kind, start, length, wait, true)
267end lock_range
268
269--- Give up the process's record locks on `length` bytes of the file open
270--- as `opened` from `start`, as `lock_range` counts them.
271public link(symbol: "_landin_posix_unlock_range") unlock_range:
272 (opened: descriptor, start: u64, length: u64) -> none ! errno =
273 fail einval when start > largest_offset or length > largest_offset
274 try system_lock_range(opened, shared_lock, start, length, false, false)
275end unlock_range