diff --git a/src/vfs.c b/src/vfs.c index 0c795c1..24a1ad8 100644 --- a/src/vfs.c +++ b/src/vfs.c @@ -381,6 +381,179 @@ static bool ensureCapacity(VfsNode* node, uint64 needed) 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); if (!node) { if (!(flags & oCreate))