Skip to content

Commit 9780ac8

Browse files
0.18.0 --- the atoms of openkal 0.17, the stop word given back, and a start without execveat (#38)
Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent 88b82e0 commit 9780ac8

14 files changed

Lines changed: 1005 additions & 41 deletions

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ for Linux, written on the kernel's own system-call interface.
55

66
```toml
77
[dependencies]
8-
openkal = "0.16.0"
8+
openkal = "0.17.0"
99

1010
[target.'cfg(os = "linux")'.dependencies]
11-
openkal-linux = "0.17.0"
11+
openkal-linux = "0.18.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.17.0"
4+
version = "0.18.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

@@ -63,7 +63,7 @@ provides-interfaces = [
6363
]
6464

6565
[dependencies]
66-
openkal = "0.16.0"
66+
openkal = "0.17.0"
6767

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

‎src/endpoint.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ inline int from_kernel(const ksockaddr_storage& in, kal_endpoint& out) {
9898
return kal_ok;
9999
}
100100

101+
// A connection made at a name (0.17): it has no address and port to state.
102+
if (in.family == 1 /* AF_UNIX */) return kal_err_not_supported;
103+
101104
return kal_err_invalid;
102105
}
103106

‎src/env.cpp‎

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ namespace okl {
1515
int g_argc = 0;
1616
char** g_argv = nullptr;
1717
char** g_envp = nullptr;
18+
// Whether this program was started with SIGPIPE ignored: what a program it
19+
// starts is given back (process.cpp).
20+
bool g_pipe_started_ignored = false;
1821
okl_ulong* g_auxv = nullptr;
1922

2023
void record(int argc, char** argv, char** envp) {
@@ -124,19 +127,35 @@ bool recover(char*** argv_out, int* argc_out, char*** envp_out) {
124127
// NOT A POLICY CHOICE ABOUT SIGNALS IN GENERAL. This is the one signal an
125128
// ordinary openkal operation provokes; the rest are left exactly as this program
126129
// was started with.
130+
//
131+
// AND IT IS THIS PROGRAM'S, NOT PASSED ON. An ignored disposition survives
132+
// `execve', so every program started from here would otherwise begin with
133+
// SIGPIPE ignored --- `yes | head' inside a started shell then reports a write
134+
// error instead of ending, and a shell cannot undo an ignore it was started
135+
// with. What was found here is recorded, and `kal_process_start' gives a
136+
// started program the disposition this one was started with (process.cpp).
127137
[[gnu::constructor(101)]] void quiet_the_signal_openkal_cannot_report() {
128138
// struct k_sigaction as this kernel takes it: handler, flags, restorer, mask.
129-
struct { void* handler; unsigned long flags; void* restorer; unsigned long mask; }
130-
ignore{ reinterpret_cast<void*>(1) /* SIG_IGN */, 0, nullptr, 0 };
131-
okl::sys(okl::nr_rt_sigaction, 13 /* SIGPIPE */,
132-
reinterpret_cast<okl_long>(&ignore), 0, sizeof ignore.mask);
139+
struct k_sigaction { void* handler; unsigned long flags; void* restorer; unsigned long mask; };
140+
k_sigaction ignore{ reinterpret_cast<void*>(1) /* SIG_IGN */, 0, nullptr, 0 };
141+
k_sigaction found{};
142+
if (!okl::failed(okl::sys(okl::nr_rt_sigaction, 13 /* SIGPIPE */,
143+
reinterpret_cast<okl_long>(&ignore),
144+
reinterpret_cast<okl_long>(&found), sizeof ignore.mask)))
145+
okl::g_pipe_started_ignored = found.handler == reinterpret_cast<void*>(1);
133146
}
134147

148+
// THE PUBLISHED HANDLE FIRST, THE REGISTERS ONLY WHERE THERE IS NONE. The
149+
// check above tells a shifted vector from a real one, not every set of
150+
// register contents from one: a static program over musl (an ok image's M3
151+
// program) passed it with whatever the registers held, and its image saw no
152+
// environment at all. `environ' is the C library's own record of the vectors
153+
// it was started with, so it is read wherever the library has one.
135154
[[gnu::constructor(101)]] void capture(int argc, char** argv, char** envp) {
136155
if (okl::g_argv != nullptr) return;
137-
if (plausible(argc, argv, envp)) { okl::record(argc, argv, envp); return; }
138156
char** rargv = nullptr; char** renvp = nullptr; int rargc = 0;
139-
if (recover(&rargv, &rargc, &renvp)) okl::record(rargc, rargv, renvp);
157+
if (recover(&rargv, &rargc, &renvp)) { okl::record(rargc, rargv, renvp); return; }
158+
if (plausible(argc, argv, envp)) okl::record(argc, argv, envp);
140159
}
141160
} // namespace
142161

