fprime/Os/FileSystem.hpp
Thomas Boyer-Chammard 578e61f1da
Format Os Module (#3959)
* Format Os module

* Add Os to format-check CI

* Remove double semi-colon
2025-07-31 15:40:30 -07:00

400 lines
17 KiB
C++

// ======================================================================
// \title Os/FileSystem.hpp
// \brief Os::FileSystem interface definition
// ======================================================================
#ifndef _OS_FILESYSTEM_HPP_
#define _OS_FILESYSTEM_HPP_
#include <Fw/FPrimeBasicTypes.hpp>
#include <Os/Directory.hpp>
#include <Os/File.hpp>
#include <Os/Os.hpp>
namespace Os {
struct FileSystemHandle {};
class FileSystemInterface {
public:
// Size of file chunks to use for file system operations (e.g. copyFile)
static constexpr FwSizeType FILE_SYSTEM_FILE_CHUNK_SIZE = FW_FILE_CHUNK_SIZE; //!< Size of file system chunk
enum Status {
OP_OK, //!< Operation was successful
ALREADY_EXISTS, //!< File already exists
NO_SPACE, //!< No space left
NO_PERMISSION, //!< No permission to write
NOT_DIR, //!< Path is not a directory
IS_DIR, //!< Path is a directory
NOT_EMPTY, //!< directory is not empty
INVALID_PATH, //!< Path is too long, too many sym links, etc.
DOESNT_EXIST, //!< Path doesn't exist
FILE_LIMIT, //!< Too many files or links
BUSY, //!< Operand is in use by the system or by a process
NO_MORE_FILES, //!< Directory stream has no more files
BUFFER_TOO_SMALL, //!< Buffer size is too small to hold full path (for getWorkingDirectory)
EXDEV_ERROR, // Operation not supported across devices (e.g. rename)
OVERFLOW_ERROR, // Operation failed due to overflow in calculation of the result
NOT_SUPPORTED, //!< Operation is not supported by the current implementation
OTHER_ERROR, //!< other OS-specific error
};
enum PathType {
FILE, //!< Path is a file
DIRECTORY, //!< Path is a directory
OTHER, //!< Path is not a file or directory, e.g. a socket
NOT_EXIST, //!< Path does not exist
};
//! \brief default constructor
FileSystemInterface() = default;
//! \brief default virtual destructor
virtual ~FileSystemInterface() = default;
//! \brief copy constructor is forbidden
FileSystemInterface(const FileSystemInterface& other) = delete;
//! \brief assignment operator is forbidden
FileSystemInterface& operator=(const FileSystemInterface& other) = delete;
//! \brief return the underlying FileSystem handle (implementation specific)
//! \return internal FileSystem handle representation
virtual FileSystemHandle* getHandle() = 0;
//! \brief provide a pointer to a FileSystem delegate object
static FileSystemInterface* getDelegate(FileSystemHandleStorage& aligned_new_memory);
// ------------------------------------------------------------------
// FileSystem operations to be implemented by an OSAL implementation
// ------------------------------------------------------------------
// These functions are to be overridden in each OS implementation
// See an example in in Os/Posix/FileSystem.hpp
//! \brief Remove a directory at the specified path
//! \param path The path of the directory to remove
//! \return Status of the operation
virtual Status _removeDirectory(const char* path) = 0;
//! \brief Remove a file at the specified path
//! \param path The path of the file to remove
//! \return Status of the operation
virtual Status _removeFile(const char* path) = 0;
//! \brief Rename (or move) a file from source to destination
//! \param sourcePath The path of the source file
//! \param destPath The path of the destination file
//! \return Status of the operation
virtual Status _rename(const char* sourcePath, const char* destPath) = 0;
//! \brief Get filesystem free and total space in bytes on the filesystem containing the specified path
//! \param path The path on the filesystem to query
//! \param totalBytes Reference to store the total bytes on the filesystem
//! \param freeBytes Reference to store the free bytes on the filesystem
//! \return Status of the operation
virtual Status _getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) = 0;
//! \brief Get the type of the path (file, directory, etc.)
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path to check
//! \param pathType Reference to store the path type
//! \return Status of the operation
virtual Status _getPathType(const char* path, PathType& pathType) = 0;
//! \brief Get the current working directory
//! \param path Buffer to store the current working directory path
//! \param bufferSize Size of the buffer
//! \return Status of the operation
virtual Status _getWorkingDirectory(char* path, FwSizeType bufferSize) = 0;
//! \brief Change the current working directory to the specified path
//! \param path The path of the new working directory
//! \return Status of the operation
virtual Status _changeWorkingDirectory(const char* path) = 0;
};
//! \brief FileSystem class
//!
//! This class provides a common interface for file system operations.
//! This class uses the singleton pattern and should be accessed through
//! its static functions, for example using `Os::FileSystem::removeFile(path)`.
class FileSystem final : public FileSystemInterface {
private:
FileSystem(); //!< Constructor (private because singleton pattern)
public:
~FileSystem() final; //!< Destructor
//! \brief return the underlying FileSystem handle (implementation specific)
//! \return internal FileSystem handle representation
FileSystemHandle* getHandle() override;
// ------------------------------------------------------------
// Implementation-specific FileSystem member functions
// ------------------------------------------------------------
//! \brief Remove a directory at the specified path
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path of the directory to remove
//! \return Status of the operation
Status _removeDirectory(const char* path) override;
//! \brief Remove a file at the specified path
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path of the file to remove
//! \return Status of the operation
Status _removeFile(const char* path) override;
//! \brief Rename a file from source to destination
//!
//! If the rename fails due to a cross-device operation, this function should return EXDEV_ERROR
//! and moveFile can be used instead to force a copy-and-remove.
//!
//! It is invalid to pass `nullptr` as sourcePath or destPath.
//!
//! \param sourcePath The path of the source file
//! \param destPath The path of the destination file
//! \return Status of the operation
Status _rename(const char* sourcePath, const char* destPath) override;
//! \brief Get filesystem free and total space in bytes on the filesystem containing the specified path
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path on the filesystem to query
//! \param totalBytes Reference to store the total bytes on the filesystem
//! \param freeBytes Reference to store the free bytes on the filesystem
//! \return Status of the operation
Status _getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) override;
//! \brief Get the current working directory
//!
//! Writes the current working directory path to the provided buffer of size bufferSize.
//! If the buffer is too small to hold the full path, the function will return BUFFER_TOO_SMALL.
//!
//! It is invalid to pass `nullptr` as the path.
//! It is invalid to pass a bufferSize of 0.
//!
//! \param path Buffer to store the current working directory path
//! \param bufferSize Size of the buffer
//! \return Status of the operation
Status _getWorkingDirectory(char* path, FwSizeType bufferSize) override;
//! \brief Change the current working directory to the specified path
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path of the new working directory
//! \return Status of the operation
Status _changeWorkingDirectory(const char* path) override;
//! \brief Get the type of the path (file, directory, etc.)
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path to check
//! \param pathType Reference to store the path type
//! \return Status of the operation
Status _getPathType(const char* path, PathType& pathType) override;
// ------------------------------------------------------------
// Implementation-specific FileSystem static functions
// ------------------------------------------------------------
// These are static variants that are exposed to the user, and call the above member functions
//! \brief Remove a directory at the specified path
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path of the directory to remove
//! \return Status of the operation
static Status removeDirectory(const char* path);
//! \brief Remove a file at the specified path
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path of the file to remove
//! \return Status of the operation
static Status removeFile(const char* path);
//! \brief Rename a file from source to destination
//!
//! If the rename fails due to a cross-device operation, this function should return EXDEV_ERROR
//! and moveFile can be used instead to force a copy-and-remove.
//!
//! It is invalid to pass `nullptr` as sourcePath or destPath.
//!
//! \param sourcePath The path of the source file
//! \param destPath The path of the destination file
//! \return Status of the operation
static Status rename(const char* sourcePath, const char* destPath);
//! \brief Get filesystem free and total space in bytes on the filesystem containing the specified path
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path on the filesystem to query
//! \param totalBytes Reference to store the total bytes on the filesystem
//! \param freeBytes Reference to store the free bytes on the filesystem
//! \return Status of the operation
static Status getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes);
//! \brief Get the current working directory
//!
//! Writes the current working directory path to the provided buffer of size bufferSize.
//! If the buffer is too small to hold the full path, the function will return BUFFER_TOO_SMALL.
//!
//! It is invalid to pass `nullptr` as the path.
//! It is invalid to pass a bufferSize of 0.
//!
//! \param path Buffer to store the current working directory path
//! \param bufferSize Size of the buffer
//! \return Status of the operation
static Status getWorkingDirectory(char* path, FwSizeType bufferSize);
//! \brief Change the current working directory to the specified path
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path of the new working directory
//! \return Status of the operation
static Status changeWorkingDirectory(const char* path);
// ------------------------------------------------------------
// Additional functions built on top of OS-specific operations
// ------------------------------------------------------------
//! \brief Return true if the path exists, false otherwise
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path to check for existence
//! \return True if the path exists, false otherwise
static bool exists(const char* path);
//! \brief Return the type of the path (file, directory, or doesn't exist)
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path to check for existence
//! \return PathType enum representing the type of the path (FILE, DIRECTORY, NOT_EXIST)
static PathType getPathType(const char* path);
//! \brief Touch a file at the specified path, creating it if it doesn't exist
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path of the file to touch
//! \return Status of the operation
static Status touch(const char* path);
//! \brief Create a new directory at the specified path.
//!
//! The optional errorIfAlreadyExists (default=false) parameter can be set to true
//! to return an error status if the directory already exists.
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path where the new directory will be created
//! \param errorIfAlreadyExists If true, returns an error if the directory already exists
//! \return Status of the operation
static Status createDirectory(const char* path, bool errorIfAlreadyExists = false);
//! \brief Append the source file to the destination file
//!
//! This function opens both files, and iteratively reads the source by chunks and writes
//! chunks to the destination.
//! If the destination file does not exist and createMissingDest is true, a new file is created.
//!
//! It is invalid to pass `nullptr` as either the source or destination path.
//!
//! \param sourcePath The path of the source file
//! \param destPath The path of the destination file
//! \param createMissingDest If true, creates a new file if the destination doesn't exist
//! \return Status of the operation
static Status appendFile(const char* sourcePath, const char* destPath, bool createMissingDest = false);
//! \brief Copy a file from the source path to the destination path
//!
//! This function opens both files, and iteratively reads the source by chunks and writes
//! chunks to the destination.
//!
//! It is invalid to pass `nullptr` as either the source or destination path.
//!
//! \param sourcePath The path of the source file
//! \param destPath The path of the destination file
//! \return Status of the operation
static Status copyFile(const char* sourcePath, const char* destPath);
//! \brief Move a file from sourcePath to destPath
//!
//! This is done by first trying to rename, and if renaming fails,
//! copy it and then remove the original
//!
//! It is invalid to pass `nullptr` as either the source or destination path.
//!
//! \param sourcePath The path of the source file
//! \param destPath The path of the destination file
//! \return Status of the operation
static Status moveFile(const char* sourcePath, const char* destPath);
//! \brief Get the size of the file (in bytes) at the specified path
//!
//! It is invalid to pass `nullptr` as the path.
//!
//! \param path The path of the file
//! \param size Reference to store the size of the file
//! \return Status of the operation
static Status getFileSize(const char* path, FwSizeType& size);
public:
//! \brief initialize singleton
static void init();
//! \brief get a reference to singleton
//! \return reference to singleton
static FileSystem& getSingleton();
private:
// ------------------------------------------------------------
// Internal helper functions
// ------------------------------------------------------------
//! \brief Convert a File::Status to a FileSystem::Status
static Status handleFileError(File::Status fileStatus);
//! \brief Convert a Directory::Status to a FileSystem::Status
static Status handleDirectoryError(Directory::Status dirStatus);
//! \brief A helper function that writes all the file information in the source
//! file to the destination file (replaces/appends to end/etc. depending
//! on destination file mode).
//!
//! Files must already be open and will remain open after this function
//! completes.
//!
//! @param source File to copy data from
//! @param destination File to copy data to
//! @param size The number of bytes to copy
static Status copyFileData(File& source, File& destination, FwSizeType size);
private:
// This section is used to store the implementation-defined FileSystem handle. To Os::FileSystem and fprime, this
// type is opaque and thus normal allocation cannot be done. Instead, we allow the implementor to store then handle
// in the byte-array here and set `handle` to that address for storage.
alignas(FW_HANDLE_ALIGNMENT) FileSystemHandleStorage m_handle_storage; //!< FileSystem handle storage
FileSystemInterface& m_delegate;
};
} // namespace Os
#endif