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 "SolidSyslogFileBlockDevice.h"
6 : :
7 : : #include <stdbool.h>
8 : : #include <stddef.h>
9 : : #include <stdint.h>
10 : :
11 : : #include "SolidSyslogBlockDeviceDefinition.h"
12 : : #include "SolidSyslogError.h"
13 : : #include "SolidSyslogFile.h"
14 : : #include "SolidSyslogFileBlockDeviceErrors.h"
15 : : #include "SolidSyslogFileBlockDevicePrivate.h"
16 : : #include "SolidSyslogFormatter.h"
17 : : #include "SolidSyslogNullBlockDevice.h"
18 : : #include "SolidSyslogTunables.h"
19 : :
20 : : const struct SolidSyslogErrorSource SolidSyslogFileBlockDeviceErrorSource = {
21 : : "FileBlockDevice",
22 : : SolidSyslogFileBlockDeviceErrors_Name
23 : : };
24 : :
25 : : struct SolidSyslogFile;
26 : :
27 : : static const char FILE_EXTENSION[] = ".log";
28 : :
29 : : enum
30 : : {
31 : : SEQUENCE_DIGITS = 2U,
32 : : FILENAME_SUFFIX = SEQUENCE_DIGITS + sizeof(FILE_EXTENSION) - 1U,
33 : : MAX_PREFIX_LENGTH = (size_t) SOLIDSYSLOG_MAX_PATH_SIZE - (size_t) FILENAME_SUFFIX - 1U,
34 : : /* Two-digit on-disk sequence - indices > 99 cannot be represented
35 : : * uniquely. Without this guard, a wide blockIndex would be narrowed
36 : : * to uint8_t and alias an existing block (256 -> 00). */
37 : : MAX_BLOCK_INDEX = 99U
38 : : };
39 : :
40 : 31735 : static inline bool FileBlockDevice_IsValidBlockIndex(size_t blockIndex)
41 : : {
42 : 31735 : return blockIndex <= MAX_BLOCK_INDEX;
43 : : }
44 : :
45 : : /* vtable - forward-declared because SolidSyslogFileBlockDevice_Initialise wires them before their definitions */
46 : : static bool FileBlockDevice_Acquire(struct SolidSyslogBlockDevice* base, size_t blockIndex);
47 : : static bool FileBlockDevice_Dispose(struct SolidSyslogBlockDevice* base, size_t blockIndex);
48 : : static bool FileBlockDevice_Exists(struct SolidSyslogBlockDevice* base, size_t blockIndex);
49 : : // NOLINTBEGIN(bugprone-easily-swappable-parameters) -- vtable signature: blockIndex / offset are positional, distinct semantics
50 : : static bool FileBlockDevice_Read(
51 : : struct SolidSyslogBlockDevice* base,
52 : : size_t blockIndex,
53 : : size_t offset,
54 : : void* buf,
55 : : size_t count
56 : : );
57 : : // NOLINTEND(bugprone-easily-swappable-parameters)
58 : : static bool FileBlockDevice_Append(
59 : : struct SolidSyslogBlockDevice* base,
60 : : size_t blockIndex,
61 : : const void* buf,
62 : : size_t count
63 : : );
64 : : // NOLINTBEGIN(bugprone-easily-swappable-parameters) -- vtable signature: blockIndex / offset are positional, distinct semantics
65 : : static bool FileBlockDevice_WriteAt(
66 : : struct SolidSyslogBlockDevice* base,
67 : : size_t blockIndex,
68 : : size_t offset,
69 : : const void* buf,
70 : : size_t count
71 : : );
72 : : // NOLINTEND(bugprone-easily-swappable-parameters)
73 : : static bool FileBlockDevice_Size(struct SolidSyslogBlockDevice* base, size_t blockIndex, size_t* size);
74 : : static size_t FileBlockDevice_GetBlockSize(struct SolidSyslogBlockDevice* base);
75 : :
76 : : static inline struct SolidSyslogFileBlockDevice* FileBlockDevice_SelfFromBase(struct SolidSyslogBlockDevice* base);
77 : : static inline void FileBlockDevice_CloseIfOpen(struct OpenHandle* handle);
78 : :
79 : 352 : void SolidSyslogFileBlockDevice_Initialise(
80 : : struct SolidSyslogBlockDevice* base,
81 : : struct SolidSyslogFile* file,
82 : : const char* pathPrefix,
83 : : size_t blockSize
84 : : )
85 : : {
86 : 352 : struct SolidSyslogFileBlockDevice* self = FileBlockDevice_SelfFromBase(base);
87 : 352 : self->Base.Acquire = FileBlockDevice_Acquire;
88 : 352 : self->Base.Dispose = FileBlockDevice_Dispose;
89 : 352 : self->Base.Exists = FileBlockDevice_Exists;
90 : 352 : self->Base.Read = FileBlockDevice_Read;
91 : 352 : self->Base.Append = FileBlockDevice_Append;
92 : 352 : self->Base.WriteAt = FileBlockDevice_WriteAt;
93 : 352 : self->Base.Size = FileBlockDevice_Size;
94 : 352 : self->Base.GetBlockSize = FileBlockDevice_GetBlockSize;
95 : 352 : self->Handle = (struct OpenHandle) {.File = file, .BlockIndex = 0, .IsOpen = false};
96 : 352 : self->PathPrefix = pathPrefix;
97 : 352 : self->BlockSize = blockSize;
98 : 352 : }
99 : :
100 : 352 : void SolidSyslogFileBlockDevice_Cleanup(struct SolidSyslogBlockDevice* base)
101 : : {
102 : : /* Close the cached file handle while we can still see it; then overwrite the
103 : : * abstract base with the shared NullBlockDevice vtable so use-after-destroy is
104 : : * a safe no-op rather than a NULL-fn-pointer crash. Derived fields are private
105 : : * to this TU; the next SolidSyslogFileBlockDevice_Initialise overwrites them. */
106 : 352 : struct SolidSyslogFileBlockDevice* self = FileBlockDevice_SelfFromBase(base);
107 : 352 : FileBlockDevice_CloseIfOpen(&self->Handle);
108 : 352 : *base = *SolidSyslogNullBlockDevice_Get();
109 : 352 : }
110 : :
111 : 33050 : static inline struct SolidSyslogFileBlockDevice* FileBlockDevice_SelfFromBase(struct SolidSyslogBlockDevice* base)
112 : : {
113 : 33050 : return (struct SolidSyslogFileBlockDevice*) base;
114 : : }
115 : :
116 : 352 : static inline void FileBlockDevice_CloseIfOpen(struct OpenHandle* handle)
117 : : {
118 [ + + ]: 352 : if (handle->IsOpen)
119 : : {
120 : 229 : SolidSyslogFile_Close(handle->File);
121 : 229 : handle->IsOpen = false;
122 : : }
123 : 352 : }
124 : :
125 : : static bool FileBlockDevice_EnsureHandleOpenOnBlock(
126 : : struct OpenHandle* handle,
127 : : const struct SolidSyslogFileBlockDevice* self,
128 : : size_t blockIndex
129 : : );
130 : : static bool FileBlockDevice_OpenHandleOnBlock(
131 : : struct OpenHandle* handle,
132 : : const struct SolidSyslogFileBlockDevice* self,
133 : : size_t blockIndex
134 : : );
135 : : static inline const char* FileBlockDevice_FormatBlockFilename(
136 : : const struct SolidSyslogFileBlockDevice* self,
137 : : SolidSyslogFormatterStorage* storage,
138 : : size_t blockIndex
139 : : );
140 : :
141 : 476 : static bool FileBlockDevice_Acquire(struct SolidSyslogBlockDevice* base, size_t blockIndex)
142 : : {
143 : 476 : bool ready = false;
144 : :
145 [ + + ]: 476 : if (FileBlockDevice_IsValidBlockIndex(blockIndex))
146 : : {
147 : 473 : struct SolidSyslogFileBlockDevice* self = FileBlockDevice_SelfFromBase(base);
148 : 473 : ready = FileBlockDevice_EnsureHandleOpenOnBlock(&self->Handle, self, blockIndex);
149 : :
150 [ + + ]: 473 : if (ready)
151 : : {
152 : 466 : SolidSyslogFile_Truncate(self->Handle.File);
153 : : }
154 : : }
155 : :
156 : 476 : return ready;
157 : : }
158 : :
159 : 5368 : static inline bool FileBlockDevice_IsHandleAlreadyOpenOnBlock(
160 : : const struct OpenHandle* handle,
161 : : bool underlyingFileIsOpen,
162 : : size_t blockIndex
163 : : )
164 : : {
165 [ + + + + : 5368 : return handle->IsOpen && underlyingFileIsOpen && (handle->BlockIndex == blockIndex);
+ + ]
166 : : }
167 : :
168 : 5368 : static bool FileBlockDevice_EnsureHandleOpenOnBlock(
169 : : struct OpenHandle* handle,
170 : : const struct SolidSyslogFileBlockDevice* self,
171 : : size_t blockIndex
172 : : )
173 : : {
174 : 5368 : bool underlyingFileIsOpen = SolidSyslogFile_IsOpen(handle->File);
175 : 5368 : bool ready = FileBlockDevice_IsHandleAlreadyOpenOnBlock(handle, underlyingFileIsOpen, blockIndex);
176 : :
177 [ + + ]: 5368 : if (!ready)
178 : : {
179 : 1081 : ready = FileBlockDevice_OpenHandleOnBlock(handle, self, blockIndex);
180 : : }
181 : :
182 : 5368 : return ready;
183 : : }
184 : :
185 : 1081 : static bool FileBlockDevice_OpenHandleOnBlock(
186 : : struct OpenHandle* handle,
187 : : const struct SolidSyslogFileBlockDevice* self,
188 : : size_t blockIndex
189 : : )
190 : : {
191 [ + + ]: 1081 : if (SolidSyslogFile_IsOpen(handle->File))
192 : : {
193 : 748 : SolidSyslogFile_Close(handle->File);
194 : : }
195 : 1081 : handle->IsOpen = false;
196 : :
197 : : SolidSyslogFormatterStorage nameStorage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(SOLIDSYSLOG_MAX_PATH_SIZE)];
198 : 1081 : const char* name = FileBlockDevice_FormatBlockFilename(self, nameStorage, blockIndex);
199 : :
200 : 1081 : bool opened = SolidSyslogFile_Open(handle->File, name);
201 : :
202 [ + + ]: 1081 : if (opened)
203 : : {
204 : 1067 : handle->BlockIndex = blockIndex;
205 : 1067 : handle->IsOpen = true;
206 : : }
207 : :
208 : 1081 : return opened;
209 : : }
210 : :
211 : 27439 : static inline const char* FileBlockDevice_FormatBlockFilename(
212 : : const struct SolidSyslogFileBlockDevice* self,
213 : : SolidSyslogFormatterStorage* storage,
214 : : size_t blockIndex
215 : : )
216 : : {
217 : 27439 : struct SolidSyslogFormatter* formatter = SolidSyslogFormatter_Create(storage, SOLIDSYSLOG_MAX_PATH_SIZE);
218 : :
219 : 27439 : SolidSyslogFormatter_BoundedString(formatter, self->PathPrefix, MAX_PREFIX_LENGTH);
220 : 27439 : SolidSyslogFormatter_TwoDigit(formatter, (uint8_t) blockIndex);
221 : 27439 : SolidSyslogFormatter_BoundedString(formatter, FILE_EXTENSION, sizeof(FILE_EXTENSION) - 1U);
222 : :
223 : 27439 : return SolidSyslogFormatter_AsFormattedBuffer(formatter);
224 : : }
225 : :
226 : : static inline void FileBlockDevice_CloseIfHoldingBlock(struct OpenHandle* handle, size_t blockIndex);
227 : :
228 : 110 : static bool FileBlockDevice_Dispose(struct SolidSyslogBlockDevice* base, size_t blockIndex)
229 : : {
230 : 110 : bool disposed = false;
231 : :
232 [ + + ]: 110 : if (FileBlockDevice_IsValidBlockIndex(blockIndex))
233 : : {
234 : 109 : struct SolidSyslogFileBlockDevice* self = FileBlockDevice_SelfFromBase(base);
235 : :
236 : 109 : FileBlockDevice_CloseIfHoldingBlock(&self->Handle, blockIndex);
237 : :
238 : : SolidSyslogFormatterStorage nameStorage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(SOLIDSYSLOG_MAX_PATH_SIZE)];
239 : 109 : const char* name = FileBlockDevice_FormatBlockFilename(self, nameStorage, blockIndex);
240 : 109 : disposed = SolidSyslogFile_Delete(self->Handle.File, name);
241 : : }
242 : :
243 : 110 : return disposed;
244 : : }
245 : :
246 : 109 : static inline void FileBlockDevice_CloseIfHoldingBlock(struct OpenHandle* handle, size_t blockIndex)
247 : : {
248 [ + + + + ]: 109 : if (handle->IsOpen && (handle->BlockIndex == blockIndex))
249 : : {
250 : 76 : SolidSyslogFile_Close(handle->File);
251 : 76 : handle->IsOpen = false;
252 : : }
253 : 109 : }
254 : :
255 : 26250 : static bool FileBlockDevice_Exists(struct SolidSyslogBlockDevice* base, size_t blockIndex)
256 : : {
257 : 26250 : bool exists = false;
258 : :
259 [ + + ]: 26250 : if (FileBlockDevice_IsValidBlockIndex(blockIndex))
260 : : {
261 : 26249 : struct SolidSyslogFileBlockDevice* self = FileBlockDevice_SelfFromBase(base);
262 : : SolidSyslogFormatterStorage nameStorage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(SOLIDSYSLOG_MAX_PATH_SIZE)];
263 : 26249 : const char* name = FileBlockDevice_FormatBlockFilename(self, nameStorage, blockIndex);
264 : 26249 : exists = SolidSyslogFile_Exists(self->Handle.File, name);
265 : : }
266 : :
267 : 26250 : return exists;
268 : : }
269 : :
270 : : // NOLINTBEGIN(bugprone-easily-swappable-parameters) -- vtable signature: blockIndex / offset are positional, distinct semantics
271 : 3125 : static bool FileBlockDevice_Read(
272 : : struct SolidSyslogBlockDevice* base,
273 : : size_t blockIndex,
274 : : size_t offset,
275 : : void* buf,
276 : : size_t count
277 : : )
278 : : {
279 : 3125 : bool read = false;
280 : :
281 [ + + ]: 3125 : if (FileBlockDevice_IsValidBlockIndex(blockIndex))
282 : : {
283 : 3124 : struct SolidSyslogFileBlockDevice* self = FileBlockDevice_SelfFromBase(base);
284 [ + + ]: 3124 : if (FileBlockDevice_EnsureHandleOpenOnBlock(&self->Handle, self, blockIndex))
285 : : {
286 : 3123 : SolidSyslogFile_SeekTo(self->Handle.File, offset);
287 : 3123 : read = SolidSyslogFile_Read(self->Handle.File, buf, count);
288 : : }
289 : : }
290 : :
291 : 3125 : return read;
292 : : }
293 : :
294 : : // NOLINTEND(bugprone-easily-swappable-parameters)
295 : :
296 : 1102 : static bool FileBlockDevice_Append(
297 : : struct SolidSyslogBlockDevice* base,
298 : : size_t blockIndex,
299 : : const void* buf,
300 : : size_t count
301 : : )
302 : : {
303 : 1102 : bool written = false;
304 : :
305 [ + + ]: 1102 : if (FileBlockDevice_IsValidBlockIndex(blockIndex))
306 : : {
307 : 1101 : struct SolidSyslogFileBlockDevice* self = FileBlockDevice_SelfFromBase(base);
308 [ + + ]: 1101 : if (FileBlockDevice_EnsureHandleOpenOnBlock(&self->Handle, self, blockIndex))
309 : : {
310 : 1100 : size_t end = 0U;
311 [ + + ]: 1100 : if (SolidSyslogFile_Size(self->Handle.File, &end))
312 : : {
313 : 1096 : SolidSyslogFile_SeekTo(self->Handle.File, end);
314 : 1096 : written = SolidSyslogFile_Write(self->Handle.File, buf, count);
315 : : }
316 : : }
317 : : }
318 : :
319 : 1102 : return written;
320 : : }
321 : :
322 : : // NOLINTBEGIN(bugprone-easily-swappable-parameters) -- vtable signature: blockIndex / offset are positional, distinct semantics
323 : 458 : static bool FileBlockDevice_WriteAt(
324 : : struct SolidSyslogBlockDevice* base,
325 : : size_t blockIndex,
326 : : size_t offset,
327 : : const void* buf,
328 : : size_t count
329 : : )
330 : : {
331 : 458 : bool written = false;
332 : :
333 [ + + ]: 458 : if (FileBlockDevice_IsValidBlockIndex(blockIndex))
334 : : {
335 : 457 : struct SolidSyslogFileBlockDevice* self = FileBlockDevice_SelfFromBase(base);
336 [ + + ]: 457 : if (FileBlockDevice_EnsureHandleOpenOnBlock(&self->Handle, self, blockIndex))
337 : : {
338 : 456 : SolidSyslogFile_SeekTo(self->Handle.File, offset);
339 : 456 : written = SolidSyslogFile_Write(self->Handle.File, buf, count);
340 : : }
341 : : }
342 : :
343 : 458 : return written;
344 : : }
345 : :
346 : : // NOLINTEND(bugprone-easily-swappable-parameters)
347 : :
348 : 214 : static bool FileBlockDevice_Size(struct SolidSyslogBlockDevice* base, size_t blockIndex, size_t* size)
349 : : {
350 : 214 : bool known = false;
351 : 214 : *size = 0U;
352 : :
353 [ + + ]: 214 : if (FileBlockDevice_IsValidBlockIndex(blockIndex))
354 : : {
355 : 213 : struct SolidSyslogFileBlockDevice* self = FileBlockDevice_SelfFromBase(base);
356 : 213 : known = FileBlockDevice_EnsureHandleOpenOnBlock(&self->Handle, self, blockIndex);
357 : :
358 [ + + ]: 213 : if (known)
359 : : {
360 : 209 : known = SolidSyslogFile_Size(self->Handle.File, size);
361 : : }
362 : : }
363 : :
364 : 214 : return known;
365 : : }
366 : :
367 : 620 : static size_t FileBlockDevice_GetBlockSize(struct SolidSyslogBlockDevice* base)
368 : : {
369 : 620 : return FileBlockDevice_SelfFromBase(base)->BlockSize;
370 : : }
|