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)
|
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))
|
||||||
|
|||||||
Reference in new issue
Block a user