From 2749e7f1c2bdc352acbc704a1909d3d3e4215548 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 26 Oct 2018 14:24:11 +0200 Subject: [PATCH] Implement sys$chdir() and teach sh+ls to cd around and browse different dirs. --- Kernel/Syscall.cpp | 2 + Kernel/Syscall.h | 1 + Kernel/Task.cpp | 38 ++++++++---- Kernel/Task.h | 4 +- Kernel/_fs_contents | Bin 1024000 -> 1024000 bytes Kernel/errno.h | 2 + LibC/dirent.cpp | 2 +- LibC/string.cpp | 1 + LibC/unistd.cpp | 6 ++ LibC/unistd.h | 1 + Userland/ls.cpp | 4 +- Userland/sh.cpp | 76 ++++++++++++++++++++++-- VirtualFileSystem/FileHandle.cpp | 5 ++ VirtualFileSystem/FileHandle.h | 4 ++ VirtualFileSystem/VirtualFileSystem.cpp | 24 +++++--- VirtualFileSystem/VirtualFileSystem.h | 10 ++-- 16 files changed, 147 insertions(+), 33 deletions(-) diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 6dc00beb29..9af8c2fcef 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -110,6 +110,8 @@ DWORD handle(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3) return 0; case Syscall::GetArguments: return current->sys$get_arguments((int*)arg1, (char***)arg2); + case Syscall::PosixChdir: + return current->sys$chdir((const char*)arg1); default: kprintf("int0x80: Unknown function %x requested {%x, %x, %x}\n", function, arg1, arg2, arg3); break; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 5f225fc12d..9eb832e484 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -32,6 +32,7 @@ enum Function { PosixGettimeofday = 0x2000, PosixGethostname = 0x2001, GetArguments = 0x2002, + PosixChdir = 0x2003, }; void initialize(); diff --git a/Kernel/Task.cpp b/Kernel/Task.cpp index 798cfb2c5f..9034a1ebbd 100644 --- a/Kernel/Task.cpp +++ b/Kernel/Task.cpp @@ -205,7 +205,14 @@ Task* Task::createUserTask(const String& path, uid_t uid, gid_t gid, pid_t paren return nullptr; } - auto handle = VirtualFileSystem::the().open(path); + RetainPtr cwd; + { + InterruptDisabler disabler; + if (auto* parentTask = Task::fromPID(parentPID)) + cwd = parentTask->m_cwd.copyRef(); + } + + auto handle = VirtualFileSystem::the().open(path, cwd.ptr()); if (!handle) { error = -ENOENT; // FIXME: Get a more detailed error from VFS. return nullptr; @@ -327,9 +334,9 @@ Task::Task(String&& name, uid_t uid, gid_t gid, pid_t parentPID, RingLevel ring) auto* parentTask = Task::fromPID(parentPID); if (parentTask) - m_cwd = parentTask->m_cwd; + m_cwd = parentTask->m_cwd.copyRef(); else - m_cwd = "/"; + m_cwd = nullptr; m_nextRegion = LinearAddress(0x600000); @@ -712,22 +719,29 @@ int Task::sys$close(int fd) int Task::sys$lstat(const char* path, void* statbuf) { - auto handle = VirtualFileSystem::the().open(move(path)); + auto handle = VirtualFileSystem::the().open(move(path), m_cwd.ptr()); if (!handle) return -1; handle->stat((Unix::stat*)statbuf); return 0; } +int Task::sys$chdir(const char* path) +{ + auto handle = VirtualFileSystem::the().open(path, m_cwd.ptr()); + if (!handle) + return -ENOENT; // FIXME: More detailed error. + if (!handle->isDirectory()) + return -ENOTDIR; + m_cwd = handle->vnode(); + kprintf("m_cwd <- %p (%u)\n", m_cwd.ptr(), handle->vnode()->inode.index()); + return 0; +} + int Task::sys$getcwd(char* buffer, size_t size) { - if (size < m_cwd.length() + 1) { - // FIXME: return -ERANGE; - return -1; - } - memcpy(buffer, m_cwd.characters(), m_cwd.length()); - buffer[m_cwd.length()] = '\0'; - return 0; + // FIXME: Implement! + return -ENOTIMPL; } int Task::sys$open(const char* path, size_t pathLength) @@ -744,7 +758,7 @@ int Task::sys$open(const char* path, size_t pathLength) FileHandle* Task::openFile(String&& path) { - auto handle = VirtualFileSystem::the().open(move(path)); + auto handle = VirtualFileSystem::the().open(move(path), m_cwd.ptr()); if (!handle) { #ifdef DEBUG_IO kprintf("vfs::open() failed\n"); diff --git a/Kernel/Task.h b/Kernel/Task.h index e2d9c35834..7cdc52e17b 100644 --- a/Kernel/Task.h +++ b/Kernel/Task.h @@ -6,6 +6,7 @@ #include "TSS.h" #include #include "i386.h" +#include //#define TASK_SANITY_CHECKS @@ -98,6 +99,7 @@ public: int sys$munmap(void*, size_t size); int sys$get_dir_entries(int fd, void*, size_t); int sys$getcwd(char*, size_t); + int sys$chdir(const char*); int sys$sleep(unsigned seconds); int sys$gettimeofday(timeval*); int sys$gethostname(char* name, size_t length); @@ -150,7 +152,7 @@ private: pid_t m_waitee { -1 }; int m_fdBlockedOnRead { -1 }; - String m_cwd; + RetainPtr m_cwd; struct Region { Region(LinearAddress, size_t, RetainPtr&&, String&&); diff --git a/Kernel/_fs_contents b/Kernel/_fs_contents index fed031cd30eaaed2ebe7c68da0f7785e29254a95..c1123370d141b71a761f07c64b05f10bb6d5a45c 100644 GIT binary patch delta 107460 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 delta 30700 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 Vg&LlAg&N*=g&Mx?3N`!>lmVdsh&ccN diff --git a/Kernel/errno.h b/Kernel/errno.h index 6b321a05f1..d487ec94a2 100644 --- a/Kernel/errno.h +++ b/Kernel/errno.h @@ -37,3 +37,5 @@ #define ENAMETOOLONG 36 // Name too long #define EOVERFLOW 75 // Value too large for defined data type + +#define ENOTIMPL 999 // Not implemented diff --git a/LibC/dirent.cpp b/LibC/dirent.cpp index caea450c5b..bda115d456 100644 --- a/LibC/dirent.cpp +++ b/LibC/dirent.cpp @@ -46,7 +46,7 @@ dirent* readdir(DIR* dirp) dirp->nextptr = dirp->buffer; } - if (dirp->nextptr > (dirp->buffer + dirp->buffer_size)) + if (dirp->nextptr >= (dirp->buffer + dirp->buffer_size)) return nullptr; auto* sys_ent = (sys_dirent*)dirp->nextptr; diff --git a/LibC/string.cpp b/LibC/string.cpp index 82877ec401..a72bbb7cda 100644 --- a/LibC/string.cpp +++ b/LibC/string.cpp @@ -69,6 +69,7 @@ const char* strerror(int errnum) case ERANGE: return "Math result not representable"; case ENAMETOOLONG: return "Name too long"; case EOVERFLOW: return "Value too large for data type"; + case ENOTIMPL: return "Not implemented"; } printf("strerror() missing string for errnum=%d\n", errnum); return "Unknown error"; diff --git a/LibC/unistd.cpp b/LibC/unistd.cpp index 8a39d7f085..da7e559103 100644 --- a/LibC/unistd.cpp +++ b/LibC/unistd.cpp @@ -51,6 +51,12 @@ int lstat(const char* path, stat* statbuf) __RETURN_WITH_ERRNO(rc, rc, -1); } +int chdir(const char* path) +{ + int rc = Syscall::invoke(Syscall::PosixChdir, (dword)path); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + char* getcwd(char* buffer, size_t size) { int rc = Syscall::invoke(Syscall::PosixGetcwd, (dword)buffer, (dword)size); diff --git a/LibC/unistd.h b/LibC/unistd.h index 42e61367ad..6a5b40f3a7 100644 --- a/LibC/unistd.h +++ b/LibC/unistd.h @@ -11,6 +11,7 @@ int open(const char* path); ssize_t read(int fd, void* buf, size_t count); int close(int fd); pid_t waitpid(pid_t); +int chdir(const char* path); char* getcwd(char* buffer, size_t size); int lstat(const char* path, stat* statbuf); int sleep(unsigned seconds); diff --git a/Userland/ls.cpp b/Userland/ls.cpp index eab5de08c6..4847e5ede9 100644 --- a/Userland/ls.cpp +++ b/Userland/ls.cpp @@ -4,14 +4,14 @@ int main(int c, char** v) { - DIR* dirp = opendir("/"); + DIR* dirp = opendir("."); if (!dirp) { printf("opendir failed :(\n"); return 1; } char pathbuf[256]; while (auto* de = readdir(dirp)) { - sprintf(pathbuf, "/%s", de->d_name); + sprintf(pathbuf, "%s", de->d_name); stat st; int rc = lstat(pathbuf, &st); if (rc == -1) { diff --git a/Userland/sh.cpp b/Userland/sh.cpp index b4adda1731..30d9526b7f 100644 --- a/Userland/sh.cpp +++ b/Userland/sh.cpp @@ -3,6 +3,9 @@ #include #include #include +#include + +char* g_cwd = nullptr; static void prompt() { @@ -12,12 +15,63 @@ static void prompt() printf("$ "); } +static int sh_pwd(int, const char**) +{ + printf("cwd: %s\n", g_cwd); +} + +static int sh_cd(int argc, const char** argv) +{ + if (argc == 1) { + printf("usage: cd \n"); + return 0; + } + + char pathbuf[128]; + if (argv[1][1] == '/') + memcpy(pathbuf, argv[1], strlen(argv[1])); + else + sprintf(pathbuf, "%s/%s", g_cwd, argv[1]); + struct stat st; + int rc = lstat(pathbuf, &st); + if (rc < 0) { + printf("lstat(%s) failed: %s\n", pathbuf, strerror(errno)); + return 1; + } + if (!S_ISDIR(st.st_mode)) { + printf("Not a directory: %s\n", pathbuf); + return 1; + } + rc = chdir(pathbuf); + if (rc < 0) { + printf("chdir(%s) failed: %s\n", pathbuf, strerror(errno)); + return 1; + } + memcpy(g_cwd, pathbuf, strlen(pathbuf)); + return 0; +} + +static bool handle_builtin(int argc, const char** argv, int& retval) +{ + if (argc == 0) + return false; + if (!strcmp(argv[0], "cd")) { + retval = sh_cd(argc, argv); + return true; + } + if (!strcmp(argv[0], "pwd")) { + retval = sh_pwd(argc, argv); + return true; + } + return false; +} + static int runcmd(char* cmd) { if (cmd[0] == 0) return 0; char buf[128]; - sprintf(buf, "/bin/%s", cmd); + memcpy(buf, cmd, 128); const char* argv[32]; size_t argi = 1; @@ -30,16 +84,27 @@ static int runcmd(char* cmd) } } argv[argi + 1] = nullptr; - int ret = spawn(argv[0], argv); + + int retval = 0; + if (handle_builtin(argi, argv, retval)) { + return 0; + } + + const char* search_path = "/bin"; + + char pathbuf[128]; + sprintf(pathbuf, "%s/%s", search_path, argv[0]); + int ret = spawn(pathbuf, argv); if (ret == -1) { printf("spawn failed: %s (%s)\n", cmd, strerror(errno)); return 1; } + // FIXME: waitpid should give us the spawned process's exit status waitpid(ret); - return 0; + return retval; } -int main(int c, char** v) +int main(int, char**) { char linebuf[128]; int linedx = 0; @@ -50,6 +115,9 @@ int main(int c, char** v) printf("failed to open /dev/keyboard :(\n"); return 1; } + g_cwd = (char*)malloc(1024); + g_cwd[0] = '/'; + g_cwd[1] = '\0'; prompt(); for (;;) { char keybuf[16]; diff --git a/VirtualFileSystem/FileHandle.cpp b/VirtualFileSystem/FileHandle.cpp index f7cb6aeb67..5aa8c54115 100644 --- a/VirtualFileSystem/FileHandle.cpp +++ b/VirtualFileSystem/FileHandle.cpp @@ -128,6 +128,11 @@ ByteBuffer FileHandle::readEntireFile() return m_vnode->fileSystem()->readEntireInode(m_vnode->inode); } +bool FileHandle::isDirectory() const +{ + return m_vnode->metadata().isDirectory(); +} + ssize_t FileHandle::get_dir_entries(byte* buffer, size_t size) { Locker locker(VirtualFileSystem::lock()); diff --git a/VirtualFileSystem/FileHandle.h b/VirtualFileSystem/FileHandle.h index 23c584df25..9bbabdcf0c 100644 --- a/VirtualFileSystem/FileHandle.h +++ b/VirtualFileSystem/FileHandle.h @@ -20,6 +20,10 @@ public: String absolutePath() const; + bool isDirectory() const; + + VirtualFileSystem::Node* vnode() { return m_vnode.ptr(); } + #ifdef SERENITY int fd() const { return m_fd; } void setFD(int fd) { m_fd = fd; } diff --git a/VirtualFileSystem/VirtualFileSystem.cpp b/VirtualFileSystem/VirtualFileSystem.cpp index d7880a1b02..0cb6eb83af 100644 --- a/VirtualFileSystem/VirtualFileSystem.cpp +++ b/VirtualFileSystem/VirtualFileSystem.cpp @@ -168,9 +168,9 @@ void VirtualFileSystem::freeNode(Node* node) m_nodeFreeList.append(move(node)); } -bool VirtualFileSystem::isDirectory(const String& path) +bool VirtualFileSystem::isDirectory(const String& path, Node* base) { - auto inode = resolvePath(path); + auto inode = resolvePath(path, base); if (!inode.isValid()) return false; @@ -355,11 +355,11 @@ bool VirtualFileSystem::touch(const String& path) return inode.fileSystem()->setModificationTime(inode, ktime(nullptr)); } -OwnPtr VirtualFileSystem::open(const String& path) +OwnPtr VirtualFileSystem::open(const String& path, Node* base) { Locker locker(VirtualFileSystem::lock()); - auto inode = resolvePath(path); + auto inode = resolvePath(path, base); if (!inode.isValid()) return nullptr; auto vnode = getOrCreateNode(inode); @@ -368,7 +368,7 @@ OwnPtr VirtualFileSystem::open(const String& path) return make(move(vnode)); } -OwnPtr VirtualFileSystem::create(const String& path) +OwnPtr VirtualFileSystem::create(const String& path, Node* base) { Locker locker(VirtualFileSystem::lock()); @@ -378,7 +378,7 @@ OwnPtr VirtualFileSystem::create(const String& path) return nullptr; } -OwnPtr VirtualFileSystem::mkdir(const String& path) +OwnPtr VirtualFileSystem::mkdir(const String& path, Node* base) { Locker locker(VirtualFileSystem::lock()); @@ -398,10 +398,18 @@ InodeIdentifier VirtualFileSystem::resolveSymbolicLink(const String& basePath, I return resolvePath(buf); } -InodeIdentifier VirtualFileSystem::resolvePath(const String& path) +InodeIdentifier VirtualFileSystem::resolvePath(const String& path, Node* base) { + if (path.isEmpty()) + return { }; + auto parts = path.split('/'); - InodeIdentifier inode = m_rootNode->inode; + InodeIdentifier inode; + + if (path[0] == '/') + inode = m_rootNode->inode; + else + inode = base ? base->inode : m_rootNode->inode; for (unsigned i = 0; i < parts.size(); ++i) { auto& part = parts[i]; diff --git a/VirtualFileSystem/VirtualFileSystem.h b/VirtualFileSystem/VirtualFileSystem.h index 53e8fcb4a1..6c20bc6ad1 100644 --- a/VirtualFileSystem/VirtualFileSystem.h +++ b/VirtualFileSystem/VirtualFileSystem.h @@ -51,7 +51,7 @@ public: VirtualFileSystem(); ~VirtualFileSystem(); - bool isDirectory(const String& path); + bool isDirectory(const String& path, Node* base = nullptr); void listDirectory(const String& path); void listDirectoryRecursively(const String& path); @@ -64,9 +64,9 @@ public: bool mountRoot(RetainPtr&&); bool mount(RetainPtr&&, const String& path); - OwnPtr open(const String& path); - OwnPtr create(const String& path); - OwnPtr mkdir(const String& path); + OwnPtr open(const String& path, Node* base = nullptr); + OwnPtr create(const String& path, Node* base = nullptr); + OwnPtr mkdir(const String& path, Node* base = nullptr); bool isRoot(InodeIdentifier) const; @@ -79,7 +79,7 @@ private: void enumerateDirectoryInode(InodeIdentifier, Function); String absolutePath(InodeIdentifier); - InodeIdentifier resolvePath(const String& path); + InodeIdentifier resolvePath(const String& path, Node* base = nullptr); InodeIdentifier resolveSymbolicLink(const String& basePath, InodeIdentifier symlinkInode); RetainPtr allocateNode();