feat: extend vfs with blockDevRead and blockDevWrite
This commit is contained in:
1 parent
db5a6274c9
commit
86c742f0c8
1 file changed
+173
@@ -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))
|
||||
|
||||
Reference in new issue
Block a user