SolidSyslog coverage report
Current view: top level - Core/Source - SolidSyslogBlockStoreStatic.c (source / functions) Coverage Total Hit
Test: SolidSyslog Lines: 100.0 % 80 80
Test Date: 2026-11-12 23:14:35 Functions: 100.0 % 10 10
Branches: 100.0 % 30 30

             Branch data     Line data    Source code
       1                 :             : /* SPDX-FileCopyrightText: Copyright 2026 Cozens Software Solutions Limited
       2                 :             :  * SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 OR LicenseRef-PolyForm-Internal-Use-1.0.0 OR LicenseRef-COSOSO-Commercial
       3                 :             :  */
       4                 :             : 
       5                 :             : #include "SolidSyslogBlockStore.h"
       6                 :             : 
       7                 :             : #include <stdbool.h>
       8                 :             : #include <stddef.h>
       9                 :             : #include <stdint.h>
      10                 :             : 
      11                 :             : #include "SolidSyslogBlockSequencePrivate.h"
      12                 :             : #include "SolidSyslogRecordStorePrivate.h"
      13                 :             : #include "SolidSyslogBlockDevice.h"
      14                 :             : #include "SolidSyslogBlockStoreErrors.h"
      15                 :             : #include "SolidSyslogBlockStorePrivate.h"
      16                 :             : #include "SolidSyslogError.h"
      17                 :             : #include "SolidSyslogErrorCategory.h"
      18                 :             : #include "SolidSyslogNullSecurityPolicy.h"
      19                 :             : #include "SolidSyslogNullStore.h"
      20                 :             : #include "SolidSyslogPoolAllocator.h"
      21                 :             : #include "SolidSyslogPrival.h"
      22                 :             : #include "SolidSyslogSecurityPolicyDefinition.h"
      23                 :             : #include "SolidSyslogTunables.h"
      24                 :             : 
      25                 :             : struct SolidSyslogStore;
      26                 :             : 
      27                 :             : static inline size_t BlockStore_IndexFromHandle(const struct SolidSyslogStore* base);
      28                 :             : static inline void BlockStore_CleanupAtIndex(size_t index, void* context);
      29                 :             : static bool BlockStore_IsValidConfig(const struct SolidSyslogBlockStoreConfig* config);
      30                 :             : static struct SolidSyslogSecurityPolicy* BlockStore_ResolveSecurityPolicy(struct SolidSyslogSecurityPolicy* configured);
      31                 :             : static inline bool BlockStore_TrailerFits(const struct SolidSyslogSecurityPolicy* policy);
      32                 :             : static struct SolidSyslogStore* BlockStore_CreateInSlot(
      33                 :             :     const struct SolidSyslogBlockStoreConfig* config,
      34                 :             :     struct SolidSyslogSecurityPolicy* policy
      35                 :             : );
      36                 :             : static struct SolidSyslogBlockSequenceConfig BlockStore_BuildBlockSequenceConfig(
      37                 :             :     const struct SolidSyslogBlockStoreConfig* config,
      38                 :             :     const struct SolidSyslogRecordStore* recordStore
      39                 :             : );
      40                 :             : static bool BlockStore_DeviceCanHoldOneRecord(
      41                 :             :     const struct SolidSyslogBlockStoreConfig* config,
      42                 :             :     const struct SolidSyslogRecordStore* recordStore
      43                 :             : );
      44                 :             : 
      45                 :             : static bool BlockStore_InUse[SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE];
      46                 :             : static struct SolidSyslogBlockStore BlockStore_Pool[SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE];
      47                 :             : static struct SolidSyslogPoolAllocator BlockStore_Allocator = {BlockStore_InUse, SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE};
      48                 :             : 
      49                 :       19196 : struct SolidSyslogStore* SolidSyslogBlockStore_Create(const struct SolidSyslogBlockStoreConfig* config)
      50                 :             : {
      51                 :       19196 :     struct SolidSyslogStore* result = SolidSyslogNullStore_Get();
      52                 :             : 
      53         [ +  + ]:       19196 :     if (BlockStore_IsValidConfig(config))
      54                 :             :     {
      55                 :       19188 :         result = BlockStore_CreateInSlot(config, BlockStore_ResolveSecurityPolicy(config->SecurityPolicy));
      56                 :             :     }
      57                 :             : 
      58                 :       19196 :     return result;
      59                 :             : }
      60                 :             : 
      61                 :       19196 : static bool BlockStore_IsValidConfig(const struct SolidSyslogBlockStoreConfig* config)
      62                 :             : {
      63                 :       19196 :     bool valid = false;
      64                 :             : 
      65         [ +  + ]:       19196 :     if (config == NULL)
      66                 :             :     {
      67                 :           3 :         BlockStore_Report(
      68                 :             :             SOLIDSYSLOG_BAD_CONFIG_FATAL_SEVERITY,
      69                 :             :             SOLIDSYSLOG_CAT_BAD_CONFIG,
      70                 :             :             SOLIDSYSLOG_BLOCK_STORE_ERROR_NULL_CONFIG
      71                 :             :         );
      72                 :             :     }
      73         [ +  + ]:       19193 :     else if (config->BlockDevice == NULL)
      74                 :             :     {
      75                 :           3 :         BlockStore_Report(
      76                 :             :             SOLIDSYSLOG_BAD_CONFIG_FATAL_SEVERITY,
      77                 :             :             SOLIDSYSLOG_CAT_BAD_CONFIG,
      78                 :             :             SOLIDSYSLOG_BLOCK_STORE_ERROR_NULL_BLOCK_DEVICE
      79                 :             :         );
      80                 :             :     }
      81         [ +  + ]:       19190 :     else if (!BlockStore_TrailerFits(BlockStore_ResolveSecurityPolicy(config->SecurityPolicy)))
      82                 :             :     {
      83                 :           2 :         BlockStore_Report(
      84                 :             :             SOLIDSYSLOG_BAD_CONFIG_FATAL_SEVERITY,
      85                 :             :             SOLIDSYSLOG_CAT_BAD_CONFIG,
      86                 :             :             SOLIDSYSLOG_BLOCK_STORE_ERROR_TRAILER_TOO_LARGE
      87                 :             :         );
      88                 :             :     }
      89                 :             :     else
      90                 :             :     {
      91                 :       19188 :         valid = true;
      92                 :             :     }
      93                 :             : 
      94                 :       19196 :     return valid;
      95                 :             : }
      96                 :             : 
      97                 :       38378 : static struct SolidSyslogSecurityPolicy* BlockStore_ResolveSecurityPolicy(struct SolidSyslogSecurityPolicy* configured)
      98                 :             : {
      99                 :       38378 :     struct SolidSyslogSecurityPolicy* resolved = configured;
     100                 :             : 
     101         [ +  + ]:       38378 :     if (resolved == NULL)
     102                 :             :     {
     103                 :       19242 :         resolved = SolidSyslogNullSecurityPolicy_Get();
     104                 :             :     }
     105                 :             : 
     106                 :       38378 :     return resolved;
     107                 :             : }
     108                 :             : 
     109                 :             : /* Every record's trailer is reserved at SOLIDSYSLOG_MAX_INTEGRITY_SIZE, so a policy
     110                 :             :  * with a wider one cannot seal into it. Storing records without that policy would
     111                 :             :  * keep them unprotected, which the policy contract forbids. */
     112                 :       19190 : static inline bool BlockStore_TrailerFits(const struct SolidSyslogSecurityPolicy* policy)
     113                 :             : {
     114                 :       19190 :     return policy->TrailerSize <= SOLIDSYSLOG_MAX_INTEGRITY_SIZE;
     115                 :             : }
     116                 :             : 
     117                 :       19188 : static struct SolidSyslogStore* BlockStore_CreateInSlot(
     118                 :             :     const struct SolidSyslogBlockStoreConfig* config,
     119                 :             :     struct SolidSyslogSecurityPolicy* policy
     120                 :             : )
     121                 :             : {
     122                 :       19188 :     struct SolidSyslogStore* result = SolidSyslogNullStore_Get();
     123                 :       19188 :     size_t index = SolidSyslogPoolAllocator_AcquireFirstFree(&BlockStore_Allocator);
     124                 :             : 
     125         [ +  + ]:       19188 :     if (SolidSyslogPoolAllocator_IndexIsValid(&BlockStore_Allocator, index))
     126                 :             :     {
     127                 :       19186 :         struct SolidSyslogRecordStore* recordStore = SolidSyslogRecordStore_Create(policy);
     128                 :             : 
     129         [ +  + ]:       19186 :         if (recordStore != NULL)
     130                 :             :         {
     131         [ +  + ]:       19185 :             if (!BlockStore_DeviceCanHoldOneRecord(config, recordStore))
     132                 :             :             {
     133                 :             :                 /* The device's block is smaller than one worst-case record. The store
     134                 :             :                  * still works - BuildBlockSequenceConfig grows the block to the minimum so
     135                 :             :                  * a record always fits - but the device was configured below a usable size,
     136                 :             :                  * so surface it as a WARNING (delivered, degraded) rather than failing. */
     137                 :           4 :                 BlockStore_Report(
     138                 :             :                     SOLIDSYSLOG_SEVERITY_WARNING,
     139                 :             :                     SOLIDSYSLOG_CAT_BAD_CONFIG,
     140                 :             :                     SOLIDSYSLOG_BLOCK_STORE_ERROR_BLOCK_TOO_SMALL
     141                 :             :                 );
     142                 :             :             }
     143                 :             : 
     144                 :             :             struct SolidSyslogBlockSequenceConfig blockConfig =
     145                 :       19185 :                 BlockStore_BuildBlockSequenceConfig(config, recordStore);
     146                 :       19185 :             struct SolidSyslogBlockSequence* blockSequence = SolidSyslogBlockSequence_Create(&blockConfig);
     147                 :             : 
     148         [ +  + ]:       19185 :             if (blockSequence != NULL)
     149                 :             :             {
     150                 :       19183 :                 SolidSyslogBlockStore_Initialise(&BlockStore_Pool[index].Base, recordStore, blockSequence);
     151                 :       19183 :                 result = &BlockStore_Pool[index].Base;
     152                 :             :             }
     153                 :             :             else
     154                 :             :             {
     155                 :           2 :                 SolidSyslogRecordStore_Destroy(recordStore);
     156                 :           2 :                 (void) SolidSyslogPoolAllocator_FreeIfInUse(&BlockStore_Allocator, index, NULL, NULL);
     157                 :             :             }
     158                 :             :         }
     159                 :             :         else
     160                 :             :         {
     161                 :           1 :             (void) SolidSyslogPoolAllocator_FreeIfInUse(&BlockStore_Allocator, index, NULL, NULL);
     162                 :             :         }
     163                 :             :     }
     164                 :             : 
     165         [ +  + ]:       19188 :     if (result == SolidSyslogNullStore_Get())
     166                 :             :     {
     167                 :           5 :         BlockStore_Report(
     168                 :             :             SOLIDSYSLOG_POOL_EXHAUSTED_SEVERITY,
     169                 :             :             SOLIDSYSLOG_CAT_POOL_EXHAUSTED,
     170                 :             :             SOLIDSYSLOG_BLOCK_STORE_ERROR_POOL_EXHAUSTED
     171                 :             :         );
     172                 :             :     }
     173                 :             : 
     174                 :       19188 :     return result;
     175                 :             : }
     176                 :             : 
     177                 :       19185 : static struct SolidSyslogBlockSequenceConfig BlockStore_BuildBlockSequenceConfig(
     178                 :             :     const struct SolidSyslogBlockStoreConfig* config,
     179                 :             :     const struct SolidSyslogRecordStore* recordStore
     180                 :             : )
     181                 :             : {
     182                 :             :     /* The device is the single source of truth for block size, but a block smaller than
     183                 :             :      * one worst-case record is grown to that floor so a single record always fits (Create
     184                 :             :      * has already emitted a WARNING for that case). */
     185                 :       19185 :     size_t minBlockSize = SolidSyslogRecordStore_RecordSize(recordStore, SOLIDSYSLOG_MAX_MESSAGE_SIZE);
     186                 :       19185 :     size_t deviceBlockSize = SolidSyslogBlockDevice_GetBlockSize(config->BlockDevice);
     187                 :       19185 :     size_t maxBlockSize = (deviceBlockSize < minBlockSize) ? minBlockSize : deviceBlockSize;
     188                 :             : 
     189                 :       19185 :     struct SolidSyslogBlockSequenceConfig blockConfig = {
     190                 :       19185 :         .BlockDevice = config->BlockDevice,
     191                 :             :         .MaxBlockSize = maxBlockSize,
     192                 :       19185 :         .MaxBlocks = config->MaxBlocks,
     193                 :       19185 :         .GetDiscardPolicy = config->GetDiscardPolicy,
     194                 :       19185 :         .DiscardPolicyContext = config->DiscardPolicyContext,
     195                 :       19185 :         .GetCapacityThreshold = config->GetCapacityThreshold,
     196                 :       19185 :         .OnThresholdCrossed = config->OnThresholdCrossed,
     197                 :       19185 :         .ThresholdContext = config->ThresholdContext,
     198                 :             :     };
     199                 :       19185 :     return blockConfig;
     200                 :             : }
     201                 :             : 
     202                 :             : /* True when the device's block can hold one worst-case record (max message + the active
     203                 :             :  * policy's trailer + record framing). When false the block is grown to that floor and a
     204                 :             :  * WARNING is emitted - the store works, but the device's configured size was degraded. */
     205                 :       19185 : static bool BlockStore_DeviceCanHoldOneRecord(
     206                 :             :     const struct SolidSyslogBlockStoreConfig* config,
     207                 :             :     const struct SolidSyslogRecordStore* recordStore
     208                 :             : )
     209                 :             : {
     210                 :       19185 :     size_t minBlockSize = SolidSyslogRecordStore_RecordSize(recordStore, SOLIDSYSLOG_MAX_MESSAGE_SIZE);
     211                 :       19185 :     return SolidSyslogBlockDevice_GetBlockSize(config->BlockDevice) >= minBlockSize;
     212                 :             : }
     213                 :             : 
     214                 :       19201 : void SolidSyslogBlockStore_Destroy(struct SolidSyslogStore* base)
     215                 :             : {
     216                 :       19201 :     size_t index = BlockStore_IndexFromHandle(base);
     217                 :       19201 :     bool released = false;
     218                 :             : 
     219         [ +  + ]:       19201 :     if (SolidSyslogPoolAllocator_IndexIsValid(&BlockStore_Allocator, index))
     220                 :             :     {
     221                 :             :         /* Pull the inner pool pointers out of the slot before FreeIfInUse runs
     222                 :             :          * the SolidSyslogBlockStore_Cleanup callback, because Cleanup overwrites the
     223                 :             :          * abstract base with the NullStore vtable but the derived RecordStore /
     224                 :             :          * BlockSequence pointers stay in the slot. After the outer FreeIfInUse
     225                 :             :          * releases the ConfigLock we destroy the inner slots - keeps each pool's
     226                 :             :          * lock acquisition sequential rather than nested. */
     227                 :             :         /* cppcheck-suppress variableScope -- read here, before FreeIfInUse, as the comment above says. */
     228                 :       19184 :         struct SolidSyslogRecordStore* recordStore = BlockStore_Pool[index].RecordStore;
     229                 :             :         /* cppcheck-suppress variableScope -- as above. */
     230                 :       19184 :         struct SolidSyslogBlockSequence* blockSequence = BlockStore_Pool[index].BlockSequence;
     231                 :             : 
     232                 :       19184 :         released = SolidSyslogPoolAllocator_FreeIfInUse(&BlockStore_Allocator, index, BlockStore_CleanupAtIndex, NULL);
     233                 :             : 
     234         [ +  + ]:       19184 :         if (released)
     235                 :             :         {
     236                 :       19183 :             SolidSyslogBlockSequence_Destroy(blockSequence);
     237                 :       19183 :             SolidSyslogRecordStore_Destroy(recordStore);
     238                 :             :         }
     239                 :             :     }
     240                 :             : 
     241         [ +  + ]:       19201 :     if (!released)
     242                 :             :     {
     243                 :          18 :         BlockStore_Report(
     244                 :             :             SOLIDSYSLOG_UNKNOWN_DESTROY_SEVERITY,
     245                 :             :             SOLIDSYSLOG_CAT_UNKNOWN_DESTROY,
     246                 :             :             SOLIDSYSLOG_BLOCK_STORE_ERROR_UNKNOWN_DESTROY
     247                 :             :         );
     248                 :             :     }
     249                 :       19201 : }
     250                 :             : 
     251                 :       19201 : static inline size_t BlockStore_IndexFromHandle(const struct SolidSyslogStore* base)
     252                 :             : {
     253                 :       19201 :     size_t result = SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE;
     254         [ +  + ]:       19218 :     for (size_t poolIndex = 0U; poolIndex < SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE; poolIndex++)
     255                 :             :     {
     256         [ +  + ]:       19201 :         if (base == &BlockStore_Pool[poolIndex].Base)
     257                 :             :         {
     258                 :       19184 :             result = poolIndex;
     259                 :       19184 :             break;
     260                 :             :         }
     261                 :             :     }
     262                 :       19201 :     return result;
     263                 :             : }
     264                 :             : 
     265                 :       19183 : static inline void BlockStore_CleanupAtIndex(size_t index, void* context)
     266                 :             : {
     267                 :             :     (void) context;
     268                 :       19183 :     SolidSyslogBlockStore_Cleanup(&BlockStore_Pool[index].Base);
     269                 :       19183 : }
        

Generated by: LCOV version 2.0-1