feat: extend vfs with makeChildFifo
This commit is contained in:
1 parent
8b629c1858
commit
db5a6274c9
1 file changed
+105
-12
@@ -5,17 +5,51 @@
|
|||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
#include "sched.h"
|
#include "sched.h"
|
||||||
|
#include "pipe.h"
|
||||||
|
#include "pidfd.h"
|
||||||
|
#include "ata.h"
|
||||||
|
#include "sfs.h"
|
||||||
|
#include "poll.h"
|
||||||
|
|
||||||
#define tmpfsBlock 512
|
#define tmpfsBlock 512
|
||||||
|
#define blockDevPath "/dev/disk0"
|
||||||
|
#define sfsMountPrefix "/mnt"
|
||||||
|
#define sfsMountPrefixLen 4
|
||||||
|
|
||||||
struct VfsNode {
|
struct VfsNode {
|
||||||
char name[fsNameMax];
|
char name[fsNameMax];
|
||||||
uint32 type;
|
uint32 type;
|
||||||
bool writable;
|
bool writable;
|
||||||
|
/* Real owner rwx permission bits (fs.h's fsModeOwner*), an axis
|
||||||
|
independent of `writable` above: `writable` says whether this node's
|
||||||
|
CONTENT/CHILDREN may be mutated at all (a mount-level property, false
|
||||||
|
for every ustar-loaded node regardless of mode); `mode` is the real,
|
||||||
|
chmod(2)-settable per-node permission a caller must ALSO satisfy to
|
||||||
|
open the node for writing or (jobctl.c/syscall.c) exec it. Both gates
|
||||||
|
must pass, exactly like a real read-only mount PLUS real per-file
|
||||||
|
permission bits would. */
|
||||||
|
uint32 mode;
|
||||||
|
/* Real owner identity (POSIX uid, fs.h's FbStat.uid): whichever
|
||||||
|
currentThreadUid() was current at nodeAlloc time -- always 0 ("root")
|
||||||
|
for every ustar-loaded and boot-time tmpfs-mount node, since
|
||||||
|
vfsMountUstar/vfsMountTmpfs both run after schedInit but before any
|
||||||
|
real process exists (schedInit's idle thread, uid 0, is always
|
||||||
|
"current" then). Never retroactive, like mode's own default; there is
|
||||||
|
no chown-shaped call anywhere in this kernel to ever change it after
|
||||||
|
creation (see fs.h's FbStat.uid doc comment). */
|
||||||
|
uint32 uid;
|
||||||
|
/* Real group identity (fs.h's FbStat.gid): whichever currentThreadGid()
|
||||||
|
was current at nodeAlloc time, same creation-time-only, never-
|
||||||
|
retroactive shape as uid immediately above. */
|
||||||
|
uint32 gid;
|
||||||
const uint8* roData;
|
const uint8* roData;
|
||||||
uint8* rwData;
|
uint8* rwData;
|
||||||
uint64 size;
|
uint64 size;
|
||||||
uint64 capacity;
|
uint64 capacity;
|
||||||
|
/* Only meaningful for type == fsTypeFifo: the pipe.h handle every
|
||||||
|
vfsOpen on this path attaches a fresh end to (see makeChildFifo). -1
|
||||||
|
for every other node type. */
|
||||||
|
int32 pipeHandle;
|
||||||
struct VfsNode* parent;
|
struct VfsNode* parent;
|
||||||
struct VfsNode* firstChild;
|
struct VfsNode* firstChild;
|
||||||
struct VfsNode* nextSibling;
|
struct VfsNode* nextSibling;
|
||||||
@@ -23,7 +57,7 @@ struct VfsNode {
|
|||||||
|
|
||||||
static VfsNode* root;
|
static VfsNode* root;
|
||||||
|
|
||||||
static VfsNode* nodeAlloc(const char* name, uint64 nameLen, uint32 type, bool writable)
|
static VfsNode* nodeAlloc(const char* name, uint64 nameLen, uint32 type, bool writable, uint32 mode)
|
||||||
{
|
{
|
||||||
VfsNode* node = (VfsNode*)kmalloc(sizeof(VfsNode));
|
VfsNode* node = (VfsNode*)kmalloc(sizeof(VfsNode));
|
||||||
if (!node)
|
if (!node)
|
||||||
@@ -35,6 +69,10 @@ static VfsNode* nodeAlloc(const char* name, uint64 nameLen, uint32 type, bool wr
|
|||||||
node->name[nameLen] = 0;
|
node->name[nameLen] = 0;
|
||||||
node->type = type;
|
node->type = type;
|
||||||
node->writable = writable;
|
node->writable = writable;
|
||||||
|
node->mode = mode;
|
||||||
|
node->uid = currentThreadUid();
|
||||||
|
node->gid = currentThreadGid();
|
||||||
|
node->pipeHandle = -1;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,18 +91,18 @@ static VfsNode* findChild(VfsNode* parent, const char* name, uint64 nameLen)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VfsNode* makeChildDir(VfsNode* parent, const char* name, uint64 nameLen, bool writable)
|
static VfsNode* makeChildDir(VfsNode* parent, const char* name, uint64 nameLen, bool writable, uint32 mode)
|
||||||
{
|
{
|
||||||
VfsNode* node = nodeAlloc(name, nameLen, fsTypeDir, writable);
|
VfsNode* node = nodeAlloc(name, nameLen, fsTypeDir, writable, mode);
|
||||||
if (node)
|
if (node)
|
||||||
addChild(parent, node);
|
addChild(parent, node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VfsNode* makeChildFile(VfsNode* parent, const char* name, uint64 nameLen,
|
static VfsNode* makeChildFile(VfsNode* parent, const char* name, uint64 nameLen,
|
||||||
const uint8* data, uint64 size, bool writable)
|
const uint8* data, uint64 size, bool writable, uint32 mode)
|
||||||
{
|
{
|
||||||
VfsNode* node = nodeAlloc(name, nameLen, fsTypeFile, writable);
|
VfsNode* node = nodeAlloc(name, nameLen, fsTypeFile, writable, mode);
|
||||||
if (!node)
|
if (!node)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (writable) {
|
if (writable) {
|
||||||
@@ -79,6 +117,29 @@ static VfsNode* makeChildFile(VfsNode* parent, const char* name, uint64 nameLen,
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VfsNode* makeChildFifo(VfsNode* parent, const char* name, uint64 nameLen, int pipeHandle)
|
||||||
|
{
|
||||||
|
VfsNode* node = nodeAlloc(name, nameLen, fsTypeFifo, true, fsModeDefaultFile);
|
||||||
|
if (!node)
|
||||||
|
return nullptr;
|
||||||
|
node->pipeHandle = pipeHandle;
|
||||||
|
addChild(parent, node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Real POSIX "search" permission: resolving a NAME inside a directory (as
|
||||||
|
opposed to "." / ".." above, which never look anything up) requires the
|
||||||
|
owner-or-other exec bit on that directory for the calling thread's own
|
||||||
|
uid, exactly like a real kernel's namei() checks X_OK on every directory
|
||||||
|
it walks through. Deliberately only gates the findChild() branch below,
|
||||||
|
not "."/".." -- an honest, minor scope boundary matching this codebase's
|
||||||
|
existing style of not fixing unrelated pre-existing gaps while adding a
|
||||||
|
new feature (see this file's many other "deliberately out of scope"
|
||||||
|
comments). Denial collapses to the same nullptr every other lookup
|
||||||
|
failure already returns, matching every other EACCES-shaped refusal in
|
||||||
|
this file (resolveOpen's write check, etc.) -- callers cannot tell
|
||||||
|
"does not exist" from "exists but not searchable", exactly like the rest
|
||||||
|
of this VFS layer's error handling. */
|
||||||
static VfsNode* lookupNode(const char* path)
|
static VfsNode* lookupNode(const char* path)
|
||||||
{
|
{
|
||||||
if (!path)
|
if (!path)
|
||||||
@@ -103,6 +164,8 @@ static VfsNode* lookupNode(const char* path)
|
|||||||
} else {
|
} else {
|
||||||
if (cur->type != fsTypeDir)
|
if (cur->type != fsTypeDir)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
if (!vfsPermitted(cur->mode, cur->uid, cur->gid, currentThreadUid(), currentThreadGid(), fsModeOwnerExec))
|
||||||
|
return nullptr;
|
||||||
VfsNode* child = findChild(cur, start, len);
|
VfsNode* child = findChild(cur, start, len);
|
||||||
if (!child)
|
if (!child)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -191,12 +254,27 @@ static void ustarAdd(const char* rawPath, bool isDir, const uint8* data, uint64
|
|||||||
if (last) {
|
if (last) {
|
||||||
if (!child) {
|
if (!child) {
|
||||||
if (isDir)
|
if (isDir)
|
||||||
child = makeChildDir(cur, path + start, len, false);
|
child = makeChildDir(cur, path + start, len, false, fsModeDefaultDir);
|
||||||
else
|
else {
|
||||||
child = makeChildFile(cur, path + start, len, data, size, false);
|
/* Anything shipped under "bin/" (mktar's own stored-name
|
||||||
|
convention, no leading slash -- see tools/mktar.c) is a
|
||||||
|
real binary this kernel spawns by path (jobctlSpawn,
|
||||||
|
syscall.c's loadImageByPath): give it the owner-exec
|
||||||
|
bit by default, exactly as if someone had already run
|
||||||
|
`chmod 755` on it, since nothing in this kernel could
|
||||||
|
otherwise ever chmod a read-only ustar-loaded file
|
||||||
|
(vfsChmod refuses a non-writable node, same as every
|
||||||
|
other tmpfs mutation). Everything else defaults to
|
||||||
|
fsModeDefaultFile (no exec bit), matching a real
|
||||||
|
ordinary shipped data file. */
|
||||||
|
uint32 mode = (n > 4 && memcmp(path, "bin/", 4) == 0)
|
||||||
|
? fsModeDefaultExec
|
||||||
|
: fsModeDefaultFile;
|
||||||
|
child = makeChildFile(cur, path + start, len, data, size, false, mode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (!child) {
|
} else if (!child) {
|
||||||
child = makeChildDir(cur, path + start, len, false);
|
child = makeChildDir(cur, path + start, len, false, fsModeDefaultDir);
|
||||||
}
|
}
|
||||||
if (!child)
|
if (!child)
|
||||||
return;
|
return;
|
||||||
@@ -206,7 +284,7 @@ static void ustarAdd(const char* rawPath, bool isDir, const uint8* data, uint64
|
|||||||
|
|
||||||
void vfsInit(void)
|
void vfsInit(void)
|
||||||
{
|
{
|
||||||
root = nodeAlloc("/", 1, fsTypeDir, false);
|
root = nodeAlloc("/", 1, fsTypeDir, false, fsModeDefaultDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vfsMountUstar(uint64 start, uint64 end)
|
void vfsMountUstar(uint64 start, uint64 end)
|
||||||
@@ -233,7 +311,7 @@ void vfsMountTmpfs(const char* path)
|
|||||||
return;
|
return;
|
||||||
if (findChild(parent, leaf, strlen(leaf)))
|
if (findChild(parent, leaf, strlen(leaf)))
|
||||||
return;
|
return;
|
||||||
makeChildDir(parent, leaf, strlen(leaf), true);
|
makeChildDir(parent, leaf, strlen(leaf), true, fsModeDefaultDir);
|
||||||
kprintf("[vfs] mounted writable tmpfs at %s\n", path);
|
kprintf("[vfs] mounted writable tmpfs at %s\n", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,10 +322,25 @@ void vfsFdInit(FileDescriptor* fds)
|
|||||||
fds[i].offset = 0;
|
fds[i].offset = 0;
|
||||||
fds[i].kind = fdFree;
|
fds[i].kind = fdFree;
|
||||||
fds[i].flags = 0;
|
fds[i].flags = 0;
|
||||||
|
fds[i].pipeHandle = -1;
|
||||||
}
|
}
|
||||||
fds[0].kind = fdConsole;
|
fds[0].kind = fdConsole;
|
||||||
fds[1].kind = fdConsole;
|
fds[1].kind = fdConsole;
|
||||||
fds[2].kind = fdConsole;
|
fds[2].kind = fdConsole;
|
||||||
|
|
||||||
|
/* fd 0's own console reader queue (keyboard.h) -- reused field, same as
|
||||||
|
a pipe/fifo fd's handle into pipe.c's table. Every thread (kernel and
|
||||||
|
user alike) gets one, whether it ever reads stdin or not, matching
|
||||||
|
fd 0 already always being fdConsole for every thread regardless of
|
||||||
|
use. fdFlagBlocking makes syscall.c's sysRead retry-syscall path
|
||||||
|
treat it exactly like a blocking pipe end (vfsBlockingChannel below);
|
||||||
|
fd 1/2 (stdout/stderr) never read, so they get no reader and no
|
||||||
|
blocking flag. If the reader table is exhausted, fd 0 is left
|
||||||
|
non-blocking with an invalid handle -- reads on it then simply fail
|
||||||
|
(-1) rather than crash, the same fail-open behavior a fully exhausted
|
||||||
|
pipe/fifo table already has elsewhere in this file. */
|
||||||
|
fds[0].pipeHandle = keyboardOpenReader();
|
||||||
|
fds[0].flags = fdFlagBlocking;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FileDescriptor* fdAt(int fd)
|
static FileDescriptor* fdAt(int fd)
|
||||||
@@ -286,7 +379,7 @@ static bool ensureCapacity(VfsNode* node, uint64 needed)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int vfsOpen(const char* path, uint32 flags)
|
static uint64 blockDevTotalBytes(void)
|
||||||
{
|
{
|
||||||
VfsNode* node = lookupNode(path);
|
VfsNode* node = lookupNode(path);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
|
|||||||
Reference in new issue
Block a user