Skip to content
Merged
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
72 changes: 72 additions & 0 deletions Bdd/Targets/Common/BddTargetFatFsMount.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* ChaN-FatFs implementation of the shared pipeline's FS-mount seam — see
* BddTargetFatFsMount.h. Extracted from BddTargetFreeRtosPipeline.c in
* SolidSyslog S29.05 when the two FreeRTOS targets first diverged on the
* filesystem (FatFs on lwIP, FreeRTOS-Plus-FAT on Plus-TCP). The logic here is
* the former in-pipeline EnsureFatFsMounted / f_unmount / SolidSyslogFatFsFile_*
* path, lifted verbatim. */

#include "BddTargetFatFsMount.h"

#include "SolidSyslogFatFsFile.h"

#include "ff.h" /* f_mount / f_mkfs — eager mount-or-format on the `set store file` rebuild trigger. */

#include <stdio.h>

/* FATFS object lives in .bss because f_mount stores its address inside the FatFs
* volume registry — the object must outlive every f_open / f_stat / f_unlink.
* One per volume (FF_VOLUMES = 1). */
static FATFS fatfs;
static bool fatfsMounted = false;

bool BddTargetFatFsMount_Mount(void)
{
if (fatfsMounted)
{
return true;
}
FRESULT res = f_mount(&fatfs, "", 1); /* opt=1 → mount immediately, surface FR_NO_FILESYSTEM here */
if (res == FR_NO_FILESYSTEM)
{
/* Fresh disk image — lay down a FAT and re-mount. FM_FAT keeps the
* formatter on FAT12/16; at the shared 8 MiB geometry auto cluster
* sizing clears the ~4085-cluster boundary, so this lands FAT16 (the
* geometry the FreeRTOS-Plus-FAT formatter needs on the sibling
* target). The work buffer is sized to FF_MAX_SS (512 B), the minimum
* f_mkfs accepts on a FAT12/16 volume. */
static BYTE workBuffer[FF_MAX_SS];
const MKFS_PARM opts = {.fmt = FM_FAT | FM_SFD, .n_fat = 1, .align = 1, .n_root = 0, .au_size = 0};
res = f_mkfs("", &opts, workBuffer, sizeof(workBuffer));
if (res == FR_OK)
{
res = f_mount(&fatfs, "", 1);
}
}
if (res != FR_OK)
{
(void) printf("[solidsyslog] fatfs mount failed: FRESULT=%d\n", (int) res);
return false;
}
fatfsMounted = true;
return true;
}

void BddTargetFatFsMount_Unmount(void)
{
if (fatfsMounted)
{
(void) f_unmount("");
fatfsMounted = false;
}
}

struct SolidSyslogFile* BddTargetFatFsMount_CreateFile(void)
{
return SolidSyslogFatFsFile_Create();
}

void BddTargetFatFsMount_DestroyFile(struct SolidSyslogFile* file)
{
/* FatFsFile_Destroy → Close → f_close flushes the underlying FIL's dir entry. */
SolidSyslogFatFsFile_Destroy(file);
}
33 changes: 33 additions & 0 deletions Bdd/Targets/Common/BddTargetFatFsMount.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef BDDTARGETFATFSMOUNT_H
#define BDDTARGETFATFSMOUNT_H

#include "ExternC.h"

#include <stdbool.h>

EXTERN_C_BEGIN

/* ChaN-FatFs implementation of the shared pipeline's FS-mount seam
* (struct BddTargetFreeRtosPipelineConfig). Owns the single-volume FATFS
* registry object + mounted flag and wraps the ff_* lifecycle so the
* pipeline stays FS-vendor-agnostic. Wired by the lwIP target's main.c;
* the FreeRTOS-Plus-TCP target uses the FreeRTOS-Plus-FAT sibling
* (BddTargetPlusFatMount). */

/* Mount volume 0, formatting on first use if the disk image has no FAT
* yet. Idempotent — repeated calls short-circuit on the mounted flag.
* Returns false on an unrecoverable mount/format failure so the caller
* can leave the target on its original store. */
bool BddTargetFatFsMount_Mount(void);

/* Unmount volume 0 if mounted; no-op otherwise. */
void BddTargetFatFsMount_Unmount(void);

/* Create / destroy the ChaN-FatFs SolidSyslogFile adapter (forward-declared
* to keep ff.h / SolidSyslogFatFsFile.h out of the seam's public surface). */
struct SolidSyslogFile* BddTargetFatFsMount_CreateFile(void);
void BddTargetFatFsMount_DestroyFile(struct SolidSyslogFile * file);

EXTERN_C_END