@@ -202,4 +221,35 @@ kal_intptr kal_env_var_at(kal_uintptr index, char* out, kal_uintptr cap) {
202221
return -kal_err_not_found;
203222
}
204223

224+
// THE IMAGE THE KERNEL STARTED (0.17). `/proc/self/exe' names the file the
225+
// kernel mapped, resolved; where the pseudo-file system is not mounted, the
226+
// name the kernel was given (AT_EXECFN), which is the starter's spelling and
227+
// may be relative to where the starter was --- the environment's answer
228+
// either way, which is what the declaration asks for.
229+
// A program built for its environment was loaded by nothing the interface
230+
// names (clause 12: only an image has a loader to name).
231+
kal_intptr kal_env_loader(char* out, kal_uintptr cap) {
232+
(void)out; (void)cap;
233+
return -kal_err_not_found;
234+
}
235+
236+
kal_intptr kal_env_image(char* out, kal_uintptr cap) {
237+
char buf[4096];
238+
const okl_long n = okl::sys(okl::nr_readlinkat, okl::at_fdcwd,
239+
reinterpret_cast<okl_long>("/proc/self/exe"),
240+
reinterpret_cast<okl_long>(buf), sizeof buf);
241+
if (!okl::failed(n) && n > 0 && n < static_cast<okl_long>(sizeof buf))
242+
return give(buf, static_cast<kal_uintptr>(n), out, cap);
243+
if (const okl_ulong fn = okl::auxval(31 /* AT_EXECFN */); fn != 0) {
244+
const char* name = reinterpret_cast<const char*>(fn);
245+
return give(name, okl::length(name), out, cap);
246+
}
247+
return -kal_err_not_found;
248+
}
249+
250+
// For conventions only, as the declaration restricts it.
251+
kal_intptr kal_env_system(char* out, kal_uintptr cap) {
252+
return give("linux", 5, out, cap);
253+
}
254+
205255
}

‎src/exec.cpp‎

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include "sys.h"
22
#include <openkal/exec.h>
33

4+
namespace okl { okl_ulong auxval(okl_ulong key); }
5+
46
// openkal.exec on this kernel.
57
//
68
// A mapping obtained writable and made executable afterwards. The kernel places
@@ -18,7 +20,18 @@
1820

