feat: extend vfs with sfsFifoFind and sfsFifoHandleFor
This commit is contained in:
1 parent
86c742f0c8
commit
7115dab220
1 file changed
+173
@@ -554,6 +554,179 @@ typedef struct SfsFifoBinding {
|
||||
int32 inodeNum;
|
||||
int32 pipeHandle;
|
||||
uint32 incarnation;
|
||||
uint64 generation;
|
||||
} SfsFifoBinding;
|
||||
|
||||
static SfsFifoBinding sfsFifoBindings[maxSfsFifoBindings];
|
||||
|
||||
/* Finds inodeNum's live binding, discarding it first if it is stale by
|
||||
either check above. Returns nullptr if there is no valid binding (never
|
||||
bound, or just evicted as stale). */
|
||||
static SfsFifoBinding* sfsFifoFind(int32 inodeNum)
|
||||
{
|
||||
uint64 gen = sfsGeneration();
|
||||
for (uint32 i = 0; i < maxSfsFifoBindings; i++) {
|
||||
SfsFifoBinding* b = &sfsFifoBindings[i];
|
||||
if (b->inodeNum != inodeNum)
|
||||
continue;
|
||||
if (b->generation != gen || pipeIncarnation(b->pipeHandle) != b->incarnation) {
|
||||
b->inodeNum = 0;
|
||||
return nullptr;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Returns inodeNum's live pipe handle, creating a fresh pipeCreateDetached
|
||||
one (and a new table slot) the first time this inode is opened since the
|
||||
last time nobody had it open -- exactly the "nobody attached yet, keep
|
||||
retrying rather than report broken/EOF" contract pipeCreateDetached
|
||||
itself already documents. -1 if pipe.c's own table is exhausted, or this
|
||||
inode has no existing binding and the (much smaller) binding table above
|
||||
is itself full. */
|
||||
static int32 sfsFifoHandleFor(int32 inodeNum)
|
||||
{
|
||||
SfsFifoBinding* existing = sfsFifoFind(inodeNum);
|
||||
if (existing)
|
||||
return existing->pipeHandle;
|
||||
|
||||
int handle = pipeCreateDetached();
|
||||
if (handle < 0)
|
||||
return -1;
|
||||
|
||||
for (uint32 i = 0; i < maxSfsFifoBindings; i++) {
|
||||
if (sfsFifoBindings[i].inodeNum == 0) {
|
||||
sfsFifoBindings[i].inodeNum = inodeNum;
|
||||
sfsFifoBindings[i].pipeHandle = handle;
|
||||
sfsFifoBindings[i].incarnation = pipeIncarnation(handle);
|
||||
sfsFifoBindings[i].generation = sfsGeneration();
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
pipeDestroyIfUnattached(handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Called when inodeNum's own directory entry is actually gone for good
|
||||
(sfsRemove reached the last link, see vfsUnlink) -- evicts the table slot
|
||||
FIRST, then attempts pipeDestroyIfUnattached on the handle it held:
|
||||
unlinking a fifo that is currently open must leave the still-open ends
|
||||
alone (they keep working off their own copy of the handle number,
|
||||
matching the tmpfs fifo's own vfsUnlink comment below), so the destroy is
|
||||
a no-op unless nobody ever attached to it. Evicting before destroying
|
||||
(rather than after) closes the narrow window where a concurrent opener
|
||||
of the SAME inode number could otherwise see the stale slot as still
|
||||
valid and attach to a handle number pipe.c is about to hand out to
|
||||
something completely unrelated. */
|
||||
static void sfsFifoForget(int32 inodeNum)
|
||||
{
|
||||
for (uint32 i = 0; i < maxSfsFifoBindings; i++) {
|
||||
if (sfsFifoBindings[i].inodeNum == inodeNum) {
|
||||
int handle = sfsFifoBindings[i].pipeHandle;
|
||||
sfsFifoBindings[i].inodeNum = 0;
|
||||
pipeDestroyIfUnattached(handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int64 sfsFdRead(FileDescriptor* d, void* buffer, uint64 length)
|
||||
{
|
||||
int64 got = sfsReadAt(d->pipeHandle, d->offset, buffer, length);
|
||||
if (got > 0)
|
||||
d->offset += (uint64)got;
|
||||
return got;
|
||||
}
|
||||
|
||||
static int64 sfsFdWrite(FileDescriptor* d, const void* buffer, uint64 length)
|
||||
{
|
||||
int64 written = sfsWriteAt(d->pipeHandle, d->offset, buffer, length);
|
||||
if (written > 0)
|
||||
d->offset += (uint64)written;
|
||||
return written;
|
||||
}
|
||||
|
||||
int vfsMkfifo(const char* path)
|
||||
{
|
||||
char resolvedPath[fsPathMax];
|
||||
vfsResolvePath(path, resolvedPath, sizeof(resolvedPath));
|
||||
path = resolvedPath;
|
||||
|
||||
const char* rel;
|
||||
/* Persistent named pipe (sfs.h's sfsTypeFifo): only the directory entry
|
||||
and its type are created here -- the live pipe.h handle two openers
|
||||
actually rendezvous over is created lazily by vfsOpen the first time
|
||||
anyone opens it (sfsFifoHandleFor above), the same "create on first
|
||||
real use" split every other SFS-backed thing keeps between "what is
|
||||
on disk" (sfs.c, no pipe.h dependency at all) and "what is live in
|
||||
this boot's memory" (here). */
|
||||
if (isSfsPath(path, &rel))
|
||||
return sfsCreateFifo(rel) >= 0 ? 0 : -1;
|
||||
if (strcmp(path, blockDevPath) == 0)
|
||||
return -1;
|
||||
if (lookupNode(path))
|
||||
return -1;
|
||||
|
||||
char leaf[fsNameMax];
|
||||
VfsNode* parent = splitParent(path, leaf, sizeof(leaf));
|
||||
if (!parent || parent->type != fsTypeDir || !parent->writable
|
||||
|| !vfsPermitted(parent->mode, parent->uid, parent->gid, currentThreadUid(), currentThreadGid(), fsModeOwnerWrite))
|
||||
return -1;
|
||||
|
||||
int handle = pipeCreateDetached();
|
||||
if (handle < 0)
|
||||
return -1;
|
||||
|
||||
if (!makeChildFifo(parent, leaf, strlen(leaf), handle)) {
|
||||
pipeDestroyIfUnattached(handle);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Shared core of vfsOpen/vfsOpenInto below: resolves `path`+`flags` and, on
|
||||
success, fills EVERY field of `d` (whichever FileDescriptor the caller
|
||||
decided on -- a freshly allocated slot in the calling thread's own table
|
||||
for vfsOpen, or an explicit slot in some other, not-yet-running thread's
|
||||
table for vfsOpenInto) and returns 0. On failure, returns -1 having
|
||||
written NOTHING to `d` at all -- every early return below is before the
|
||||
first store into `d`, so a caller never has to clean up a half-written
|
||||
descriptor. */
|
||||
static int resolveOpen(FileDescriptor* d, const char* path, uint32 flags)
|
||||
{
|
||||
const char* rel;
|
||||
if (isSfsPath(path, &rel)) {
|
||||
int32 inodeNum = sfsLookup(rel);
|
||||
if (inodeNum < 0) {
|
||||
if (!(flags & oCreate))
|
||||
return -1;
|
||||
inodeNum = sfsCreateFile(rel);
|
||||
if (inodeNum < 0)
|
||||
return -1;
|
||||
} else if (sfsTypeOf(inodeNum) == sfsTypeFifo) {
|
||||
/* One-directional only, exactly like the tmpfs fifo branch
|
||||
below and vfsPipeAttach's wantWrite bool -- a real O_RDWR
|
||||
fifo fd has no equivalent here, since a single fd can only be
|
||||
fdPipeRead or fdPipeWrite, never both. */
|
||||
uint32 mode = flags & (oWriteOnly | oReadWrite);
|
||||
bool wantWrite;
|
||||
if (mode == oWriteOnly)
|
||||
wantWrite = true;
|
||||
else if (mode == oReadOnly)
|
||||
wantWrite = false;
|
||||
else
|
||||
return -1;
|
||||
|
||||
int handle = sfsFifoHandleFor(inodeNum);
|
||||
if (handle < 0)
|
||||
return -1;
|
||||
|
||||
d->node = nullptr;
|
||||
d->offset = 0;
|
||||
d->kind = wantWrite ? fdPipeWrite : fdPipeRead;
|
||||
d->flags = fdFlagBlocking;
|
||||
d->pipeHandle = handle;
|
||||
VfsNode* node = lookupNode(path);
|
||||
if (!node) {
|
||||
if (!(flags & oCreate))
|
||||
|
||||
Reference in new issue
Block a user