Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a proper File class #287

Merged
merged 46 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
d5dafc2
Add file class
flo12356 May 4, 2024
8078a77
Restructure file class
flo12356 May 11, 2024
78262a9
Refactor LfsWrapper a bit
PatrickKa May 12, 2024
25e3c2f
WIP try to fix errors in lfsWrapper.test.cpp
flo12356 May 26, 2024
a4cb6c0
Add NewOutcome.test.cpp
PatrickKa Jun 1, 2024
128ce0f
Format Outcome.hpp and print error messages before aborting
PatrickKa Jun 1, 2024
9b6ec67
Format file system if mounting does not work
PatrickKa Jun 1, 2024
84934c9
Add -g to debug flags in CMake presets
PatrickKa Jun 1, 2024
b0aef6e
Change debug optimization level from -Og to -O0
PatrickKa Jun 9, 2024
77951c1
Turn lfs wrapper test into normal executable
PatrickKa Jun 9, 2024
0d47ee1
Fix move construction and assignment of File
PatrickKa Jun 11, 2024
4fb6f2c
Add File.isOpen_
PatrickKa Jun 11, 2024
32e288a
Move template functions to ipp
flo12356 Jun 14, 2024
59762bd
Add LfsWrapper Unit tests
flo12356 Jun 14, 2024
c446e0c
Add flag check befor read/write
flo12356 Jun 19, 2024
23acc96
Disable warning on missing field initializers
PatrickKa Jun 20, 2024
808318a
Use statically allocated file buffer
PatrickKa Jun 20, 2024
d4aad23
Add fs::ErrorCode::unsupportedOperation
PatrickKa Jun 21, 2024
5c4dae0
Remove [[nodiscard]] from function definitions
PatrickKa Jun 21, 2024
d8c14c3
Use almost always auto in LfsWrapper.test.cpp
PatrickKa Jun 21, 2024
c8636c0
Make flags in LfsWrapper unsigned
PatrickKa Jun 21, 2024
3c6069a
Rename storage to memory
PatrickKa Jul 6, 2024
9222232
WIP: Add move function
flo12356 Aug 2, 2024
dcb7b4f
WIP: Replace move with swap
flo12356 Aug 5, 2024
5ef2acb
Revert "WIP: Replace move with swap"
PatrickKa Aug 7, 2024
b39d22d
Refactor `fs::File`
PatrickKa Aug 7, 2024
5d6f9a6
Fix rebase error
flo12356 Aug 10, 2024
4b0b47b
Add internal read write functions for LfsWrapper
flo12356 Aug 12, 2024
5235e71
Fix target_link_libraries of FileSystem
PatrickKa Aug 12, 2024
1a22c2b
Remove LfsWrapperExecutable and NewOutcome tests
PatrickKa Aug 12, 2024
7f3d2f8
Fix target_link_libraries of LfsWrapper test
PatrickKa Aug 12, 2024
fa05e0e
Fix includes of LfsWrapper test
PatrickKa Aug 12, 2024
faf9b4e
Fix includes for LfsWrapper.ipp
flo12356 Aug 17, 2024
49d2a2d
Refactor LfsWrapper read/write function
flo12356 Aug 17, 2024
486a054
Fix USE_NO_RODOS compile define for unit tests
flo12356 Aug 17, 2024
d8fb579
Move USE_NO_RODOS define
flo12356 Aug 18, 2024
6e9c0cd
Add lockfile for LfsWrapper
flo12356 Sep 19, 2024
bbb2326
Add unit test for LfsWrapper lockfile
flo12356 Sep 20, 2024
8a698cc
Reduce max. path length to 30 characters
PatrickKa Oct 5, 2024
c8c2e43
Refactor `LfsWrapper.[ch]pp`
PatrickKa Oct 5, 2024
678be93
Get rid of `USE_NO_RODOS` and rework `LfsWrapper.test.cpp`
PatrickKa Oct 5, 2024
8fae95c
Link correct libraries for `Sts1CobcSw_FramSections`
PatrickKa Oct 5, 2024
1624f4b
Add closeAndKeepLockFile for LfsWrapper
flo12356 Oct 8, 2024
b9fd968
Fix const correctness in LfsWrapper
flo12356 Oct 8, 2024
79a7572
Use `File::CloseAndKeepLockFile()` in `File::Close()`
PatrickKa Oct 8, 2024
d4e7a26
Add `ErrorCode::fileLocked`
PatrickKa Oct 8, 2024
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
7 changes: 4 additions & 3 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@
"W_NULL": "-Wnull-dereference -Wzero-as-null-pointer-constant -Wstrict-null-sentinel",
"W_CLASS": "-Wsuggest-override -Woverloaded-virtual",
"W_COND": "-Wduplicated-branches -Wduplicated-cond",
"W_REST": "-Wimplicit-fallthrough=5 -Wshadow -Wlogical-op -Wformat=2 -Wundef -Wno-long-long",
"WARNINGS": "$env{W_BASIC} $env{W_CONVERSION} $env{W_CAST} $env{W_NULL} $env{W_CLASS} $env{W_COND} $env{W_REST}"
"W_NO": "-Wno-missing-field-initializers -Wno-long-long",
"W_REST": "-Wimplicit-fallthrough=5 -Wshadow -Wlogical-op -Wformat=2 -Wundef",
"WARNINGS": "$env{W_BASIC} $env{W_CONVERSION} $env{W_CAST} $env{W_NULL} $env{W_CLASS} $env{W_COND} $env{W_NO} $env{W_REST}"
}
},
{
Expand All @@ -81,7 +82,7 @@
"inherits": "warnings-unix",
"cacheVariables": {
"CMAKE_CXX_FLAGS": "$env{WARNINGS}",
"CMAKE_CXX_FLAGS_DEBUG": "-Og -DDEBUG",
"CMAKE_CXX_FLAGS_DEBUG": "-O0 -g -DDEBUG",
"CMAKE_CXX_FLAGS_RELEASE": "-Os"
}
},
Expand Down
8 changes: 5 additions & 3 deletions Sts1CobcSw/FileSystem/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
target_sources(Sts1CobcSw_FileSystem PRIVATE LfsWrapper.cpp)
target_link_libraries(Sts1CobcSw_FileSystem PUBLIC littlefs::littlefs Sts1CobcSw_Outcome)
target_link_libraries(Sts1CobcSw_FileSystem PRIVATE Sts1CobcSw_Serial)
target_link_libraries(
Sts1CobcSw_FileSystem PUBLIC etl::etl littlefs::littlefs Sts1CobcSw_Outcome
Sts1CobcSw_ProgramId Sts1CobcSw_Serial
)

