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 "SolidSyslogLittleFsFile.h"
6 : :
7 : : #include <stdbool.h>
8 : : #include <stddef.h>
9 : : #include <stdint.h>
10 : :
11 : : #include "SolidSyslogError.h"
12 : : #include "SolidSyslogErrorCategory.h"
13 : : #include "SolidSyslogFileCategories.h"
14 : : #include "SolidSyslogFileDefinition.h"
15 : : #include "SolidSyslogLittleFsFilePrivate.h"
16 : : #include "SolidSyslogNullFile.h"
17 : : #include "lfs.h"
18 : :
19 : : const struct SolidSyslogErrorSource SolidSyslogLittleFsFileErrorSource = {"LittleFsFile", SolidSyslogFileErrors_Name};
20 : :
21 : : /* The File contract says Open creates the file when absent and never truncates
22 : : * an existing one. */
23 : : #define LITTLEFSFILE_READ_WRITE_OR_CREATE (LFS_O_RDWR | LFS_O_CREAT)
24 : :
25 : : static bool LittleFsFile_Open(struct SolidSyslogFile* base, const char* path);
26 : : static void LittleFsFile_Close(struct SolidSyslogFile* base);
27 : : static bool LittleFsFile_IsOpen(struct SolidSyslogFile* base);
28 : : static bool LittleFsFile_Read(struct SolidSyslogFile* base, void* buf, size_t count);
29 : : static bool LittleFsFile_Write(struct SolidSyslogFile* base, const void* buf, size_t count);
30 : : static void LittleFsFile_SeekTo(struct SolidSyslogFile* base, size_t offset);
31 : : static bool LittleFsFile_Size(struct SolidSyslogFile* base, size_t* size);
32 : : static void LittleFsFile_Truncate(struct SolidSyslogFile* base);
33 : : static bool LittleFsFile_Exists(struct SolidSyslogFile* base, const char* path);
34 : : static bool LittleFsFile_Delete(struct SolidSyslogFile* base, const char* path);
35 : :
36 : : static inline struct SolidSyslogLittleFsFile* LittleFsFile_SelfFromBase(struct SolidSyslogFile* base);
37 : : static inline bool LittleFsFile_IsValidSetup(const lfs_t* filesystem, const void* fileBuffer, uint32_t fileBufferBytes);
38 : : static inline void LittleFsFile_ReportFailure(enum SolidSyslogFileErrors code, int nativeCode);
39 : : static inline bool LittleFsFile_IsWholeTransfer(lfs_ssize_t transferred, size_t count);
40 : : static inline bool LittleFsFile_Sync(struct SolidSyslogLittleFsFile* self);
41 : : static inline int LittleFsFile_SeekToStart(struct SolidSyslogLittleFsFile* self);
42 : :
43 : 55 : bool SolidSyslogLittleFsFile_Initialise(
44 : : struct SolidSyslogFile* base,
45 : : lfs_t* filesystem,
46 : : void* fileBuffer,
47 : : uint32_t fileBufferBytes
48 : : )
49 : : {
50 : 55 : struct SolidSyslogLittleFsFile* self = LittleFsFile_SelfFromBase(base);
51 : 55 : bool valid = LittleFsFile_IsValidSetup(filesystem, fileBuffer, fileBufferBytes);
52 : : /* Start from the Null vtable so any slot this adapter has not filled is a
53 : : * safe no-op rather than a NULL dispatch. */
54 : 55 : self->Base = *SolidSyslogNullFile_Get();
55 [ + + ]: 55 : if (valid == true)
56 : : {
57 : 51 : self->Filesystem = filesystem;
58 : 51 : self->OpenConfig.buffer = fileBuffer;
59 : 51 : self->Base.Open = LittleFsFile_Open;
60 : 51 : self->Base.Close = LittleFsFile_Close;
61 : 51 : self->Base.IsOpen = LittleFsFile_IsOpen;
62 : 51 : self->Base.Read = LittleFsFile_Read;
63 : 51 : self->Base.Write = LittleFsFile_Write;
64 : 51 : self->Base.SeekTo = LittleFsFile_SeekTo;
65 : 51 : self->Base.Size = LittleFsFile_Size;
66 : 51 : self->Base.Truncate = LittleFsFile_Truncate;
67 : 51 : self->Base.Exists = LittleFsFile_Exists;
68 : 51 : self->Base.Delete = LittleFsFile_Delete;
69 : 51 : self->IsOpen = false;
70 : : }
71 : 55 : return valid;
72 : : }
73 : :
74 : 1258 : static inline struct SolidSyslogLittleFsFile* LittleFsFile_SelfFromBase(struct SolidSyslogFile* base)
75 : : {
76 : 1258 : return (struct SolidSyslogLittleFsFile*) base;
77 : : }
78 : :
79 : : /* Checked here rather than at Open so a wiring fault is one report at Create
80 : : * instead of a surprise on the first record the store writes. The size cannot
81 : : * change later: cache_size is fixed when the integrator mounts. */
82 : 55 : static inline bool LittleFsFile_IsValidSetup(const lfs_t* filesystem, const void* fileBuffer, uint32_t fileBufferBytes)
83 : : {
84 : 55 : bool valid = false;
85 [ + + ]: 55 : if (filesystem == NULL)
86 : : {
87 : 2 : LittleFsFile_Report(
88 : : SOLIDSYSLOG_BAD_CONFIG_FATAL_SEVERITY,
89 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
90 : : SOLIDSYSLOG_FILE_ERROR_NULL_FILESYSTEM
91 : : );
92 : : }
93 [ + + + + ]: 53 : else if ((fileBuffer == NULL) || (fileBufferBytes < filesystem->cfg->cache_size))
94 : : {
95 : 2 : LittleFsFile_Report(
96 : : SOLIDSYSLOG_BAD_CONFIG_FATAL_SEVERITY,
97 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
98 : : SOLIDSYSLOG_FILE_ERROR_BUFFER_TOO_SMALL
99 : : );
100 : : }
101 : : else
102 : : {
103 : 51 : valid = true;
104 : : }
105 : 55 : return valid;
106 : : }
107 : :
108 : 55 : void SolidSyslogLittleFsFile_Cleanup(struct SolidSyslogFile* base)
109 : : {
110 : : /* Close first: the slot is about to be reusable, and LittleFS would
111 : : * otherwise keep a file open against a buffer the caller may reclaim. */
112 : 55 : LittleFsFile_Close(base);
113 : : /* Overwrite the abstract base with the shared NullFile vtable so
114 : : * use-after-destroy is a safe no-op rather than a NULL-fn-pointer crash. */
115 : 55 : *base = *SolidSyslogNullFile_Get();
116 : 55 : }
117 : :
118 : 36 : static bool LittleFsFile_Open(struct SolidSyslogFile* base, const char* path)
119 : : {
120 : 36 : struct SolidSyslogLittleFsFile* self = LittleFsFile_SelfFromBase(base);
121 : : int result =
122 : 36 : lfs_file_opencfg(self->Filesystem, &self->Handle, path, LITTLEFSFILE_READ_WRITE_OR_CREATE, &self->OpenConfig);
123 : 36 : self->IsOpen = (result == LFS_ERR_OK);
124 [ + + ]: 36 : if (!self->IsOpen)
125 : : {
126 : 1 : LittleFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_OPEN_FAILED, result);
127 : : }
128 : 36 : return self->IsOpen;
129 : : }
130 : :
131 : : /* The fault, with the negative lfs code behind it when the call answered one.
132 : : * A short transfer answers a count, not a code. */
133 : : // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) -- every caller passes a named code and the result it read
134 : 18 : static inline void LittleFsFile_ReportFailure(enum SolidSyslogFileErrors code, int nativeCode)
135 : : {
136 [ + + ]: 18 : if (nativeCode != LFS_ERR_OK)
137 : : {
138 : 16 : SolidSyslog_ErrorWithNativeCode(
139 : : SOLIDSYSLOG_FILE_IO_FAILED_SEVERITY,
140 : : &SolidSyslogLittleFsFileErrorSource,
141 : : SOLIDSYSLOG_CAT_FILE_IO_FAILED,
142 : : (int32_t) code,
143 : : (int32_t) nativeCode
144 : : );
145 : : }
146 : : else
147 : : {
148 : 2 : LittleFsFile_Report(SOLIDSYSLOG_FILE_IO_FAILED_SEVERITY, SOLIDSYSLOG_CAT_FILE_IO_FAILED, code);
149 : : }
150 : 18 : }
151 : :
152 : 68 : static void LittleFsFile_Close(struct SolidSyslogFile* base)
153 : : {
154 : 68 : struct SolidSyslogLittleFsFile* self = LittleFsFile_SelfFromBase(base);
155 [ + + ]: 68 : if (self->IsOpen)
156 : : {
157 : 35 : int result = lfs_file_close(self->Filesystem, &self->Handle);
158 : : /* The file is released whether or not the close could flush. */
159 : 35 : self->IsOpen = false;
160 [ + + ]: 35 : if (result != LFS_ERR_OK)
161 : : {
162 : 2 : LittleFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_CLOSE_FAILED, result);
163 : : }
164 : : }
165 : 68 : }
166 : :
167 : 66 : static bool LittleFsFile_IsOpen(struct SolidSyslogFile* base)
168 : : {
169 : 66 : return LittleFsFile_SelfFromBase(base)->IsOpen;
170 : : }
171 : :
172 : 33 : static bool LittleFsFile_Read(struct SolidSyslogFile* base, void* buf, size_t count)
173 : : {
174 : 33 : struct SolidSyslogLittleFsFile* self = LittleFsFile_SelfFromBase(base);
175 : 33 : lfs_ssize_t read = lfs_file_read(self->Filesystem, &self->Handle, buf, (lfs_size_t) count);
176 : : /* A short read answers a count, which the store judges; a failure answers
177 : : * a negative code. The contract fails them the same way. */
178 [ + + ]: 33 : if (read < 0)
179 : : {
180 : 1 : LittleFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_READ_FAILED, (int) read);
181 : : }
182 : 33 : return LittleFsFile_IsWholeTransfer(read, count);
183 : : }
184 : :
185 : : /* lfs_file_read and lfs_file_write answer a negative code, or how many bytes
186 : : * moved. */
187 : 55 : static inline bool LittleFsFile_IsWholeTransfer(lfs_ssize_t transferred, size_t count)
188 : : {
189 [ + + + + ]: 55 : return (transferred >= 0) && ((size_t) transferred == count);
190 : : }
191 : :
192 : 22 : static bool LittleFsFile_Write(struct SolidSyslogFile* base, const void* buf, size_t count)
193 : : {
194 : 22 : struct SolidSyslogLittleFsFile* self = LittleFsFile_SelfFromBase(base);
195 : 22 : lfs_ssize_t written = lfs_file_write(self->Filesystem, &self->Handle, buf, (lfs_size_t) count);
196 : 22 : bool wroteAllData = LittleFsFile_IsWholeTransfer(written, count);
197 [ + + ]: 22 : if (wroteAllData)
198 : : {
199 : : /* The File contract makes a true return mean the bytes are on the media:
200 : : * the BlockStore treats it as durable across power loss, so the sync is
201 : : * part of the write rather than something a caller arranges. */
202 : 19 : wroteAllData = LittleFsFile_Sync(self);
203 : : }
204 : : else
205 : : {
206 : : /* A short write has no code behind it. */
207 : 3 : LittleFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_WRITE_FAILED, (written < 0) ? (int) written : LFS_ERR_OK);
208 : : }
209 : 22 : return wroteAllData;
210 : : }
211 : :
212 : 19 : static inline bool LittleFsFile_Sync(struct SolidSyslogLittleFsFile* self)
213 : : {
214 : 19 : int result = lfs_file_sync(self->Filesystem, &self->Handle);
215 [ + + ]: 19 : if (result != LFS_ERR_OK)
216 : : {
217 : 6 : LittleFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_SYNC_FAILED, result);
218 : : }
219 : 19 : return result == LFS_ERR_OK;
220 : : }
221 : :
222 : 46 : static void LittleFsFile_SeekTo(struct SolidSyslogFile* base, size_t offset)
223 : : {
224 : 46 : struct SolidSyslogLittleFsFile* self = LittleFsFile_SelfFromBase(base);
225 : 46 : lfs_soff_t position = lfs_file_seek(self->Filesystem, &self->Handle, (lfs_soff_t) offset, LFS_SEEK_SET);
226 : : /* lfs answers the new position, or a negative code. */
227 [ + + ]: 46 : if (position < 0)
228 : : {
229 : 1 : LittleFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_SEEK_FAILED, (int) position);
230 : : }
231 : 46 : }
232 : :
233 : 18 : static bool LittleFsFile_Size(struct SolidSyslogFile* base, size_t* size)
234 : : {
235 : 18 : struct SolidSyslogLittleFsFile* self = LittleFsFile_SelfFromBase(base);
236 : 18 : lfs_soff_t length = lfs_file_size(self->Filesystem, &self->Handle);
237 : : /* lfs reports an error as a negative value. */
238 : 18 : bool known = length >= 0;
239 [ + + ]: 18 : if (!known)
240 : : {
241 : 2 : LittleFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_SIZE_FAILED, (int) length);
242 : : }
243 [ + + ]: 18 : *size = known ? (size_t) length : 0U;
244 : 18 : return known;
245 : : }
246 : :
247 : : /* Seek as well as truncate: the contract leaves the file open at length zero,
248 : : * and the shared position would otherwise sit past the new end. The seek is
249 : : * part of the truncate, so either step failing is the truncate failing. */
250 : 9 : static void LittleFsFile_Truncate(struct SolidSyslogFile* base)
251 : : {
252 : 9 : struct SolidSyslogLittleFsFile* self = LittleFsFile_SelfFromBase(base);
253 : 9 : int result = lfs_file_truncate(self->Filesystem, &self->Handle, 0);
254 [ + + ]: 9 : if (result == LFS_ERR_OK)
255 : : {
256 : 8 : result = LittleFsFile_SeekToStart(self);
257 : : }
258 [ + + ]: 9 : if (result != LFS_ERR_OK)
259 : : {
260 : 2 : LittleFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_TRUNCATE_FAILED, result);
261 : : }
262 : 9 : }
263 : :
264 : : /* LFS_ERR_OK, or the negative code the seek answered. */
265 : 8 : static inline int LittleFsFile_SeekToStart(struct SolidSyslogLittleFsFile* self)
266 : : {
267 : 8 : lfs_soff_t position = lfs_file_seek(self->Filesystem, &self->Handle, 0, LFS_SEEK_SET);
268 : 8 : return (position < 0) ? (int) position : LFS_ERR_OK;
269 : : }
270 : :
271 : : /* A path that is not there is the answer, not a fault, so nothing is reported. */
272 : 902 : static bool LittleFsFile_Exists(struct SolidSyslogFile* base, const char* path)
273 : : {
274 : 902 : struct SolidSyslogLittleFsFile* self = LittleFsFile_SelfFromBase(base);
275 : : struct lfs_info info;
276 : 902 : return lfs_stat(self->Filesystem, path, &info) == LFS_ERR_OK;
277 : : }
278 : :
279 : 3 : static bool LittleFsFile_Delete(struct SolidSyslogFile* base, const char* path)
280 : : {
281 : 3 : struct SolidSyslogLittleFsFile* self = LittleFsFile_SelfFromBase(base);
282 : 3 : int result = lfs_remove(self->Filesystem, path);
283 : : /* The contract counts an absent path as removed. */
284 [ + + + + ]: 3 : return (result == LFS_ERR_OK) || (result == LFS_ERR_NOENT);
285 : : }
|