From ada99c21af980d24f8a65d19a59aa1ea574f05b6 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sat, 28 Apr 2007 10:33:23 +0000 Subject: [PATCH] * Init kernel IO context. * Added command management, an interactive input loop, and a few basic commands. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20876 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/tools/fs_shell/fd.cpp | 2 +- src/tools/fs_shell/fd.h | 4 +- src/tools/fs_shell/fssh.cpp | 629 +++++++++++++++++++++++++++++++++++- src/tools/fs_shell/fssh.h | 77 +++++ 4 files changed, 694 insertions(+), 18 deletions(-) create mode 100644 src/tools/fs_shell/fssh.h diff --git a/src/tools/fs_shell/fd.cpp b/src/tools/fs_shell/fd.cpp index ac9e266d86..ab32fa10e1 100644 --- a/src/tools/fs_shell/fd.cpp +++ b/src/tools/fs_shell/fd.cpp @@ -27,7 +27,7 @@ namespace FSShell { -io_context gKernelIOContext; +io_context* gKernelIOContext; /*** General fd routines ***/ diff --git a/src/tools/fs_shell/fd.h b/src/tools/fs_shell/fd.h index eec22f55b3..c9857dd548 100644 --- a/src/tools/fs_shell/fd.h +++ b/src/tools/fs_shell/fd.h @@ -16,7 +16,7 @@ struct file_descriptor; struct select_sync; struct vnode; -extern io_context gKernelIOContext; +extern io_context* gKernelIOContext; struct fd_ops { @@ -107,7 +107,7 @@ static io_context* get_current_io_context(bool kernel); static inline struct io_context * get_current_io_context(bool /*kernel*/) { - return &gKernelIOContext; + return gKernelIOContext; } diff --git a/src/tools/fs_shell/fssh.cpp b/src/tools/fs_shell/fssh.cpp index e0ef0852ce..f952a5a479 100644 --- a/src/tools/fs_shell/fssh.cpp +++ b/src/tools/fs_shell/fssh.cpp @@ -5,23 +5,34 @@ #include +#include "fssh.h" + +#include #include #include +#include +#include + +#include "fd.h" +#include "fssh_dirent.h" #include "fssh_errors.h" #include "fssh_module.h" +#include "fssh_stat.h" +#include "fssh_string.h" #include "module.h" #include "syscalls.h" #include "vfs.h" -using namespace FSShell; +extern fssh_module_info *modules[]; + namespace FSShell { - extern fssh_file_system_module_info gRootFileSystem; -} -extern fssh_module_info *modules[]; +extern fssh_file_system_module_info gRootFileSystem; + +const char* kMountPoint = "/myfs"; static fssh_status_t @@ -32,7 +43,7 @@ init_kernel() // init module subsystem error = module_init(NULL); if (error != FSSH_B_OK) { - fprintf(stderr, "module_init() failed: %s\n", strerror(error)); + fprintf(stderr, "module_init() failed: %s\n", fssh_strerror(error)); return error; } else printf("module_init() succeeded!\n"); @@ -45,23 +56,39 @@ init_kernel() // init VFS error = vfs_init(NULL); if (error != FSSH_B_OK) { - fprintf(stderr, "initializing VFS failed: %s\n", strerror(error)); + fprintf(stderr, "initializing VFS failed: %s\n", fssh_strerror(error)); return error; } else printf("VFS initialized successfully!\n"); + // init kernel IO context + gKernelIOContext = (io_context*)vfs_new_io_context(NULL); + if (!gKernelIOContext) { + fprintf(stderr, "creating IO context failed!\n"); + return FSSH_B_NO_MEMORY; + } + // mount root FS fssh_dev_t rootDev = _kern_mount("/", NULL, "rootfs", 0, NULL, 0); if (rootDev < 0) { - fprintf(stderr, "mounting rootfs failed: %s\n", strerror(rootDev)); + fprintf(stderr, "mounting rootfs failed: %s\n", fssh_strerror(rootDev)); return rootDev; } else printf("mounted rootfs successfully!\n"); - // create mount point for the client FS - error = _kern_create_dir(-1, "/myfs", 0775); + // set cwd to "/" + error = _kern_setcwd(-1, "/"); if (error != FSSH_B_OK) { - fprintf(stderr, "creating mount point failed: %s\n", strerror(error)); + fprintf(stderr, "setting cwd failed: %s\n", fssh_strerror(error)); + return error; + } else + printf("set cwd successfully!\n"); + + // create mount point for the client FS + error = _kern_create_dir(-1, kMountPoint, 0775); + if (error != FSSH_B_OK) { + fprintf(stderr, "creating mount point failed: %s\n", + fssh_strerror(error)); return error; } else printf("mount point created successfully!\n"); @@ -70,6 +97,570 @@ init_kernel() } +// #pragma mark - Command + +Command::Command(const char* name, const char* description) + : fName(name), + fDescription(description) +{ +} + + +Command::Command(command_function* function, const char* name, + const char* description) + : fName(name), + fDescription(description), + fFunction(function) +{ +} + + +Command::~Command() +{ +} + + +const char* +Command::Name() const +{ + return fName.c_str(); +} + + +const char* +Command::Description() const +{ + return fDescription.c_str(); +} + + +int +Command::Do(int argc, const char* const* argv) +{ + if (!fFunction) { + fprintf(stderr, "No function given for command \"%s\"\n", Name()); + return 1; + } + + return (*fFunction)(argc, argv); +} + + +// #pragma mark - CommandManager + +CommandManager::CommandManager() +{ +} + + +CommandManager* +CommandManager::Default() +{ + if (!sManager) + sManager = new CommandManager; + return sManager; +} + + +void +CommandManager::AddCommand(Command* command) +{ + // The command name may consist of several aliases. Split them and + // register the command for each of them. + char _names[1024]; + char* names = _names; + strcpy(names, command->Name()); + + char* cookie; + while (char* name = strtok_r(names, " /", &cookie)) { + fCommands[name] = command; + names = NULL; + } +} + + +void +CommandManager::AddCommand(command_function* function, const char* name, + const char* description) +{ + AddCommand(new Command(function, name, description)); +} + + +void +CommandManager::AddCommands(command_function* function, const char* name, + const char* description, ...) +{ + va_list args; + va_start(args, description); + + while (function) { + AddCommand(function, name, description); + + function = va_arg(args, command_function*); + if (function) { + name = va_arg(args, const char*); + description = va_arg(args, const char*); + } + } + + va_end(args); +} + + +Command* +CommandManager::FindCommand(const char* name) const +{ + CommandMap::const_iterator it = fCommands.find(name); + if (it == fCommands.end()) + return NULL; + + return it->second; +} + + +void +CommandManager::ListCommands() const +{ + for (CommandMap::const_iterator it = fCommands.begin(); + it != fCommands.end(); ++it) { + const char* name = it->first.c_str(); + Command* command = it->second; + printf("%-16s - %s\n", name, command->Description()); + } +} + + +CommandManager* CommandManager::sManager = NULL; + + +// #pragma mark - Commands + + +static int +command_cd(int argc, const char* const* argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: cd \n"); + return 1; + } + const char* directory = argv[1]; + + fssh_status_t error = _kern_setcwd(-1, directory); + if (error != FSSH_B_OK) { + fprintf(stderr, "Error: cd %s: %s\n", directory, fssh_strerror(error)); + return 1; + } + + return 0; +} + + +static int +command_help(int argc, const char* const* argv) +{ + printf("supported commands:\n"); + CommandManager::Default()->ListCommands(); + return 0; +} + + +static void +list_entry(const char* file, const char* name = NULL) +{ + // construct path, if a leaf name is given + std::string path; + if (name) { + path = file; + path += '/'; + path += name; + file = path.c_str(); + } else + name = file; + + // stat the file + struct fssh_stat st; + fssh_status_t error = _kern_read_stat(-1, file, false, &st, sizeof(st)); + if (error != FSSH_B_OK) { + fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", file, + fssh_strerror(error)); + return; + } + + // get time + struct tm time; + time_t fileTime = st.fssh_st_mtime; + localtime_r(&fileTime, &time); + + // get permissions + std::string permissions; + fssh_mode_t mode = st.fssh_st_mode; + // user + permissions += ((mode & FSSH_S_IRUSR) ? 'r' : '-'); + permissions += ((mode & FSSH_S_IWUSR) ? 'w' : '-'); + if (mode & FSSH_S_ISUID) + permissions += 's'; + else + permissions += ((mode & FSSH_S_IXUSR) ? 'x' : '-'); + // group + permissions += ((mode & FSSH_S_IRGRP) ? 'r' : '-'); + permissions += ((mode & FSSH_S_IWGRP) ? 'w' : '-'); + if (mode & FSSH_S_ISGID) + permissions += 's'; + else + permissions += ((mode & FSSH_S_IXGRP) ? 'x' : '-'); + // others + permissions += ((mode & FSSH_S_IROTH) ? 'r' : '-'); + permissions += ((mode & FSSH_S_IWOTH) ? 'w' : '-'); + permissions += ((mode & FSSH_S_IXOTH) ? 'x' : '-'); + + // get file type + char fileType = '?'; + if (FSSH_S_ISREG(mode)) { + fileType = '-'; + } else if (FSSH_S_ISLNK(mode)) { + fileType = 'l'; + } else if (FSSH_S_ISBLK(mode)) { + fileType = 'b'; + } else if (FSSH_S_ISDIR(mode)) { + fileType = 'd'; + } else if (FSSH_S_ISCHR(mode)) { + fileType = 'c'; + } else if (FSSH_S_ISFIFO(mode)) { + fileType = 'f'; + } else if (FSSH_S_ISINDEX(mode)) { + fileType = 'i'; + } + + // get link target + std::string nameSuffix; + if (FSSH_S_ISLNK(mode)) { + char buffer[FSSH_B_PATH_NAME_LENGTH]; + fssh_size_t size = sizeof(buffer); + error = _kern_read_link(-1, file, buffer, &size); + if (error == FSSH_B_OK) { + nameSuffix += " -> "; + nameSuffix += buffer; + } else { + fprintf(stderr, "Error: Failed to read symlink \"%s\": %s\n", file, + fssh_strerror(error)); + } + } + + printf("%c%s %2d %2d %10lld %d-%02d-%02d %02d:%02d:%02d %s%s\n", + fileType, permissions.c_str(), (int)st.fssh_st_uid, (int)st.fssh_st_gid, + st.fssh_st_size, + 1900 + time.tm_year, 1 + time.tm_mon, time.tm_mday, + time.tm_hour, time.tm_min, time.tm_sec, + name, nameSuffix.c_str()); +} + + +static int +command_ls(int argc, const char* const* argv) +{ + const char* const currentDirFiles[] = { ".", NULL }; + const char* const* files; + if (argc >= 2) + files = argv + 1; + else + files = currentDirFiles; + + for (; *files; files++) { + const char* file = *files; + // stat file + struct fssh_stat st; + fssh_status_t error = _kern_read_stat(-1, file, false, &st, sizeof(st)); + if (error != FSSH_B_OK) { + fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", file, + fssh_strerror(error)); + continue; + } + + // if it is a directory, print its entries + if (FSSH_S_ISDIR(st.fssh_st_mode)) { + printf("%s:\n", file); + + // open dir + int fd = _kern_open_dir(-1, file); + if (fd < 0) { + fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", + file, fssh_strerror(fd)); + continue; + } + + // iterate through the entries + char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; + fssh_dirent* entry = (fssh_dirent*)buffer; + fssh_ssize_t entriesRead = 0; + while ((entriesRead = _kern_read_dir(fd, entry, sizeof(buffer), 1)) + == 1) { + list_entry(file, entry->d_name); + } + + if (entriesRead < 0) { + fprintf(stderr, "Error: reading dir \"%s\" failed: %s\n", + file, fssh_strerror(entriesRead)); + } + + // close dir + error = _kern_close(fd); + if (error != FSSH_B_OK) { + fprintf(stderr, "Error: Closing dir \"%s\" (fd: %d) failed: " + "%s\n", file, fd, fssh_strerror(error)); + continue; + } + + } else + list_entry(file); + } + + return 0; +} + + +static int +command_quit(int argc, const char* const* argv) +{ + return COMMAND_RESULT_EXIT; +} + + +static void +register_commands() +{ + CommandManager::Default()->AddCommands( + command_cd, "cd", "change current directory", + command_ls, "ls", "list files or directories", + command_help, "help", "list supported commands", + command_quit, "quit/exit", "quit the shell", + NULL + ); +} + + +// #pragma mark - ArgVector + + +class ArgVector { +public: + ArgVector() + : fArgc(0), + fArgv(NULL) + { + } + + ~ArgVector() + { + _Cleanup(); + } + + int Argc() const + { + return fArgc; + } + + const char* const* Argv() const + { + return fArgv; + } + + bool Parse(const char* commandLine) + { + _Cleanup(); + + // init temporary arg/argv storage + std::string currentArg; + std::vector argVector; + + fCurrentArg = ¤tArg; + fCurrentArgStarted = false; + fArgVector = &argVector; + + for (; *commandLine; commandLine++) { + char c = *commandLine; + + // whitespace delimits args and is otherwise ignored + if (isspace(c)) { + _PushCurrentArg(); + continue; + } + + switch (c) { + case '\'': + // quoted string -- no quoting + while (*++commandLine != '\'') { + c = *commandLine; + if (c == '\0') { + fprintf(stderr, "Error: Unterminated quoted " + "string.\n"); + return false; + } + _PushCharacter(c); + } + break; + + case '"': + // quoted string -- some quoting + while (*++commandLine != '"') { + c = *commandLine; + if (c == '\0') { + fprintf(stderr, "Error: Unterminated quoted " + "string.\n"); + return false; + } + + if (c == '\\') { + c = *++commandLine; + if (c == '\0') { + fprintf(stderr, "Error: Unterminated quoted " + "string.\n"); + return false; + } + + // only '\' and '"' can be quoted, otherwise the + // the '\' is treated as a normal char + if (c != '\\' && c != '"') + _PushCharacter('\\'); + } + + _PushCharacter(c); + } + break; + + case '\\': + // quoted char + c = *++commandLine; + if (c == '\0') { + fprintf(stderr, "Error: Command line ends with " + "'\\'.\n"); + return false; + } + _PushCharacter(c); + break; + + default: + // normal char + _PushCharacter(c); + break; + } + } + + // commit last arg + _PushCurrentArg(); + + // build arg vector + fArgc = argVector.size(); + fArgv = new char*[fArgc + 1]; + for (int i = 0; i < fArgc; i++) { + int len = argVector[i].length(); + fArgv[i] = new char[len + 1]; + memcpy(fArgv[i], argVector[i].c_str(), len + 1); + } + fArgv[fArgc] = NULL; + + return true; + } + +private: + void _Cleanup() + { + if (fArgv) { + for (int i = 0; i < fArgc; i++) + delete[] fArgv[i]; + delete[] fArgv; + } + } + + void _PushCurrentArg() + { + if (fCurrentArgStarted) { + fArgVector->push_back(*fCurrentArg); + fCurrentArgStarted = false; + } + } + + void _PushCharacter(char c) + { + if (!fCurrentArgStarted) { + *fCurrentArg = ""; + fCurrentArgStarted = true; + } + + *fCurrentArg += c; + } + +private: + // temporaries + std::string* fCurrentArg; + bool fCurrentArgStarted; + std::vector* fArgVector; + + int fArgc; + char** fArgv; +}; + + +// #pragma mark - input loop + + +static char* +read_command_line(char* buffer, int bufferSize) +{ + // print prompt (including cwd, if available) + char directory[FSSH_B_PATH_NAME_LENGTH]; + if (_kern_getcwd(directory, sizeof(directory)) == FSSH_B_OK) + printf("fssh:%s> ", directory); + else + printf("fssh> "); + fflush(stdout); + + // read input line + return fgets(buffer, bufferSize, stdin); +} + + +static void +input_loop() +{ + static const int kInputBufferSize = 100 * 1024; + char* inputBuffer = new char[kInputBufferSize]; + + for (;;) { + // read command line + if (!read_command_line(inputBuffer, kInputBufferSize)) + break; + + // construct argv vector + ArgVector argVector; + if (!argVector.Parse(inputBuffer) || argVector.Argc() == 0) + continue; + int argc = argVector.Argc(); + const char* const* argv = argVector.Argv(); + + // find command + Command* command = CommandManager::Default()->FindCommand(argv[0]); + if (!command) { + fprintf(stderr, "Error: Invalid command \"%s\". Type \"help\" for " + "a list of supported commands\n", argv[0]); + continue; + } + + int result = command->Do(argc, argv); + if (result == COMMAND_RESULT_EXIT) + break; + } + + delete[] inputBuffer; +} + + +} // namespace FSShell + + +using namespace FSShell; + + int main(int argc, const char* const* argv) { @@ -92,23 +683,31 @@ main(int argc, const char* const* argv) // init kernel error = init_kernel(); if (error != FSSH_B_OK) { - fprintf(stderr, "initializing kernel failed: %s\n", strerror(error)); + fprintf(stderr, "initializing kernel failed: %s\n", + fssh_strerror(error)); return error; } else printf("kernel initialized successfully!\n"); // mount FS - fssh_dev_t fsDev = _kern_mount("/myfs", device, fsName, 0, NULL, 0); + fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0); if (fsDev < 0) { - fprintf(stderr, "mounting FS failed: %s\n", strerror(fsDev)); + fprintf(stderr, "mounting FS failed: %s\n", fssh_strerror(fsDev)); return 1; } else printf("mounted FS successfully!\n"); + // register commands + register_commands(); + + // process commands + input_loop(); + // unmount FS - error = _kern_unmount("/myfs", 0); + _kern_setcwd(-1, "/"); // avoid a "busy" vnode + error = _kern_unmount(kMountPoint, 0); if (error != FSSH_B_OK) { - fprintf(stderr, "unmounting FS failed: %s\n", strerror(error)); + fprintf(stderr, "unmounting FS failed: %s\n", fssh_strerror(error)); return error; } else printf("FS unmounted successfully!\n"); diff --git a/src/tools/fs_shell/fssh.h b/src/tools/fs_shell/fssh.h new file mode 100644 index 0000000000..0d7ea2e41b --- /dev/null +++ b/src/tools/fs_shell/fssh.h @@ -0,0 +1,77 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#ifndef _FSSH_FSSH_H +#define _FSSH_FSSH_H + + +#include + +#include +#include + + +typedef int command_function(int argc, const char* const* argv); + + +namespace FSShell { + + +enum { + COMMAND_RESULT_EXIT = -1, +}; + + +// Command +class Command { +public: + Command(const char* name, + const char* description); + Command(command_function* function, + const char* name, const char* description); + virtual ~Command(); + + const char* Name() const; + const char* Description() const; + + virtual int Do(int argc, const char* const* argv); + +private: + std::string fName; + std::string fDescription; + command_function* fFunction; +}; + + +// CommandManager +class CommandManager { +private: + CommandManager(); + +public: + static CommandManager* Default(); + + void AddCommand(Command* command); + void AddCommand(command_function* function, + const char* name, const char* description); + void AddCommands(command_function* function, + const char* name, const char* description, + ...); + Command* FindCommand(const char* name) const; + void ListCommands() const; + +private: + typedef std::map CommandMap; + + CommandMap fCommands; + + static CommandManager* sManager; +}; + + +} // namespace FSShell + + +#endif // _FSSH_FSSH_H