if(CMAKE_SYSTEM_NAME STREQUAL Generic)
target_sources(Sts1CobcSw_FileSystem PRIVATE FileSystem.cpp LfsFlash.cpp)
target_link_libraries(Sts1CobcSw_FileSystem PRIVATE Sts1CobcSw_Periphery)
target_link_libraries(Sts1CobcSw_FileSystem PRIVATE rodos::rodos Sts1CobcSw_Periphery)
else()
target_sources(Sts1CobcSw_FileSystem PRIVATE LfsRam.cpp)
endif()
3 changes: 3 additions & 0 deletions Sts1CobcSw/FileSystem/ErrorsAndResult.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ enum class ErrorCode
noMemory = LFS_ERR_NOMEM, // No more memory available
noAttribute = LFS_ERR_NOATTR, // No data/attr available
nameTooLong = LFS_ERR_NAMETOOLONG, // File name too long
fileNotOpen = 1,
unsupportedOperation,
fileLocked,
};


Expand Down
16 changes: 11 additions & 5 deletions Sts1CobcSw/FileSystem/LfsFlash.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
//! @file
//! @brief Implement a flash storage device for littlefs.
//! @brief Implement a flash memory device for littlefs.

#include <Sts1CobcSw/FileSystem/LfsStorageDevice.hpp> // IWYU pragma: associated
#include <Sts1CobcSw/FileSystem/LfsMemoryDevice.hpp> // IWYU pragma: associated
#include <Sts1CobcSw/Periphery/Flash.hpp>
#include <Sts1CobcSw/Serial/Byte.hpp>

#include <rodos_no_using_namespace.h>

#include <algorithm>
#include <array>
#include <span>


Expand All @@ -29,9 +30,14 @@ auto Lock(lfs_config const * config) -> int;
auto Unlock(lfs_config const * config) -> int;


auto readBuffer = flash::Page{};
auto readBuffer = std::array<Byte, lfsCacheSize>{};
auto programBuffer = decltype(readBuffer){};
auto lookaheadBuffer = flash::Page{};
auto lookaheadBuffer = std::array<Byte, 64>{}; // NOLINT(*magic-numbers)

// littlefs requires the lookaheadBuffer size to be a multiple of 8
static_assert(lookaheadBuffer.size() % 8 == 0); // NOLINT(*magic-numbers)
// littlefs requires the cacheSize to be a multiple of the read_size and prog_size, i.e., pageSize
static_assert(lfsCacheSize % flash::pageSize == 0);

