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 "keyboard.h"
|
||||
#include "sched.h"
|
||||
#include "pipe.h"
|
||||
#include "pidfd.h"
|
||||
#include "ata.h"
|
||||
#include "sfs.h"
|
||||
#include "poll.h"
|
||||
|
||||
#define tmpfsBlock 512
|
||||
#define blockDevPath "/dev/disk0"
|
||||
#define sfsMountPrefix "/mnt"
|
||||
#define sfsMountPrefixLen 4
|
||||
|
||||
struct VfsNode {
|
||||
char name[fsNameMax];
|
||||
uint32 type;
|
||||
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;
|
||||
uint8* rwData;
|
||||
uint64 size;
|
||||
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* firstChild;
|
||||
struct VfsNode* nextSibling;
|
||||
@@ -23,7 +57,7 @@ struct VfsNode {
|
||||
|
||||
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));
|
||||
if (!node)
|
||||
@@ -35,6 +69,10 @@ static VfsNode* nodeAlloc(const char* name, uint64 nameLen, uint32 type, bool wr
|
||||
node->name[nameLen] = 0;
|
||||
node->type = type;
|
||||
node->writable = writable;
|
||||
node->mode = mode;
|
||||
node->uid = currentThreadUid();
|
||||
node->gid = currentThreadGid();
|
||||
node->pipeHandle = -1;
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -53,18 +91,18 @@ static VfsNode* findChild(VfsNode* parent, const char* name, uint64 nameLen)
|
||||
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)
|
||||
addChild(parent, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
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)
|
||||
return nullptr;
|
||||
if (writable) {
|
||||
@@ -79,6 +117,29 @@ static VfsNode* makeChildFile(VfsNode* parent, const char* name, uint64 nameLen,
|
||||
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)
|
||||
{
|
||||
if (!path)
|
||||
@@ -103,6 +164,8 @@ static VfsNode* lookupNode(const char* path)
|
||||
} else {
|
||||
if (cur->type != fsTypeDir)
|
||||
return nullptr;
|
||||
if (!vfsPermitted(cur->mode, cur->uid, cur->gid, currentThreadUid(), currentThreadGid(), fsModeOwnerExec))
|
||||
return nullptr;
|
||||
VfsNode* child = findChild(cur, start, len);
|
||||
if (!child)
|
||||
return nullptr;
|
||||
@@ -191,12 +254,27 @@ static void ustarAdd(const char* rawPath, bool isDir, const uint8* data, uint64
|
||||
if (last) {
|
||||
if (!child) {
|
||||
if (isDir)
|
||||
child = makeChildDir(cur, path + start, len, false);
|
||||
else
|
||||
child = makeChildFile(cur, path + start, len, data, size, false);
|
||||
child = makeChildDir(cur, path + start, len, false, fsModeDefaultDir);
|
||||
else {
|
||||
/* 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) {
|
||||
child = makeChildDir(cur, path + start, len, false);
|
||||
child = makeChildDir(cur, path + start, len, false, fsModeDefaultDir);
|
||||
}
|
||||
if (!child)
|
||||
return;
|
||||
@@ -206,7 +284,7 @@ static void ustarAdd(const char* rawPath, bool isDir, const uint8* data, uint64
|
||||
|
||||
void vfsInit(void)
|
||||
{
|
||||
root = nodeAlloc("/", 1, fsTypeDir, false);
|
||||
root = nodeAlloc("/", 1, fsTypeDir, false, fsModeDefaultDir);
|
||||
}
|
||||
|
||||
void vfsMountUstar(uint64 start, uint64 end)
|
||||
@@ -233,7 +311,7 @@ void vfsMountTmpfs(const char* path)
|
||||
return;
|
||||
if (findChild(parent, leaf, strlen(leaf)))
|
||||
return;
|
||||
makeChildDir(parent, leaf, strlen(leaf), true);
|
||||
makeChildDir(parent, leaf, strlen(leaf), true, fsModeDefaultDir);
|
||||
kprintf("[vfs] mounted writable tmpfs at %s\n", path);
|
||||
}
|
||||
|
||||
@@ -244,10 +322,25 @@ void vfsFdInit(FileDescriptor* fds)
|
||||
fds[i].offset = 0;
|
||||
fds[i].kind = fdFree;
|
||||
fds[i].flags = 0;
|
||||
fds[i].pipeHandle = -1;
|
||||
}
|
||||
fds[0].kind = fdConsole;
|
||||
fds[1].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)
|
||||
@@ -286,7 +379,7 @@ static bool ensureCapacity(VfsNode* node, uint64 needed)
|
||||
return true;
|
||||
}
|
||||
|
||||
int vfsOpen(const char* path, uint32 flags)
|
||||
static uint64 blockDevTotalBytes(void)
|
||||
{
|
||||
VfsNode* node = lookupNode(path);
|
||||
if (!node) {
|
||||
|
||||
Reference in new issue
Block a user