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

             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 <stdint.h>
       9                 :             : 
      10                 :             : #include "SolidSyslogBlockSequencePrivate.h"
      11                 :             : #include "SolidSyslogRecordStorePrivate.h"
      12                 :             : #include "SolidSyslogBlockDevice.h"
      13                 :             : #include "SolidSyslogBlockStoreErrors.h"
      14                 :             : #include "SolidSyslogBlockStorePrivate.h"
      15                 :             : #include "SolidSyslogError.h"
      16                 :             : #include "SolidSyslogNullStore.h"
      17                 :             : #include "SolidSyslogStoreCategories.h"
      18                 :             : #include "SolidSyslogStoreDefinition.h"
      19                 :             : 
      20                 :             : const struct SolidSyslogErrorSource SolidSyslogBlockStoreErrorSource = {"BlockStore", SolidSyslogBlockStoreErrors_Name};
      21                 :             : 
      22                 :             : /* vtable - forward-declared because BlockStore_InitialiseVtable references them before their definitions */
      23                 :             : static enum SolidSyslogStoreWriteResult BlockStore_Write(struct SolidSyslogStore* base, const void* data, size_t size);
      24                 :             : static bool BlockStore_ReadNextUnsent(struct SolidSyslogStore* base, void* data, size_t maxSize, size_t* bytesRead);
      25                 :             : static void BlockStore_MarkSent(struct SolidSyslogStore* base);
      26                 :             : static bool BlockStore_HasUnsent(struct SolidSyslogStore* base);
      27                 :             : static size_t BlockStore_GetTotalBytes(struct SolidSyslogStore* base);
      28                 :             : static size_t BlockStore_GetUsedBytes(struct SolidSyslogStore* base);
      29                 :             : static bool BlockStore_IsTransient(struct SolidSyslogStore* base);
      30                 :             : 
      31                 :             : static inline struct SolidSyslogBlockStore* BlockStore_SelfFromBase(struct SolidSyslogStore* base);
      32                 :             : static inline void BlockStore_InitialiseVtable(struct SolidSyslogBlockStore* self);
      33                 :             : static void BlockStore_ResumeFromExistingBlock(struct SolidSyslogBlockStore* self);
      34                 :             : static enum SolidSyslogRecordStoreScanResult BlockStore_ScanReadBlock(struct SolidSyslogBlockStore* self);
      35                 :             : static void BlockStore_CheckWriteBlock(struct SolidSyslogBlockStore* self);
      36                 :             : static bool BlockStore_AbandonRestOfReadBlock(struct SolidSyslogBlockStore* self);
      37                 :             : static void BlockStore_Freeze(struct SolidSyslogBlockStore* self, enum SolidSyslogBlockStoreErrors failedOperation);
      38                 :             : 
      39                 :             : /* ------------------------------------------------------------------
      40                 :             :  * Initialise / Cleanup - private lifecycle pair invoked by Static.c.
      41                 :             :  *
      42                 :             :  * Initialise stores the two inner pool pointers (Static.c acquired them
      43                 :             :  * before this call), wires the vtable, and runs the existing-block
      44                 :             :  * resume scan. Cleanup is a pure vtable swap to NullStore for
      45                 :             :  * use-after-destroy crash-safety - Static.c destroys the inner pool
      46                 :             :  * slots outside the outer FreeIfInUse lock, which keeps the per-pool
      47                 :             :  * ConfigLock acquisitions sequential rather than nested.
      48                 :             :  * ----------------------------------------------------------------*/
      49                 :             : 
      50                 :       19183 : void SolidSyslogBlockStore_Initialise(
      51                 :             :     struct SolidSyslogStore* base,
      52                 :             :     struct SolidSyslogRecordStore* recordStore,
      53                 :             :     struct SolidSyslogBlockSequence* blockSequence
      54                 :             : )
      55                 :             : {
      56                 :       19183 :     struct SolidSyslogBlockStore* self = BlockStore_SelfFromBase(base);
      57                 :       19183 :     self->RecordStore = recordStore;
      58                 :       19183 :     self->BlockSequence = blockSequence;
      59                 :       19183 :     self->LossReported = false;
      60                 :       19183 :     self->RefusalReported = false;
      61                 :       19183 :     self->EvictionReported = false;
      62                 :       19183 :     self->SealFailureReported = false;
      63                 :       19183 :     BlockStore_InitialiseVtable(self);
      64                 :             : 
      65         [ +  + ]:       19183 :     if (SolidSyslogBlockSequence_Open(self->BlockSequence))
      66                 :             :     {
      67                 :       18859 :         BlockStore_ResumeFromExistingBlock(self);
      68                 :             :     }
      69                 :             :     else
      70                 :             :     {
      71                 :         324 :         BlockStore_Freeze(self, SolidSyslogBlockSequence_FailedOperation(self->BlockSequence));
      72                 :             :     }
      73                 :       19183 : }
      74                 :             : 
      75                 :       21490 : void SolidSyslogBlockStore_Cleanup(struct SolidSyslogStore* base)
      76                 :             : {
      77                 :             :     /* Overwrite the abstract base with the shared NullStore vtable so
      78                 :             :      * use-after-destroy is a safe no-op rather than a NULL-fn-pointer crash.
      79                 :             :      * The inner pool pointers are still in the slot at this point - Static.c
      80                 :             :      * pulls them out before FreeIfInUse and destroys them after the outer
      81                 :             :      * lock is released. */
      82                 :       21490 :     *base = *SolidSyslogNullStore_Get();
      83                 :       21490 : }
      84                 :             : 
      85                 :      996680 : static inline struct SolidSyslogBlockStore* BlockStore_SelfFromBase(struct SolidSyslogStore* base)
      86                 :             : {
      87                 :      996680 :     return (struct SolidSyslogBlockStore*) base;
      88                 :             : }
      89                 :             : 
      90                 :       19183 : static inline void BlockStore_InitialiseVtable(struct SolidSyslogBlockStore* self)
      91                 :             : {
      92                 :       19183 :     self->Base.Write = BlockStore_Write;
      93                 :       19183 :     self->Base.ReadNextUnsent = BlockStore_ReadNextUnsent;
      94                 :       19183 :     self->Base.MarkSent = BlockStore_MarkSent;
      95                 :       19183 :     self->Base.HasUnsent = BlockStore_HasUnsent;
      96                 :       19183 :     self->Base.GetTotalBytes = BlockStore_GetTotalBytes;
      97                 :       19183 :     self->Base.GetUsedBytes = BlockStore_GetUsedBytes;
      98                 :       19183 :     self->Base.IsTransient = BlockStore_IsTransient;
      99                 :       19183 : }
     100                 :             : 
     101                 :             : /* A corrupt block is abandoned and the block after it scanned in its turn, so
     102                 :             :  * the cursor comes to rest on the first record still to send. */
     103                 :       18859 : static void BlockStore_ResumeFromExistingBlock(struct SolidSyslogBlockStore* self)
     104                 :             : {
     105                 :       18859 :     enum SolidSyslogRecordStoreScanResult scanned = BlockStore_ScanReadBlock(self);
     106                 :             : 
     107   [ +  +  +  + ]:       18972 :     while ((scanned == SOLIDSYSLOG_RECORD_STORE_SCAN_CORRUPT) && BlockStore_AbandonRestOfReadBlock(self))
     108                 :             :     {
     109                 :         113 :         scanned = BlockStore_ScanReadBlock(self);
     110                 :             :     }
     111                 :             : 
     112                 :             :     /* A read that failed, or a dispose that failed on leaving a corrupt block,
     113                 :             :      * has frozen the store: the device is not asked about the write block. */
     114         [ +  + ]:       18859 :     if ((scanned != SOLIDSYSLOG_RECORD_STORE_SCAN_DEVICE_FAILED) &&
     115         [ +  + ]:       18670 :         !SolidSyslogBlockSequence_DeviceFailed(self->BlockSequence))
     116                 :             :     {
     117                 :       18669 :         BlockStore_CheckWriteBlock(self);
     118                 :             :     }
     119                 :       18859 : }
     120                 :             : 
     121                 :             : /* Puts the cursor on the read block's first unsent record, or at its end where
     122                 :             :  * the records stop being readable. A device that fails freezes the store. */
     123                 :       18972 : static enum SolidSyslogRecordStoreScanResult BlockStore_ScanReadBlock(struct SolidSyslogBlockStore* self)
     124                 :             : {
     125                 :       18972 :     size_t readBlockSize = 0U;
     126                 :       18972 :     enum SolidSyslogRecordStoreScanResult scanned = SOLIDSYSLOG_RECORD_STORE_SCAN_DEVICE_FAILED;
     127                 :             : 
     128         [ +  + ]:       18972 :     if (SolidSyslogBlockSequence_ReadBlockSize(self->BlockSequence, &readBlockSize))
     129                 :             :     {
     130                 :       18911 :         size_t cursor = SolidSyslogRecordStore_FindFirstUnsent(
     131                 :             :             self->RecordStore,
     132                 :       18911 :             SolidSyslogBlockSequence_BlockDevice(self->BlockSequence),
     133                 :       18911 :             SolidSyslogBlockSequence_ReadSequence(self->BlockSequence),
     134                 :             :             readBlockSize,
     135                 :             :             &scanned
     136                 :             :         );
     137                 :       18911 :         SolidSyslogBlockSequence_SetReadCursor(self->BlockSequence, cursor);
     138                 :             : 
     139         [ +  + ]:       18911 :         if (scanned == SOLIDSYSLOG_RECORD_STORE_SCAN_DEVICE_FAILED)
     140                 :             :         {
     141                 :         128 :             BlockStore_Freeze(self, SOLIDSYSLOG_BLOCK_STORE_ERROR_READ_FAILED);
     142                 :             :         }
     143                 :             :     }
     144                 :             :     else
     145                 :             :     {
     146                 :          61 :         BlockStore_Freeze(self, SOLIDSYSLOG_BLOCK_STORE_ERROR_SIZE_FAILED);
     147                 :             :     }
     148                 :             : 
     149                 :       18972 :     return scanned;
     150                 :             : }
     151                 :             : 
     152                 :             : /* Nothing reads the write block past its first unsent record before the next
     153                 :             :  * write appends after its last, so the block has to end on a record boundary
     154                 :             :  * now, or the write would land inside a record a power cut cut short. The scan
     155                 :             :  * has walked the read block as far as the cursor; a write block the read has
     156                 :             :  * not reached is walked whole. */
     157                 :       18669 : static void BlockStore_CheckWriteBlock(struct SolidSyslogBlockStore* self)
     158                 :             : {
     159                 :       18669 :     size_t from = SolidSyslogBlockSequence_ReadIsBehindWrite(self->BlockSequence)
     160                 :             :                       ? 0U
     161         [ +  + ]:       18669 :                       : SolidSyslogBlockSequence_ReadCursor(self->BlockSequence);
     162                 :       18669 :     enum SolidSyslogRecordStoreScanResult scanned = SolidSyslogRecordStore_ScanToEnd(
     163                 :             :         self->RecordStore,
     164                 :       18669 :         SolidSyslogBlockSequence_BlockDevice(self->BlockSequence),
     165                 :       18669 :         SolidSyslogBlockSequence_WriteSequence(self->BlockSequence),
     166                 :             :         from,
     167                 :       18669 :         SolidSyslogBlockSequence_WritePosition(self->BlockSequence)
     168                 :             :     );
     169                 :             : 
     170         [ +  + ]:       18669 :     if (scanned == SOLIDSYSLOG_RECORD_STORE_SCAN_DEVICE_FAILED)
     171                 :             :     {
     172                 :         115 :         BlockStore_Freeze(self, SOLIDSYSLOG_BLOCK_STORE_ERROR_READ_FAILED);
     173                 :             :     }
     174         [ +  + ]:       18554 :     else if (scanned == SOLIDSYSLOG_RECORD_STORE_SCAN_CORRUPT)
     175                 :             :     {
     176                 :        1227 :         SolidSyslogBlockSequence_MarkWriteBlockCorrupt(self->BlockSequence);
     177                 :             :     }
     178                 :             :     else
     179                 :             :     {
     180                 :             :         /* Whole to the end: the next write appends after the last record. */
     181                 :             :     }
     182                 :       18669 : }
     183                 :             : 
     184                 :             : /* A device that has failed once is not trusted with audit records again. The
     185                 :             :  * store takes on the NullStore vtable through Cleanup, so it never calls the
     186                 :             :  * device again: what it held stays on the media for the next start, and
     187                 :             :  * Service sends new records directly because the store is now transient. The
     188                 :             :  * slot is still in use, so Destroy releases it as usual. */
     189                 :        2307 : static void BlockStore_Freeze(struct SolidSyslogBlockStore* self, enum SolidSyslogBlockStoreErrors failedOperation)
     190                 :             : {
     191                 :        2307 :     BlockStore_Report(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_CAT_STORE_FROZEN, failedOperation);
     192                 :        2307 :     SolidSyslogBlockStore_Cleanup(&self->Base);
     193                 :        2307 : }
     194                 :             : 
     195                 :             : static enum SolidSyslogStoreWriteResult BlockStore_StoreRecord(
     196                 :             :     struct SolidSyslogBlockStore* self,
     197                 :             :     const void* data,
     198                 :             :     size_t size
     199                 :             : );
     200                 :             : static bool BlockStore_FreezeIfDeviceFailed(struct SolidSyslogBlockStore* self);
     201                 :             : static void BlockStore_ReportRefusal(struct SolidSyslogBlockStore* self);
     202                 :             : static void BlockStore_ReportEviction(struct SolidSyslogBlockStore* self);
     203                 :             : static void BlockStore_ReportSealFailed(struct SolidSyslogBlockStore* self);
     204                 :             : 
     205                 :      261503 : static enum SolidSyslogStoreWriteResult BlockStore_Write(struct SolidSyslogStore* base, const void* data, size_t size)
     206                 :             : {
     207                 :      261503 :     enum SolidSyslogStoreWriteResult result = SOLIDSYSLOG_STORE_WRITE_NOT_RETAINED;
     208                 :             : 
     209                 :             :     /* A record of no bytes would read back as nothing unsent and jam the cursor. */
     210         [ +  + ]:      261503 :     if (size > 0U)
     211                 :             :     {
     212                 :      261502 :         result = BlockStore_StoreRecord(BlockStore_SelfFromBase(base), data, size);
     213                 :             :     }
     214                 :             : 
     215                 :      261503 :     return result;
     216                 :             : }
     217                 :             : 
     218                 :             : /* A frozen store has not retained the record and, transient from then on,
     219                 :             :  * leaves it to the sender; a record the policy would not seal is lost here. */
     220                 :      261502 : static enum SolidSyslogStoreWriteResult BlockStore_StoreRecord(
     221                 :             :     struct SolidSyslogBlockStore* self,
     222                 :             :     const void* data,
     223                 :             :     size_t size
     224                 :             : )
     225                 :             : {
     226                 :      261502 :     size_t recordSize = SolidSyslogRecordStore_RecordSize(self->RecordStore, (uint16_t) size);
     227                 :      261502 :     bool readBlockChanged = false;
     228                 :      261502 :     enum SolidSyslogStoreWriteResult result = SOLIDSYSLOG_STORE_WRITE_NOT_RETAINED;
     229                 :      261502 :     bool prepared = SolidSyslogBlockSequence_PrepareForWrite(self->BlockSequence, recordSize, &readBlockChanged);
     230                 :      261502 :     bool frozen = BlockStore_FreezeIfDeviceFailed(self);
     231                 :             : 
     232         [ +  + ]:      261502 :     if (frozen)
     233                 :             :     {
     234                 :             :         /* The freeze has reported which operation failed. */
     235                 :             :     }
     236         [ +  + ]:      261495 :     else if (!prepared)
     237                 :             :     {
     238                 :         253 :         BlockStore_ReportRefusal(self);
     239                 :             :     }
     240                 :             :     else
     241                 :             :     {
     242                 :      261242 :         BlockStore_ReportEviction(self);
     243                 :             : 
     244         [ +  + ]:      261242 :         if (readBlockChanged)
     245                 :             :         {
     246                 :         873 :             SolidSyslogRecordStore_ForgetLastRead(self->RecordStore);
     247                 :             :         }
     248                 :             : 
     249                 :      261242 :         enum SolidSyslogRecordStoreAppendResult appended = SolidSyslogRecordStore_Append(
     250                 :             :             self->RecordStore,
     251                 :      261242 :             SolidSyslogBlockSequence_BlockDevice(self->BlockSequence),
     252                 :      261242 :             SolidSyslogBlockSequence_WriteSequence(self->BlockSequence),
     253                 :             :             data,
     254                 :             :             size
     255                 :             :         );
     256         [ +  + ]:      261242 :         if (appended == SOLIDSYSLOG_RECORD_STORE_APPEND_SUCCEEDED)
     257                 :             :         {
     258                 :      260435 :             result = SOLIDSYSLOG_STORE_WRITE_KEPT;
     259                 :      260435 :             SolidSyslogBlockSequence_NoteRecordWritten(self->BlockSequence, recordSize);
     260                 :             :             /* A record kept ends a run of refusals or seal failures. */
     261                 :      260435 :             self->RefusalReported = false;
     262                 :      260435 :             self->SealFailureReported = false;
     263                 :             :         }
     264         [ +  + ]:         807 :         else if (appended == SOLIDSYSLOG_RECORD_STORE_APPEND_DEVICE_FAILED)
     265                 :             :         {
     266                 :          25 :             BlockStore_Freeze(self, SOLIDSYSLOG_BLOCK_STORE_ERROR_APPEND_FAILED);
     267                 :             :         }
     268                 :             :         else
     269                 :             :         {
     270                 :         782 :             result = SOLIDSYSLOG_STORE_WRITE_LOST;
     271                 :         782 :             BlockStore_ReportSealFailed(self);
     272                 :             :         }
     273                 :             :     }
     274                 :             : 
     275                 :      261502 :     return result;
     276                 :             : }
     277                 :             : 
     278                 :      495375 : static bool BlockStore_FreezeIfDeviceFailed(struct SolidSyslogBlockStore* self)
     279                 :             : {
     280                 :      495375 :     bool failed = SolidSyslogBlockSequence_DeviceFailed(self->BlockSequence);
     281                 :             : 
     282         [ +  + ]:      495375 :     if (failed)
     283                 :             :     {
     284                 :          50 :         BlockStore_Freeze(self, SolidSyslogBlockSequence_FailedOperation(self->BlockSequence));
     285                 :             :     }
     286                 :             : 
     287                 :      495375 :     return failed;
     288                 :             : }
     289                 :             : 
     290                 :             : /* The discard policy turned the record away. One report stands for a run of
     291                 :             :  * refusals; the next record the store keeps ends the run. */
     292                 :         253 : static void BlockStore_ReportRefusal(struct SolidSyslogBlockStore* self)
     293                 :             : {
     294         [ +  + ]:         253 :     if (!self->RefusalReported)
     295                 :             :     {
     296                 :          35 :         BlockStore_Report(
     297                 :             :             SOLIDSYSLOG_SEVERITY_WARNING,
     298                 :             :             SOLIDSYSLOG_CAT_STORE_RECORDS_LOST,
     299                 :             :             SOLIDSYSLOG_BLOCK_STORE_ERROR_RECORD_REFUSED
     300                 :             :         );
     301                 :          35 :         self->RefusalReported = true;
     302                 :             :     }
     303                 :         253 : }
     304                 :             : 
     305                 :             : /* One report stands for a run of rotations that each evicted unsent records;
     306                 :             :  * the next rotation that evicts nothing unsent ends the run. A write that does
     307                 :             :  * not rotate leaves the run as it was. */
     308                 :      261242 : static void BlockStore_ReportEviction(struct SolidSyslogBlockStore* self)
     309                 :             : {
     310         [ +  + ]:      261242 :     if (!SolidSyslogBlockSequence_EvictedUnsent(self->BlockSequence))
     311                 :             :     {
     312                 :      260618 :         self->EvictionReported = false;
     313                 :             :     }
     314         [ +  + ]:         624 :     else if (!self->EvictionReported)
     315                 :             :     {
     316                 :          29 :         BlockStore_Report(
     317                 :             :             SOLIDSYSLOG_SEVERITY_WARNING,
     318                 :             :             SOLIDSYSLOG_CAT_STORE_RECORDS_LOST,
     319                 :             :             SOLIDSYSLOG_BLOCK_STORE_ERROR_BLOCK_EVICTED
     320                 :             :         );
     321                 :          29 :         self->EvictionReported = true;
     322                 :             :     }
     323                 :             :     else
     324                 :             :     {
     325                 :             :         /* Still the run already reported. */
     326                 :             :     }
     327                 :      261242 : }
     328                 :             : 
     329                 :             : /* The policy reports why it would not seal; this reports that the record is
     330                 :             :  * lost, once for a run of them. */
     331                 :         782 : static void BlockStore_ReportSealFailed(struct SolidSyslogBlockStore* self)
     332                 :             : {
     333         [ +  + ]:         782 :     if (!self->SealFailureReported)
     334                 :             :     {
     335                 :          35 :         BlockStore_Report(
     336                 :             :             SOLIDSYSLOG_SEVERITY_ERROR,
     337                 :             :             SOLIDSYSLOG_CAT_STORE_WRITE_FAILED,
     338                 :             :             SOLIDSYSLOG_BLOCK_STORE_ERROR_SEAL_FAILED
     339                 :             :         );
     340                 :          35 :         self->SealFailureReported = true;
     341                 :             :     }
     342                 :         782 : }
     343                 :             : 
     344                 :      249155 : static bool BlockStore_HasUnsent(struct SolidSyslogStore* base)
     345                 :             : {
     346                 :      249155 :     return SolidSyslogBlockSequence_HasUnsent(BlockStore_SelfFromBase(base)->BlockSequence);
     347                 :             : }
     348                 :             : 
     349                 :           8 : static size_t BlockStore_GetTotalBytes(struct SolidSyslogStore* base)
     350                 :             : {
     351                 :           8 :     return SolidSyslogBlockSequence_TotalBytes(BlockStore_SelfFromBase(base)->BlockSequence);
     352                 :             : }
     353                 :             : 
     354                 :           8 : static size_t BlockStore_GetUsedBytes(struct SolidSyslogStore* base)
     355                 :             : {
     356                 :           8 :     return SolidSyslogBlockSequence_UsedBytes(BlockStore_SelfFromBase(base)->BlockSequence);
     357                 :             : }
     358                 :             : 
     359                 :             : /* BlockStore retains records - a BlockStore_Write rejection here is the discard
     360                 :             :  * policy speaking (Newest or Halt), and the message must NOT
     361                 :             :  * bypass older stored records via a Service direct-send fallback. */
     362                 :         235 : static bool BlockStore_IsTransient(struct SolidSyslogStore* base)
     363                 :             : {
     364                 :             :     (void) base;
     365                 :         235 :     return false;
     366                 :             : }
     367                 :             : 
     368                 :             : static enum SolidSyslogRecordStoreReadResult BlockStore_ReadCurrent(
     369                 :             :     struct SolidSyslogBlockStore* self,
     370                 :             :     void* data,
     371                 :             :     size_t maxSize,
     372                 :             :     size_t* bytesRead
     373                 :             : );
     374                 :             : static bool BlockStore_StepPast(struct SolidSyslogBlockStore* self, enum SolidSyslogRecordStoreReadResult result);
     375                 :             : static bool BlockStore_StepPastRejected(struct SolidSyslogBlockStore* self);
     376                 :             : static bool BlockStore_StepPastCurrent(struct SolidSyslogBlockStore* self);
     377                 :             : static void BlockStore_ReportLoss(struct SolidSyslogBlockStore* self, enum SolidSyslogBlockStoreErrors fault);
     378                 :             : static bool BlockStore_ReadNextBlockIfBehind(struct SolidSyslogBlockStore* self);
     379                 :             : 
     380                 :      235756 : static bool BlockStore_ReadNextUnsent(struct SolidSyslogStore* base, void* data, size_t maxSize, size_t* bytesRead)
     381                 :             : {
     382                 :      235756 :     struct SolidSyslogBlockStore* self = BlockStore_SelfFromBase(base);
     383                 :      235756 :     *bytesRead = 0;
     384                 :             : 
     385         [ +  + ]:      235756 :     if (SolidSyslogBlockSequence_HasUnsent(self->BlockSequence))
     386                 :             :     {
     387                 :      235223 :         enum SolidSyslogRecordStoreReadResult result = BlockStore_ReadCurrent(self, data, maxSize, bytesRead);
     388                 :             : 
     389         [ +  + ]:      240120 :         while (BlockStore_StepPast(self, result))
     390                 :             :         {
     391                 :        4897 :             result = BlockStore_ReadCurrent(self, data, maxSize, bytesRead);
     392                 :             :         }
     393                 :             : 
     394         [ +  + ]:      235223 :         if (result == SOLIDSYSLOG_RECORD_STORE_READ_SUCCEEDED)
     395                 :             :         {
     396                 :             :             /* A record that reads cleanly ends a run of lost ones. */
     397                 :      231984 :             self->LossReported = false;
     398                 :             :         }
     399         [ +  + ]:        3239 :         else if (result == SOLIDSYSLOG_RECORD_STORE_READ_DEVICE_FAILED)
     400                 :             :         {
     401                 :             :             /* A block size the sequence could not read is noted there and named
     402                 :             :              * by it; any other failure is the read of the record itself. */
     403         [ +  + ]:        1610 :             if (!BlockStore_FreezeIfDeviceFailed(self))
     404                 :             :             {
     405                 :        1572 :                 BlockStore_Freeze(self, SOLIDSYSLOG_BLOCK_STORE_ERROR_READ_FAILED);
     406                 :             :             }
     407                 :             :         }
     408                 :             :         else
     409                 :             :         {
     410                 :             :             /* Nothing left to read, or a record the policy could not open: with
     411                 :             :              * no verdict on it, it stays where it is to be tried again on the
     412                 :             :              * next pass, and the records behind it wait with it. */
     413                 :             :         }
     414                 :             :     }
     415                 :             : 
     416                 :             :     /* A record of no bytes is nothing to send. */
     417                 :      235756 :     return *bytesRead > 0U;
     418                 :             : }
     419                 :             : 
     420                 :      240120 : static enum SolidSyslogRecordStoreReadResult BlockStore_ReadCurrent(
     421                 :             :     struct SolidSyslogBlockStore* self,
     422                 :             :     void* data,
     423                 :             :     size_t maxSize,
     424                 :             :     size_t* bytesRead
     425                 :             : )
     426                 :             : {
     427                 :      240120 :     size_t readBlockSize = 0U;
     428                 :      240120 :     enum SolidSyslogRecordStoreReadResult result = SOLIDSYSLOG_RECORD_STORE_READ_DEVICE_FAILED;
     429                 :             : 
     430         [ +  + ]:      240120 :     if (SolidSyslogBlockSequence_ReadBlockSize(self->BlockSequence, &readBlockSize))
     431                 :             :     {
     432                 :      240082 :         result = SolidSyslogRecordStore_Read(
     433                 :             :             self->RecordStore,
     434                 :      240082 :             SolidSyslogBlockSequence_BlockDevice(self->BlockSequence),
     435                 :      240082 :             SolidSyslogBlockSequence_ReadSequence(self->BlockSequence),
     436                 :      240082 :             SolidSyslogBlockSequence_ReadCursor(self->BlockSequence),
     437                 :             :             readBlockSize,
     438                 :             :             data,
     439                 :             :             maxSize,
     440                 :             :             bytesRead
     441                 :             :         );
     442                 :             :     }
     443                 :             : 
     444                 :      240120 :     return result;
     445                 :             : }
     446                 :             : 
     447                 :             : /* Moves the cursor past what the read met, where there is something to move
     448                 :             :  * past: a record the policy rejected, the end of a block the write has left
     449                 :             :  * behind, or damage. A record the policy could not open is not moved past.
     450                 :             :  * Says whether there is a next record to try. */
     451                 :      240120 : static bool BlockStore_StepPast(struct SolidSyslogBlockStore* self, enum SolidSyslogRecordStoreReadResult result)
     452                 :             : {
     453                 :      240120 :     bool stepped = false;
     454                 :             : 
     455         [ +  + ]:      240120 :     if (result == SOLIDSYSLOG_RECORD_STORE_READ_REJECTED)
     456                 :             :     {
     457                 :        2020 :         stepped = BlockStore_StepPastRejected(self);
     458                 :             :     }
     459         [ +  + ]:      238100 :     else if (result == SOLIDSYSLOG_RECORD_STORE_READ_FINISHED_WITH)
     460                 :             :     {
     461                 :        1765 :         stepped = BlockStore_StepPastCurrent(self);
     462                 :             :     }
     463         [ +  + ]:      236335 :     else if (result == SOLIDSYSLOG_RECORD_STORE_READ_END_OF_DATA)
     464                 :             :     {
     465                 :         780 :         stepped = BlockStore_ReadNextBlockIfBehind(self);
     466                 :             :     }
     467         [ +  + ]:      235555 :     else if (result == SOLIDSYSLOG_RECORD_STORE_READ_CORRUPT)
     468                 :             :     {
     469                 :        1648 :         stepped = BlockStore_AbandonRestOfReadBlock(self);
     470                 :             :     }
     471                 :             :     else
     472                 :             :     {
     473                 :             :         /* A record to hand back, one the policy could not open, or a device
     474                 :             :          * failure. */
     475                 :             :     }
     476                 :             : 
     477                 :      240120 :     return stepped;
     478                 :             : }
     479                 :             : 
     480                 :             : /* Reads on in the next block where the write has left this one behind; at the
     481                 :             :  * end of the write block there is nothing more. The block the read leaves has
     482                 :             :  * nothing left to deliver, however the read got to its end - a mark, a step
     483                 :             :  * past a rejected record, or the rest given up as corrupt - so it is disposed
     484                 :             :  * of as the read leaves it. Says whether the read has moved on: a dispose that
     485                 :             :  * fails freezes the store, and the read moves nowhere. */
     486                 :        3293 : static bool BlockStore_ReadNextBlockIfBehind(struct SolidSyslogBlockStore* self)
     487                 :             : {
     488                 :        3293 :     bool movedOn = false;
     489                 :             : 
     490         [ +  + ]:        3293 :     if (SolidSyslogBlockSequence_ReadIsBehindWrite(self->BlockSequence))
     491                 :             :     {
     492                 :        1228 :         SolidSyslogBlockSequence_LeaveReadBlock(self->BlockSequence);
     493                 :        1228 :         SolidSyslogRecordStore_ForgetLastRead(self->RecordStore);
     494                 :        1228 :         movedOn = !BlockStore_FreezeIfDeviceFailed(self);
     495                 :             :     }
     496                 :             : 
     497                 :        3293 :     return movedOn;
     498                 :             : }
     499                 :             : 
     500                 :             : /* The rest of the read block cannot be read. Behind the write block the next
     501                 :             :  * block is read instead. In the write block nothing unsent remains, and the
     502                 :             :  * block is closed so the next write starts a new one rather than appending
     503                 :             :  * after the damage. A store frozen on the way out of the block has reported
     504                 :             :  * that, and nothing more is reported or marked. */
     505                 :        2513 : static bool BlockStore_AbandonRestOfReadBlock(struct SolidSyslogBlockStore* self)
     506                 :             : {
     507                 :        2513 :     bool movedOn = BlockStore_ReadNextBlockIfBehind(self);
     508                 :             : 
     509         [ +  + ]:        2513 :     if (!SolidSyslogBlockSequence_DeviceFailed(self->BlockSequence))
     510                 :             :     {
     511                 :        2511 :         BlockStore_ReportLoss(self, SOLIDSYSLOG_BLOCK_STORE_ERROR_RECORD_CORRUPT);
     512                 :             : 
     513         [ +  + ]:        2511 :         if (!movedOn)
     514                 :             :         {
     515                 :        1423 :             SolidSyslogBlockSequence_SetReadCursor(
     516                 :             :                 self->BlockSequence,
     517                 :        1423 :                 SolidSyslogBlockSequence_WritePosition(self->BlockSequence)
     518                 :             :             );
     519                 :        1423 :             SolidSyslogBlockSequence_MarkWriteBlockCorrupt(self->BlockSequence);
     520                 :             :         }
     521                 :             :     }
     522                 :             : 
     523                 :        2513 :     return movedOn;
     524                 :             : }
     525                 :             : 
     526                 :             : /* Moves the cursor to the record after the rejected one, writing nothing: the
     527                 :             :  * policy did not vouch for the length that says where the record ends, so a
     528                 :             :  * mark written there could land inside the record that really follows. The
     529                 :             :  * record is read and rejected again each time the store starts, until its
     530                 :             :  * block is done with. */
     531                 :        2020 : static bool BlockStore_StepPastRejected(struct SolidSyslogBlockStore* self)
     532                 :             : {
     533                 :        2020 :     BlockStore_ReportLoss(self, SOLIDSYSLOG_BLOCK_STORE_ERROR_RECORD_REJECTED);
     534                 :        2020 :     return BlockStore_StepPastCurrent(self);
     535                 :             : }
     536                 :             : 
     537                 :             : /* The cursor moves to the record after the one just read, by the length its
     538                 :             :  * header gave. A record already marked sent is met here only behind an unmarked
     539                 :             :  * rejected one, where the resume scan stopped. */
     540                 :        3785 : static bool BlockStore_StepPastCurrent(struct SolidSyslogBlockStore* self)
     541                 :             : {
     542                 :        3785 :     SolidSyslogBlockSequence_SetReadCursor(
     543                 :             :         self->BlockSequence,
     544                 :             :         SolidSyslogRecordStore_StepPastLastRead(self->RecordStore)
     545                 :             :     );
     546                 :        3785 :     return true;
     547                 :             : }
     548                 :             : 
     549                 :             : /* One report stands for a whole run of records lost one after another; the
     550                 :             :  * first of them says what went wrong. */
     551                 :        4531 : static void BlockStore_ReportLoss(struct SolidSyslogBlockStore* self, enum SolidSyslogBlockStoreErrors fault)
     552                 :             : {
     553         [ +  + ]:        4531 :     if (!self->LossReported)
     554                 :             :     {
     555                 :        4151 :         BlockStore_Report(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_CAT_STORE_RECORD_DISCARDED, fault);
     556                 :        4151 :         self->LossReported = true;
     557                 :             :     }
     558                 :        4531 : }
     559                 :             : 
     560                 :      231068 : static void BlockStore_MarkSent(struct SolidSyslogStore* base)
     561                 :             : {
     562                 :      231068 :     struct SolidSyslogBlockStore* self = BlockStore_SelfFromBase(base);
     563                 :      231068 :     size_t nextCursor = 0U;
     564                 :      231068 :     enum SolidSyslogRecordStoreMarkResult marked = SolidSyslogRecordStore_MarkLastReadAsSent(
     565                 :             :         self->RecordStore,
     566                 :      231068 :         SolidSyslogBlockSequence_BlockDevice(self->BlockSequence),
     567                 :             :         &nextCursor
     568                 :             :     );
     569                 :             : 
     570         [ +  + ]:      231068 :     if (marked == SOLIDSYSLOG_RECORD_STORE_MARK_DEVICE_FAILED)
     571                 :             :     {
     572                 :             :         /* The record was delivered. Freezing is what stops it being read and
     573                 :             :          * sent again, since its flag still says unsent. */
     574                 :          32 :         BlockStore_Freeze(self, SOLIDSYSLOG_BLOCK_STORE_ERROR_MARK_SENT_FAILED);
     575                 :             :     }
     576         [ +  + ]:      231036 :     else if (marked == SOLIDSYSLOG_RECORD_STORE_MARK_SUCCEEDED)
     577                 :             :     {
     578                 :      231035 :         SolidSyslogBlockSequence_SetReadCursor(self->BlockSequence, nextCursor);
     579                 :             : 
     580                 :      231035 :         bool readBlockChanged = false;
     581                 :      231035 :         SolidSyslogBlockSequence_DisposeReadBlockIfDrained(self->BlockSequence, &readBlockChanged);
     582                 :             : 
     583   [ +  +  +  + ]:      231035 :         if (!BlockStore_FreezeIfDeviceFailed(self) && readBlockChanged)
     584                 :             :         {
     585                 :       11824 :             SolidSyslogRecordStore_ForgetLastRead(self->RecordStore);
     586                 :             :         }
     587                 :             :     }
     588                 :             :     else
     589                 :             :     {
     590                 :             :         /* Nothing read since the last mark - nothing to do. */
     591                 :             :     }
     592                 :      231068 : }
        

Generated by: LCOV version 2.0-1