diff --git a/src/shell.c b/src/shell.c index e680d7a..65b435a 100644 --- a/src/shell.c +++ b/src/shell.c @@ -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)); }