#endif /* BDDTARGETFATFSMOUNT_H */
99 changes: 29 additions & 70 deletions Bdd/Targets/Common/BddTargetFreeRtosPipeline.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#include "SolidSyslogCrc16Policy.h"
#include "SolidSyslogEndpoint.h"
#include "SolidSyslogError.h"
#include "SolidSyslogFatFsFile.h"
#include "SolidSyslogFileBlockDevice.h"
#include "SolidSyslogFormatter.h"
#include "SolidSyslogFreeRtosMutex.h"
Expand All @@ -43,8 +42,6 @@
#include "SolidSyslogTimeQualitySd.h"
#include "SolidSyslogTunables.h"

#include "ff.h" /* f_mount / f_mkfs — eager mount-or-format on the `set store file` rebuild trigger. */

#include <FreeRTOS.h>
#include <task.h>

Expand Down Expand Up @@ -100,15 +97,12 @@ static volatile bool solidSyslogTeardown = false;

/* File-backed store storage. Lives in .bss so it persists across the `set store
* file` rebuild; only populated when that command fires. STORE_PATH_PREFIX is
* "STORE" — sequence-numbered FAT filenames land as STORE00.log, STORE01.log,
* … which fit 8.3 short-filename mode (LFN=0 in our shared ffconf.h). */
static const char STORE_PATH_PREFIX[] = "STORE";

/* FATFS object lives in .bss because f_mount stores its address inside the FatFs
* volume registry — the object must outlive every f_open / f_stat / f_unlink.
* One per volume (FF_VOLUMES = 1). */
static FATFS fatfs;
static bool fatfsMounted = false;
* "/STORE" — sequence-numbered filenames land at the volume root as
* /STORE00.log, /STORE01.log, … which fit 8.3 short-filename mode. The leading
* slash makes the path absolute: ChaN-FatFs treats it as the default-drive root
* (unchanged behaviour), and FreeRTOS-Plus-FAT's ff_stdio requires an absolute
* path when ffconfigHAS_CWD is 0 (its prvABSPath is a pass-through). */
static const char STORE_PATH_PREFIX[] = "/STORE";

static struct SolidSyslogFile* storeFile = NULL;
static struct SolidSyslogBlockDevice* storeBlockDevice = NULL;
Expand Down Expand Up @@ -175,7 +169,6 @@ static bool OnSet(const char* name, const char* value);
static struct SolidSyslogSecurityPolicy* CreateSecurityPolicy(void);
static void DestroySecurityPolicy(void);
static bool RebuildWithFileStore(void);
static bool EnsureFatFsMounted(void);
static void DestroyCurrentStore(void);
static enum SolidSyslogDiscardPolicy MapDiscardPolicy(const char* policy);
static void OnStoreFull(void* context);
Expand Down Expand Up @@ -541,18 +534,20 @@ static struct SolidSyslogSecurityPolicy* CreateSecurityPolicy(void)
return policy;
}

