feat: extend vfs with vfsRmdir and vfsOwnerOrRoot

This commit is contained in:
dualfroz committed 2026-09-08 21:03:37 +02:00
1 parent 4640fb5eb5
commit 5b5b0203dd
1 file changed
+173
+173
View File
@@ -1364,6 +1364,179 @@ int vfsUnlink(const char* path)
return 0;
}
/* Real rmdir(2): the counterpart vfsUnlink above deliberately refuses. Only
ever removes a directory, and only an EMPTY one (ENOTDIR/ENOTEMPTY in
real errno terms, both collapsed to -1 here like every other vfs.c entry
point). root itself can never be removed -- root is created non-writable
(nodeAlloc("/", ...) in vfsInit), so the same `!node->writable` check
every other mutating entry point already relies on covers it for free,
no separate identity check needed. */
int vfsRmdir(const char* path)
{
char resolvedPath[fsPathMax];
vfsResolvePath(path, resolvedPath, sizeof(resolvedPath));
path = resolvedPath;
const char* rel;
if (isSfsPath(path, &rel)) {
int32 inodeNum = sfsLookup(rel);
if (inodeNum < 0 || sfsTypeOf(inodeNum) != sfsTypeDir)
return -1;
/* sfsRemove's own `inode.type == sfsTypeDir && inode.size > 0`
check (sfs.c) still refuses a non-empty directory -- kept as
defense in depth rather than duplicated here. */
return sfsRemove(rel) ? 0 : -1;
}
VfsNode* node = lookupNode(path);
if (!node || !node->writable || node->type != fsTypeDir || node->firstChild)
return -1;
/* Same parent-directory write check as vfsUnlink above -- removing a
directory ENTRY needs write permission on the PARENT, not the
directory being removed itself. */
VfsNode* parent = node->parent;
if (!vfsPermitted(parent->mode, parent->uid, parent->gid, currentThreadUid(), currentThreadGid(), fsModeOwnerWrite))
return -1;
VfsNode** link = &parent->firstChild;
while (*link && *link != node)
link = &(*link)->nextSibling;
if (*link)
*link = node->nextSibling;
kfree(node);
return 0;
}
bool vfsOwnerOrRoot(uint32 ownerUid, uint32 callerUid)
{
return callerUid == 0 || callerUid == ownerUid;
}
int vfsChmod(const char* path, uint32 mode)
{
char resolvedPath[fsPathMax];
vfsResolvePath(path, resolvedPath, sizeof(resolvedPath));
path = resolvedPath;
const char* rel;
if (isSfsPath(path, &rel)) {
int32 inodeNum = sfsLookup(rel);
if (inodeNum < 0)
return -1;
if (!vfsOwnerOrRoot(sfsUidOf(inodeNum), currentThreadUid()))
return -1;
return sfsChmod(inodeNum, mode) ? 0 : -1;
}
VfsNode* node = lookupNode(path);
if (!node || !node->writable)
return -1;
if (!vfsOwnerOrRoot(node->uid, currentThreadUid()))
return -1;
node->mode = mode;
return 0;
}
int vfsChown(const char* path, uint32 newUid)
{
if (currentThreadUid() != 0)
return -1;
char resolvedPath[fsPathMax];
vfsResolvePath(path, resolvedPath, sizeof(resolvedPath));
path = resolvedPath;
const char* rel;
if (isSfsPath(path, &rel)) {
int32 inodeNum = sfsLookup(rel);
if (inodeNum < 0)
return -1;
return sfsChown(inodeNum, newUid) ? 0 : -1;
}
VfsNode* node = lookupNode(path);
if (!node || !node->writable)
return -1;
node->uid = newUid;
return 0;
}
int vfsChgrp(const char* path, uint32 newGid)
{
uint32 callerUid = currentThreadUid();
bool isRoot = callerUid == 0;
/* Non-root: only the CALLER's own current gid may ever be assigned
(this kernel's one-gid-per-thread "membership"), and only by the
file's own owner -- both conditions checked up front so neither
branch below needs to repeat them. */
if (!isRoot && newGid != currentThreadGid())
return -1;
char resolvedPath[fsPathMax];
vfsResolvePath(path, resolvedPath, sizeof(resolvedPath));
path = resolvedPath;
const char* rel;
if (isSfsPath(path, &rel)) {
int32 inodeNum = sfsLookup(rel);
if (inodeNum < 0)
return -1;
if (!isRoot && sfsUidOf(inodeNum) != callerUid)
return -1;
return sfsChgrp(inodeNum, newGid) ? 0 : -1;
}
VfsNode* node = lookupNode(path);
if (!node || !node->writable)
return -1;
if (!isRoot && node->uid != callerUid)
return -1;
node->gid = newGid;
return 0;
}
int vfsSymlink(const char* path, const char* target)
{
/* Only `path` (where the symlink itself is created) is resolved against
the calling thread's cwd -- `target` (the string stored INSIDE it) is
kept exactly as given, never resolved, matching real symlink(2)
(a relative target is only ever interpreted later, at traversal
time, relative to the symlink's own directory -- not the creating
process's cwd at creation time). */
char resolvedPath[fsPathMax];
vfsResolvePath(path, resolvedPath, sizeof(resolvedPath));
const char* rel;
if (!isSfsPath(resolvedPath, &rel))
return -1;
return sfsCreateSymlink(rel, target) >= 0 ? 0 : -1;
}
int64 vfsReadlink(const char* path, char* buffer, uint64 cap)
{
char resolvedPath[fsPathMax];
vfsResolvePath(path, resolvedPath, sizeof(resolvedPath));
path = resolvedPath;
const char* rel;
if (!isSfsPath(path, &rel))
return -1;
int32 inodeNum = sfsLookupNoFollow(rel);
if (inodeNum < 0 || sfsTypeOf(inodeNum) != sfsTypeSymlink)
return -1;
if (!sfsReadlink(inodeNum, buffer, cap))
return -1;
return (int64)strlen(buffer);
}
int vfsLink(const char* existingPath, const char* newPath)
{
char resolvedExisting[fsPathMax];
}
VfsNode* vfsLookup(const char* path)
{
return lookupNode(path);