Update the llfs documentation
This commit is contained in:
116
docs/llfs.md
116
docs/llfs.md
@@ -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`
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user