lfs_config const lfsConfig = lfs_config{.context = nullptr,
.read = &Read,
Expand All @@ -51,7 +57,7 @@ lfs_config const lfsConfig = lfs_config{.context = nullptr,
.read_buffer = readBuffer.data(),
.prog_buffer = programBuffer.data(),
.lookahead_buffer = lookaheadBuffer.data(),
.name_max = LFS_NAME_MAX,
.name_max = maxPathLength,
.file_max = LFS_FILE_MAX,
.attr_max = LFS_ATTR_MAX,
.metadata_max = flash::sectorSize,
Expand Down
15 changes: 15 additions & 0 deletions Sts1CobcSw/FileSystem/LfsMemoryDevice.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <littlefs/lfs.h>


namespace sts1cobcsw::fs
{
inline constexpr auto lfsCacheSize = 256;
// Our longest path should be strlen("/programs/65536.zip.lock") = 25 characters long
inline constexpr auto maxPathLength = 30;
extern lfs_config const lfsConfig;


auto Initialize() -> void;
}
15 changes: 10 additions & 5 deletions Sts1CobcSw/FileSystem/LfsRam.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! @file
//! @brief Simulate a storage device for littlefs in RAM.
//! @brief Simulate a memory device for littlefs in RAM.
//!
//! This is useful for testing the file system without using a real flash memory.

#include <Sts1CobcSw/FileSystem/LfsStorageDevice.hpp> // IWYU pragma: associated
#include <Sts1CobcSw/FileSystem/LfsMemoryDevice.hpp> // IWYU pragma: associated
#include <Sts1CobcSw/Serial/Byte.hpp>

#include <algorithm>
Expand Down Expand Up @@ -35,9 +35,14 @@ constexpr auto sectorSize = 4 * 1024;
constexpr auto memorySize = 128 * 1024 * 1024;

auto memory = std::vector<Byte>();
auto readBuffer = std::array<Byte, pageSize>{};
auto readBuffer = std::array<Byte, lfsCacheSize>{};
auto programBuffer = decltype(readBuffer){};
auto lookaheadBuffer = std::array<Byte, pageSize>{};
auto lookaheadBuffer = std::array<Byte, 64>{}; // NOLINT(*magic-numbers)

// littlefs requires the lookaheadBuffer size to be a multiple of 8
static_assert(lookaheadBuffer.size() % 8 == 0); // NOLINT(*magic-numbers)
// littlefs requires the cacheSize to be a multiple of the read_size and prog_size, i.e., pageSize
static_assert(lfsCacheSize % pageSize == 0);

lfs_config const lfsConfig = lfs_config{.context = nullptr,
.read = &Read,
Expand All @@ -57,7 +62,7 @@ lfs_config const lfsConfig = lfs_config{.context = nullptr,
.read_buffer = readBuffer.data(),
.prog_buffer = programBuffer.data(),
.lookahead_buffer = lookaheadBuffer.data(),
.name_max = LFS_NAME_MAX,
.name_max = maxPathLength,
.file_max = LFS_FILE_MAX,
.attr_max = LFS_ATTR_MAX,
.metadata_max = sectorSize,
Expand Down
12 changes: 0 additions & 12 deletions Sts1CobcSw/FileSystem/LfsStorageDevice.hpp

This file was deleted.

211 changes: 209 additions & 2 deletions Sts1CobcSw/FileSystem/LfsWrapper.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#include <Sts1CobcSw/FileSystem/LfsStorageDevice.hpp>
#include <Sts1CobcSw/FileSystem/LfsWrapper.hpp>
#include <Sts1CobcSw/Outcome/Outcome.hpp>

Expand All @@ -10,13 +9,221 @@ namespace sts1cobcsw::fs
auto lfs = lfs_t{};


[[nodiscard]] auto Mount() -> Result<void>
// FIXME: For some reason this allocates 1024 bytes on the heap. With LFS_NO_MALLOC defined, it
// crashes with a SEGFAULT.
auto Mount() -> Result<void>
{
auto error = lfs_mount(&lfs, &lfsConfig);
if(error == 0)
{
return outcome_v2::success();
}
error = lfs_format(&lfs, &lfsConfig);
if(error != 0)
{
return static_cast<ErrorCode>(error);
}
error = lfs_mount(&lfs, &lfsConfig);
if(error == 0)
{
return outcome_v2::success();
}
return static_cast<ErrorCode>(error);
}


auto Unmount() -> Result<void>
{
auto error = lfs_unmount(&lfs);
if(error != 0)
{
return static_cast<ErrorCode>(error);
}
return outcome_v2::success();
}


auto Open(Path const & path, unsigned int flags) -> Result<File>
{
auto file = File();
// We need to create a temporary with Path(path) here since append() modifies the object and we
// don't want to change the original path
file.lockFilePath_ = Path(path).append(".lock");
auto createLockFileResult = file.CreateLockFile();
if(createLockFileResult.has_error())
{
return ErrorCode::fileLocked;
}
auto error = lfs_file_opencfg(
&lfs, &file.lfsFile_, path.c_str(), static_cast<int>(flags), &file.lfsFileConfig_);
if(error == 0)
{
file.path_ = path;
file.openFlags_ = flags;
file.isOpen_ = true;
return file;
}
return static_cast<ErrorCode>(error);
}


File::File(File && other) noexcept
{
MoveConstructFrom(&other);
}


auto File::operator=(File && other) noexcept -> File &
{
if(this != &other)
{
MoveConstructFrom(&other);
}
return *this;
}


File::~File()
{
// Only close the file if it is not in a default initialized or moved-from state
if(not path_.empty())
{
(void)Close();
}
}


auto File::Size() const -> Result<int>
{
if(not isOpen_)
{
return ErrorCode::fileNotOpen;
}
auto size = lfs_file_size(&lfs, &lfsFile_);
if(size >= 0)
{
return size;
}
return static_cast<ErrorCode>(size);
}


auto File::Close() const -> Result<void>
{
if(not isOpen_)
{
return outcome_v2::success();
}
auto error = lfs_remove(&lfs, lockFilePath_.c_str());
if(error != 0)
{
return static_cast<ErrorCode>(error);
}
return CloseAndKeepLockFile();
}


// This function handles the weird way in which lfs_file_t objects are moved. It is more efficient
// than using the usual copy-and-swap idiom, because the latter would require to open and close the
// file more often.
auto File::MoveConstructFrom(File * other) noexcept -> void
{
if(other->path_.empty())
{
return;
}
auto error = lfs_file_opencfg(&lfs,
&lfsFile_,
other->path_.c_str(),
static_cast<int>(other->openFlags_),
&lfsFileConfig_);
if(error == 0)
{
path_ = other->path_;
lockFilePath_ = other->lockFilePath_;
openFlags_ = other->openFlags_;
isOpen_ = true;
}
(void)other->CloseAndKeepLockFile();
other->path_ = "";
other->lockFilePath_ = "";
other->openFlags_ = 0;
other->lfsFile_ = {};
}


auto File::CreateLockFile() const noexcept -> Result<void>
{
auto lfsLockFile = lfs_file_t{};
auto lockFileBuffer = std::array<Byte, lfsCacheSize>{};
auto lfsLockFileConfig = lfs_file_config{.buffer = lockFileBuffer.data()};
auto error = lfs_file_opencfg(&lfs,
&lfsLockFile,
lockFilePath_.c_str(),
static_cast<unsigned int>(LFS_O_RDWR) | LFS_O_CREAT | LFS_O_EXCL,
&lfsLockFileConfig);
if(error == 0)
{
error = lfs_file_close(&lfs, &lfsLockFile);
}
if(error == 0)
{
return outcome_v2::success();
}
return static_cast<ErrorCode>(error);
}


auto File::Read(void * buffer, std::size_t size) const -> Result<int>
{
if(not isOpen_)
{
return ErrorCode::fileNotOpen;
}
if((openFlags_ & LFS_O_RDONLY) == 0U)
{
return ErrorCode::unsupportedOperation;
}
auto nReadBytes = lfs_file_read(&lfs, &lfsFile_, buffer, size);
if(nReadBytes >= 0)
{
return nReadBytes;
}
return static_cast<ErrorCode>(nReadBytes);
}


auto File::Write(void const * buffer, std::size_t size) -> Result<int>
{
if(not isOpen_)
{
return ErrorCode::fileNotOpen;
}
if((openFlags_ & LFS_O_WRONLY) == 0U)
{
return ErrorCode::unsupportedOperation;
}
auto nWrittenBytes = lfs_file_write(&lfs, &lfsFile_, buffer, size);
if(nWrittenBytes >= 0)
{
return nWrittenBytes;
}
return static_cast<ErrorCode>(nWrittenBytes);
}


auto File::CloseAndKeepLockFile() const -> Result<void>
{
if(not isOpen_)
{
return outcome_v2::success();
}
auto error = lfs_file_close(&lfs, &lfsFile_);
if(error != 0)
{
return static_cast<ErrorCode>(error);
}
isOpen_ = false;
return outcome_v2::success();
}
}
Loading
Loading