Update the llfs documentation

This commit is contained in:
L-diy
2023-11-12 02:49:03 +01:00
parent af46235eba
commit edc81ad3a2
3 changed files with 257 additions and 6 deletions

View File

@@ -1,17 +1,52 @@
# LLFS (Linked List File System)
## Introduction
The llfs filesystem can be generated using the mkllfss utility.
It is a simple filesystem that uses a linked list to store files.
The filesystem is stored in a single c file (`llfs_data.c`), which can be compiled and read by the llfs library.
The llfs filesystem is a flat filesystem, meaning that it does not support directories.
The llfs filesystem can be generated using the [mkllfs](mkllfs.md) utility.
The resulting C file encapsulates the filesystem data within a linked list which can be used by the llfs library.
As a flat filesystem, llfs lacks support for directories.
Using the llfs API, information about the files in the filesystem can be retrieved,
and the files can be read using direct memory access.
Alternatively, the POSIX file functions can be used.
But this is more resource intensive, as data must be copied before using it.
It's essential to note that the llfs filesystem operates in a read-only mode,
restricting operations solely to read functions.
## Table of contents
- [Introduction](#introduction)
- [Table of contents](#table-of-contents)
- [Initialization](#initialization)
- [Usage of the llfs API](#usage-of-the-llfs-api)
- [The `llfs_file_t` struct](#the-llfs_file_t-struct)
- [Getting a list of files](#getting-a-list-of-files)
- [Iterator function (not recommended)](#iterator-function-not-recommended)
- [Reading a file](#reading-a-file)
- [Getting the number of files](#getting-the-number-of-files)
- [Using the POSIX file functions](#using-the-posix-file-functions)
## Initialization
Before using the llfs API, or the file related POSIX (fopen, fgetc, ...) functions, the filesystem must be initialized by calling `llfs_init()`.
## Usage of the llfs API
### The `llfs_file_t` struct
The `llfs_file_t` struct contains information about a file in the filesystem.
```c
typedef struct {
const uint8_t* data; // Pointer to the file data (len bytes)
const char* name; // Null-terminated string with the filename
size_t len; // Length of the file data
} llfs_file_t;
```
The data pointer points to the data of the file in the filesystem, and can be used to read the file.
### Getting a list of files
```c
#include "llfs.h"
void main(void) {
llfs_init();
// Allocate space for 10 files
llfs_file_t file_list[10];
@@ -33,13 +68,50 @@ Result:
[Info] (2031) [main]: File: file1.txt, size: 77
```
It is also possible to use a file extension filter (e.g. `*.bmp`, `*.txt`, `*.py`).
```c
// ...
size_t file_count = llfs_file_list(file_list, 10, "*.bmp");
// ...
```
This will only return files with the `.bmp` extension.
````
[Info] (2009) [main]: File: image.bmp, size: 9270
[Info] (2019) [main]: File: image2.bmp, size: 7738
````
#### Iterator function (not recommended)
It is also possible to iterate through the files without allocating an array.
When the memory pointer is `NULL`, the iterator will start at the beginning of the file list.
Each call to `llfs_next_file()` will return the next file in the list,
if a filter is specified files that don't match the filter will be skipped.
```c
#include "llfs.h"
void main(void) {
llfs_init();
// Get the file list
void* mem = NULL; // Pointer for internal use by the llfs library
llfs_file_t* file;
while ((file = llfs_next_file(&mem, ".bmp")) != NULL) {
LOG_INFO(TAG, "File: %s", file->name);
}
}
```
While this method doesn't require allocating memory for the file list,
it is slower than the previous method due to the overhead calling the function for each file.
Additionally, the required memory for the filelist is very small, so it's recommended to use the first method.
### Reading a file
```c
#include "llfs.h"
void main(void) {
llfs_init();
// Get a file by name
llfs_file_t *file = llfs_file_open("filename with a space.txt");
llfs_file_t* file = llfs_file_open("filename with a space.txt");
if (file != NULL) {
// Print the file name, size and data
@@ -55,3 +127,37 @@ Result:
[Info] (2040) [main]: File found: filename with a space.txt, size: 61
[Info] (2047) [main]: File data: This is a file with a space in it's filename.
```
### Getting the number of files
```c
#include "llfs.h"
void main(void) {
llfs_init();
// Get the number of files
size_t file_count = llfs_file_count();
// Print the number of files
LOG_INFO(TAG, "File count: %d", file_count);
}
```
## Using the POSIX file functions
The llfs library also supports the POSIX file functions.
As the file system is read-only, write functions are not implemented.
There is also a limit on the number of files that can be open concurrently,
this is set by the `POSIX_MAX_FILES` macro in `llfs.c`.
The default value is 10, but there are already 3 files in use (stdin, stdout, stderr),
so the maximum number of files that can be open is 7.
The following functions are tested and working, but other functions might also work:
- `fopen`
- `fclose`
- `fgetc`
- `fread`
- `fseek`
- `ftell`
- `rewind`
- `fstat`
- `fileno`

View File

@@ -72,7 +72,7 @@ llfs_file_t* llfs_file_open(const char* name);
*
* @param[in, out] mem A pointer to a void* that is used internally to keep track of the current file
* @param[in] filter A string with file extension to filter out. (e.g. "*.txt" or "*.png")
* @return The next file in the filesystem or NULL if there are no more files
* @return The next file in the filesystem or NULL if there are no more files @ref llfs_file_t
*/
llfs_file_t* llfs_next_file(void** mem, char* filter);

View File

@@ -25,6 +25,7 @@
#define LOGGER_LEVEL_ALL
#include "log.h"
#include "llfs.h"
#include "../../Drivers/BSP/STM32746G-Discovery/stm32746g_discovery_lcd.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
@@ -112,6 +113,150 @@ int main(void)
/* USER CODE BEGIN 2 */
llfs_init();
FILE *f = fopen("test.txt", "rw");
if (f == NULL) {
LOG_INFO(TAG, "File not found test.txt");
return 1;
} else {
LOG_INFO(TAG, "File found test.txt");
}
// Test POSIX file operations
// fgetc
int c;
printf("Printing file:\n");
while ((c = fgetc(f)) != EOF) {
printf("%c", c);
}
LOG_INFO(TAG, "File printed");
// fseek
fseek(f, 0, SEEK_SET);
LOG_INFO(TAG, "File seeked to start");
// ftell
long pos = ftell(f);
LOG_INFO(TAG, "File position: %d", pos);
// fread
char buf[100];
size_t bytes_read = fread(buf, 1, 100, f);
LOG_INFO(TAG, "Read %d bytes from file", bytes_read);
printf("Read from file:\n");
for (int i = 0; i < bytes_read; i++) {
printf("%c", buf[i]);
}
// Rewind the file
LOG_INFO(TAG, "Before File rewinded, pos: %d", ftell(f));
rewind(f);
LOG_INFO(TAG, "File rewinded, pos: %d", ftell(f));
// Get the file size fstat
struct stat st;
fstat(fileno(f), &st);
LOG_INFO(TAG, "File size: %d", st.st_size);
// Get a list of all files with the .bmp extension
llfs_file_t file_list[10];
size_t num_files = llfs_file_list(file_list, 10, "*.bmp");
LOG_INFO(TAG, "Found %d files with the .bmp extension", num_files);
for (int i = 0; i < num_files; i++) {
LOG_INFO(TAG, "File %d: %s", i, file_list[i].name);
}
// Get a list of files with .txt or .html
num_files = llfs_file_list(file_list, 10, "*.txt");
LOG_INFO(TAG, "Found %d files with the .txt or .html extension", num_files);
for (int i = 0; i < num_files; i++) {
LOG_INFO(TAG, "File %d: %s", i, file_list[i].name);
}
// Loop over all files using the iterator
LOG_INFO(TAG, "Looping over all files, using the iterator");
void *mem = NULL;
llfs_file_t *file;
while ((file = llfs_next_file(&mem, NULL)) != NULL) {
LOG_INFO(TAG, "File: %s", file->name);
}
// Loop over all files with the .bmp extension using the iterator
LOG_INFO(TAG, "Looping over all files with the .bmp extension, using the iterator");
mem = NULL;
while ((file = llfs_next_file(&mem, "*.bmp")) != NULL) {
LOG_INFO(TAG, "File: %s", file->name);
}
// Get the number of files in the filesystem
size_t num_files_in_fs = llfs_file_count();
LOG_INFO(TAG, "Number of files in the filesystem: %d", num_files_in_fs);
fclose(f);
// Try opening multiple files
LOG_INFO(TAG, "Opening an closing multiple files");
FILE * f1 = fopen("test.txt", "rw");
if (f1 == NULL) {
LOG_INFO(TAG, "File not found f1");
return 1;
} else {
LOG_INFO(TAG, "File found f1");
}
// Get the fileno
int fd = fileno(f1);
LOG_INFO(TAG, "File descriptor f1: %d", fd);
FILE * f2 = fopen("test.txt", "rw");
if (f2 == NULL) {
LOG_INFO(TAG, "File not found f2");
return 1;
} else {
LOG_INFO(TAG, "File found f2");
}
// Get the fileno
fd = fileno(f2);
LOG_INFO(TAG, "File descriptorf2: %d", fd);
LOG_INFO(TAG, "Closing f1");
fclose(f1);
FILE * f3 = fopen("test.txt", "rw");
if (f3 == NULL) {
LOG_INFO(TAG, "File not found f3");
return 1;
} else {
LOG_INFO(TAG, "File found f3");
}
// Get the fileno
fd = fileno(f3);
LOG_INFO(TAG, "File descriptor f3: %d", fd);
LOG_INFO(TAG, "Closing f2");
fclose(f2);
LOG_INFO(TAG, "Closing f3");
fclose(f3);
// Try opening a file multiple times, until it fails
int i = 0;
LOG_INFO(TAG, "Opening a file multiple times, until it fails");
while (1) {
f = fopen("test.txt", "rw");
LOG_INFO(TAG, "File descriptor: %d", fileno(f));
if (f == NULL) {
LOG_INFO(TAG, "File not found test.txt");
break;
} else {
LOG_INFO(TAG, "File found test.txt");
}
i++;
}
LOG_INFO(TAG, "File opened %d times", i);
/* USER CODE END 2 */
/* Infinite loop */