Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,430 changes: 1,430 additions & 0 deletions compile_commands.json

Large diffs are not rendered by default.

20 changes: 0 additions & 20 deletions include/system/dev/dev.h

This file was deleted.

File renamed without changes.
24 changes: 24 additions & 0 deletions include/system/vfs/file_descriptor.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <compare>

namespace zest::fs {
class FileDescriptor {
public:
constexpr FileDescriptor(int fd)
: m_fd(fd) {}

explicit operator int() const {
return m_fd;
}

std::strong_ordering operator<=>(const FileDescriptor&) const = default;

private:
int m_fd;
};

inline constexpr FileDescriptor FD_STDIN{0};
inline constexpr FileDescriptor FD_STDOUT{1};
inline constexpr FileDescriptor FD_STDERR{2};
} // namespace zest::fs
33 changes: 33 additions & 0 deletions include/system/vfs/file_driver.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include "system/vfs/file_descriptor.hpp"

#include <any>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <expected>
#include <memory>
#include <span>
#include <sys/stat.h>
#include <system_error>
#include <unistd.h>

namespace zest::fs {
class FileDriver : public std::enable_shared_from_this<FileDriver> {
public:
virtual void init() = 0;
virtual std::expected<FileDescriptor, std::error_condition>
open(const char* path, int flags, int mode) = 0;
virtual std::expected<ssize_t, std::error_condition> read(std::any, std::span<std::byte>) = 0;
virtual std::expected<int, std::error_condition> write(std::any, std::span<std::byte>) = 0;
virtual std::expected<void, std::error_condition> close(std::any) = 0;
virtual std::expected<void, std::error_condition> fstat(std::any, struct stat*) = 0;
virtual bool isatty(std::any) = 0;
virtual std::expected<off_t, std::error_condition> lseek(std::any, off_t, int) = 0;
virtual int ctl(std::any, const uint32_t, void* const) = 0;
FileDescriptor add_vfs_entry(std::any data);
int32_t update_vfs_entry(FileDescriptor fd, std::any data);
virtual ~FileDriver() = default;
};
} // namespace zest::fs
20 changes: 20 additions & 0 deletions include/system/vfs/file_flags.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <sys/_default_fcntl.h>

namespace zest::fs {
struct FileFlags {
FileFlags(int flags)
: read(flags & O_RDONLY),
write(flags & O_WRONLY || flags & O_CREAT),
append(flags & O_APPEND),
truncate(flags & O_TRUNC),
create_new(flags & O_CREAT && flags & O_EXCL) {}

bool read : 1;
bool write : 1;
bool append : 1;
bool truncate : 1;
bool create_new : 1;
};
} // namespace zest::fs
File renamed without changes.
File renamed without changes.
40 changes: 40 additions & 0 deletions include/system/vfs/usd_driver.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#pragma once

#include "pros/rtos.hpp"
#include "system/vfs/file_driver.hpp"
#include "v5_apitypes_patched.h"

#include <mutex>

namespace zest::fs {
class UsdDriver : public FileDriver {
public:
void init() override {}

std::expected<FileDescriptor, std::error_condition>
open(const char* path, int flags, int mode) override;
std::expected<ssize_t, std::error_condition> read(std::any, std::span<std::byte>) override;
std::expected<int, std::error_condition> write(std::any, std::span<std::byte>) override;
std::expected<void, std::error_condition> close(std::any) override;
std::expected<void, std::error_condition> fstat(std::any, struct stat*) override;
bool isatty(std::any) override;
std::expected<off_t, std::error_condition> lseek(std::any, off_t, int) override;
int ctl(std::any, const uint32_t, void* const) override;
~UsdDriver() override = default;

private:
struct File {
FIL* fd;
bool is_writable;
};

template<typename F, typename... Args>
auto inline with_lock(F&& fun, Args... args) {
std::lock_guard lock{this->mut};
return fun(args...);
}

pros::RecursiveMutex mut{};
};

} // namespace zest::fs
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@
#include <errno.h>
#include <sys/stat.h>

