GCC Code Coverage Report


Directory: Os/Posix/
File: File.cpp
Date: 2026-09-23 21:12:59
Exec Total Coverage
Lines: 132 172 76.7%
Functions: 12 13 92.3%
Branches: 53 106 50.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title Os/Posix/File.cpp
3 // \brief posix implementation for Os::File
4 // ======================================================================
5 #include <fcntl.h>
6 #include <unistd.h>
7 #include <cerrno>
8 #include <limits>
9 #include <type_traits>
10
11 #include <Fw/Types/Assert.hpp>
12 #include <Os/File.hpp>
13 #include <Os/Posix/File.hpp>
14 #include <Os/Posix/error.hpp>
15
16 namespace Os {
17 namespace Posix {
18 namespace File {
19
20 // O_SYNC is not defined on every system. This will set up the SYNC_FLAGS variable to be O_SYNC when defined and
21 // (0) when not defined. This allows OPEN_SYNC_WRITE to fall-back to OPEN_WRITE on those systems.
22 #if defined(O_SYNC)
23 #define SYNC_FLAGS O_SYNC
24 #else
25 #define SYNC_FLAGS (0)
26 #endif
27
28 // Create constants for the max limits of the signed types
29 // These constants are used for comparisons with complementary unsigned types to avoid sign-compare warning
30 using UnsignedOffT = std::make_unsigned<off_t>::type;
31 static const UnsignedOffT OFF_T_MAX_LIMIT = static_cast<UnsignedOffT>(std::numeric_limits<off_t>::max());
32 using UnsignedSSizeT = std::make_unsigned<ssize_t>::type;
33 static const UnsignedSSizeT SSIZE_T_MAX_LIMIT = static_cast<UnsignedSSizeT>(std::numeric_limits<ssize_t>::max());
34
35 // Ensure size of FwSizeType is large enough to fit eh necessary range
36 static_assert(sizeof(FwSignedSizeType) >= sizeof(off_t),
37 "FwSignedSizeType is not large enough to store values of type off_t");
38 static_assert(sizeof(FwSignedSizeType) >= sizeof(ssize_t),
39 "FwSignedSizeType is not large enough to store values of type ssize_t");
40 static_assert(sizeof(FwSizeType) >= sizeof(size_t), "FwSizeType is not large enough to store values of type size_t");
41
42 // Now check ranges of FwSizeType
43 static_assert(std::numeric_limits<FwSignedSizeType>::max() >= std::numeric_limits<off_t>::max(),
44 "Maximum value of FwSignedSizeType less than the maximum value of off_t. Configure a larger type.");
45 static_assert(std::numeric_limits<FwSizeType>::max() >= OFF_T_MAX_LIMIT,
46 "Maximum value of FwSizeType less than the maximum value of off_t. Configure a larger type.");
47 static_assert(std::numeric_limits<FwSignedSizeType>::max() >= std::numeric_limits<ssize_t>::max(),
48 "Maximum value of FwSignedSizeType less than the maximum value of ssize_t. Configure a larger type.");
49 static_assert(std::numeric_limits<FwSignedSizeType>::min() <= std::numeric_limits<off_t>::min(),
50 "Minimum value of FwSignedSizeType larger than the minimum value of off_t. Configure a larger type.");
51 static_assert(std::numeric_limits<FwSignedSizeType>::min() <= std::numeric_limits<ssize_t>::min(),
52 "Minimum value of FwSizeType larger than the minimum value of ssize_t. Configure a larger type.");
53 static_assert(std::numeric_limits<FwSizeType>::max() >= std::numeric_limits<size_t>::max(),
54 "Maximum value of FwSizeType less than the maximum value of size_t. Configure a larger type.");
55
56 //!\brief default copy constructor
57 316 PosixFile::PosixFile(const PosixFile& other) {
58 // Must properly duplicate the file handle
59
1/1
✓ Branch 7 taken 316 times.
316 this->m_handle.m_file_descriptor = fcntl(other.m_handle.m_file_descriptor, F_DUPFD, 0);
60 316 }
61
62 ✗ PosixFile& PosixFile::operator=(const PosixFile& other) {
63 ✗ if (this != &other) {
64 ✗ this->m_handle.m_file_descriptor = fcntl(other.m_handle.m_file_descriptor, F_DUPFD, 0);
65 }
66 ✗ return *this;
67 }
68
69 1693 mode_t PosixFile::map_open_create_mode(const U32 create_mode) {
70 1693 mode_t out_mode = 0;
71
72 // Some posix systems (e.g. Darwin) use the older S_IREAD and S_IWRITE flags
73 // while other systems (e.g. Linux) use the newer S_IRUSR and S_IWUSR flags.
74 #if defined(S_IREAD)
75 1693 out_mode |= (create_mode & Os::FILE_MODE_IRUSR) ? S_IREAD : 0;
76 1693 out_mode |= (create_mode & Os::FILE_MODE_IWUSR) ? S_IWRITE : 0;
77 1693 out_mode |= (create_mode & Os::FILE_MODE_IXUSR) ? S_IEXEC : 0;
78 #else
79 out_mode |= (create_mode & Os::FILE_MODE_IRUSR) ? S_IRUSR : 0;
80 out_mode |= (create_mode & Os::FILE_MODE_IWUSR) ? S_IWUSR : 0;
81 out_mode |= (create_mode & Os::FILE_MODE_IXUSR) ? S_IXUSR : 0;
82 #endif
83
84 1693 out_mode |= (create_mode & Os::FILE_MODE_IRGRP) ? S_IRGRP : 0;
85 1693 out_mode |= (create_mode & Os::FILE_MODE_IWGRP) ? S_IWGRP : 0;
86 1693 out_mode |= (create_mode & Os::FILE_MODE_IXGRP) ? S_IXGRP : 0;
87
88 1693 out_mode |= (create_mode & Os::FILE_MODE_IROTH) ? S_IROTH : 0;
89 1693 out_mode |= (create_mode & Os::FILE_MODE_IWOTH) ? S_IWOTH : 0;
90 1693 out_mode |= (create_mode & Os::FILE_MODE_IXOTH) ? S_IXOTH : 0;
91
92 1693 out_mode |= (create_mode & Os::FILE_MODE_ISUID) ? S_ISUID : 0;
93 1693 out_mode |= (create_mode & Os::FILE_MODE_ISGID) ? S_ISGID : 0;
94 #if defined(S_ISVTX)
95 1693 out_mode |= (create_mode & Os::FILE_MODE_ISVTX) ? S_ISVTX : 0;
96 #endif
97
98 1693 return out_mode;
99 }
100
101 1693 PosixFile::Status PosixFile::open(const char* filepath,
102 PosixFile::Mode requested_mode,
103 PosixFile::OverwriteType overwrite) {
104 1693 int mode_flags = 0;
105 1693 Status status = OP_OK;
106
5/6
✓ Branch 0 taken 901 times.
✓ Branch 1 taken 140 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 509 times.
✓ Branch 4 taken 132 times.
✗ Branch 5 not taken.
1693 switch (requested_mode) {
107 901 case OPEN_READ:
108 901 mode_flags = O_RDONLY;
109 901 break;
110 140 case OPEN_WRITE:
111 140 mode_flags = O_WRONLY | O_CREAT;
112 140 break;
113 11 case OPEN_SYNC_WRITE:
114 11 mode_flags = O_WRONLY | O_CREAT | SYNC_FLAGS;
115 11 break;
116 509 case OPEN_CREATE:
117 509 mode_flags =
118
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 485 times.
509 O_WRONLY | O_CREAT | O_TRUNC | ((overwrite == PosixFile::OverwriteType::OVERWRITE) ? 0 : O_EXCL);
119 509 break;
120 132 case OPEN_APPEND:
121 132 mode_flags = O_WRONLY | O_CREAT | O_APPEND;
122 132 break;
123 ✗ default:
124 ✗ FW_ASSERT(false, requested_mode);
125 ✗ break;
126 }
127
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1693 times.
1693 int descriptor = ::open(filepath, mode_flags, map_open_create_mode(Os::FILE_DEFAULT_CREATE_MODE));
128
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1680 times.
1693 if (PosixFileHandle::INVALID_FILE_DESCRIPTOR == descriptor) {
129 13 int errno_store = errno;
130 13 status = Os::Posix::errno_to_file_status(errno_store);
131 }
132 1693 this->m_handle.m_file_descriptor = descriptor;
133 1693 return status;
134 }
135
136 1870 void PosixFile::close() {
137 // Only close file handles that are open
138
2/2
✓ Branch 4 taken 1868 times.
✓ Branch 5 taken 2 times.
1870 if (PosixFileHandle::INVALID_FILE_DESCRIPTOR != this->m_handle.m_file_descriptor) {
139 1868 (void)::close(this->m_handle.m_file_descriptor);
140 1868 this->m_handle.m_file_descriptor = PosixFileHandle::INVALID_FILE_DESCRIPTOR;
141 }
142 1870 }
143
144 3880 PosixFile::Status PosixFile::size(FwSizeType& size_result) {
145 3880 FwSizeType current_position = 0;
146
1/1
✓ Branch 7 taken 3880 times.
3880 Status status = this->position(current_position);
147 3880 size_result = 0;
148
1/2
✓ Branch 0 taken 3880 times.
✗ Branch 1 not taken.
3880 if (Os::File::Status::OP_OK == status) {
149 // Must be a coding error if current_position is larger than off_t max in Posix File
150 3880 FW_ASSERT(current_position <= OFF_T_MAX_LIMIT);
151 // Seek to the end of the file to determine size
152 3880 off_t end_of_file = ::lseek(this->m_handle.m_file_descriptor, 0, SEEK_END);
153
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3880 times.
3880 if (PosixFileHandle::ERROR_RETURN_VALUE == end_of_file) {
154 ✗ int errno_store = errno;
155 ✗ status = Os::Posix::errno_to_file_status(errno_store);
156 }
157 // Return to original position
158 3880 (void)::lseek(this->m_handle.m_file_descriptor, static_cast<off_t>(current_position), SEEK_SET);
159 3880 size_result = static_cast<FwSizeType>(end_of_file);
160 }
161 3880 return status;
162 }
163
164 8590 PosixFile::Status PosixFile::position(FwSizeType& position_result) {
165 8590 Status status = OP_OK;
166 8590 position_result = 0;
167 8590 off_t actual = ::lseek(this->m_handle.m_file_descriptor, 0, SEEK_CUR);
168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8590 times.
8590 if (PosixFileHandle::ERROR_RETURN_VALUE == actual) {
169 ✗ int errno_store = errno;
170 ✗ status = Os::Posix::errno_to_file_status(errno_store);
171 }
172 // Protected by static assertion (FwSizeType >= off_t)
173 8590 position_result = static_cast<FwSizeType>(actual);
174 8590 return status;
175 }
176
177 57 PosixFile::Status PosixFile::preallocate(FwSizeType offset, FwSizeType length) {
178 57 PosixFile::Status status = Os::File::Status::NOT_SUPPORTED;
179 // Check for larger size than posix supports
180
3/6
✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 57 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 57 times.
114 if ((length > OFF_T_MAX_LIMIT) || (offset > OFF_T_MAX_LIMIT) ||
181
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 57 times.
57 (std::numeric_limits<off_t>::max() - length) < offset) {
182 ✗ status = Os::File::Status::BAD_SIZE;
183 }
184 // posix_fallocate is only available with the posix C-API post version 200112L, however; it is not guaranteed that
185 // this call is properly implemented. This code starts with a status of "NOT_SUPPORTED". When the standard is met
186 // an attempt will be made to called posix_fallocate, and should that still return NOT_SUPPORTED then fallback
187 // code is engaged to synthesize this behavior.
188 #if _POSIX_C_SOURCE >= 200112L && !(defined(FPRIME_SYNTHETIC_FALLOCATE) && FPRIME_SYNTHETIC_FALLOCATE)
189 else {
190 int errno_status =
191 57 ::posix_fallocate(this->m_handle.m_file_descriptor, static_cast<off_t>(offset), static_cast<off_t>(length));
192 57 status = Os::Posix::errno_to_file_status(errno_status);
193 }
194 #endif
195 // When the operation is not supported or posix-API is not sufficient, fallback to a slower algorithm
196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 if (Os::File::Status::NOT_SUPPORTED == status) {
197 // Calculate size
198 ✗ FwSizeType file_size = 0;
199 ✗ status = this->size(file_size);
200 ✗ if (Os::File::Status::OP_OK == status) {
201 // Calculate current position
202 ✗ FwSizeType file_position = 0;
203 ✗ status = this->position(file_position);
204 // Check for overflow in seek calls
205 ✗ if (file_position > static_cast<FwSizeType>(std::numeric_limits<FwSignedSizeType>::max()) ||
206 ✗ file_size > static_cast<FwSizeType>(std::numeric_limits<FwSignedSizeType>::max())) {
207 ✗ status = Os::File::Status::BAD_SIZE;
208 }
209 // Only allocate when the file is smaller than the allocation
210 ✗ else if ((Os::File::Status::OP_OK == status) && (file_size < (offset + length))) {
211 ✗ const FwSizeType write_length = (offset + length) - file_size;
212 ✗ status = this->seek(static_cast<FwSignedSizeType>(file_size), PosixFile::SeekType::ABSOLUTE);
213 ✗ if (Os::File::Status::OP_OK == status) {
214 // Fill in zeros past size of file to ensure compatibility with fallocate
215 ✗ for (FwSizeType i = 0; i < write_length; i++) {
216 ✗ FwSizeType write_size = 1;
217 status =
218 ✗ this->write(reinterpret_cast<const U8*>("\0"), write_size, PosixFile::WaitType::NO_WAIT);
219 ✗ if (Status::OP_OK != status || write_size != 1) {
220 break;
221 }
222 }
223 // Return to original position
224 ✗ if (Os::File::Status::OP_OK == status) {
225 status =
226 ✗ this->seek(static_cast<FwSignedSizeType>(file_position), PosixFile::SeekType::ABSOLUTE);
227 }
228 }
229 }
230 }
231 }
232 57 return status;
233 }
234
235 131 PosixFile::Status PosixFile::seek(FwSignedSizeType offset, PosixFile::SeekType seekType) {
236 131 Status status = OP_OK;
237
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 131 times.
131 if (offset > std::numeric_limits<off_t>::max()) {
238 ✗ status = BAD_SIZE;
239 } else {
240 131 off_t actual = ::lseek(this->m_handle.m_file_descriptor, static_cast<off_t>(offset),
241 131 (seekType == SeekType::ABSOLUTE) ? SEEK_SET : SEEK_CUR);
242 131 int errno_store = errno;
243
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 65 times.
131 if (actual == PosixFileHandle::ERROR_RETURN_VALUE) {
244 66 status = Os::Posix::errno_to_file_status(errno_store);
245
3/4
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 29 times.
65 } else if ((seekType == SeekType::ABSOLUTE) && (actual != offset)) {
246 ✗ status = Os::File::Status::OTHER_ERROR;
247 }
248 }
249 131 return status;
250 }
251
252 70 PosixFile::Status PosixFile::flush() {
253 70 PosixFile::Status status = OP_OK;
254
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 70 times.
70 if (PosixFileHandle::ERROR_RETURN_VALUE == ::fsync(this->m_handle.m_file_descriptor)) {
255 ✗ int errno_store = errno;
256 ✗ status = Os::Posix::errno_to_file_status(errno_store);
257 }
258 70 return status;
259 }
260
261 476 PosixFile::Status PosixFile::read(U8* buffer, FwSizeType& size, PosixFile::WaitType wait) {
262 476 FW_ASSERT(buffer != nullptr);
263 476 Status status = OP_OK;
264 476 FwSizeType accumulated = 0;
265 // Loop up to 2 times for each by, bounded to prevent overflow
266 const FwSizeType maximum =
267
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 476 times.
476 (size > (std::numeric_limits<FwSizeType>::max() / 2)) ? std::numeric_limits<FwSizeType>::max() : size * 2;
268 // POSIX APIs are implementation dependent when dealing with sizes larger than the signed return value
269 // thus we ensure a clear decision: BAD_SIZE
270
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 476 times.
476 if (size > SSIZE_T_MAX_LIMIT) {
271 ✗ return BAD_SIZE;
272 }
273
274
3/4
✓ Branch 0 taken 828 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 550 times.
✓ Branch 4 taken 278 times.
828 for (FwSizeType i = 0; i < maximum && accumulated < size; i++) {
275 // char* for some posix implementations
276 550 ssize_t read_size = ::read(this->m_handle.m_file_descriptor, reinterpret_cast<CHAR*>(&buffer[accumulated]),
277 550 static_cast<size_t>(size - accumulated));
278 // Non-interrupt error
279
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 549 times.
550 if (PosixFileHandle::ERROR_RETURN_VALUE == read_size) {
280 1 int errno_store = errno;
281 // Interrupted w/o read, try again
282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (EINTR == errno_store) {
283 ✗ continue;
284 }
285 1 status = Os::Posix::errno_to_file_status(errno_store);
286 1 break;
287 }
288 // End-of-file
289
2/2
✓ Branch 0 taken 130 times.
✓ Branch 1 taken 419 times.
549 else if (read_size == 0) {
290 130 break;
291 }
292 419 accumulated += static_cast<FwSizeType>(read_size);
293 // Stop looping when we had a good read and are not waiting
294
2/2
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 352 times.
419 if (not wait) {
295 67 break;
296 }
297 }
298 476 size = accumulated;
299 476 return status;
300 }
301
302 604 PosixFile::Status PosixFile::write(const U8* buffer, FwSizeType& size, PosixFile::WaitType wait) {
303 604 FW_ASSERT(buffer != nullptr);
304 604 Status status = OP_OK;
305 604 FwSizeType accumulated = 0;
306 // Loop up to 2 times for each by, bounded to prevent overflow
307 const FwSizeType maximum =
308
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 604 times.
604 (size > (std::numeric_limits<FwSizeType>::max() / 2)) ? std::numeric_limits<FwSizeType>::max() : size * 2;
309 // POSIX APIs are implementation dependent when dealing with sizes larger than the signed return value
310 // thus we ensure a clear decision: BAD_SIZE
311
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 604 times.
604 if (size > SSIZE_T_MAX_LIMIT) {
312 ✗ return BAD_SIZE;
313 }
314
315
3/4
✓ Branch 0 taken 1206 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 604 times.
✓ Branch 4 taken 602 times.
1206 for (FwSizeType i = 0; i < maximum && accumulated < size; i++) {
316 // char* for some posix implementations
317 ssize_t write_size =
318 604 ::write(this->m_handle.m_file_descriptor, reinterpret_cast<const CHAR*>(&buffer[accumulated]),
319 604 static_cast<size_t>(size - accumulated));
320 // Non-interrupt error
321
3/4
✓ Branch 0 taken 602 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 602 times.
604 if (PosixFileHandle::ERROR_RETURN_VALUE == write_size || write_size < 0) {
322 2 int errno_store = errno;
323 // Interrupted w/o write, try again
324
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (EINTR == errno_store) {
325 ✗ continue;
326 }
327 2 status = Os::Posix::errno_to_file_status(errno_store);
328 2 break;
329 }
330 602 accumulated += static_cast<FwSizeType>(write_size);
331 }
332 604 size = accumulated;
333 // When waiting, sync to disk
334
2/2
✓ Branch 0 taken 579 times.
✓ Branch 1 taken 25 times.
604 if (wait) {
335 579 int fsync_return = ::fsync(this->m_handle.m_file_descriptor);
336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 579 times.
579 if (PosixFileHandle::ERROR_RETURN_VALUE == fsync_return) {
337 ✗ int errno_store = errno;
338 ✗ status = Os::Posix::errno_to_file_status(errno_store);
339 }
340 }
341 604 return status;
342 }
343
344 2 FileHandle* PosixFile::getHandle() {
345 2 return &this->m_handle;
346 }
347
348 } // namespace File
349 } // namespace Posix
350 } // namespace Os
351