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 : }
|