1921
namespace {
2022

21-
constexpr okl_uptr kPage = 4096;
23+
// The kernel's page, as it told this program at inception (AT_PAGESZ). Four
24+
// kibibytes was written here, and an aarch64 kernel built for sixteen or
25+
// sixty-four rounds every protection to its own page, so a part published at a
26+
// four-kibibyte boundary would have taken its neighbour with it.
27+
okl_uptr page() {
28+
static okl_uptr p = 0;
29+
if (p == 0) {
30+
const okl_ulong a = okl::auxval(6 /* AT_PAGESZ */);
31+
p = (a != 0 && (a & (a - 1)) == 0) ? static_cast<okl_uptr>(a) : 4096;
32+
}
33+
return p;
34+
}
2235

2336
okl_uptr round_up(okl_uptr n, okl_uptr to) { return (n + to - 1) & ~(to - 1); }
2437

@@ -28,7 +41,7 @@ extern "C" {
2841

2942
void* kal_exec_alloc(kal_uintptr size) {
3043
if (size == 0) return nullptr;
31-
const okl_uptr bytes = round_up(static_cast<okl_uptr>(size), kPage);
44+
const okl_uptr bytes = round_up(static_cast<okl_uptr>(size), page());
3245
const okl_long r = okl::sys(okl::nr_mmap, 0, static_cast<okl_long>(bytes),
3346
okl::prot_read | okl::prot_write,
3447
okl::map_private | okl::map_anonymous, -1, 0);
@@ -38,7 +51,7 @@ void* kal_exec_alloc(kal_uintptr size) {
3851

3952
int kal_exec_publish(void* p, kal_uintptr size) {
4053
if (p == nullptr || size == 0) return kal_err_invalid;
41-
const okl_uptr bytes = round_up(static_cast<okl_uptr>(size), kPage);
54+
const okl_uptr bytes = round_up(static_cast<okl_uptr>(size), page());
4255
const okl_long r = okl::sys(okl::nr_mprotect, reinterpret_cast<okl_long>(p),
4356
static_cast<okl_long>(bytes),
4457
okl::prot_read | okl::prot_exec);
@@ -85,9 +98,25 @@ int kal_exec_publish(void* p, kal_uintptr size) {
8598
return kal_ok;
8699
}
87100

101+
// PART OF A REGION (0.17): the kernel protects per page, so a part is a run of
102+
// whole pages, and a part that is not is refused rather than rounded --- a
103+
// rounded part would take writable data beside it out of reach.
104+
int kal_exec_publish_part(void* region, kal_uintptr offset, kal_uintptr size) {
105+
const okl_uptr g = page();
106+
if (region == nullptr || size == 0 || (offset & (g - 1)) != 0 || (size & (g - 1)) != 0
107+
|| (reinterpret_cast<okl_uptr>(region) & (g - 1)) != 0)
108+
return kal_err_invalid;
109+
const okl_long r = okl::sys(okl::nr_mprotect,
110+
reinterpret_cast<okl_long>(static_cast<char*>(region) + offset),
111+
static_cast<okl_long>(size), okl::prot_read | okl::prot_exec);
112+
return okl::failed(r) ? okl::translate(r) : kal_ok;
113+
}
114+
115+
kal_uintptr kal_exec_granularity(void) { return page(); }
116+
88117
void kal_exec_free(void* p, kal_uintptr size) {
89118
if (p == nullptr || size == 0) return;
90-
const okl_uptr bytes = round_up(static_cast<okl_uptr>(size), kPage);
119+
const okl_uptr bytes = round_up(static_cast<okl_uptr>(size), page());
91120
okl::sys(okl::nr_munmap, reinterpret_cast<okl_long>(p),
92121
static_cast<okl_long>(bytes));
93122
}

‎src/fs.cpp‎

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ kal_uintptr volume_props(int fd) {
135135
const kal_uintptr conservative =
136136
KAL_FS_PROP_MODIFIED_TIME | KAL_FS_PROP_ATOMIC_RENAME
137137
| KAL_FS_PROP_LOCKS | KAL_FS_PROP_CAPACITY;
138+
// A NAME HELD OPEN IS REMOVED AT ONCE (0.17, clause 7.14) on every format
139+
// whose names the VFS itself keeps: `unlink' drops the entry and the inode
140+
// lives on until its last descriptor closes. Not in the conservative set,
141+
// because a format the VFS does not keep may not: a network volume's client
142+
// renames a held file to a hidden name and removes it at the close, and a
143+
// user-space format may do the same, so the directory is not empty.
144+
const kal_uintptr local = conservative | KAL_FS_PROP_REMOVE_OPEN;
138145
if (fd < 0) return 0;
139146

140147
okl::kstatfs sf{};
@@ -143,12 +150,23 @@ kal_uintptr volume_props(int fd) {
143150

144151
switch (sf.f_type) {
145152
// Formats with a case-sensitive namespace and nodes that name others.
153+
//
154+
// A rename that does not replace (0.17) is `renameat2' with
155+
// RENAME_NOREPLACE, which each of these formats implements except the
156+
// one whose support arrived late and is not known from its type; a
157+
// socket bound to a name (0.17, KAL_FS_PROP_LOCAL_ENDPOINT) is a node
158+
// every one of them stores.
146159
case okl::fs_ext234: case okl::fs_btrfs: case okl::fs_xfs:
147160
case okl::fs_f2fs: case okl::fs_tmpfs: case okl::fs_overlay:
148-
case okl::fs_zfs: case okl::fs_bcachefs:
149-
return conservative | KAL_FS_PROP_CASE_SENSITIVE
161+
case okl::fs_bcachefs:
162+
return local | KAL_FS_PROP_CASE_SENSITIVE
150163
| KAL_FS_PROP_LINKS | KAL_FS_PROP_MAKE_LINKS
151-
| KAL_FS_PROP_EXECUTABLE;
164+
| KAL_FS_PROP_EXECUTABLE
165+
| KAL_FS_PROP_RENAME_NO_REPLACE | KAL_FS_PROP_LOCAL_ENDPOINT;
166+
case okl::fs_zfs:
167+
return local | KAL_FS_PROP_CASE_SENSITIVE
168+
| KAL_FS_PROP_LINKS | KAL_FS_PROP_MAKE_LINKS
169+
| KAL_FS_PROP_EXECUTABLE | KAL_FS_PROP_LOCAL_ENDPOINT;
152170

153171
// Read-only formats: the nodes are there and none can be made, and a
154172
// rename cannot be atomic because there is no rename.
@@ -164,21 +182,31 @@ kal_uintptr volume_props(int fd) {
164182
// names another. `symlink' on such a volume reports EPERM, and this is
165183
// where a caller learns that before it tries.
166184
case okl::fs_msdos: case okl::fs_exfat:
167-
return conservative;
185+
return local;
168186

169187
// A case-insensitive namespace, with nodes that name others.
170188
case okl::fs_ntfs: case okl::fs_ntfs3:
171-
return conservative | KAL_FS_PROP_LINKS | KAL_FS_PROP_MAKE_LINKS;
189+
return local | KAL_FS_PROP_LINKS | KAL_FS_PROP_MAKE_LINKS;
172190
// The same, on a format that stores a mode.
173191
case okl::fs_hfsplus:
174-
return conservative | KAL_FS_PROP_LINKS | KAL_FS_PROP_MAKE_LINKS
192+
return local | KAL_FS_PROP_LINKS | KAL_FS_PROP_MAKE_LINKS
175193
| KAL_FS_PROP_EXECUTABLE;
176194

177195
default:
178196
return conservative;
179197
}
180198
}
181199

200+
} // namespace (reopened below)
201+
202+
namespace okl {
203+
// The volume's properties, for the interfaces outside this file that ask one
204+
// (openkal.net's rendezvous at a name).
205+
kal_uintptr volume_props_of(int fd) { return volume_props(fd); }
206+
} // namespace okl
207+
208+
namespace {
209+
182210
// Writes no more of the structure than the caller says exists on its side, and
183211
// reports which fields it filled.
184212
//
@@ -594,6 +622,27 @@ int kal_fs_rename(kal_dir from, const char* a, kal_uintptr alen,
594622
return okl::failed(r) ? okl::translate(r) : kal_ok;
595623
}
596624

625+
// A RENAME THAT DOES NOT REPLACE (0.17) is the kernel's own: `renameat2' with
626+
// RENAME_NOREPLACE tests and renames in one act. A format that does not
627+
// implement the flag answers EINVAL, and a kernel before 3.15 ENOSYS; both are
628+
// a volume that cannot, which `kal_fs_props' answers before the call for the
629+
// formats this file knows.
630+
int kal_fs_rename_flags(kal_dir from, const char* a, kal_uintptr alen,
631+
kal_dir to, const char* b, kal_uintptr blen,
632+
kal_uintptr flags) {
633+
if ((flags & ~static_cast<kal_uintptr>(KAL_RENAME_NO_REPLACE)) != 0) return kal_err_invalid;
634+
if (flags == 0) return kal_fs_rename(from, a, alen, to, b, blen);
635+
const int f = okl::unpack(from.h), t2 = okl::unpack(to.h);
636+
if (f < 0 || t2 < 0 || !okl::acceptable(a, alen) || !okl::acceptable(b, blen)) return kal_err_invalid;
637+
if ((volume_props(t2) & KAL_FS_PROP_RENAME_NO_REPLACE) == 0) return kal_err_not_supported;
638+
okl::terminated ta(a, alen), tb(b, blen);
639+
if (!ta.ok || !tb.ok) return kal_err_invalid;
640+
const okl_long r = okl::sys(okl::nr_renameat2, f, reinterpret_cast<okl_long>(ta.buf),
641+
t2, reinterpret_cast<okl_long>(tb.buf), 1 /* RENAME_NOREPLACE */);
642+
if (r == -okl::e_inval || r == -okl::e_nosys) return kal_err_not_supported;
643+
return okl::failed(r) ? okl::translate(r) : kal_ok;
644+
}
645+
597646
// Enumeration holds a descriptor of its own, obtained by opening the directory
598647
// through itself. A duplicate would share the file offset with the handle the
599648
// caller holds, so two enumerations of one directory would consume each

‎src/net.cpp‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include "endpoint.h"
44
#include <openkal/net.h>
55

6+
namespace okl { kal_uintptr volume_props_of(int fd); } // fs.cpp
7+
68
// openkal.net upon the kernel's socket calls.
79
//
810
// A CONNECTION IS AN OWNED HANDLE AND THE STREAM IS BORROWED FROM IT, exactly as
@@ -127,6 +129,79 @@ int kal_net_accept(kal_net_listener l, kal_net_conn* out) {
127129
}
128130
}
129131

132+
// A RENDEZVOUS AT A NAME (0.17): a socket of the local family bound to a name
133+
// in the directory. The kernel binds to a path and not beneath a descriptor,
134+
// so the directory is named through the descriptor itself,
135+
// `/proc/self/fd/<dir>/<name>', which the kernel resolves through the
136+
// descriptor --- the directory the caller holds, wherever it has since been
137+
// moved. The address holds 108 bytes, and a name that does not fit beside
138+
// that prefix is one this environment cannot place (kal_err_invalid).
139+
static int local_address(kal_dir base, const char* name, kal_uintptr len,
140+
unsigned char (&addr)[110], okl_long& alen) {
141+
const int dir = okl::unpack(base.h);
142+
if (dir < 0 || !okl::acceptable(name, len) || len == 0) return kal_err_invalid;
143+
for (kal_uintptr i = 0; i < len; ++i) if (name[i] == '/') return kal_err_invalid;
144+
if ((okl::volume_props_of(dir) & KAL_FS_PROP_LOCAL_ENDPOINT) == 0) return kal_err_not_supported;
145+
char path[108];
146+
const char prefix[] = "/proc/self/fd/";
147+
okl_uptr at = 0;
148+
for (okl_uptr i = 0; i + 1 < sizeof prefix; ++i) path[at++] = prefix[i];
149+
char d[12]; int n = 0;
150+
for (unsigned v = static_cast<unsigned>(dir); ; v /= 10) { d[n++] = static_cast<char>('0' + v % 10); if (v < 10) break; }
151+
while (n) path[at++] = d[--n];
152+
path[at++] = '/';
153+
if (at + len + 1 > sizeof path) return kal_err_invalid;
154+
for (kal_uintptr i = 0; i < len; ++i) path[at++] = name[i];
155+
path[at] = '\0';
156+
addr[0] = 1; addr[1] = 0; // sa_family = AF_UNIX
157+
for (okl_uptr i = 0; i <= at; ++i) addr[2 + i] = static_cast<unsigned char>(path[i]);
158+
alen = static_cast<okl_long>(2 + at + 1);
159+
return kal_ok;
160+
}
161+
162+
int kal_net_listen_at(kal_dir base, const char* name, kal_uintptr len, kal_net_listener* out) {
163+
if (out == nullptr) return kal_err_invalid;
164+
unsigned char addr[110] = {};
165+
okl_long alen = 0;
166+
if (const int rc = local_address(base, name, len, addr, alen); rc != kal_ok) return rc;
167+
const okl_long fd = okl::sys(okl::nr_socket, 1 /* AF_UNIX */, okl::sock_stream | okl::sock_cloexec, 0);
168+
if (okl::failed(fd)) return okl::translate(fd);
169+
if (const okl_long r = okl::sys(okl::nr_bind, fd, reinterpret_cast<okl_long>(addr), alen); okl::failed(r)) {
170+
okl::sys(okl::nr_close, fd);
171+
return r == -98 /* EADDRINUSE */ ? kal_err_exists : okl::translate(r);
172+
}
173+
if (const okl_long r = okl::sys(okl::nr_listen, fd, 128); okl::failed(r)) {
174+
okl::sys(okl::nr_close, fd);
175+
return okl::translate(r);
176+
}
177+
out->h = okl::pack(static_cast<int>(fd));
178+
if (out->h == 0) { okl::sys(okl::nr_close, fd); return kal_err_no_memory; }
179+
return kal_ok;
180+
}
181+
182+
int kal_net_connect_at(kal_dir base, const char* name, kal_uintptr len, kal_net_conn* out) {
183+
if (out == nullptr) return kal_err_invalid;
184+
unsigned char addr[110] = {};
185+
okl_long alen = 0;
186+
if (const int rc = local_address(base, name, len, addr, alen); rc != kal_ok) return rc;
187+
const okl_long fd = okl::sys(okl::nr_socket, 1 /* AF_UNIX */, okl::sock_stream | okl::sock_cloexec, 0);
188+
if (okl::failed(fd)) return okl::translate(fd);
189+
for (;;) {
190+
const okl_long r = okl::sys(okl::nr_connect, fd, reinterpret_cast<okl_long>(addr), alen);
191+
if (okl::interrupted(r)) continue;
192+
if (okl::failed(r)) {
193+
okl::sys(okl::nr_close, fd);
194+
// Nothing listening: no name, or a name a listener left behind.
195+
return (r == -okl::e_noent || r == -111 /* ECONNREFUSED */) ? kal_err_not_found
196+
: okl::translate(r);
197+
}
198+
break;
199+
}
200+
out->h = okl::pack(static_cast<int>(fd));
201+
if (out->h == 0) { okl::sys(okl::nr_close, fd); return kal_err_no_memory; }
202+
return kal_ok;
203+
}
204+
130205
kal_stream kal_net_stream(kal_net_conn c) {
131206
// The bare descriptor, for the reason kal_fs_stream gives: openkal.stream's
132207
// operations take whatever the environment's transfer calls take, and a

0 commit comments

Comments
 (0)