feat: extend vfs with blockDevRead and blockDevWrite

This commit is contained in:
dualfroz committed 2026-09-08 19:30:50 +02:00
1 parent db5a6274c9
commit 86c742f0c8
1 file changed
+173
+173
View File
@@ -381,6 +381,179 @@ static bool ensureCapacity(VfsNode* node, uint64 needed)
static uint64 blockDevTotalBytes(void) static uint64 blockDevTotalBytes(void)
{ {
return ataSectorCount() * (uint64)ataSectorSize;
}
/* Reads/writes at most one sector's worth per iteration so a request that
straddles a sector boundary (or several) is handled as a sequence of
aligned sector transfers, each preceded by a read-modify-write step when
the access does not start or end on a sector boundary. */
static int64 blockDevRead(FileDescriptor* d, void* buffer, uint64 length)
{
uint64 total = blockDevTotalBytes();
if (d->offset >= total)
return 0;
uint64 want = length;
if (want > total - d->offset)
want = total - d->offset;
uint8* dst = (uint8*)buffer;
uint64 done = 0;
while (done < want) {
uint64 offset = d->offset + done;
uint64 lba = offset / ataSectorSize;
uint64 within = offset % ataSectorSize;
uint64 chunk = ataSectorSize - within;
if (chunk > want - done)
chunk = want - done;
uint8 sector[ataSectorSize];
if (!ataReadSectors(lba, 1, sector))
break;
memcpy(dst + done, sector + within, chunk);
done += chunk;
}
d->offset += done;
if (done > 0)
return (int64)done;
return want == 0 ? 0 : -1;
}
static int64 blockDevWrite(FileDescriptor* d, const void* buffer, uint64 length)
{
uint64 total = blockDevTotalBytes();
if (d->offset >= total)
return -1;
uint64 want = length;
if (want > total - d->offset)
want = total - d->offset;
const uint8* src = (const uint8*)buffer;
uint64 done = 0;
while (done < want) {
uint64 offset = d->offset + done;
uint64 lba = offset / ataSectorSize;
uint64 within = offset % ataSectorSize;
uint64 chunk = ataSectorSize - within;
if (chunk > want - done)
chunk = want - done;
uint8 sector[ataSectorSize];
if (chunk == ataSectorSize) {
memcpy(sector, src + done, ataSectorSize);
} else if (!ataReadSectors(lba, 1, sector)) {
break;
} else {
memcpy(sector + within, src + done, chunk);
}
if (!ataWriteSectors(lba, 1, sector))
break;
done += chunk;
}
d->offset += done;
if (done > 0)
return (int64)done;
return want == 0 ? 0 : -1;
}
/* True for "/mnt" itself and for anything under "/mnt/". relOut receives the
path with the prefix stripped: "" (or "/foo") for sfsLookup, which treats
an empty relative path as the sfs root. */
static bool isSfsPath(const char* path, const char** relOut)
{
if (strncmp(path, sfsMountPrefix, sfsMountPrefixLen) != 0)
return false;
char after = path[sfsMountPrefixLen];
if (after != '\0' && after != '/')
return false;
*relOut = path + sfsMountPrefixLen;
return true;
}
/* See vfs.h's own doc comment for the full contract -- this is the single
place shell.c's old per-builtin static cwd's logic now lives, shared by
every path-taking entry point below instead of only shell.c's own
handful of commands. */
void vfsResolvePath(const char* path, char* out, uint64 cap)
{
if (path[0] == '/') {
uint64 len = strlen(path);
if (len >= cap)
len = cap - 1;
memcpy(out, path, len);
out[len] = 0;
return;
}
const char* cwd = currentThreadCwd();
uint64 pos = strlen(cwd);
if (pos >= cap)
pos = cap - 1;
memcpy(out, cwd, pos);
if (pos == 0 || out[pos - 1] != '/') {
if (pos < cap - 1)
out[pos++] = '/';
}
uint64 i = 0;
while (path[i] && pos < cap - 1)
out[pos++] = path[i++];
out[pos] = 0;
}
static uint32 sfsTypeToFsType(uint32 sfsType)
{
if (sfsType == sfsTypeDir)
return fsTypeDir;
if (sfsType == sfsTypeFifo)
return fsTypeFifo;
return fsTypeFile;
}
/* Persistent (sfs.h) named-pipe bindings for "/mnt" fifos: sfs.c stores only
that a path IS a fifo (the inode's type), never a live byte stream -- the
actual pipe.h handle carrying bytes between two independent openers of
the same persistent path is purely in-memory here, exactly like the
VfsNode-resident one a tmpfs fifo carries on its own node forever (see
makeChildFifo above), except an SFS inode number has nowhere on-disk to
remember a handle. This small fixed table maps inode number -> live
handle, created lazily on the FIRST open() of either direction (or the
first open() since every previous session's ends have both gone away,
see sfsFifoForget) and reused by every later open() of the same inode
until then.
Slot 0 is never a real fifo inode (allocInodeNum, sfs.c, starts at 1;
inode 0 is permanently marked used by sfsFormat as a sentinel), so a
zero-initialized table already starts every slot correctly "free" --
inodeNum 0 doubles as "empty slot" the same way pipeHandle -1 already
means "no handle" everywhere else in this file.
Two INDEPENDENT staleness checks guard against two INDEPENDENT ways a
remembered (inodeNum, handle) pair can stop meaning what it used to:
- sfsGeneration() (sfs.h) catches "the whole filesystem underneath was
wiped since" -- every sfsTest.c test starts with its own sfsFormat()
call, which reuses inode numbers for completely unrelated entries
dozens of times a boot; without this check, a binding left over from
a wiped filesystem could silently hand a brand-new, unrelated
inode's opener the OLD fifo's (possibly still-leaked) handle.
- pipeIncarnation(handle) (pipe.h) catches "pipe.c's own table recycled
this NUMBER for an unrelated pipe since" -- an entirely ordinary
event that needs no filesystem format at all: the moment BOTH ends
of a completed fifo round-trip close, pipeReleaseIfUnused frees the
slot back to the general pool, and the very next unrelated
vfsPipe()/vfsMkfifo() anywhere in the kernel is free to claim that
exact number for something else entirely. pipeValid(handle) alone
cannot tell "still my pipe, nobody has closed it" apart from
"already reused by a stranger" -- only the incarnation counter can.
Either mismatch is treated exactly like "never bound": the stale slot is
evicted -- WITHOUT touching pipe.c, since whatever handle number it
remembers may already mean something else by now -- and a fresh handle
is created as if this were the first open ever. */
#define maxSfsFifoBindings 8
typedef struct SfsFifoBinding {
int32 inodeNum;
int32 pipeHandle;
uint32 incarnation;
VfsNode* node = lookupNode(path); VfsNode* node = lookupNode(path);
if (!node) { if (!node) {
if (!(flags & oCreate)) if (!(flags & oCreate))