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 "SolidSyslogPosixFile.h"
6 : :
7 : : #include <errno.h>
8 : : #include <fcntl.h>
9 : : #include <stdbool.h>
10 : : #include <stddef.h>
11 : : #include <sys/stat.h>
12 : : #include <sys/types.h>
13 : : #include <unistd.h>
14 : :
15 : : #include "SolidSyslogError.h"
16 : : #include "SolidSyslogErrorCategory.h"
17 : : #include "SolidSyslogFileCategories.h"
18 : : #include "SolidSyslogFileDefinition.h"
19 : : #include "SolidSyslogNullFile.h"
20 : : #include "SolidSyslogPosixFileErrors.h"
21 : : #include "SolidSyslogPosixFilePrivate.h"
22 : :
23 : : const struct SolidSyslogErrorSource SolidSyslogPosixFileErrorSource = {"PosixFile", SolidSyslogFileErrors_Name};
24 : :
25 : : #define POSIXFILE_OWNER_READ_WRITE (S_IRUSR | S_IWUSR)
26 : : #define POSIXFILE_DEFAULT_PERMISSIONS POSIXFILE_OWNER_READ_WRITE
27 : :
28 : : enum
29 : : {
30 : : INVALID_FD = -1
31 : : };
32 : :
33 : : static bool PosixFile_Open(struct SolidSyslogFile* base, const char* path);
34 : : static void PosixFile_Close(struct SolidSyslogFile* base);
35 : : static bool PosixFile_IsOpen(struct SolidSyslogFile* base);
36 : : static bool PosixFile_Read(struct SolidSyslogFile* base, void* buf, size_t count);
37 : : static bool PosixFile_Write(struct SolidSyslogFile* base, const void* buf, size_t count);
38 : : static void PosixFile_SeekTo(struct SolidSyslogFile* base, size_t offset);
39 : : static bool PosixFile_Size(struct SolidSyslogFile* base, size_t* size);
40 : : static void PosixFile_Truncate(struct SolidSyslogFile* base);
41 : : static bool PosixFile_Exists(struct SolidSyslogFile* base, const char* path);
42 : : static bool PosixFile_Delete(struct SolidSyslogFile* base, const char* path);
43 : :
44 : : static inline struct SolidSyslogPosixFile* PosixFile_SelfFromBase(struct SolidSyslogFile* base);
45 : : static inline void PosixFile_ReportFailure(enum SolidSyslogFileErrors code, int nativeErrno);
46 : : static inline bool PosixFile_IsWholeTransfer(ssize_t transferred, size_t count);
47 : : static inline bool PosixFile_IsRemovedOrAbsent(int status, int unlinkErrno);
48 : :
49 : 34 : void SolidSyslogPosixFile_Initialise(struct SolidSyslogFile* base)
50 : : {
51 : 34 : struct SolidSyslogPosixFile* self = PosixFile_SelfFromBase(base);
52 : 34 : self->Base.Open = PosixFile_Open;
53 : 34 : self->Base.Close = PosixFile_Close;
54 : 34 : self->Base.IsOpen = PosixFile_IsOpen;
55 : 34 : self->Base.Read = PosixFile_Read;
56 : 34 : self->Base.Write = PosixFile_Write;
57 : 34 : self->Base.SeekTo = PosixFile_SeekTo;
58 : 34 : self->Base.Size = PosixFile_Size;
59 : 34 : self->Base.Truncate = PosixFile_Truncate;
60 : 34 : self->Base.Exists = PosixFile_Exists;
61 : 34 : self->Base.Delete = PosixFile_Delete;
62 : 34 : self->Fd = INVALID_FD;
63 : 34 : }
64 : :
65 : 34 : void SolidSyslogPosixFile_Cleanup(struct SolidSyslogFile* base)
66 : : {
67 : 34 : PosixFile_Close(base);
68 : : /* Overwrite the abstract base with the shared NullFile vtable so
69 : : * use-after-destroy is a safe no-op rather than a NULL-fn-pointer crash. */
70 : 34 : *base = *SolidSyslogNullFile_Get();
71 : 34 : }
72 : :
73 : 340 : static inline struct SolidSyslogPosixFile* PosixFile_SelfFromBase(struct SolidSyslogFile* base)
74 : : {
75 : 340 : return (struct SolidSyslogPosixFile*) base;
76 : : }
77 : :
78 : 31 : static bool PosixFile_Open(struct SolidSyslogFile* base, const char* path)
79 : : {
80 : 31 : struct SolidSyslogPosixFile* self = PosixFile_SelfFromBase(base);
81 : 31 : self->Fd = open(path, O_RDWR | O_CREAT, POSIXFILE_DEFAULT_PERMISSIONS);
82 : : /* Read errno straight after the call that set it, with nothing between
83 : : * (MISRA 22.10). */
84 [ + + ]: 31 : int openErrno = (self->Fd == INVALID_FD) ? errno : 0;
85 : 31 : bool opened = self->Fd != INVALID_FD;
86 [ + + ]: 31 : if (!opened)
87 : : {
88 : 2 : PosixFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_OPEN_FAILED, openErrno);
89 : : }
90 : 31 : return opened;
91 : : }
92 : :
93 : : /* The fault, with the errno behind it when the call left one. */
94 : : // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) -- every caller passes a named code and the errno it read
95 : 11 : static inline void PosixFile_ReportFailure(enum SolidSyslogFileErrors code, int nativeErrno)
96 : : {
97 [ + + ]: 11 : if (nativeErrno != 0)
98 : : {
99 : 10 : SolidSyslog_ErrorWithNativeCode(
100 : : SOLIDSYSLOG_FILE_IO_FAILED_SEVERITY,
101 : : &SolidSyslogPosixFileErrorSource,
102 : : SOLIDSYSLOG_CAT_FILE_IO_FAILED,
103 : : (int32_t) code,
104 : : (int32_t) nativeErrno
105 : : );
106 : : }
107 : : else
108 : : {
109 : 1 : PosixFile_Report(SOLIDSYSLOG_FILE_IO_FAILED_SEVERITY, SOLIDSYSLOG_CAT_FILE_IO_FAILED, code);
110 : : }
111 : 11 : }
112 : :
113 : 54 : static void PosixFile_Close(struct SolidSyslogFile* base)
114 : : {
115 : 54 : struct SolidSyslogPosixFile* self = PosixFile_SelfFromBase(base);
116 [ + + ]: 54 : if (PosixFile_IsOpen(base))
117 : : {
118 : 29 : int status = close(self->Fd);
119 [ + + ]: 29 : int closeErrno = (status != 0) ? errno : 0;
120 : : /* The descriptor is released whether or not the close could flush. */
121 : 29 : self->Fd = INVALID_FD;
122 [ + + ]: 29 : if (status != 0)
123 : : {
124 : 2 : PosixFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_CLOSE_FAILED, closeErrno);
125 : : }
126 : : }
127 : 54 : }
128 : :
129 : 115 : static bool PosixFile_IsOpen(struct SolidSyslogFile* base)
130 : : {
131 : 115 : const struct SolidSyslogPosixFile* self = PosixFile_SelfFromBase(base);
132 : 115 : return self->Fd != INVALID_FD;
133 : : }
134 : :
135 : 22 : static bool PosixFile_Read(struct SolidSyslogFile* base, void* buf, size_t count)
136 : : {
137 : 22 : struct SolidSyslogPosixFile* self = PosixFile_SelfFromBase(base);
138 : 22 : ssize_t transferred = read(self->Fd, buf, count);
139 : : /* A short read sets no errno; the store judges it. */
140 [ + + ]: 22 : int readErrno = (transferred < 0) ? errno : 0;
141 [ + + ]: 22 : if (transferred < 0)
142 : : {
143 : 1 : PosixFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_READ_FAILED, readErrno);
144 : : }
145 : 22 : return PosixFile_IsWholeTransfer(transferred, count);
146 : : }
147 : :
148 : : /* read and write answer -1, or how many bytes moved. */
149 : 43 : static inline bool PosixFile_IsWholeTransfer(ssize_t transferred, size_t count)
150 : : {
151 [ + + + + ]: 43 : return (transferred >= 0) && ((size_t) transferred == count);
152 : : }
153 : :
154 : 21 : static bool PosixFile_Write(struct SolidSyslogFile* base, const void* buf, size_t count)
155 : : {
156 : 21 : struct SolidSyslogPosixFile* self = PosixFile_SelfFromBase(base);
157 : 21 : ssize_t written = write(self->Fd, buf, count);
158 : : /* A short write sets no errno. */
159 [ + + ]: 21 : int writeErrno = (written < 0) ? errno : 0;
160 : 21 : bool wrote = PosixFile_IsWholeTransfer(written, count);
161 [ + + ]: 21 : if (!wrote)
162 : : {
163 : 2 : PosixFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_WRITE_FAILED, writeErrno);
164 : : }
165 : 21 : return wrote;
166 : : }
167 : :
168 : 36 : static void PosixFile_SeekTo(struct SolidSyslogFile* base, size_t offset)
169 : : {
170 : 36 : struct SolidSyslogPosixFile* self = PosixFile_SelfFromBase(base);
171 : 36 : off_t position = lseek(self->Fd, (off_t) offset, SEEK_SET);
172 [ + + ]: 36 : int seekErrno = (position < 0) ? errno : 0;
173 [ + + ]: 36 : if (position < 0)
174 : : {
175 : 1 : PosixFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_SEEK_FAILED, seekErrno);
176 : : }
177 : 36 : }
178 : :
179 : 18 : static bool PosixFile_Size(struct SolidSyslogFile* base, size_t* size)
180 : : {
181 : 18 : struct SolidSyslogPosixFile* self = PosixFile_SelfFromBase(base);
182 : 18 : off_t end = lseek(self->Fd, 0, SEEK_END);
183 : 18 : bool known = end >= 0;
184 [ + + ]: 18 : int sizeErrno = known ? 0 : errno;
185 [ + + ]: 18 : if (!known)
186 : : {
187 : 2 : PosixFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_SIZE_FAILED, sizeErrno);
188 : : }
189 [ + + ]: 18 : *size = known ? (size_t) end : 0U;
190 : 18 : return known;
191 : : }
192 : :
193 : 9 : static void PosixFile_Truncate(struct SolidSyslogFile* base)
194 : : {
195 : 9 : struct SolidSyslogPosixFile* self = PosixFile_SelfFromBase(base);
196 : 9 : int status = ftruncate(self->Fd, 0);
197 [ + + ]: 9 : int truncateErrno = (status != 0) ? errno : 0;
198 [ + + ]: 9 : if (status != 0)
199 : : {
200 : 1 : PosixFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_TRUNCATE_FAILED, truncateErrno);
201 : : }
202 : 9 : }
203 : :
204 : : /* A path that is not there is the answer, not a fault, so nothing is reported. */
205 : 205 : static bool PosixFile_Exists(struct SolidSyslogFile* base, const char* path)
206 : : {
207 : : (void) base;
208 : 205 : return access(path, F_OK) == 0;
209 : : }
210 : :
211 : 7 : static bool PosixFile_Delete(struct SolidSyslogFile* base, const char* path)
212 : : {
213 : : (void) base;
214 : 7 : int status = unlink(path);
215 [ + + ]: 7 : int unlinkErrno = (status != 0) ? errno : 0;
216 : 7 : return PosixFile_IsRemovedOrAbsent(status, unlinkErrno);
217 : : }
218 : :
219 : : /* The contract counts an absent path as removed. */
220 : 7 : static inline bool PosixFile_IsRemovedOrAbsent(int status, int unlinkErrno)
221 : : {
222 [ + + + + ]: 7 : return (status == 0) || (unlinkErrno == ENOENT);
223 : : }
|