/* `set store file` trigger: swap the default NullStore for a FatFs-backed
* BlockStore. One-way for the lifetime of this QEMU instance. The lifecycle
* mutex blocks the Service task across the Destroy → re-Create transition. */
/* `set store file` trigger: swap the default NullStore for a file-backed
* BlockStore over the platform FS-mount seam. One-way for the lifetime of this
* QEMU instance. The lifecycle mutex blocks the Service task across the
* Destroy → re-Create transition. */
static bool RebuildWithFileStore(void)
{
SolidSyslogMutex_Lock(lifecycleMutex);

/* FatFs does NOT auto-mount on first f_open — mount (and format-on-first-use)
* before tearing down the existing store so a mount failure leaves the
* target running on the original NullStore (zero-disruption); return false
* so OnSet reports the failure to the harness. */
if (!EnsureFatFsMounted())
/* The FS layer does NOT auto-mount on first open — mount (and
* format-on-first-use) via the platform FS-mount seam before tearing down
* the existing store so a mount failure leaves the target running on the
* original NullStore (zero-disruption); return false so OnSet reports the
* failure to the harness. */
if (!g_config->MountStore())
{
SolidSyslogMutex_Unlock(lifecycleMutex);
return false;
Expand All @@ -563,7 +558,7 @@ static bool RebuildWithFileStore(void)
solidSyslog = NULL;
DestroyCurrentStore();

storeFile = SolidSyslogFatFsFile_Create();
storeFile = g_config->CreateStoreFile();
storeBlockDevice = SolidSyslogFileBlockDevice_Create(storeFile, STORE_PATH_PREFIX);

struct SolidSyslogSecurityPolicy* policy = CreateSecurityPolicy();
Expand Down Expand Up @@ -593,40 +588,6 @@ static bool RebuildWithFileStore(void)
return true;
}

/* Mount volume 0; format-on-first-use if the disk image has no FAT yet.
* Idempotent — subsequent calls short-circuit on fatfsMounted. The work buffer
* for f_mkfs is sized to FF_MAX_SS (512 B), the minimum f_mkfs accepts on a
* FAT12/16 volume. */
static bool EnsureFatFsMounted(void)
{
if (fatfsMounted)
{
return true;
}
FRESULT res = f_mount(&fatfs, "", 1); /* opt=1 → mount immediately, surface FR_NO_FILESYSTEM here */
if (res == FR_NO_FILESYSTEM)
{
/* Fresh disk image — lay down a FAT and re-mount. FM_FAT keeps the
* formatter on FAT12/16; at the shared 8 MiB geometry auto cluster
* sizing clears the ~4085-cluster boundary, so this lands FAT16 (the
* geometry the later FreeRTOS-Plus-FAT formatter needs). */
static BYTE workBuffer[FF_MAX_SS];
const MKFS_PARM opts = {.fmt = FM_FAT | FM_SFD, .n_fat = 1, .align = 1, .n_root = 0, .au_size = 0};
res = f_mkfs("", &opts, workBuffer, sizeof(workBuffer));
if (res == FR_OK)
{
res = f_mount(&fatfs, "", 1);
}
}
if (res != FR_OK)
{
(void) printf("[solidsyslog] fatfs mount failed: FRESULT=%d\n", (int) res);
return false;
}
fatfsMounted = true;
return true;
}

static void DestroySecurityPolicy(void)
{
if (strcmp(installedSecurityPolicy, "hmac-sha256") == 0)
Expand All @@ -646,15 +607,16 @@ static void DestroySecurityPolicy(void)
}

/* Tears down whichever store is currently installed (file-backed or null).
* FatFsFile_Destroy → Close → f_close flushes the underlying FIL's dir entry. */
* The platform DestroyStoreFile hook's Close flushes any buffered dir entry /
* file data through to the media. */
static void DestroyCurrentStore(void)
{
if (currentStoreIsFile)
{
SolidSyslogBlockStore_Destroy(currentStore);
SolidSyslogFileBlockDevice_Destroy(storeBlockDevice);
DestroySecurityPolicy();
SolidSyslogFatFsFile_Destroy(storeFile);
g_config->DestroyStoreFile(storeFile);
}
/* else: NullStore is shared and immutable — nothing to destroy. */
}
Expand Down Expand Up @@ -702,11 +664,12 @@ static void OnThresholdCrossed(void* context)

/* Full teardown of every shared resource. Two entry points — `quit` (falls
* through after BddTargetInteractive_Run returns) and `set shutdown 1` — both
* route through here. f_unmount fires regardless so the next session's f_mount
* finds STORE*.log directory entries up-to-date (power_cycle_replay relies on
* this). The lifecycle mutex held across the SolidSyslog + store destroy keeps
* Service from racing the teardown. The platform's network teardown runs last,
* after the shared resources are released. */
* route through here. The platform UnmountStore hook fires regardless so the
* next session's mount finds STORE*.log directory entries up-to-date
* (power_cycle_replay relies on this). The lifecycle mutex held across the
* SolidSyslog + store destroy keeps Service from racing the teardown. The
* platform's network teardown runs last, after the shared resources are
* released. */
static void TeardownAll(void)
{
SolidSyslogMutex_Lock(lifecycleMutex);
Expand All @@ -720,11 +683,7 @@ static void TeardownAll(void)
SolidSyslogMetaSd_Destroy(metaSd);
SolidSyslogStdAtomicCounter_Destroy(atomicCounter);
DestroyCurrentStore();
if (fatfsMounted)
{
(void) f_unmount("");
fatfsMounted = false;
}
g_config->UnmountStore();
SolidSyslogMutex_Unlock(lifecycleMutex);

/* Wait for Service to observe the teardown flag and vTaskDelete itself
Expand Down Expand Up @@ -791,8 +750,8 @@ void BddTargetFreeRtosPipeline_InteractiveTask(void* argument)
* very first iteration without a NULL check. */
lifecycleMutex = SolidSyslogFreeRtosMutex_Create();

/* Default store is NullStore — flipped to FatFs/BlockStore by `set store
* file` via RebuildWithFileStore(). */
/* Default store is NullStore — flipped to the file-backed BlockStore by
* `set store file` via RebuildWithFileStore(). */
currentStore = SolidSyslogNullStore_Get();
currentStoreIsFile = false;

Expand Down
34 changes: 27 additions & 7 deletions Bdd/Targets/Common/BddTargetFreeRtosPipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@
#include "SolidSyslogSender.h"
#include "SolidSyslogStringFunction.h"

#include <stdbool.h>
#include <stdint.h>

/* Shared FreeRTOS BDD-target pipeline.
*
* Everything platform-independent lives in this component: the SolidSyslog
* lifecycle, the FatFs-backed store + security-policy machinery (crc16 /
* lifecycle, the file-backed store + security-policy machinery (crc16 /
* hmac-sha256 / aes-256-gcm / null), the SD set, the interactive `set` handler,
* the CircularBuffer + Service drain task, the console glue, and the
* mount/format-on-first-use semihosting FatFs path. Both FreeRTOS BDD targets
* (Bdd/Targets/FreeRtos = FreeRTOS-Plus-TCP, Bdd/Targets/FreeRtosLwip = lwIP)
* drive this; their main.c files keep only the network backend behind the seam
* below — the adapter wiring (PlusTcp vs LwipRaw) and the IP-stack bring-up that
* genuinely differ. See SolidSyslog S29.03. */
* the CircularBuffer + Service drain task, and the console glue. Both FreeRTOS
* BDD targets (Bdd/Targets/FreeRtos = FreeRTOS-Plus-TCP, Bdd/Targets/FreeRtosLwip
* = lwIP) drive this; their main.c files keep only the network backend and the
* FS-mount seam behind the config below — the network adapter wiring (PlusTcp vs
* LwipRaw), the IP-stack bring-up, and the filesystem vendor (FreeRTOS-Plus-FAT
* vs ChaN-FatFs) that genuinely differ. See SolidSyslog S29.03 (network seam)
* and S29.05 (FS-mount seam). */

/* Forward declaration — the FS-mount seam traffics in SolidSyslogFile handles
* without the pipeline header pulling SolidSyslogFile.h. */
struct SolidSyslogFile;

/* The platform seam each target injects via BddTargetFreeRtosPipeline_SetConfig. */
struct BddTargetFreeRtosPipelineConfig
Expand All @@ -35,6 +41,20 @@ struct BddTargetFreeRtosPipelineConfig
/* Tear down the sender + platform adapters. Runs on the interactive task
* after the shared pipeline teardown (SolidSyslog / SD / store / buffer). */
void (*TeardownNetwork)(void);

/* FS-mount seam — the FS-vendor-specific half of the file-backed store.
* The Plus-TCP target wires the FreeRTOS-Plus-FAT shim (BddTargetPlusFatMount);
* the lwIP target wires the ChaN-FatFs shim (BddTargetFatFsMount). The
* pipeline drives the store machinery (BlockStore / FileBlockDevice /
* security policy) FS-vendor-agnostically through these four hooks. */
/* Mount + format-on-first-use; idempotent; false on unrecoverable failure
* (the rebuild path then leaves the target on its current store). */
bool (*MountStore)(void);
/* Unmount on teardown; safe to call when not mounted. */
void (*UnmountStore)(void);
/* Create / destroy the platform SolidSyslogFile adapter for the store. */
struct SolidSyslogFile* (*CreateStoreFile)(void);
void (*DestroyStoreFile)(struct SolidSyslogFile* file);
};

/* Install the platform seam. Call once from main() before the tasks run. */
Expand Down
30 changes: 30 additions & 0 deletions Bdd/Targets/Common/BddTargetPlusFatMount.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* FreeRTOS-Plus-FAT implementation of the shared pipeline's FS-mount seam — see
* BddTargetPlusFatMount.h. The Plus-FAT sibling of BddTargetFatFsMount: the
* volume mount/unmount lives in the FF_Disk_t media driver (FFSemihostingDisk);
* the SolidSyslogFile adapter is SolidSyslogPlusFatFile. SolidSyslog S29.05. */

#include "BddTargetPlusFatMount.h"

#include "FFSemihostingDisk.h"
#include "SolidSyslogPlusFatFile.h"

bool BddTargetPlusFatMount_Mount(void)
{
return FFSemihostingDisk_Mount();
}

void BddTargetPlusFatMount_Unmount(void)
{
FFSemihostingDisk_Unmount();
}

struct SolidSyslogFile* BddTargetPlusFatMount_CreateFile(void)
{
return SolidSyslogPlusFatFile_Create();
}

void BddTargetPlusFatMount_DestroyFile(struct SolidSyslogFile* file)
{
/* PlusFatFile_Destroy → Close → ff_fclose flushes the file's dir entry. */
SolidSyslogPlusFatFile_Destroy(file);
}
Loading
Loading