Skip to content

Commit 034e87b

Browse files
authored
0.9.0 --- the five operations openkal 0.10 adds, and why each was missing (#22)
* 0.9.0 --- the five operations openkal 0.10 adds, and why each was missing Implements `kal_fs_lock', `kal_fs_unlock', `kal_fs_capacity', `kal_fs_set_modified_at', `kal_process_spawn_bound' and `kal_task_parallelism', and claims `KAL_FS_PROP_LOCKS', `KAL_FS_PROP_CAPACITY' and `KAL_PROCESS_PROP_BOUND_LIFETIME'. ⭐⭐ THE LOCK IS THE OPEN-FILE FORM AND NOT THE PROCESS FORM, WHICH IS THE WHOLE DIFFERENCE. This kernel's oldest record lock is held by the PROCESS and is released as soon as that process closes ANY descriptor for the node --- so a library that opened one file twice destroyed its own lock, and two parts of one program could not exclude each other at all. openkal states the holder as the `kal_file', which is exactly what `F_OFD_*' describes. Measured, and it is the observation that tells the two forms apart: with one file locked, a SECOND open file of the same name IN THE SAME PROGRAM is refused with `kal_err_again'. The process-held form would have granted it. ⚠️ Release upon death is the half a caller cannot build for itself. Exclusion it can --- `KAL_OPEN_EXCLUSIVE' and a name --- but nothing above this line releases that name when its holder dies, so a program that ended abnormally while holding one would be locked out of its own file for ever. --- the other four ---------------------------------------------------------- `kal_fs_capacity' reports BYTES, from `f_bavail' and not `f_bfree': the second counts blocks only a privileged writer may reach, and the question is what THIS program could use. Measured: 1500829MB. `kal_fs_set_modified_at' is the stated route to a directory's time. There was none --- the file-taking form takes a `kal_file' and a directory is a `kal_dir' --- so openkal-musl reached one by opening the directory for READING and setting the time on that, outside anything the interface said. Measured: a directory stamped and read back at 1700000000. `kal_task_parallelism' asks for the set THIS context may run on rather than the set the machine has: a program confined to two processors should size itself against two. Zero is "cannot say" and is not one. Measured: 32. `kal_process_spawn_bound' arms `PR_SET_PDEATHSIG' with SIGKILL --- not a signal the started program may decline, because the caller asked for a program that does not outlive it and not for one that is invited not to. ⚠️ AND IT IS ARMED BEFORE THE REPLACEMENT AND CHECKED AFTER. The setting survives the replacement, but the caller could have ended in between, in which case the signal is already spent and this image would survive it. Reading the parent's identity after arming closes that window. ⚠️ THE PROPERTY WORD WAS THE ONE THING THE PROBE CAUGHT. `kal_process_spawn_bound' was implemented and `KAL_PROCESS_PROP_BOUND_LIFETIME' was not claimed, so a caller asking first would have been told the operation was unavailable while it worked. Nine observations green and that one red, which is what the property word is for. * A range another holder has is reported as one value, not either of two ⚠️⚠️ THE STANDARD THIS CALL COMES FROM NAMES TWO VALUES FOR ONE CONDITION AND LEAVES THE CHOICE TO THE SYSTEM. openkal names one. For a lock attempt that does not wait, a range another holder has is reported as EITHER of two errors --- the system decides. So a program written against the standard accepts both, and an implementation of openkal must NOT pass that choice on: `kal_err_again' is the answer a caller polls upon, and the other value translates to `kal_err_permission', which a caller reads as "asking again will not help" and acts upon by stopping. ⭐ NARROWED TO THE ATTEMPT THAT DOES NOT WAIT, because that is the only path for which the two values carry this meaning. A permission failure anywhere else keeps its own answer. ⚠️ Found by looking rather than by failing: this kernel answers the first value in practice, so the observation passes here either way. The sibling implementation is on a system whose lineage answers the other one, and the two implementations must not disagree about what a caller sees. * Say what `base` is, because the comment claimed a property the code lacks The comment above `execveat' said the started program's working directory is the directory supplied here. It is not: `b' resolves the program's NAME, and resolving a name is not entering a directory. The started program inherits this implementation's working directory, whatever it happens to be. Found by a consumer's own test, not by reading: chdir, start a program, ask it for its working directory, and it answers the directory the caller left --- with a host as control answering the one the caller entered. Not fixable here, and the comment now says why: an `fchdir(b)' would make the sentence true and the behaviour no better, since `b' is whichever preopen the name resolved under. Naming a program and naming where it runs are two directories and the interface carries one. * Put back the version requirement a local path had replaced `git add -A' swept in a working-tree edit that pointed openkal at a checkout on my disk. The repository's own guard rejected it, which is what it is for.
1 parent 3f16c78 commit 034e87b

6 files changed

Lines changed: 307 additions & 13 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ for Linux, written on the kernel's own system-call interface.
88
openkal = "0.9.0"
99

1010
[target.'cfg(os = "linux")'.dependencies]
11-
openkal-linux = "0.8.0"
11+
openkal-linux = "0.9.0"
1212
```
1313

1414
## Why it does not use a C library

mcpp.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
namespace = "mcpplibs"
33
name = "openkal-linux"
4-
version = "0.8.0"
4+
version = "0.9.0"
55
description = "The reference implementation of openkal for Linux, written on the kernel's own system-call interface so that it can be placed beneath a C library as well as above one."
66
license = "Apache-2.0"
77

@@ -18,7 +18,7 @@ authors = ["mcpplibs"]
1818
repo = "https://github.com/mcpplibs/openkal-linux"
1919

2020
[dependencies]
21-
openkal = "0.9.0"
21+
openkal = "0.10.0"
2222

2323
# The package contributes definitions and no modules. The interface it
2424
# implements is declared by the specification package, which this package

src/fs.cpp

Lines changed: 129 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,33 @@ int kal_fs_set_modified(kal_file f, kal_u64 modified_ns) {
275275
return okl::failed(r) ? okl::translate(r) : kal_ok;
276276
}
277277

278+
// The same, upon a NAME. Version 0.10.
279+
//
280+
// ⚠️⚠️ ADDED BECAUSE THE FORM ABOVE CANNOT REACH A DIRECTORY, AND A CONSUMER
281+
// PAID FOR THAT. `kal_fs_set_modified' takes a `kal_file'; a directory is opened
282+
// as a `kal_dir'; there was no third thing. openkal-musl reached a lock
283+
// directory's timestamp by opening the directory for READING and setting the
284+
// time on that, which worked here and was outside anything the interface said.
285+
// This is the stated route.
286+
int kal_fs_set_modified_at(kal_dir base, const char* name, kal_uintptr len,
287+
kal_u64 modified_ns) {
288+
const int b = okl::unpack(base.h);
289+
if (b < 0 || !okl::acceptable(name, len)) return kal_err_invalid;
290+
okl::terminated t(name, len); if (!t.ok) return kal_err_invalid;
291+
292+
constexpr okl_i64 utime_omit = 0x3ffffffe;
293+
okl::ktimespec times[2];
294+
times[0].sec = 0; times[0].nsec = utime_omit;
295+
times[1].sec = static_cast<okl_i64>(modified_ns / 1000000000u);
296+
times[1].nsec = static_cast<okl_i64>(modified_ns % 1000000000u);
297+
298+
// Resolves, because opening resolves and this is stated to agree with it.
299+
const okl_long r = okl::sys(okl::nr_utimensat, b,
300+
reinterpret_cast<okl_long>(t.buf),
301+
reinterpret_cast<okl_long>(times), 0);
302+
return okl::failed(r) ? okl::translate(r) : kal_ok;
303+
}
304+
278305
int kal_fs_mkdir(kal_dir base, const char* name, kal_uintptr len) {
279306
const int b = okl::unpack(base.h);
280307
if (b < 0 || !okl::acceptable(name, len)) return kal_err_invalid;
@@ -379,8 +406,15 @@ int kal_fs_list_next(kal_dir, kal_uintptr* iter,
379406
// them.
380407
kal_uintptr kal_fs_props(kal_dir d) {
381408
const int fd = okl::unpack(d.h);
409+
// ⭐ LOCKS AND CAPACITY ARE IN THE CONSERVATIVE SET, and that is a claim
410+
// about this kernel rather than about the volume: an open-file lock and
411+
// `fstatfs' are answered by the VFS for every format beneath it, including
412+
// the read-only ones --- a lock excludes writers a read-only volume does not
413+
// have, which is a true answer and not a useful one. A format that could not
414+
// would have to be excluded by name here, and this kernel has none.
382415
const kal_uintptr conservative =
383-
KAL_FS_PROP_MODIFIED_TIME | KAL_FS_PROP_ATOMIC_RENAME;
416+
KAL_FS_PROP_MODIFIED_TIME | KAL_FS_PROP_ATOMIC_RENAME
417+
| KAL_FS_PROP_LOCKS | KAL_FS_PROP_CAPACITY;
384418
if (fd < 0) return 0;
385419

386420
okl::kstatfs sf{};
@@ -399,9 +433,10 @@ kal_uintptr kal_fs_props(kal_dir d) {
399433
// rename cannot be atomic because there is no rename.
400434
case okl::fs_squashfs: case okl::fs_erofs:
401435
return KAL_FS_PROP_MODIFIED_TIME | KAL_FS_PROP_CASE_SENSITIVE
402-
| KAL_FS_PROP_LINKS;
436+
| KAL_FS_PROP_LINKS | KAL_FS_PROP_LOCKS | KAL_FS_PROP_CAPACITY;
403437
case okl::fs_iso9660:
404-
return KAL_FS_PROP_MODIFIED_TIME | KAL_FS_PROP_CASE_SENSITIVE;
438+
return KAL_FS_PROP_MODIFIED_TIME | KAL_FS_PROP_CASE_SENSITIVE
439+
| KAL_FS_PROP_LOCKS | KAL_FS_PROP_CAPACITY;
405440

406441
// The FAT family stores neither a case distinction nor a node that
407442
// names another. `symlink' on such a volume reports EPERM, and this is
@@ -418,6 +453,97 @@ kal_uintptr kal_fs_props(kal_dir d) {
418453
}
419454
}
420455

456+
// --- exclusion upon a range of a file --------------------------------------
457+
//
458+
// ⭐⭐ THE OPEN-FILE FORM, AND THE DIFFERENCE IS THE WHOLE REASON THIS IS
459+
// WORTH SPECIFYING.
460+
//
461+
// This kernel's oldest record lock is held by the PROCESS and is released as
462+
// soon as that process closes ANY descriptor for the node --- so a library that
463+
// opened one file twice destroyed its own lock, and two parts of one program
464+
// could not exclude each other at all. openkal states that the holder is the
465+
// `kal_file', which is exactly what `F_OFD_*' describes: the lock belongs to the
466+
// open file description and ends when the last descriptor for it closes, and
467+
// when the program ends however it ends.
468+
//
469+
// ⚠️ Releasing on death is the half a caller cannot build for itself. Exclusion
470+
// it can: `KAL_OPEN_EXCLUSIVE' and a name beside the file. What nothing above
471+
// this line can do is release that name when its holder dies, so a program that
472+
// ended abnormally while holding one would be locked out of its own file for
473+
// ever.
474+
static int lock_range(kal_file f, kal_u64 start, kal_u64 len,
475+
short type, bool wait) {
476+
const int fd = okl::unpack(f.h);
477+
if (fd < 0) return kal_err_invalid;
478+
479+
okl::kflock fl{};
480+
fl.l_type = type;
481+
fl.l_whence = okl::seek_set;
482+
fl.l_start = static_cast<okl_i64>(start);
483+
// openkal spells "to the end, however far that comes to be" as zero, and so
484+
// does this kernel. The two agree, so nothing is translated.
485+
fl.l_len = static_cast<okl_i64>(len);
486+
487+
const okl_long cmd = wait ? okl::f_ofd_setlkw : okl::f_ofd_setlk;
488+
okl_long r;
489+
do {
490+
r = okl::sys(okl::nr_fcntl, fd, cmd, reinterpret_cast<okl_long>(&fl));
491+
} while (okl::interrupted(r));
492+
if (!okl::failed(r)) return kal_ok;
493+
494+
// ⚠️⚠️ TWO VALUES MEAN ONE THING HERE, AND openkal NAMES ONE OF THEM.
495+
//
496+
// The standard this call comes from says a range another holder has is
497+
// reported as EITHER of two values, and leaves the choice to the system ---
498+
// so a caller must accept both and an implementation of openkal must not
499+
// pass that choice on. openkal says `kal_err_again', which is the answer a
500+
// caller polls upon; the other value translates to `permission', which a
501+
// caller reads as "asking again will not help" and acts upon by stopping.
502+
//
503+
// ⭐ Narrowed to the attempt that does NOT wait, because that is the only
504+
// one for which the two values carry this meaning. A permission failure on
505+
// any other path keeps its own answer.
506+
if (!wait && (-r == okl::e_acces || -r == okl::e_again)) return kal_err_again;
507+
return okl::translate(r);
508+
}
509+
510+
int kal_fs_lock(kal_file f, kal_u64 start, kal_u64 len, kal_uintptr mode) {
511+
const bool shared = (mode & KAL_LOCK_SHARED) != 0;
512+
const bool exclusive = (mode & KAL_LOCK_EXCLUSIVE) != 0;
513+
// One of the two, and not both and not neither: a caller that asked for
514+
// both asked for something no environment has, and one that asked for
515+
// neither did not say what it wanted.
516+
if (shared == exclusive) return kal_err_invalid;
517+
return lock_range(f, start, len,
518+
shared ? okl::lock_read : okl::lock_write,
519+
(mode & KAL_LOCK_WAIT) != 0);
520+
}
521+
522+
int kal_fs_unlock(kal_file f, kal_u64 start, kal_u64 len) {
523+
// Never waits: releasing is not a request another holder can block.
524+
return lock_range(f, start, len, okl::lock_unlock, false);
525+
}
526+
527+
// --- how much the volume holds ----------------------------------------------
528+
int kal_fs_capacity(kal_dir d, kal_u64* total, kal_u64* available) {
529+
const int fd = okl::unpack(d.h);
530+
if (fd < 0) return kal_err_invalid;
531+
532+
okl::kstatfs sf{};
533+
const okl_long r = okl::sys(okl::nr_fstatfs, fd, reinterpret_cast<okl_long>(&sf));
534+
if (okl::failed(r)) return okl::translate(r);
535+
536+
// In bytes, because that is what the interface says and what a caller of it
537+
// wants; this kernel reports blocks and the size of one.
538+
const kal_u64 unit = static_cast<kal_u64>(sf.f_bsize);
539+
// ⚠️ `f_bavail' AND NOT `f_bfree'. The second counts blocks the volume has,
540+
// including those only a privileged writer may reach; the first counts the
541+
// ones THIS program could actually use, which is the question asked.
542+
if (total) *total = static_cast<kal_u64>(sf.f_blocks) * unit;
543+
if (available) *available = static_cast<kal_u64>(sf.f_bavail) * unit;
544+
return kal_ok;
545+
}
546+
421547
// Nodes whose content is another name.
422548
int kal_fs_link_create(kal_dir base, const char* name, kal_uintptr len,
423549
const char* target, kal_uintptr target_len,

src/process.cpp

Lines changed: 114 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,34 @@ int kal_process_spawn(kal_dir base,
200200
if (in != 0) okl::sys(okl::nr_dup3, in, 0, 0);
201201
if (ou != 0) okl::sys(okl::nr_dup3, ou, 1, 0);
202202
if (er != 0) okl::sys(okl::nr_dup3, er, 2, 0);
203-
// The started program's working directory is the directory supplied
204-
// here, expressed by naming the program relative to it. There is no
205-
// operation that changes a working directory afterwards, because a
206-
// working directory that can be changed is shared mutable state
207-
// between execution contexts.
203+
// ⚠️⚠️ THIS COMMENT USED TO CLAIM A PROPERTY THIS CODE DOES NOT HAVE.
204+
// It said the started program's working directory is the directory
205+
// supplied here. It is not. `b' is the directory the NAME resolves
206+
// against and nothing more --- `execveat' takes a dirfd to resolve
207+
// `p.buf', and resolving a name is not entering a directory. The
208+
// started program's working directory is this implementation's own,
209+
// whatever that happens to be, inherited across the clone above.
210+
//
211+
// ⭐ Found by a consumer's test rather than by reading, which is the
212+
// point: `chdir' then start a program, ask it for its working
213+
// directory, and it answers the directory the caller left --- against
214+
// a host as control, which answers the one the caller entered.
215+
//
216+
// ⇒ AND IT IS NOT FIXABLE HERE. An `fchdir(b)' before the replacement
217+
// would make the sentence true and the behaviour no better: `b' is
218+
// whichever preopen the name resolved under --- for a program named
219+
// `/usr/bin/sh' that is the root --- so the started program would get
220+
// an arbitrary directory instead of a different arbitrary directory.
221+
// Naming the program and naming where it runs are two directories, and
222+
// openkal has an argument for one of them. openkal-musl's `chdir'
223+
// therefore rebinds its own table and cannot do better; the interface
224+
// has no operation that carries the second directory across a spawn.
225+
//
226+
// There is deliberately no operation that changes a working directory
227+
// afterwards, because a working directory that can be changed is
228+
// shared mutable state between execution contexts. That refusal is
229+
// sound and is NOT what is missing --- what is missing is a way to say,
230+
// at the moment of starting, which directory the program starts in.
208231
const okl_long why =
209232
okl::sys(okl::nr_execveat, b, reinterpret_cast<okl_long>(p.buf),
210233
reinterpret_cast<okl_long>(args.slots),
@@ -225,6 +248,90 @@ int kal_process_spawn(kal_dir base,
225248
return kal_ok;
226249
}
227250

251+
// The same, with the started program's lifetime bound to this one's. 0.10.
252+
//
253+
// ⭐⭐ WHY THIS EXISTS, AND IT IS NOT A CONVENIENCE.
254+
//
255+
// A C library asked for `execve' composes it out of what this interface has:
256+
// start the program, wait for it, end with its status. That composition leaves
257+
// THREE images where a system with the operation has two --- the caller, a copy
258+
// that waits, and the program --- and `kal_process_terminate' upon the identifier
259+
// the caller holds reaches the WAITER. Measured with a host as control: identical
260+
// status words, opposite outcomes; the caller is told the program died on the
261+
// signal it sent, while the program runs to completion, unsupervised.
262+
// openkal-linux#13.
263+
//
264+
// ⚠️ THE BINDING IS SET IN THE STARTED IMAGE AND NOT FROM HERE, which is why it
265+
// is a second spawn rather than an operation applied to a handle. This kernel's
266+
// facility answers "end this context when the one that started it ends", and only
267+
// that context can ask for it.
268+
//
269+
// ⚠️ AND IT IS ASKED FOR BEFORE THE REPLACEMENT AND CHECKED AFTER: the setting
270+
// survives the replacement, but the parent could have ended in between --- in
271+
// which case the signal has already been delivered and there is nothing to
272+
// notice. Reading the parent's identity after arming closes that window: if it
273+
// is no longer the one that armed, this image ends now rather than becoming the
274+
// orphan the caller asked not to have.
275+
int kal_process_spawn_bound(kal_dir base,
276+
const char* path, kal_uintptr path_len,
277+
const char** argv, const kal_uintptr* argv_lens, kal_uintptr argc,
278+
const char** envp, const kal_uintptr* envp_lens, kal_uintptr envc,
279+
const kal_spawn_streams* streams,
280+
kal_process* out) {
281+
const int b = okl::unpack(base.h);
282+
if (b < 0 || out == nullptr) return kal_err_invalid;
283+
if (!okl::acceptable(path, path_len)) return kal_err_invalid;
284+
okl::terminated p(path, path_len);
285+
if (!p.ok) return kal_err_invalid;
286+
287+
vector args, envs;
288+
if (!args.build(argv, argv_lens, argc)) return kal_err_no_memory;
289+
if (!envs.build(envp, envp_lens, envc)) return kal_err_no_memory;
290+
291+
const okl_long in = streams ? static_cast<okl_long>(streams->in.h) : 0;
292+
const okl_long ou = streams ? static_cast<okl_long>(streams->out.h) : 0;
293+
const okl_long er = streams ? static_cast<okl_long>(streams->err.h) : 0;
294+
295+
const okl_long mine = okl::sys(okl::nr_getpid);
296+
297+
exec_report report;
298+
report.open(0);
299+
300+
const okl_long child = okl::sys(okl::nr_clone, 17 /* SIGCHLD */, 0, 0, 0, 0);
301+
if (okl::failed(child)) { report.close_both(); return okl::translate(child); }
302+
303+
if (child == 0) {
304+
if (in != 0) okl::sys(okl::nr_dup3, in, 0, 0);
305+
if (ou != 0) okl::sys(okl::nr_dup3, ou, 1, 0);
306+
if (er != 0) okl::sys(okl::nr_dup3, er, 2, 0);
307+
308+
// 9 is SIGKILL: the binding must not be something the started program
309+
// can decline, because the caller asked for a program that does not
310+
// outlive it and not for one that is invited not to.
311+
okl::sys(okl::nr_prctl, okl::pr_set_pdeathsig, 9, 0, 0, 0);
312+
// The window: if the caller ended between the clone and the line above,
313+
// the signal is already spent and this image would survive it.
314+
if (okl::sys(okl::nr_getppid) != mine)
315+
okl::sys(okl::nr_exit_group, 127);
316+
317+
const okl_long why =
318+
okl::sys(okl::nr_execveat, b, reinterpret_cast<okl_long>(p.buf),
319+
reinterpret_cast<okl_long>(args.slots),
320+
reinterpret_cast<okl_long>(envs.slots), 0);
321+
report.say(why);
322+
okl::sys(okl::nr_exit_group, 127);
323+
for (;;) { }
324+
}
325+
326+
if (const okl_long why = report.heard()) {
327+
reap(child);
328+
return okl::translate(why);
329+
}
330+
331+
*out = kal_process{ static_cast<kal_uintptr>(child) };
332+
return kal_ok;
333+
}
334+
228335
// A channel: a pair of streams of which one end is meant to cross a spawn.
229336
//
230337
// WHY THIS IS A KERNEL FACILITY AND kal::kit's CHANNEL IS NOT. A started program
@@ -389,7 +496,8 @@ void kal_process_close(kal_process) { }
389496
kal_uintptr kal_process_props(void) {
390497
return KAL_PROCESS_PROP_TERMINATE | KAL_PROCESS_PROP_STREAM_PASSING
391498
| KAL_PROCESS_PROP_EXIT_STATUS
392-
| KAL_PROCESS_PROP_CHANNEL | KAL_PROCESS_PROP_GRANT_DIR;
499+
| KAL_PROCESS_PROP_CHANNEL | KAL_PROCESS_PROP_GRANT_DIR
500+
| KAL_PROCESS_PROP_BOUND_LIFETIME;
393501
}
394502

395503
}

src/sys.h

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ enum : okl_long {
9191
nr_readv = 19, nr_writev = 20, nr_sched_yield = 24, nr_nanosleep = 35,
9292
nr_getpid = 39, nr_clone = 56, nr_execve = 59, nr_exit = 60, nr_wait4 = 61,
9393
nr_kill = 62, nr_ftruncate = 77, nr_getcwd = 79, nr_fsync = 74,
94-
nr_fcntl = 72,
94+
nr_fcntl = 72, nr_prctl = 157, nr_sched_getaffinity = 204, nr_getppid = 110,
9595
nr_arch_prctl = 158, nr_gettid = 186, nr_futex = 202,
9696
nr_getdents64 = 217, nr_set_tid_address = 218, nr_clock_gettime = 228,
9797
nr_clock_getres = 229, nr_exit_group = 231, nr_tgkill = 234,
@@ -180,6 +180,7 @@ enum : okl_long {
180180
nr_getpid = 172, nr_mmap = 222, nr_munmap = 215, nr_mprotect = 226,
181181
nr_clone = 220, nr_execve = 221, nr_wait4 = 260, nr_renameat = 38,
182182
nr_dup3 = 24, nr_execveat = 281, nr_dup2 = -1, nr_fcntl = 25,
183+
nr_prctl = 167, nr_sched_getaffinity = 123, nr_getppid = 173,
183184
nr_arch_prctl = -1, nr_utimensat = 88, nr_symlinkat = 36, nr_fstatfs = 44,
184185
nr_getrandom = 278,
185186
// openkal.net and openkal.datagram
@@ -252,6 +253,26 @@ enum : okl_long {
252253
// already had there. `dup3' cannot do this: it is told the number, and it
253254
// closes what is on it.
254255
f_dupfd_cloexec = 1030,
256+
257+
// ⭐⭐ THE OPEN-FILE FORM AND NOT THE PROCESS FORM, WHICH IS THE WHOLE
258+
// DIFFERENCE.
259+
//
260+
// This kernel's oldest record lock is held by the PROCESS, and it is
261+
// released as soon as that process closes ANY descriptor for the node ---
262+
// so a library that opened one file twice destroyed its own lock, and two
263+
// parts of one program could not exclude each other at all. openkal states
264+
// the holder as the `kal_file', which is exactly what these describe: the
265+
// lock belongs to the open file description and ends when the last
266+
// descriptor for it closes.
267+
f_ofd_getlk = 36, f_ofd_setlk = 37, f_ofd_setlkw = 38,
268+
269+
// A lock's kind, and where a range begins.
270+
lock_read = 0, lock_write = 1, lock_unlock = 2,
271+
seek_set = 0,
272+
273+
// "End this context when the one that started it ends", which is what
274+
// binding a started program's lifetime is expressed as here.
275+
pr_set_pdeathsig = 1,
255276
#if defined(__x86_64__)
256277
o_directory = 0200000, o_nofollow = 0400000,
257278
#else
@@ -304,6 +325,19 @@ inline bool interrupted(okl_long r) { return r == -e_intr; }
304325
// What the kernel reports about the volume a descriptor is on. The layout is
305326
// the kernel's own `struct statfs', which is one layout on every architecture
306327
// this implementation supports because both are LP64.
328+
// The kernel's own `struct flock'. One layout on both architectures this
329+
// implementation builds for: two shorts, padded to eight, then two 64-bit
330+
// positions and the identifier of a holder this implementation never asks about.
331+
struct kflock {
332+
short l_type;
333+
short l_whence;
334+
int l_pad;
335+
okl_i64 l_start;
336+
okl_i64 l_len;
337+
int l_pid;
338+
int l_pad2;
339+
};
340+
307341
struct kstatfs {
308342
okl_long f_type;
309343
okl_long f_bsize;

0 commit comments

Comments
 (0)