feat: extend vfs with vfsChdir and vfsGetcwd
This commit is contained in:
1 parent
5b5b0203dd
commit
4dee5e3009
1 file changed
+106
@@ -1535,6 +1535,62 @@ int64 vfsReadlink(const char* path, char* buffer, uint64 cap)
|
|||||||
int vfsLink(const char* existingPath, const char* newPath)
|
int vfsLink(const char* existingPath, const char* newPath)
|
||||||
{
|
{
|
||||||
char resolvedExisting[fsPathMax];
|
char resolvedExisting[fsPathMax];
|
||||||
|
char resolvedNew[fsPathMax];
|
||||||
|
vfsResolvePath(existingPath, resolvedExisting, sizeof(resolvedExisting));
|
||||||
|
vfsResolvePath(newPath, resolvedNew, sizeof(resolvedNew));
|
||||||
|
existingPath = resolvedExisting;
|
||||||
|
newPath = resolvedNew;
|
||||||
|
|
||||||
|
const char* existingRel;
|
||||||
|
const char* newRel;
|
||||||
|
/* Cross-filesystem hardlinks (one path under /mnt, the other not) are
|
||||||
|
not supported -- same scope limit as a real link(2) across different
|
||||||
|
mounted filesystems (EXDEV), just expressed as a flat rejection since
|
||||||
|
this kernel has exactly one linkable filesystem. */
|
||||||
|
if (!isSfsPath(existingPath, &existingRel) || !isSfsPath(newPath, &newRel))
|
||||||
|
return -1;
|
||||||
|
return sfsLink(existingRel, newRel) >= 0 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vfsChdir(const char* path)
|
||||||
|
{
|
||||||
|
char resolvedPath[fsPathMax];
|
||||||
|
vfsResolvePath(path, resolvedPath, sizeof(resolvedPath));
|
||||||
|
|
||||||
|
const char* rel;
|
||||||
|
if (isSfsPath(resolvedPath, &rel)) {
|
||||||
|
int32 inodeNum = sfsLookup(rel);
|
||||||
|
if (inodeNum < 0 || sfsTypeOf(inodeNum) != sfsTypeDir)
|
||||||
|
return -1;
|
||||||
|
/* No parent-walk primitive exists for sfs.h inodes (unlike vfsPath
|
||||||
|
below for the VfsNode tree), so the /mnt branch stores exactly
|
||||||
|
what vfsResolvePath produced -- the one honestly-scoped gap
|
||||||
|
vfsChdir's own doc comment (vfs.h) calls out. */
|
||||||
|
return currentThreadSetCwd(resolvedPath) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
VfsNode* node = lookupNode(resolvedPath);
|
||||||
|
if (!node || node->type != fsTypeDir)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Canonicalizes away any "."/".." components the caller typed by
|
||||||
|
walking the resolved node's REAL parent chain (vfsPath), rather than
|
||||||
|
storing vfsResolvePath's own plain string join verbatim -- shell.c's
|
||||||
|
old changeDir already did exactly this before this subsystem
|
||||||
|
existed, so this keeps that behavior for the VfsNode-tree case. */
|
||||||
|
char canonical[fsPathMax];
|
||||||
|
vfsPath(node, canonical, sizeof(canonical));
|
||||||
|
return currentThreadSetCwd(canonical) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64 vfsGetcwd(char* out, uint64 cap)
|
||||||
|
{
|
||||||
|
const char* cwd = currentThreadCwd();
|
||||||
|
uint64 len = strlen(cwd);
|
||||||
|
if (len >= cap)
|
||||||
|
return -1;
|
||||||
|
memcpy(out, cwd, len + 1);
|
||||||
|
return (int64)len;
|
||||||
}
|
}
|
||||||
|
|
||||||
VfsNode* vfsLookup(const char* path)
|
VfsNode* vfsLookup(const char* path)
|
||||||
@@ -1567,6 +1623,56 @@ uint64 vfsSize(VfsNode* node)
|
|||||||
return node->size;
|
return node->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 vfsMode(VfsNode* node)
|
||||||
|
{
|
||||||
|
return node->mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 vfsUid(VfsNode* node)
|
||||||
|
{
|
||||||
|
return node->uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 vfsGid(VfsNode* node)
|
||||||
|
{
|
||||||
|
return node->gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Real Unix owner/other permission algorithm, the one real choke-point
|
||||||
|
piece of logic resolveOpen's two branches (tmpfs, SFS) and syscall.c's
|
||||||
|
loadImageByPath/jobctl.c's jobctlSpawn all now share instead of each
|
||||||
|
re-deriving it -- previously all four sites just tested a single bit
|
||||||
|
unconditionally (`mode & fsModeOwnerWrite`), honest only because there
|
||||||
|
was no uid model yet to make owner-vs-other meaningful. Deliberately a
|
||||||
|
pure function of its four arguments, not of "whoever is currently
|
||||||
|
running" -- every real call site passes currentThreadUid() in explicitly
|
||||||
|
for `callerUid`, so this stays testable against arbitrary uid/mode/owner
|
||||||
|
combinations without ever touching a live thread's own credential (see
|
||||||
|
coreTest.c's own KTEST coverage). `wantOwnerBit` is always one of
|
||||||
|
fs.h's fsModeOwner* constants; the matching "other" bit is derived by
|
||||||
|
shifting it right 6 (fsModeOther*'s own doc comment explains why that is
|
||||||
|
exact, not approximate) rather than requiring a second argument.
|
||||||
|
|
||||||
|
Deliberately NO uid-0-bypasses-everything superuser rule, unlike real
|
||||||
|
Unix's root: every boot-time process starts at uid 0 (sched.h's
|
||||||
|
Thread.uid doc comment), so a blanket bypass would have silently undone
|
||||||
|
the entire pre-existing "chmod denies write even to the file's own
|
||||||
|
creator" contract every current permission test (this file's own
|
||||||
|
resolveOpen tests, permtest.c's live demo) already relies on -- caught
|
||||||
|
by exactly those tests going red the first time this function existed
|
||||||
|
with a bypass in it. uid 0 is simply this kernel's default starting
|
||||||
|
identity, gated by the same owner/other bits as any other uid; its only
|
||||||
|
real asymmetry is currentThreadSetUid's own one-way privilege-drop rule
|
||||||
|
(sched.h), not a free pass here. */
|
||||||
|
bool vfsPermitted(uint32 mode, uint32 ownerUid, uint32 ownerGid, uint32 callerUid, uint32 callerGid, uint32 wantOwnerBit)
|
||||||
|
{
|
||||||
|
if (callerUid == ownerUid)
|
||||||
|
return (mode & wantOwnerBit) != 0;
|
||||||
|
if (callerGid == ownerGid)
|
||||||
|
return (mode & (wantOwnerBit >> 3)) != 0;
|
||||||
|
return (mode & (wantOwnerBit >> 6)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
const void* vfsFileData(VfsNode* node)
|
const void* vfsFileData(VfsNode* node)
|
||||||
{
|
{
|
||||||
if (!node)
|
if (!node)
|
||||||
|
|||||||
Reference in new issue
Block a user