Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ for Linux, written on the kernel's own system-call interface.

```toml
[dependencies]
openkal = "0.14.0"
openkal = "0.14.1"

[target.'cfg(os = "linux")'.dependencies]
openkal-linux = "0.14.0"
openkal-linux = "0.15.1"
```

## Why it does not use a C library
Expand Down Expand Up @@ -101,6 +101,29 @@ resolve an absolute path and report one, and a name of `"."` leaves it able to
do only the first — which version 0.4 did, and which is why `getcwd` could not
have worked above it.

**A started program receives the three streams and its grants, and nothing
else.** Clause 7.13. Every source is first moved above the positions being
filled, so that placing one cannot overwrite another; everything above the
grants is then marked to close on replacement, including what the calling
program itself inherited (`close_range`, from Linux 5.11; `/proc/self/fd` or the
descriptor limit before it). The program is started through a descriptor for its
own file with `O_CLOEXEC`, and only a program that needs an interpreter --- a
`#!` script, or a `binfmt_misc` binary --- keeps that descriptor: the kernel
refuses such a program with `ENOENT` before the point of no return, and the start
is repeated with the flag cleared. Such a script observes `$0` as `/dev/fd/<n>`.

**Granted directories are named in the environment.** A grant arrives as a
descriptor at 3 and upward, and its name in the variable
`KAL_PREOPENS=<pid>{;<fd>,<len>,<name>}`, the arrangement of systemd's
`LISTEN_FDS` and `LISTEN_FDNAMES`. `<pid>` is written by the started process
itself, so a value inherited through a program that does not read it names no
one. A program started with grants enumerates exactly those directories, the
first of which is the directory it regards as the one it was started in; a
program started without them enumerates the working directory and `/`, as
before. A grant names directories to a program that confines itself to its
preopens; it does not confine a program that opens `/` on its own, which is the
environment's responsibility (clause 11, entry 6).

**Interruption is retried, not reported.** A caller cannot distinguish an
interrupted call from a genuine failure without knowledge of the platform, and
an implementation that reports it produces short transfers on any system that
Expand Down
4 changes: 2 additions & 2 deletions mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
namespace = "mcpplibs"
name = "openkal-linux"
version = "0.15.0"
version = "0.15.1"
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."
license = "Apache-2.0"

Expand Down Expand Up @@ -63,7 +63,7 @@ provides-interfaces = [
]

[dependencies]
openkal = "0.14.0"
openkal = "0.14.1"

# The package contributes definitions and no modules. The interface it
# implements is declared by the specification package, which this package
Expand Down
69 changes: 67 additions & 2 deletions src/fs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <openkal/fs.h>
#include <openkal/memory.h>

namespace okl { extern char** g_envp; }

namespace {


Expand All @@ -20,11 +22,74 @@ struct preopen { const char* name; kal_uintptr len; okl_uptr handle; };

char g_cwd[4096];

constexpr kal_uintptr kMaxGrants = 16;

// THE DIRECTORIES A STARTER GRANTED, WHEN THE PROGRAM WAS STARTED WITH GRANTS.
//
// They arrive as descriptors at 3 and upward, and their names in the variable
// `kal_process_spawn' writes (see `vector::build_env' in process.cpp):
//
// KAL_PREOPENS=<pid>{;<fd>,<len>,<name>}
//
// The value is read only when `<pid>' is this process, so one inherited through
// a program that does not read it is not mistaken for a grant. A descriptor that
// is not a directory is reported as a preopen this program may not use, which is
// how an entry that could not be opened is reported anyway. Each descriptor is
// marked to close on replacement as it is taken over, so that a grant reaches
// the program it was made to and not that program's own children (SPEC.md
// clause 7.13). The names point into the environment, which lives as long as
// the program does.
//
// Answers false when there is no such value, which leaves the directories this
// implementation supplies by default.
bool granted(preopen* t, kal_uintptr* n) {
constexpr char key[] = "KAL_PREOPENS=";
constexpr okl_uptr key_len = sizeof key - 1;
const char* v = nullptr;
for (char** e = okl::g_envp; e && *e; ++e) {
okl_uptr i = 0;
while (i < key_len && (*e)[i] == key[i]) ++i;
if (i == key_len) { v = *e + key_len; break; }
}
if (v == nullptr) return false;

auto number = [&](okl_uptr& out) {
if (*v < '0' || *v > '9') return false;
out = 0;
while (*v >= '0' && *v <= '9') out = out * 10 + static_cast<okl_uptr>(*v++ - '0');
return true;
};
okl_uptr pid = 0;
if (!number(pid) || static_cast<okl_long>(pid) != okl::sys(okl::nr_getpid)) return false;

kal_uintptr k = 0;
while (*v == ';' && k < kMaxGrants) {
++v;
okl_uptr fd = 0, len = 0;
if (!number(fd) || *v++ != ',' || !number(len) || *v++ != ',') return false;
for (okl_uptr i = 0; i < len; ++i) if (v[i] == '\0') return false;

okl::kstat st{};
const bool dir = !okl::failed(okl::sys(okl::nr_fstat, static_cast<okl_long>(fd),
reinterpret_cast<okl_long>(&st)))
&& (st.mode & okl::s_ifmt) == okl::s_ifdir;
if (dir) okl::sys(okl::nr_fcntl, static_cast<okl_long>(fd), okl::f_setfd, okl::fd_cloexec);
t[k++] = { v, len, dir ? okl::pack(static_cast<int>(fd)) : 0u };
v += len;
}
if (*v != '\0') return false;
*n = k;
return true;
}

preopen* table(kal_uintptr* count) {
static preopen t[2];
static preopen t[kMaxGrants];
static kal_uintptr n = 2;
static bool opened = false;
if (!opened) {
opened = true;
if (granted(t, &n)) { if (count) *count = n; return t; }
n = 2;

const okl_long n = okl::sys(okl::nr_getcwd, reinterpret_cast<okl_long>(g_cwd),
static_cast<okl_long>(sizeof g_cwd));
Expand All @@ -45,7 +110,7 @@ preopen* table(kal_uintptr* count) {
t[0] = { g_cwd, cwd_len, okl::failed(fd0) ? 0u : okl::pack(static_cast<int>(fd0)) };
t[1] = { "/", 1, okl::failed(fd1) ? 0u : okl::pack(static_cast<int>(fd1)) };
}
if (count) *count = 2;
if (count) *count = n;
return t;
}

Expand Down
Loading
Loading