feat: extend shell with removeFile and removeDir

This commit is contained in:
dualfroz committed 2026-09-08 12:44:10 +02:00
1 parent 9e1f907da5
commit 230e271ea1
1 file changed
+172 -4
+172 -4
View File
@@ -1150,7 +1150,172 @@ static int makeDir(const char* argument)
return 0;
}
static void changeDir(const char* argument)
/* Real `rm`: refuses a directory outright (real rm without -r never
descends), matching vfsUnlink's own EISDIR-shaped refusal (vfs.c) --
the vfsStat check here exists only to give a friendlier message than
vfsUnlink's plain -1 would on its own, the actual enforcement is
vfsUnlink's, not this. */
static int removeFile(const char* argument)
{
if (argument[0] == 0) {
kprintf("rm: missing path\n");
return 1;
}
char path[pathCapacity];
buildPath(argument, path);
FbStat stat;
if (vfsStat(path, &stat) != 0) {
kprintf("rm: no such file: %s\n", path);
return 1;
}
if (stat.type == fsTypeDir) {
kprintf("rm: %s: is a directory\n", path);
return 1;
}
if (vfsUnlink(path) != 0) {
kprintf("rm: cannot remove %s\n", path);
return 1;
}
return 0;
}
/* Real `rmdir`: only ever removes an EMPTY directory, same split from `rm`
above that real rmdir(2)/unlink(2) have. */
static int removeDir(const char* argument)
{
if (argument[0] == 0) {
kprintf("rmdir: missing path\n");
return 1;
}
char path[pathCapacity];
buildPath(argument, path);
FbStat stat;
if (vfsStat(path, &stat) != 0) {
kprintf("rmdir: no such directory: %s\n", path);
return 1;
}
if (stat.type != fsTypeDir) {
kprintf("rmdir: %s: not a directory\n", path);
return 1;
}
if (vfsRmdir(path) != 0) {
kprintf("rmdir: %s: not empty\n", path);
return 1;
}
return 0;
}
/* Real `chmod MODE PATH`: MODE is parsed as octal (strtoul base 8, klib.h),
matching real chmod(1)'s numeric-mode convention -- "755" already means
0755, no leading 0 required. Both the owner AND other triads are
meaningful now that a real uid model exists (fs.h's fsModeOwner and
fsModeOther bits, vfs.c's vfsPermitted) -- refused outright unless the
caller owns the target or is uid 0 (vfsChmod's own vfsOwnerOrRoot check).
Symbolic modes (u+x, a=r, ...) are not supported, the same "numeric only"
scope every other new builtin in this shell has picked over a fuller
grammar it does not need. */
static int doChmod(const char* argument)
{
char modeText[16];
uint64 i = 0;
while (argument[i] && argument[i] != ' ' && i < sizeof(modeText) - 1) {
modeText[i] = argument[i];
i++;
}
modeText[i] = 0;
const char* rest = argument + i;
while (*rest == ' ')
rest++;
if (modeText[0] == 0 || rest[0] == 0) {
kprintf("chmod: usage: chmod MODE PATH\n");
return 1;
}
uint32 mode = (uint32)strtoul(modeText, nullptr, 8);
char path[pathCapacity];
buildPath(rest, path);
if (vfsChmod(path, mode) != 0) {
kprintf("chmod: cannot chmod %s\n", path);
return 1;
}
return 0;
}
/* Real `chown UID PATH`: UID is parsed as a plain decimal number (strtoul
base 10, klib.h), unlike chmod's octal MODE -- a uid is never a bitmask,
so there is no "leading digit means octal" convention to match. Refused
outright unless the caller is uid 0 (vfsChown's own root-only check) --
even the file's own owner may not give it away, matching real Unix's
_POSIX_CHOWN_RESTRICTED default. */
static int doChown(const char* argument)
{
char uidText[16];
uint64 i = 0;
while (argument[i] && argument[i] != ' ' && i < sizeof(uidText) - 1) {
uidText[i] = argument[i];
i++;
}
uidText[i] = 0;
const char* rest = argument + i;
while (*rest == ' ')
rest++;
if (uidText[0] == 0 || rest[0] == 0) {
kprintf("chown: usage: chown UID PATH\n");
return 1;
}
uint32 uid = (uint32)strtoul(uidText, nullptr, 10);
char path[pathCapacity];
buildPath(rest, path);
if (vfsChown(path, uid) != 0) {
kprintf("chown: cannot chown %s\n", path);
return 1;
}
return 0;
}
/* Real `chgrp GID PATH`: decimal like chown, not octal like chmod -- a gid
is never a bitmask either. Unlike chown (root-only, full stop), the
file's own owner may also call this, but only to set the group to THIS
shell's own current gid (vfsChgrp's own owner-may-only-name-their-own-gid
rule, see its doc comment) -- root may still set any gid. */
static int doChgrp(const char* argument)
{
char gidText[16];
uint64 i = 0;
while (argument[i] && argument[i] != ' ' && i < sizeof(gidText) - 1) {
gidText[i] = argument[i];
i++;
}
gidText[i] = 0;
const char* rest = argument + i;
while (*rest == ' ')
rest++;
if (gidText[0] == 0 || rest[0] == 0) {
kprintf("chgrp: usage: chgrp GID PATH\n");
return 1;
}
uint32 gid = (uint32)strtoul(gidText, nullptr, 10);
char path[pathCapacity];
buildPath(rest, path);
if (vfsChgrp(path, gid) != 0) {
kprintf("chgrp: cannot chgrp %s\n", path);
return 1;
}
return 0;
}
/* Same "0 success, 1 failure" shape as above. */
static int changeDir(const char* argument)
{
char path[pathCapacity];
if (argument[0] == 0)
@@ -1158,10 +1323,13 @@ static void changeDir(const char* argument)
else
buildPath(argument, path);
VfsNode* node = vfsLookup(path);
if (!node || !vfsIsDir(node)) {
/* vfsChdir (vfs.h) also understands "/mnt" directories -- unlike the
vfsLookup+vfsIsDir check this used to do, which only ever walked the
VfsNode tree (ustar/tmpfs) and so could never actually cd into the
persistent on-disk filesystem at all. */
if (vfsChdir(path) != 0) {
kprintf("cd: no such directory: %s\n", path);
return;
return 1;
}
vfsPath(node, cwd, sizeof(cwd));
}