Skip to content
3 changes: 1 addition & 2 deletions Bdd/Targets/Common/BddTargetFreeRtosPipeline.c
Original file line number Diff line number Diff line change
Expand Up @@ -559,13 +559,12 @@ static bool RebuildWithFileStore(void)
DestroyCurrentStore();

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

struct SolidSyslogSecurityPolicy* policy = CreateSecurityPolicy();
currentPolicy = policy;
struct SolidSyslogBlockStoreConfig storeConfig = {
.BlockDevice = storeBlockDevice,
.MaxBlockSize = pendingMaxBlockSize,
.MaxBlocks = pendingMaxBlocks,
.DiscardPolicy = MapDiscardPolicy(pendingDiscardPolicy),
.SecurityPolicy = policy,
Expand Down
3 changes: 1 addition & 2 deletions Bdd/Targets/Linux/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,13 @@ static struct SolidSyslogStore* CreateStore(const struct BddTargetOptions* optio
{
storeFile = SolidSyslogPosixFile_Create();

storeBlockDevice = SolidSyslogFileBlockDevice_Create(storeFile, STORE_PATH_PREFIX);
storeBlockDevice = SolidSyslogFileBlockDevice_Create(storeFile, STORE_PATH_PREFIX, options->MaxBlockSize);

static size_t capacityThreshold;
capacityThreshold = options->CapacityThreshold;
securityPolicy = CreateSecurityPolicy(options);
static struct SolidSyslogBlockStoreConfig storeConfig = {0};
storeConfig.BlockDevice = storeBlockDevice;
storeConfig.MaxBlockSize = options->MaxBlockSize;
storeConfig.MaxBlocks = options->MaxBlocks;
storeConfig.DiscardPolicy = MapDiscardPolicy(options->DiscardPolicy);
storeConfig.SecurityPolicy = securityPolicy;
Expand Down
3 changes: 1 addition & 2 deletions Bdd/Targets/Windows/BddTargetWindows.c
Original file line number Diff line number Diff line change
Expand Up @@ -284,14 +284,13 @@ static struct SolidSyslogStore* CreateStore(const struct BddTargetWindowsOptions
{
storeFile = SolidSyslogWindowsFile_Create();

storeBlockDevice = SolidSyslogFileBlockDevice_Create(storeFile, STORE_PATH_PREFIX);
storeBlockDevice = SolidSyslogFileBlockDevice_Create(storeFile, STORE_PATH_PREFIX, options->MaxBlockSize);

static size_t capacityThreshold;
capacityThreshold = options->CapacityThreshold;
securityPolicy = CreateSecurityPolicy(options);
static struct SolidSyslogBlockStoreConfig storeConfig = {0};
storeConfig.BlockDevice = storeBlockDevice;
storeConfig.MaxBlockSize = options->MaxBlockSize;
storeConfig.MaxBlocks = options->MaxBlocks;
storeConfig.DiscardPolicy = MapDiscardPolicy(options->DiscardPolicy);
storeConfig.SecurityPolicy = securityPolicy;
Expand Down
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,10 +404,10 @@ live under `Core/Interface/`; platform-specific helpers (the `SolidSyslogPosix*`
| `SolidSyslogFileDefinition.h` | File implementors (extension point) | `SolidSyslogFile` vtable struct |
| `SolidSyslogFile.h` | Any code using the file abstraction | `SolidSyslogFile_Open`, `_Close`, `_IsOpen`, `_Read`, `_Write`, `_SeekTo`, `_Size`, `_Truncate` |
| `SolidSyslogNullFile.h` | Any code installing a no-op file slot (Open/IsOpen/Read/Exists return `false`, Write/Delete return `true` so callers' success paths are not tripped, SeekTo/Truncate/Close are no-ops, Size returns `0`) | `SolidSyslogNullFile_Get` |
| `SolidSyslogBlockDevice.h` | Library internals consuming a block device (BlockSequence inside BlockStore) and integrator code addressing blocks directly | `SolidSyslogBlockDevice_Acquire`, `_Dispose`, `_Exists`, `_Read`, `_Append`, `_WriteAt`, `_Size` (block-indexed I/O; Acquire makes a block ready for fresh writes, Dispose releases it) |
| `SolidSyslogBlockDevice.h` | Library internals consuming a block device (BlockSequence inside BlockStore) and integrator code addressing blocks directly | `SolidSyslogBlockDevice_Acquire`, `_Dispose`, `_Exists`, `_Read`, `_Append`, `_WriteAt`, `_Size`, `_GetBlockSize` (block-indexed I/O; Acquire makes a block ready for fresh writes, Dispose releases it; `_Size(blockIndex)` is a block's current occupancy, `_GetBlockSize()` is the device-wide per-block capacity — the device is the single source of truth a BlockStore reads at construction) |
| `SolidSyslogNullBlockDevice.h` | Any code installing a no-op block device slot (every method returns `false` / `0` — disk doesn't exist) | `SolidSyslogNullBlockDevice_Get` |
| `SolidSyslogBlockDeviceDefinition.h` | BlockDevice implementors (extension point) | `SolidSyslogBlockDevice` vtable struct (`Acquire`, `Dispose`, `Exists`, `Read`, `Append`, `WriteAt`, `Size`) |
| `SolidSyslogFileBlockDevice.h` | System setup code wiring a file-backed block device | `SolidSyslogFileBlockDevice_Create(file, pathPrefix)` (sequence-numbered filenames `<prefix><NN>.log`, one cached file handle), `_Destroy(base)`. Instance struct lives in a library-internal static pool (E11); `_Destroy` takes the handle returned by `_Create`. Pool exhaustion resolves to the shared `SolidSyslogNullBlockDevice`. |
| `SolidSyslogBlockDeviceDefinition.h` | BlockDevice implementors (extension point) | `SolidSyslogBlockDevice` vtable struct (`Acquire`, `Dispose`, `Exists`, `Read`, `Append`, `WriteAt`, `Size`, `GetBlockSize`) |
| `SolidSyslogFileBlockDevice.h` | System setup code wiring a file-backed block device | `SolidSyslogFileBlockDevice_Create(file, pathPrefix, blockSize)` (sequence-numbered filenames `<prefix><NN>.log`, one cached file handle; `blockSize` is the per-block capacity reported via `GetBlockSize`, `0` selects the `SOLIDSYSLOG_FILE_DEFAULT_BLOCK_SIZE` tunable), `_Destroy(base)`. Instance struct lives in a library-internal static pool (E11); `_Destroy` takes the handle returned by `_Create`. Pool exhaustion resolves to the shared `SolidSyslogNullBlockDevice`. |
| `SolidSyslogFileBlockDeviceErrors.h` | Any code installing an error handler that wants to react to FileBlockDevice-specific events (pointer-identity match on `FileBlockDeviceErrorSource`, switch on `enum SolidSyslogFileBlockDeviceErrors`) | `enum SolidSyslogFileBlockDeviceErrors` (`FILEBLOCKDEVICE_ERROR_*` codes + `FILEBLOCKDEVICE_ERROR_MAX` bookend), `extern const struct SolidSyslogErrorSource FileBlockDeviceErrorSource`. Integrators ignore if not handling errors per source. |
| `SolidSyslogBlockStore.h` | System setup code using a BlockDevice-backed store | `SolidSyslogBlockStoreConfig` (with `blockDevice`, `storeFullContext`, `getCapacityThreshold`, `onThresholdCrossed`, `thresholdContext`), `SolidSyslogBlockStore_Create(config)`, `_Destroy(store)`, `SolidSyslogDiscardPolicy` (`SolidSyslogDiscardPolicy_Oldest` / `_Newest` / `_Halt`), `SolidSyslogStoreFullCallback(void* context)`, `SolidSyslogStoreThresholdFunction(void* context)` (returns threshold in bytes; 0 disables; queried each Write), `SolidSyslogStoreThresholdCallback(void* context)` (edge-triggered; PassthroughBuffer recursion gotcha — see header). Instance struct lives in a library-internal static pool (E11); each slot composes one inner RecordStore + BlockSequence drawn 1:1 from sibling pools sized off the same `SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE` tunable. Pool exhaustion (or either inner Create failing) resolves to the shared `SolidSyslogNullStore`. |
| `SolidSyslogBlockStoreErrors.h` | Any code installing an error handler that wants to react to BlockStore-specific events (pointer-identity match on `BlockStoreErrorSource`, switch on `enum SolidSyslogBlockStoreErrors`) | `enum SolidSyslogBlockStoreErrors` (`BLOCKSTORE_ERROR_*` codes + `BLOCKSTORE_ERROR_MAX` bookend), `extern const struct SolidSyslogErrorSource BlockStoreErrorSource`. Integrators ignore if not handling errors per source. |
Expand Down
6 changes: 6 additions & 0 deletions Core/Interface/SolidSyslogBlockDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ EXTERN_C_BEGIN
);
size_t SolidSyslogBlockDevice_Size(struct SolidSyslogBlockDevice * device, size_t blockIndex);

/* The device-wide block capacity in bytes — how large each block can grow. Distinct from
* Size(blockIndex), which reports the current occupancy of one block. The device is the
* single source of truth for this value; a BlockStore reads it at construction rather than
* taking a separate configured size. */
size_t SolidSyslogBlockDevice_GetBlockSize(struct SolidSyslogBlockDevice * device);

EXTERN_C_END

#endif /* SOLIDSYSLOGBLOCKDEVICE_H */
1 change: 1 addition & 0 deletions Core/Interface/SolidSyslogBlockDeviceDefinition.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ EXTERN_C_BEGIN
size_t count
);
size_t (*Size)(struct SolidSyslogBlockDevice* base, size_t blockIndex);
size_t (*GetBlockSize)(struct SolidSyslogBlockDevice* base);
};

EXTERN_C_END
Expand Down
1 change: 0 additions & 1 deletion Core/Interface/SolidSyslogBlockStore.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ EXTERN_C_BEGIN
/* Required. Caller-owned: must outlive the BlockStore. SolidSyslogBlockStore_Destroy
* does NOT destroy the block device — that is the integrator's responsibility. */
struct SolidSyslogBlockDevice* BlockDevice;
size_t MaxBlockSize;
size_t MaxBlocks;
enum SolidSyslogDiscardPolicy DiscardPolicy;
struct SolidSyslogSecurityPolicy* SecurityPolicy;
Expand Down
1 change: 1 addition & 0 deletions Core/Interface/SolidSyslogBlockStoreErrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ EXTERN_C_BEGIN
{
BLOCKSTORE_ERROR_POOL_EXHAUSTED,
BLOCKSTORE_ERROR_UNKNOWN_DESTROY,
BLOCKSTORE_ERROR_BLOCK_TOO_SMALL,
BLOCKSTORE_ERROR_MAX
};

Expand Down
11 changes: 9 additions & 2 deletions Core/Interface/SolidSyslogFileBlockDevice.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef SOLIDSYSLOGFILEBLOCKDEVICE_H
#define SOLIDSYSLOGFILEBLOCKDEVICE_H

#include <stddef.h>

#include "ExternC.h"

struct SolidSyslogBlockDevice;
Expand All @@ -12,10 +14,15 @@ EXTERN_C_BEGIN
* targeted blockIndex changes — same-block runs of Read and Append (and Append-then-WriteAt
* during MarkSent) share the handle. The single-handle-per-path invariant the storage layer
* depends on (E27 #345 / S27.01) is enforced by construction here: the driver physically
* holds one file. */
* holds one file.
*
* blockSize is the per-block capacity the device reports via
* SolidSyslogBlockDevice_GetBlockSize. Pass SOLIDSYSLOG_FILE_DEFAULT_BLOCK_SIZE for the
* default. */
struct SolidSyslogBlockDevice* SolidSyslogFileBlockDevice_Create(
struct SolidSyslogFile * file,
const char* pathPrefix
const char* pathPrefix,
size_t blockSize
);
void SolidSyslogFileBlockDevice_Destroy(struct SolidSyslogBlockDevice * base);

Expand Down
23 changes: 23 additions & 0 deletions Core/Interface/SolidSyslogTunablesDefaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,29 @@
#error "SOLIDSYSLOG_MAX_INTEGRITY_SIZE must be >= 4"
#endif

/*
* Default per-block capacity (bytes) for file-backed block devices
* (SolidSyslogFileBlockDevice and any FatFs / FreeRTOS-Plus-FAT-backed
* equivalent). Supplied to SolidSyslogFileBlockDevice_Create when the
* integrator has no specific size in mind; passing 0 to _Create selects
* this default. A larger block holds more records before rotating to a
* fresh file; a smaller block rotates (and fsyncs) more often.
*
* Floor: one worst-case record — the RFC 5424 max message plus the widest
* integrity tag plus the 5-byte record framing (2 magic + 2 length +
* 1 sent-flag). Below that a block could not hold a single record, so the
* default must clear it for every SecurityPolicy. Sub-floor values
* rejected at compile time.
*/
#ifndef SOLIDSYSLOG_FILE_DEFAULT_BLOCK_SIZE
#define SOLIDSYSLOG_FILE_DEFAULT_BLOCK_SIZE 8192U
#endif

#if SOLIDSYSLOG_FILE_DEFAULT_BLOCK_SIZE < (SOLIDSYSLOG_MAX_MESSAGE_SIZE + SOLIDSYSLOG_MAX_INTEGRITY_SIZE + 5)
#error \
"SOLIDSYSLOG_FILE_DEFAULT_BLOCK_SIZE must hold one worst-case record (MAX_MESSAGE_SIZE + MAX_INTEGRITY_SIZE + 5 framing bytes)"
#endif

/*
* Number of SolidSyslog instances the library's internal static pool
* can simultaneously hold. Each instance is a small bookkeeping struct
Expand Down
5 changes: 5 additions & 0 deletions Core/Source/SolidSyslogBlockDevice.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,8 @@ size_t SolidSyslogBlockDevice_Size(struct SolidSyslogBlockDevice* device, size_t
{
return device->Size(device, blockIndex);
}

size_t SolidSyslogBlockDevice_GetBlockSize(struct SolidSyslogBlockDevice* device)
{
return device->GetBlockSize(device);
}
36 changes: 35 additions & 1 deletion Core/Source/SolidSyslogBlockStoreStatic.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "BlockSequencePrivate.h"
#include "RecordStorePrivate.h"
#include "SolidSyslogBlockDevice.h"
#include "SolidSyslogBlockStoreErrors.h"
#include "SolidSyslogBlockStorePrivate.h"
#include "SolidSyslogError.h"
Expand All @@ -26,6 +27,10 @@ static struct BlockSequenceConfig BlockStore_BuildBlockSequenceConfig(
const struct SolidSyslogBlockStoreConfig* config,
const struct RecordStore* recordStore
);
static bool BlockStore_DeviceCanHoldOneRecord(
const struct SolidSyslogBlockStoreConfig* config,
const struct RecordStore* recordStore
);

static bool BlockStore_InUse[SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE];
static struct SolidSyslogBlockStore BlockStore_Pool[SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE];
Expand All @@ -43,6 +48,19 @@ struct SolidSyslogStore* SolidSyslogBlockStore_Create(const struct SolidSyslogBl

if (recordStore != NULL)
{
if (!BlockStore_DeviceCanHoldOneRecord(config, recordStore))
{
/* The device's block is smaller than one worst-case record. The store
* still works — BuildBlockSequenceConfig grows the block to the minimum so
* a record always fits — but the device was configured below a usable size,
* so surface it as a WARNING (delivered, degraded) rather than failing. */
BlockStore_Report(
SOLIDSYSLOG_SEVERITY_WARNING,
SOLIDSYSLOG_CAT_BAD_CONFIG,
BLOCKSTORE_ERROR_BLOCK_TOO_SMALL
);
}

struct BlockSequenceConfig blockConfig = BlockStore_BuildBlockSequenceConfig(config, recordStore);
struct BlockSequence* blockSequence = BlockSequence_Create(&blockConfig);

Expand Down Expand Up @@ -88,8 +106,12 @@ static struct BlockSequenceConfig BlockStore_BuildBlockSequenceConfig(
const struct RecordStore* recordStore
)
{
/* The device is the single source of truth for block size, but a block smaller than
* one worst-case record is grown to that floor so a single record always fits (Create
* has already emitted a WARNING for that case). */
size_t minBlockSize = RecordStore_RecordSize(recordStore, SOLIDSYSLOG_MAX_MESSAGE_SIZE);
size_t maxBlockSize = (config->MaxBlockSize < minBlockSize) ? minBlockSize : config->MaxBlockSize;
size_t deviceBlockSize = SolidSyslogBlockDevice_GetBlockSize(config->BlockDevice);
size_t maxBlockSize = (deviceBlockSize < minBlockSize) ? minBlockSize : deviceBlockSize;

struct BlockSequenceConfig blockConfig = {
.BlockDevice = config->BlockDevice,
Expand All @@ -105,6 +127,18 @@ static struct BlockSequenceConfig BlockStore_BuildBlockSequenceConfig(
return blockConfig;
}

/* True when the device's block can hold one worst-case record (max message + the active
* policy's trailer + record framing). When false the block is grown to that floor and a
* WARNING is emitted — the store works, but the device's configured size was degraded. */
static bool BlockStore_DeviceCanHoldOneRecord(
const struct SolidSyslogBlockStoreConfig* config,
const struct RecordStore* recordStore
)
{
size_t minBlockSize = RecordStore_RecordSize(recordStore, SOLIDSYSLOG_MAX_MESSAGE_SIZE);
return SolidSyslogBlockDevice_GetBlockSize(config->BlockDevice) >= minBlockSize;
}

void SolidSyslogBlockStore_Destroy(struct SolidSyslogStore* base)
{
size_t index = BlockStore_IndexFromHandle(base);
Expand Down
11 changes: 10 additions & 1 deletion Core/Source/SolidSyslogFileBlockDevice.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,16 @@ static bool FileBlockDevice_WriteAt(
);
// NOLINTEND(bugprone-easily-swappable-parameters)
static size_t FileBlockDevice_Size(struct SolidSyslogBlockDevice* base, size_t blockIndex);
static size_t FileBlockDevice_GetBlockSize(struct SolidSyslogBlockDevice* base);

static inline struct SolidSyslogFileBlockDevice* FileBlockDevice_SelfFromBase(struct SolidSyslogBlockDevice* base);
static inline void FileBlockDevice_CloseIfOpen(struct OpenHandle* handle);

void FileBlockDevice_Initialise(
struct SolidSyslogBlockDevice* base,
struct SolidSyslogFile* file,
const char* pathPrefix
const char* pathPrefix,
size_t blockSize
)
{
struct SolidSyslogFileBlockDevice* self = FileBlockDevice_SelfFromBase(base);
Expand All @@ -82,8 +84,10 @@ void FileBlockDevice_Initialise(
self->Base.Append = FileBlockDevice_Append;
self->Base.WriteAt = FileBlockDevice_WriteAt;
self->Base.Size = FileBlockDevice_Size;
self->Base.GetBlockSize = FileBlockDevice_GetBlockSize;
self->Handle = (struct OpenHandle) {.File = file, .BlockIndex = 0, .IsOpen = false};
self->PathPrefix = pathPrefix;
self->BlockSize = blockSize;
}

void FileBlockDevice_Cleanup(struct SolidSyslogBlockDevice* base)
Expand Down Expand Up @@ -361,3 +365,8 @@ static size_t FileBlockDevice_Size(struct SolidSyslogBlockDevice* base, size_t b

return size;
}

static size_t FileBlockDevice_GetBlockSize(struct SolidSyslogBlockDevice* base)
{
return FileBlockDevice_SelfFromBase(base)->BlockSize;
}
4 changes: 3 additions & 1 deletion Core/Source/SolidSyslogFileBlockDevicePrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ struct SolidSyslogFileBlockDevice
struct SolidSyslogBlockDevice Base;
struct OpenHandle Handle;
const char* PathPrefix;
size_t BlockSize;
};

void FileBlockDevice_Initialise(
struct SolidSyslogBlockDevice* base,
struct SolidSyslogFile* file,
const char* pathPrefix
const char* pathPrefix,
size_t blockSize
);
void FileBlockDevice_Cleanup(struct SolidSyslogBlockDevice* base);

Expand Down
9 changes: 7 additions & 2 deletions Core/Source/SolidSyslogFileBlockDeviceStatic.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@ static struct SolidSyslogPoolAllocator FileBlockDevice_Allocator = {
SOLIDSYSLOG_FILE_BLOCK_DEVICE_POOL_SIZE
};

struct SolidSyslogBlockDevice* SolidSyslogFileBlockDevice_Create(struct SolidSyslogFile* file, const char* pathPrefix)
struct SolidSyslogBlockDevice* SolidSyslogFileBlockDevice_Create(
struct SolidSyslogFile* file,
const char* pathPrefix,
size_t blockSize
)
{
struct SolidSyslogBlockDevice* result = SolidSyslogNullBlockDevice_Get();
size_t resolvedBlockSize = (blockSize == 0U) ? (size_t) SOLIDSYSLOG_FILE_DEFAULT_BLOCK_SIZE : blockSize;
size_t index = SolidSyslogPoolAllocator_AcquireFirstFree(&FileBlockDevice_Allocator);
if (SolidSyslogPoolAllocator_IndexIsValid(&FileBlockDevice_Allocator, index))
{
FileBlockDevice_Initialise(&FileBlockDevice_Pool[index].Base, file, pathPrefix);
FileBlockDevice_Initialise(&FileBlockDevice_Pool[index].Base, file, pathPrefix, resolvedBlockSize);
result = &FileBlockDevice_Pool[index].Base;
}
else
Expand Down
8 changes: 8 additions & 0 deletions Core/Source/SolidSyslogNullBlockDevice.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ static bool NullBlockDevice_Append(
size_t count
);
static size_t NullBlockDevice_Size(struct SolidSyslogBlockDevice* base, size_t blockIndex);
static size_t NullBlockDevice_GetBlockSize(struct SolidSyslogBlockDevice* base);

struct SolidSyslogBlockDevice* SolidSyslogNullBlockDevice_Get(void)
{
Expand All @@ -42,6 +43,7 @@ struct SolidSyslogBlockDevice* SolidSyslogNullBlockDevice_Get(void)
.Append = NullBlockDevice_Append,
.WriteAt = NullBlockDevice_WriteAt,
.Size = NullBlockDevice_Size,
.GetBlockSize = NullBlockDevice_GetBlockSize,
};
return &instance;
}
Expand Down Expand Up @@ -125,3 +127,9 @@ static size_t NullBlockDevice_Size(struct SolidSyslogBlockDevice* base, size_t b
(void) blockIndex;
return 0;
}

static size_t NullBlockDevice_GetBlockSize(struct SolidSyslogBlockDevice* base)
{
(void) base;
return 0;
}
Loading
Loading