/*
* Copyright 2007-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "compatibility.h"
#include "fssh.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <vector>
#include "command_cp.h"
#include "driver_settings.h"
#include "external_commands.h"
#include "fd.h"
#include "fssh_dirent.h"
#include "fssh_errno.h"
#include "fssh_errors.h"
#include "fssh_fs_info.h"
#include "fssh_fcntl.h"
#include "fssh_module.h"
#include "fssh_node_monitor.h"
#include "fssh_stat.h"
#include "fssh_string.h"
#include "fssh_type_constants.h"
#include "module.h"
#include "partition_support.h"
#include "path_util.h"
#include "syscalls.h"
#include "vfs.h"
extern fssh_module_info *modules[];
extern fssh_file_system_module_info gRootFileSystem;
namespace FSShell {
const char* kMountPoint = "/myfs";
// command line args
static int sArgc;
static const char* const* sArgv;
static mode_t sUmask = 0022;
static fssh_status_t
init_kernel()
{
fssh_status_t error;
// init module subsystem
error = module_init(NULL);
if (error != FSSH_B_OK) {
fprintf(stderr, "module_init() failed: %s\n", fssh_strerror(error));
return error;
}
// init driver settings
error = driver_settings_init();
if (error != FSSH_B_OK) {
fprintf(stderr, "initializing driver settings failed: %s\n",
fssh_strerror(error));
return error;
}
// register built-in modules, i.e. the rootfs and the client FS
register_builtin_module(&gRootFileSystem.info);
for (int i = 0; modules[i]; i++)
register_builtin_module(modules[i]);
// init VFS
error = vfs_init(NULL);
if (error != FSSH_B_OK) {
fprintf(stderr, "initializing VFS failed: %s\n", fssh_strerror(error));
return error;
}
// 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", fssh_strerror(rootDev));
return rootDev;
}
// set cwd to "/"
error = _kern_setcwd(-1, "/");
if (error != FSSH_B_OK) {
fprintf(stderr, "setting cwd failed: %s\n", fssh_strerror(error));
return error;
}
// 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;
}
return FSSH_B_OK;
}
// #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();
}
fssh_status_t
Command::Do(int argc, const char* const* argv)
{
if (!fFunction) {
fprintf(stderr, "No function given for command \"%s\"\n", Name());
return FSSH_B_BAD_VALUE;
}
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 - Command support functions
static bool
get_permissions(const char* modeString, fssh_mode_t& _permissions)
{
// currently only octal mode is supported
if (strlen(modeString) != 3)
return false;
fssh_mode_t permissions = 0;
for (int i = 0; i < 3; i++) {
char c = modeString[i];
if (c < '0' || c > '7')
return false;
permissions = (permissions << 3) | (c - '0');
}
_permissions = permissions;
return true;
}
static fssh_dev_t
get_volume_id()
{
struct fssh_stat st;
fssh_status_t error = _kern_read_stat(-1, kMountPoint, false, &st,
sizeof(st));
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Failed to stat() mount point: %s\n",
fssh_strerror(error));
return error;
}
return st.fssh_st_dev;
}
static const char *
byte_string(int64_t numBlocks, int64_t blockSize)
{
double blocks = 1. * numBlocks * blockSize;
static char string[64];
if (blocks < 1024)
sprintf(string, "%" FSSH_B_PRId64, numBlocks * blockSize);
else {
const char* units[] = {"K", "M", "G", NULL};
int i = -1;
do {
blocks /= 1024.0;
i++;
} while (blocks >= 1024 && units[i + 1]);
sprintf(string, "%.1f%s", blocks, units[i]);
}
return string;
}
void
print_flag(uint32_t deviceFlags, uint32_t testFlag, const char *yes,
const char *no)
{
printf("%s", (deviceFlags & testFlag) != 0 ? yes : no);
}
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) - 1;
error = _kern_read_link(-1, file, buffer, &size);
if (error != FSSH_B_OK)
snprintf(buffer, sizeof(buffer), "(%s)", fssh_strerror(error));
buffer[size] = '\0';
nameSuffix += " -> ";
nameSuffix += buffer;
}
printf("%c%s %2d %2d %10" FSSH_B_PRIdOFF
" %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 fssh_status_t
create_dir(const char *path, bool createParents)
{
// stat the entry
struct fssh_stat st;
fssh_status_t error = _kern_read_stat(-1, path, false, &st, sizeof(st));
if (error == FSSH_B_OK) {
if (createParents && FSSH_S_ISDIR(st.fssh_st_mode))
return FSSH_B_OK;
fprintf(stderr, "Error: Cannot make dir, entry \"%s\" is in the way.\n",
path);
return FSSH_B_FILE_EXISTS;
}
// the dir doesn't exist yet
// if we shall create all parents, do that first
if (createParents) {
// create the parent dir path
// eat the trailing '/'s
int len = strlen(path);
while (len > 0 && path[len - 1] == '/')
len--;
// eat the last path component
while (len > 0 && path[len - 1] != '/')
len--;
// eat the trailing '/'s
while (len > 0 && path[len - 1] == '/')
len--;
// Now either nothing remains, which means we had a single component,
// a root subdir -- in those cases we can just fall through (we should
// actually never be here in case of the root dir, but anyway) -- or
// there is something left, which we can call a parent directory and
// try to create it.
if (len > 0) {
char *parentPath = (char*)malloc(len + 1);
if (!parentPath) {
fprintf(stderr, "Error: Failed to allocate memory for parent "
"path.\n");
return FSSH_B_NO_MEMORY;
}
memcpy(parentPath, path, len);
parentPath[len] = '\0';
error = create_dir(parentPath, createParents);
free(parentPath);
if (error != FSSH_B_OK)
return error;
}
}
// make the directory
error = _kern_create_dir(-1,
path, (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask);
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Failed to make directory \"%s\": %s\n", path,
fssh_strerror(error));
return error;
}
return FSSH_B_OK;
}
static fssh_status_t remove_entry(int dir, const char *entry, bool recursive,
bool force);
static fssh_status_t
remove_dir_contents(int parentDir, const char *name, bool force)
{
// open the dir
int dir = _kern_open_dir(parentDir, name);
if (dir < 0) {
fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", name,
fssh_strerror(dir));
return dir;
}
fssh_status_t error = FSSH_B_OK;
// iterate through the entries
fssh_ssize_t numRead;
char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
fssh_dirent *entry = (fssh_dirent*)buffer;
while ((numRead = _kern_read_dir(dir, entry, sizeof(buffer), 1)) > 0) {
// skip "." and ".."
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
error = remove_entry(dir, entry->d_name, true, force);
if (error != FSSH_B_OK)
break;
}
if (numRead < 0) {
fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n", name,
fssh_strerror(numRead));
error = numRead;
}
// close
_kern_close(dir);
return error;
}
static fssh_status_t
remove_entry(int dir, const char *entry, bool recursive, bool force)
{
// stat the file
struct fssh_stat st;
fssh_status_t error = _kern_read_stat(dir, entry, false, &st, sizeof(st));
if (error != FSSH_B_OK) {
if (force && error == FSSH_B_ENTRY_NOT_FOUND)
return FSSH_B_OK;
fprintf(stderr, "Error: Failed to remove \"%s\": %s\n", entry,
fssh_strerror(error));
return error;
}
if (FSSH_S_ISDIR(st.fssh_st_mode)) {
if (!recursive) {
fprintf(stderr, "Error: \"%s\" is a directory.\n", entry);
// TODO: get the full path
return FSSH_EISDIR;
}
// remove the contents
error = remove_dir_contents(dir, entry, force);
if (error != FSSH_B_OK)
return error;
// remove the directory
error = _kern_remove_dir(dir, entry);
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Failed to remove directory \"%s\": %s\n",
entry, fssh_strerror(error));
return error;
}
} else {
// remove the entry
error = _kern_unlink(dir, entry);
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Failed to remove entry \"%s\": %s\n", entry,
fssh_strerror(error));
return error;
}
}
return FSSH_B_OK;
}
static fssh_status_t
move_entry(int dir, const char *entry, int targetDir, const char* target,
bool force)
{
// stat the file
struct fssh_stat st;
fssh_status_t status = _kern_read_stat(dir, entry, false, &st, sizeof(st));
if (status != FSSH_B_OK) {
if (force && status == FSSH_B_ENTRY_NOT_FOUND)
return FSSH_B_OK;
fprintf(stderr, "Error: Failed to move \"%s\": %s\n", entry,
fssh_strerror(status));
return status;
}
return _kern_rename(dir, entry, targetDir, target);
}
// #pragma mark - Commands
static fssh_status_t
command_cd(int argc, const char* const* argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s <directory>\n", argv[0]);
return FSSH_B_BAD_VALUE;
}
const char* directory = argv[1];
fssh_status_t error = FSSH_B_OK;
if (directory[0] == ':') {
if (chdir(directory + 1) < 0)
error = fssh_get_errno();
} else
error = _kern_setcwd(-1, directory);
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: cd %s: %s\n", directory, fssh_strerror(error));
return error;
}
return FSSH_B_OK;
}
static fssh_status_t
command_chmod(int argc, const char* const* argv)
{
bool recursive = false;
// parse parameters
int argi = 1;
for (argi = 1; argi < argc; argi++) {
const char *arg = argv[argi];
if (arg[0] != '-')
break;
if (arg[1] == '\0') {
fprintf(stderr, "Error: Invalid option \"-\"\n");
return FSSH_B_BAD_VALUE;
}
for (int i = 1; arg[i]; i++) {
switch (arg[i]) {
case 'R':
recursive = true;
fprintf(stderr, "Sorry, recursive mode not supported "
"yet.\n");
return FSSH_B_BAD_VALUE;
default:
fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
return FSSH_B_BAD_VALUE;
}
}
}
// get mode
fssh_mode_t permissions;
if (argi + 1 >= argc || !get_permissions(argv[argi++], permissions)) {
printf("Usage: %s [ -R ] <octal mode> <file>...\n", argv[0]);
return FSSH_B_BAD_VALUE;
}
fssh_struct_stat st;
st.fssh_st_mode = permissions;
// chmod loop
for (; argi < argc; argi++) {
const char *file = argv[argi];
if (strlen(file) == 0) {
fprintf(stderr, "Error: An empty path is not a valid argument!\n");
return FSSH_B_BAD_VALUE;
}
fssh_status_t error = _kern_write_stat(-1, file, false, &st, sizeof(st),
FSSH_B_STAT_MODE);
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Failed to change mode of \"%s\"!\n", file);
return error;
}
}
return FSSH_B_OK;
}
static fssh_status_t
command_cat(int argc, const char* const* argv)
{
size_t numBytes = 10;
int fileStart = 1;
if (argc < 2 || strcmp(argv[1], "--help") == 0) {
printf("Usage: %s [ -n ] [FILE]...\n"
"\t -n\tNumber of bytes to read\n",
argv[0]);
return FSSH_B_OK;
}
if (argc > 3 && strcmp(argv[1], "-n") == 0) {
fileStart += 2;
numBytes = strtol(argv[2], NULL, 10);
}
const char* const* files = argv + fileStart;
for (; *files; files++) {
const char* file = *files;
int fd = _kern_open(-1, file, FSSH_O_RDONLY, FSSH_O_RDONLY);
if (fd < 0) {
fprintf(stderr, "error: %s\n", fssh_strerror(fd));
return FSSH_B_BAD_VALUE;
}
char buffer[numBytes + 1];
if (buffer == NULL) {
fprintf(stderr, "error: No memory\n");
_kern_close(fd);
return FSSH_B_NO_MEMORY;
}
if (_kern_read(fd, 0, buffer, numBytes) != (ssize_t)numBytes) {
fprintf(stderr, "error reading: %s\n", fssh_strerror(fd));
_kern_close(fd);
return FSSH_B_BAD_VALUE;
}
_kern_close(fd);
buffer[numBytes] = '\0';
printf("%s\n", buffer);
}
return FSSH_B_OK;
}
static fssh_status_t
command_help(int argc, const char* const* argv)
{
printf("supported commands:\n");
CommandManager::Default()->ListCommands();
return FSSH_B_OK;
}
static fssh_status_t
command_info(int argc, const char* const* argv)
{
fssh_dev_t volumeID = get_volume_id();
if (volumeID < 0)
return volumeID;
fssh_fs_info info;
fssh_status_t status = _kern_read_fs_info(volumeID, &info);
if (status != FSSH_B_OK)
return status;
printf("root inode: %" FSSH_B_PRIdINO "\n", info.root);
printf("flags: ");
print_flag(info.flags, FSSH_B_FS_HAS_QUERY, "Q", "-");
print_flag(info.flags, FSSH_B_FS_HAS_ATTR, "A", "-");
print_flag(info.flags, FSSH_B_FS_HAS_MIME, "M", "-");
print_flag(info.flags, FSSH_B_FS_IS_SHARED, "S", "-");
print_flag(info.flags, FSSH_B_FS_IS_PERSISTENT, "P", "-");
print_flag(info.flags, FSSH_B_FS_IS_REMOVABLE, "R", "-");
print_flag(info.flags, FSSH_B_FS_IS_READONLY, "-", "W");
printf("\nblock size: %" FSSH_B_PRIdOFF "\n", info.block_size);
printf("I/O size: %" FSSH_B_PRIdOFF "\n", info.io_size);
printf("total size: %s (%" FSSH_B_PRIdOFF " blocks)\n",
byte_string(info.total_blocks, info.block_size), info.total_blocks);
printf("free size: %s (%" FSSH_B_PRIdOFF " blocks)\n",
byte_string(info.free_blocks, info.block_size), info.free_blocks);
printf("total nodes: %" FSSH_B_PRIdOFF "\n", info.total_nodes);
printf("free nodes: %" FSSH_B_PRIdOFF "\n", info.free_nodes);
printf("volume name: %s\n", info.volume_name);
printf("fs name: %s\n", info.fsh_name);
return FSSH_B_OK;
}
static fssh_status_t
command_ln(int argc, const char* const* argv)
{
bool force = false;
bool symbolic = false;
bool dereference = true;
// parse parameters
int argi = 1;
for (argi = 1; argi < argc; argi++) {
const char *arg = argv[argi];
if (arg[0] != '-')
break;
if (arg[1] == '\0') {
fprintf(stderr, "Error: Invalid option \"-\"\n");
return FSSH_B_BAD_VALUE;
}
for (int i = 1; arg[i]; i++) {
switch (arg[i]) {
case 'f':
force = true;
break;
case 's':
symbolic = true;
break;
case 'n':
dereference = false;
break;
default:
fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
return FSSH_B_BAD_VALUE;
}
}
}
if (argc - argi != 2) {
fprintf(stderr, "Usage: %s [Options] <source> <target>\n", argv[0]);
return FSSH_B_BAD_VALUE;
}
const char *source = argv[argi];
const char *target = argv[argi + 1];
// check, if the the target is an existing directory
struct fssh_stat st;
char targetBuffer[FSSH_B_PATH_NAME_LENGTH];
fssh_status_t error = _kern_read_stat(-1, target, dereference, &st,
sizeof(st));
if (error == FSSH_B_OK) {
if (FSSH_S_ISDIR(st.fssh_st_mode)) {
// get source leaf
char leaf[FSSH_B_FILE_NAME_LENGTH];
error = get_last_path_component(source, leaf, sizeof(leaf));
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Failed to get leaf name of source "
"path: %s\n", fssh_strerror(error));
return error;
}
// compose a new path
int len = strlen(target) + 1 + strlen(leaf);
if (len > (int)sizeof(targetBuffer)) {
fprintf(stderr, "Error: Resulting target path is too long.\n");
return FSSH_B_BAD_VALUE;
}
strcpy(targetBuffer, target);
strcat(targetBuffer, "/");
strcat(targetBuffer, leaf);
target = targetBuffer;
}
}
// check, if the target exists
error = _kern_read_stat(-1, target, false, &st, sizeof(st));
if (error == FSSH_B_OK) {
if (!force) {
fprintf(stderr, "Error: Can't create link. \"%s\" is in the way.\n",
target);
return FSSH_B_FILE_EXISTS;
}
// unlink the entry
error = _kern_unlink(-1, target);
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Failed to remove \"%s\" to make way for "
"link: %s\n", target, fssh_strerror(error));
return error;
}
}
// finally create the link
if (symbolic) {
error = _kern_create_symlink(-1, target, source,
FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO);
} else
error = _kern_create_link(target, source);
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Failed to create link: %s\n",
fssh_strerror(error));
}
return error;
}
static fssh_status_t
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 FSSH_B_OK;
}
static fssh_status_t
command_mkdir(int argc, const char* const* argv)
{
bool createParents = false;
// parse parameters
int argi = 1;
for (argi = 1; argi < argc; argi++) {
const char *arg = argv[argi];
if (arg[0] != '-')
break;
if (arg[1] == '\0') {
fprintf(stderr, "Error: Invalid option \"-\"\n");
return FSSH_B_BAD_VALUE;
}
for (int i = 1; arg[i]; i++) {
switch (arg[i]) {
case 'p':
createParents = true;
break;
default:
fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
return FSSH_B_BAD_VALUE;
}
}
}
if (argi >= argc) {
printf("Usage: %s [ -p ] <dir>...\n", argv[0]);
return FSSH_B_BAD_VALUE;
}
// create loop
for (; argi < argc; argi++) {
const char *dir = argv[argi];
if (strlen(dir) == 0) {
fprintf(stderr, "Error: An empty path is not a valid argument!\n");
return FSSH_B_BAD_VALUE;
}
fssh_status_t error = create_dir(dir, createParents);
if (error != FSSH_B_OK)
return error;
}
return FSSH_B_OK;
}
static fssh_status_t
command_mkindex(int argc, const char* const* argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s <index name>\n", argv[0]);
return FSSH_B_BAD_VALUE;
}
const char* indexName = argv[1];
// get the volume ID
fssh_dev_t volumeID = get_volume_id();
if (volumeID < 0)
return volumeID;
// create the index
fssh_status_t error =_kern_create_index(volumeID, indexName,
FSSH_B_STRING_TYPE, 0);
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Failed to create index \"%s\": %s\n",
indexName, fssh_strerror(error));
return error;
}
return FSSH_B_OK;
}
static fssh_status_t
command_mv(int argc, const char* const* argv)
{
bool force = false;
// parse parameters
int argi = 1;
for (argi = 1; argi < argc; argi++) {
const char *arg = argv[argi];
if (arg[0] != '-')
break;
if (arg[1] == '\0') {
fprintf(stderr, "Error: Invalid option \"-\"\n");
return FSSH_B_BAD_VALUE;
}
for (int i = 1; arg[i]; i++) {
switch (arg[i]) {
case 'f':
force = true;
break;
default:
fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
return FSSH_B_BAD_VALUE;
}
}
}
// check params
int count = argc - 1 - argi;
if (count <= 0) {
fprintf(stderr, "Usage: %s [-f] <file>... <target>\n", argv[0]);
return FSSH_B_BAD_VALUE;
}
const char* target = argv[argc - 1];
// stat the target
struct fssh_stat st;
fssh_status_t status = _kern_read_stat(-1, target, true, &st, sizeof(st));
if (status != FSSH_B_OK && count != 1) {
fprintf(stderr, "Error: Failed to stat target \"%s\": %s\n", target,
fssh_strerror(status));
return status;
}
if (status == FSSH_B_OK && FSSH_S_ISDIR(st.fssh_st_mode)) {
// move several entries
int targetDir = _kern_open_dir(-1, target);
if (targetDir < 0) {
fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", target,
fssh_strerror(targetDir));
return targetDir;
}
// move loop
for (; argi < argc - 1; argi++) {
status = move_entry(-1, argv[argi], targetDir, argv[argi], force);
if (status != FSSH_B_OK) {
_kern_close(targetDir);
return status;
}
}
_kern_close(targetDir);
return FSSH_B_OK;
}
// rename single entry
return move_entry(-1, argv[argi], -1, target, force);
}
static fssh_status_t
command_query(int argc, const char* const* argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s <query string>\n", argv[0]);
return FSSH_B_BAD_VALUE;
}
const char* query = argv[1];
// get the volume ID
fssh_dev_t volumeID = get_volume_id();
if (volumeID < 0)
return volumeID;
// open query
int fd = _kern_open_query(volumeID, query, strlen(query), 0, -1, -1);
if (fd < 0) {
fprintf(stderr, "Error: Failed to open query: %s\n", fssh_strerror(fd));
return fd;
}
// iterate through the entries
fssh_status_t error = FSSH_B_OK;
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) {
char path[FSSH_B_PATH_NAME_LENGTH];
error = _kern_entry_ref_to_path(volumeID, entry->d_pino, entry->d_name,
path, sizeof(path));
if (error == FSSH_B_OK) {
printf(" %s\n", path);
} else {
fprintf(stderr, " failed to resolve entry (%8" FSSH_B_PRIdINO
", \"%s\")\n", entry->d_pino, entry->d_name);
}
}
if (entriesRead < 0) {
fprintf(stderr, "Error: reading query failed: %s\n",
fssh_strerror(entriesRead));
}
// close query
error = _kern_close(fd);
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Closing query (fd: %d) failed: %s\n",
fd, fssh_strerror(error));
}
return error;
}
static fssh_status_t
command_quit(int argc, const char* const* argv)
{
return COMMAND_RESULT_EXIT;
}
static fssh_status_t
command_rm(int argc, const char* const* argv)
{
bool recursive = false;
bool force = false;
// parse parameters
int argi = 1;
for (argi = 1; argi < argc; argi++) {
const char *arg = argv[argi];
if (arg[0] != '-')
break;
if (arg[1] == '\0') {
fprintf(stderr, "Error: Invalid option \"-\"\n");
return FSSH_B_BAD_VALUE;
}
for (int i = 1; arg[i]; i++) {
switch (arg[i]) {
case 'f':
force = true;
break;
case 'r':
recursive = true;
break;
default:
fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
return FSSH_B_BAD_VALUE;
}
}
}
// check params
if (argi >= argc) {
fprintf(stderr, "Usage: %s [ -r ] <file>...\n", argv[0]);
return FSSH_B_BAD_VALUE;
}
// remove loop
for (; argi < argc; argi++) {
fssh_status_t error = remove_entry(-1, argv[argi], recursive, force);
if (error != FSSH_B_OK)
return error;
}
return FSSH_B_OK;
}
static fssh_status_t
command_sync(int argc, const char* const* argv)
{
fssh_status_t error = _kern_sync();
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: syncing: %s\n", fssh_strerror(error));
return error;
}
return FSSH_B_OK;
}
static fssh_status_t
command_ioctl(int argc, const char* const* argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s <opcode>\n", argv[0]);
return FSSH_B_BAD_VALUE;
}
int rootDir = _kern_open_dir(-1, "/myfs");
if (rootDir < 0)
return rootDir;
fssh_status_t status = _kern_ioctl(rootDir, atoi(argv[1]), NULL, 0);
_kern_close(rootDir);
if (status != FSSH_B_OK) {
fprintf(stderr, "Error: ioctl failed: %s\n", fssh_strerror(status));
return status;
}
return FSSH_B_OK;
}
static void
register_commands()
{
CommandManager::Default()->AddCommands(
command_cd, "cd", "change current directory",
command_chmod, "chmod", "change file permissions",
command_cp, "cp", "copy files and directories",
command_cat, "cat", "concatenate file(s) to stdout",
command_help, "help", "list supported commands",
command_info, "info", "prints volume informations",
command_ioctl, "ioctl", "ioctl() on root, for FS debugging only",
command_ln, "ln", "create a hard or symbolic link",
command_ls, "ls", "list files or directories",
command_mkdir, "mkdir", "create directories",
command_mkindex, "mkindex", "create an index",
command_mv, "mv", "move/rename files and directories",
command_query, "query", "query for files",
command_quit, "quit/exit", "quit the shell",
command_rm, "rm", "remove files and directories",
command_sync, "sync", "syncs the file system",
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<std::string> 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<std::string>* 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(bool interactive)
{
static const int kInputBufferSize = 100 * 1024;
char* inputBuffer = new char[kInputBufferSize];
for (;;) {
// read command line
if (interactive) {
if (!read_command_line(inputBuffer, kInputBufferSize))
break;
} else {
if (!get_external_command(inputBuffer, kInputBufferSize))
break;
}
// construct argv vector
int result = FSSH_B_BAD_VALUE;
ArgVector argVector;
if (argVector.Parse(inputBuffer) && argVector.Argc() > 0) {
int argc = argVector.Argc();
const char* const* argv = argVector.Argv();
// find command
Command* command = CommandManager::Default()->FindCommand(argv[0]);
if (command) {
// execute it
result = command->Do(argc, argv);
if (result == COMMAND_RESULT_EXIT) {
if (!interactive)
reply_to_external_command(0);
break;
}
} else {
fprintf(stderr, "Error: Invalid command \"%s\". Type \"help\" "
"for a list of supported commands\n", argv[0]);
}
}
if (!interactive)
reply_to_external_command(fssh_to_host_error(result));
}
if (!interactive)
external_command_cleanup();
delete[] inputBuffer;
}
static int
standard_session(const char* device, const char* fsName, bool interactive)
{
// mount FS
fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0);
if (fsDev < 0) {
fprintf(stderr, "Error: Mounting FS failed: %s\n",
fssh_strerror(fsDev));
return 1;
}
// register commands
register_commands();
register_additional_commands();
// process commands
input_loop(interactive);
// unmount FS
_kern_setcwd(-1, "/"); // avoid a "busy" vnode
fssh_status_t error = _kern_unmount(kMountPoint, 0);
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Unmounting FS failed: %s\n",
fssh_strerror(error));
return 1;
}
return 0;
}
static int
initialization_session(const char* device, const char* fsName,
const char* volumeName, const char* initParameters)
{
fssh_status_t error = _kern_initialize_volume(fsName, device,
volumeName, initParameters);
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Initializing volume failed: %s\n",
fssh_strerror(error));
return 1;
}
return 0;
}
static void
print_usage(bool error)
{
fprintf((error ? stderr : stdout),
"Usage: %s [ --start-offset <startOffset>]\n"
" [ --end-offset <endOffset>] [-n] <device>\n"
" %s [ --start-offset <startOffset>]\n"
" [ --end-offset <endOffset>]\n"
" --initialize [-n] <device> <volume name> "
"[ <init parameters> ]\n",
sArgv[0], sArgv[0]
);
}
static void
print_usage_and_exit(bool error)
{
print_usage(error);
exit(error ? 1 : 0);
}
} // namespace FSShell
using namespace FSShell;
int
main(int argc, const char* const* argv)
{
sArgc = argc;
sArgv = argv;
// process arguments
bool interactive = true;
bool initialize = false;
const char* device = NULL;
const char* volumeName = NULL;
const char* initParameters = NULL;
fssh_off_t startOffset = 0;
fssh_off_t endOffset = -1;
// eat options
int argi = 1;
while (argi < argc && argv[argi][0] == '-') {
const char* arg = argv[argi++];
if (strcmp(arg, "--help") == 0) {
print_usage_and_exit(false);
} else if (strcmp(arg, "--initialize") == 0) {
initialize = true;
} else if (strcmp(arg, "-n") == 0) {
interactive = false;
} else if (strcmp(arg, "--start-offset") == 0) {
if (argi >= argc)
print_usage_and_exit(true);
startOffset = atoll(argv[argi++]);
} else if (strcmp(arg, "--end-offset") == 0) {
if (argi >= argc)
print_usage_and_exit(true);
endOffset = atoll(argv[argi++]);
} else {
print_usage_and_exit(true);
}
}
// get device
if (argi >= argc)
print_usage_and_exit(true);
device = argv[argi++];
// get volume name and init parameters
if (initialize) {
// volume name
if (argi >= argc)
print_usage_and_exit(true);
volumeName = argv[argi++];
// (optional) init paramaters
if (argi < argc)
initParameters = argv[argi++];
}
// more parameters are excess
if (argi < argc)
print_usage_and_exit(true);
// get FS module
if (!modules[0]) {
fprintf(stderr, "Error: Couldn't find FS module!\n");
return 1;
}
const char* fsName = modules[0]->name;
fssh_status_t error;
// init kernel
error = init_kernel();
if (error != FSSH_B_OK) {
fprintf(stderr, "Error: Initializing kernel failed: %s\n",
fssh_strerror(error));
return error;
}
// restrict access if requested
if (startOffset != 0 || endOffset != -1)
add_file_restriction(device, startOffset, endOffset);
// start the action
int result;
if (initialize) {
result = initialization_session(device, fsName, volumeName,
initParameters);
} else
result = standard_session(device, fsName, interactive);
return result;
}
↑ V576 Incorrect format. Consider checking the second actual argument of the 'printf' function. The memsize type argument is expected.
↑ V576 Incorrect format. Consider checking the third actual argument of the 'printf' function. The memsize type argument is expected.
↑ V576 Incorrect format. Consider checking the second actual argument of the 'printf' function. The memsize type argument is expected.
↑ V576 Incorrect format. Consider checking the sixth actual argument of the 'printf' function. The memsize type argument is expected.
↑ V576 Incorrect format. Consider checking the second actual argument of the 'printf' function. The memsize type argument is expected.
↑ V576 Incorrect format. Consider checking the third actual argument of the 'printf' function. The memsize type argument is expected.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fFunction.
↑ V576 Incorrect format. Consider checking the second actual argument of the 'printf' function. The memsize type argument is expected.
↑ V576 Incorrect format. Consider checking the third actual argument of the 'fprintf' function. The memsize type argument is expected.
↑ V576 Incorrect format. Consider checking the second actual argument of the 'printf' function. The memsize type argument is expected.