int chdir(const char* path) {
errno = ENOSYS;
return -1;
}


int mkdir(const char* pathname, mode_t mode) {
errno = ENOSYS;
Expand All @@ -47,11 +44,6 @@ long pathconf(const char* path, int name) {
return -1;
}

char* getcwd(char* buf, size_t size) {
errno = ENOSYS;
return NULL;
}

int _unlink(const char* name) {
errno = ENOSYS;
return -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

#include "kapi.h"
#include "system/dev/banners.h"
#include "system/vfs/banners.h"
#include "v5_api_patched.h"

#define MAX_COMMAND_LENGTH 32
Expand Down
4 changes: 2 additions & 2 deletions src/system/dev/ser_driver.c → src/system/vfs/ser_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
#include "common/cobs.h"
#include "common/set.h"
#include "kapi.h"
#include "system/dev/ser.h"
#include "system/dev/vfs.h"
#include "system/vfs/ser.h"
#include "system/vfs/vfs.h"
#include "v5_api_patched.h"

#include <string.h>
Expand Down
4 changes: 2 additions & 2 deletions src/system/dev/usd_driver.c → src/system/vfs/usd_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

// the pragma below is needed due to a FreeRTOS oversight
#include "kapi.h" // IWYU pragma: keep
#include "system/dev/usd.h"
#include "system/dev/vfs.h"
#include "system/vfs/usd.h"
#include "system/vfs/vfs.h"
#include "v5_api_patched.h"

#include <fcntl.h>
Expand Down
180 changes: 180 additions & 0 deletions src/system/vfs/usd_driver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#include "system/vfs/usd_driver.hpp"

#include "system/vfs/file_flags.hpp"
#include "v5_api_patched.h"
#include "v5_apitypes_patched.h"

#include <any>
#include <expected>

static std::error_condition map_fresult(FRESULT result) {
// See http://elm-chan.org/fsw/ff/doc/rc.html for a description of FRESULT codes
switch (result) {
case FR_OK:
return {};
case FR_DISK_ERR:
return std::errc::state_not_recoverable;
case FR_INT_ERR:
return std::errc::state_not_recoverable;
case FR_NOT_READY:
return std::errc::device_or_resource_busy;
case FR_NO_FILE:
return std::errc::no_such_file_or_directory;
case FR_NO_PATH:
return std::errc::no_such_file_or_directory;
case FR_INVALID_NAME:
return std::errc::invalid_argument;
case FR_DENIED:
return std::errc::permission_denied;
case FR_EXIST:
return std::errc::file_exists;
case FR_INVALID_OBJECT:
return std::errc::io_error;
case FR_WRITE_PROTECTED:
return std::errc::read_only_file_system;
case FR_INVALID_DRIVE:
return std::errc::no_such_device_or_address;
case FR_NOT_ENABLED:
return std::errc::io_error;
case FR_NO_FILESYSTEM:
return std::errc::no_such_device_or_address;
case FR_MKFS_ABORTED:
return std::errc::io_error;
case FR_TIMEOUT:
return std::errc::io_error;
case FR_LOCKED:
return std::errc::permission_denied;
case FR_NOT_ENOUGH_CORE:
return std::errc::not_enough_memory;
case FR_TOO_MANY_OPEN_FILES:
return std::errc::too_many_files_open_in_system;
case FR_INVALID_PARAMETER:
return std::errc::invalid_argument;
default:
__builtin_abort(); // TODO: log error
}
}

std::expected<zest::fs::FileDescriptor, std::error_condition>
zest::fs::UsdDriver::open(const char* path, int _flags, int mode [[maybe_unused]]) {
FRESULT result = this->with_lock(vexFileMountSD);
if (result != FR_OK) {
return std::unexpected{map_fresult(result)};
}

const zest::fs::FileFlags flags{_flags};
File file = {nullptr, flags.write};

if (flags.create_new && this->with_lock(vexFileStatus, path) != 0) {
// User specified create_new but the file already exists
return std::unexpected{std::errc::file_exists};
}

if (flags.read && !flags.write) {
// Open in read-only mode
file.fd = this->with_lock(vexFileOpen, path, ""); // mode is ignored
} else if (flags.write && flags.append) {
// Open in write & append mode
file.fd = this->with_lock(vexFileOpenWrite, path);
} else if (flags.write && flags.truncate) {
// Open in write mode & truncate file
file.fd = this->with_lock(vexFileOpenCreate, path);
} else if (flags.write) {
// Open in write & overwrite mode
file.fd = this->with_lock(vexFileOpenWrite, path);
this->with_lock(vexFileSeek, file.fd, 0, 0);
} else {
return std::unexpected{std::errc::invalid_argument};
}
if (file.fd == nullptr) {
return std::unexpected{std::errc::too_many_files_open_in_system};
}
return this->add_vfs_entry(std::any{file});
}

std::expected<ssize_t, std::error_condition>
zest::fs::UsdDriver::read(std::any _file, std::span<std::byte> output) {
auto [file, is_writable] = std::any_cast<File>(_file);
if (is_writable) {
return std::unexpected{std::errc::operation_not_permitted};
}
char* out_ptr = reinterpret_cast<char*>(output.data());
return this->with_lock(vexFileRead, out_ptr, 1, output.size(), file);
}

std::expected<int, std::error_condition>
zest::fs::UsdDriver::write(std::any _file, std::span<std::byte> input) {
auto [file, is_writable] = std::any_cast<File>(_file);
if (!is_writable) {
return std::unexpected{std::errc::operation_not_permitted};
}
char* in_ptr = reinterpret_cast<char*>(input.data());
return this->with_lock(vexFileWrite, in_ptr, 1, input.size(), file);
}

std::expected<void, std::error_condition> zest::fs::UsdDriver::close(std::any _file) {
FIL* file = std::any_cast<File>(_file).fd;
this->with_lock(vexFileClose, file);
return {};
}

std::expected<void, std::error_condition>
zest::fs::UsdDriver::fstat(std::any _file, struct stat* stats) {
FIL* file = std::any_cast<File>(_file).fd;
stats->st_size = this->with_lock(vexFileSize, file);
// TODO: set st_mode based to indicate file vs directory
return {};
}

bool zest::fs::UsdDriver::isatty(std::any _file [[maybe_unused]]) {
return false;
}

std::expected<off_t, std::error_condition>
zest::fs::UsdDriver::lseek(std::any _file, off_t offset, int dir) {
FIL* file = std::any_cast<File>(_file).fd;
FRESULT result;
switch (dir) {
case 0: {
result = this->with_lock(vexFileSeek, file, offset, 0);
break;
}
case 1: {
if (offset >= 0) {
result = this->with_lock(vexFileSeek, file, offset, 1);
} else {
// VEXos does not support negative seek offsets, so we calculate the offset from the
// start of the file ourselves.
auto stream_pos = this->with_lock(vexFileTell, file);
result = this->with_lock(vexFileSeek, file, stream_pos + offset, 0);
}
break;
}
case 2: {
if (offset >= 0) {
result = this->with_lock(vexFileSeek, file, offset, 2);
} else {
// VEXos does not support negative seek offsets, so we calculate the offset from the
// start of the file ourselves.
struct stat stats;
if (auto result = this->fstat(_file, &stats); !result.has_value()) {
return std::unexpected{result.error()};
}
auto file_size = stats.st_size;
result = this->with_lock(vexFileSeek, file, offset + file_size, 0);
}
break;
}
default: {
return std::unexpected{std::errc::invalid_argument};
}
}
if (auto err = map_fresult(result); err) {
return std::unexpected{err};
}
return this->with_lock(vexFileTell, file);
}

int zest::fs::UsdDriver::ctl(std::any _file [[maybe_unused]], uint32_t, void* const) {
return 0;
}
Loading
Loading