GCC Code Coverage Report


Directory: Svc/FileWorker/
File: FileWorker.cpp
Date: 2026-09-03 21:17:47
Exec Total Coverage
Lines: 291 325 89.5%
Functions: 14 14 100.0%
Branches: 220 265 83.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title FileWorker.cpp
3 // \author racheljt
4 // \brief cpp file for FileWorker component implementation class
5 // ======================================================================
6
7 #include "Svc/FileWorker/FileWorker.hpp"
8
9 namespace Svc {
10
11 // ----------------------------------------------------------------------
12 // Component construction and destruction
13 // ----------------------------------------------------------------------
14
15 19 FileWorker ::FileWorker(const char* const compName)
16 : FileWorkerComponentBase(compName),
17 19 m_state(FileWorkerState::FW_STATE_IDLE),
18 19 m_abort(false),
19 38 m_chunkSize(BLOCK_SIZE_BYTES) {}
20
21 19 void FileWorker ::configure(U64 chunkSize) {
22 19 FW_ASSERT(chunkSize > 0);
23 19 this->m_chunkSize = chunkSize;
24 19 }
25
26 38 FileWorker ::~FileWorker() {}
27
28 // ----------------------------------------------------------------------
29 // Handler implementations for typed input ports
30 // ----------------------------------------------------------------------
31
32 2 void FileWorker ::cancelIn_handler(FwIndexType portNum) {
33 2 this->m_abort.store(true, std::memory_order_relaxed);
34 2 }
35
36 7 void FileWorker ::readIn_handler(FwIndexType portNum, const Fw::StringBase& path, Fw::Buffer& buffer) {
37 // Validate inputs before processing file
38
3/3
✓ Branch 11 taken 7 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 6 times.
7 if (path.length() == 0) {
39
3/3
✓ Branch 6 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 15 taken 1 times.
1 this->log_WARNING_HI_InvalidInput(Fw::LogStringArg("readIn"), Fw::LogStringArg("empty path"));
40
1/1
✓ Branch 5 taken 1 times.
1 this->readDoneOut_out(0, FW_STATUS_INVALID_INPUT, 0);
41 1 return;
42 }
43
3/3
✓ Branch 4 taken 6 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 5 times.
6 if (!buffer.isValid()) {
44
3/3
✓ Branch 6 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 15 taken 1 times.
1 this->log_WARNING_HI_InvalidInput(Fw::LogStringArg("readIn"), Fw::LogStringArg("invalid buffer"));
45
1/1
✓ Branch 5 taken 1 times.
1 this->readDoneOut_out(0, FW_STATUS_INVALID_INPUT, 0);
46 1 return;
47 }
48
49
1/1
✓ Branch 11 taken 5 times.
5 const char* const fileName = path.toChar();
50 5 FwSizeType fileSize = 0;
51
52
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 4 times.
5 if (this->m_state != FW_STATE_IDLE) {
53
2/3
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_NotInIdle(this->m_state);
54
1/1
✓ Branch 5 taken 1 times.
1 this->readDoneOut_out(0, FW_STATUS_NOT_IDLE, 0);
55 1 return;
56 }
57
58 // New read request overrides any leftover abort state
59 4 this->m_abort.store(false, std::memory_order_relaxed);
60
61 4 this->m_state = FW_STATE_READING;
62
63 // Check CRC
64 4 U32 crcFromFile = 0;
65 4 U32 crcCalculated = 0;
66
1/1
✓ Branch 1 taken 4 times.
4 Utils::crc_stat_t crcStat = Utils::verify_checksum(fileName, crcFromFile, crcCalculated);
67
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if (crcStat != Utils::PASSED_FILE_CRC_CHECK) {
68
1/1
✓ Branch 5 taken 3 times.
3 this->log_WARNING_HI_CrcFailed(crcStat);
69
1/1
✓ Branch 5 taken 3 times.
3 this->readDoneOut_out(0, FW_STATUS_FAILED_CRC, 0);
70 3 this->m_state = FW_STATE_IDLE;
71 3 return;
72 }
73
74 // Get filesize
75
1/1
✓ Branch 1 taken 1 times.
1 Os::FileSystem::Status fsStat = Os::FileSystem::getFileSize(fileName, fileSize);
76
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (fsStat != Os::FileSystem::OP_OK) {
77 // Path is ground-controlled and the file may change between the CRC check and here
78 this->log_WARNING_HI_ReadFailedFileSize(fsStat);
79 this->readDoneOut_out(0, FW_STATUS_FAILED_FILE_SIZE, 0);
80 this->m_state = FW_STATE_IDLE;
81 return;
82 }
83
84 // Start reading
85
1/1
✓ Branch 4 taken 1 times.
1 FileWorkerStatus workerStat = this->readBufferFromFile(buffer, fileName);
86
87 // Report 0 bytes on a failed or aborted read so readDoneOut does not imply success.
88
3/4
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 13 taken 1 times.
1 this->readDoneOut_out(0, workerStat, (workerStat == FW_STATUS_DONE_READ) ? buffer.getSize() : 0);
89 1 this->m_state = FW_STATE_IDLE;
90 }
91
92 5 void FileWorker ::verifyIn_handler(FwIndexType portNum, const Fw::StringBase& path, U32 crc) {
93 // Validate inputs before processing file
94
3/3
✓ Branch 11 taken 5 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 4 times.
5 if (path.length() == 0) {
95
3/3
✓ Branch 6 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 15 taken 1 times.
1 this->log_WARNING_HI_InvalidInput(Fw::LogStringArg("verifyIn"), Fw::LogStringArg("empty path"));
96
1/1
✓ Branch 5 taken 1 times.
1 this->verifyDoneOut_out(0, FW_STATUS_INVALID_INPUT, 0);
97 1 return;
98 }
99
100
1/1
✓ Branch 11 taken 4 times.
4 const char* const fileName = path.toChar();
101 4 FwSizeType fileSize = 0;
102 4 FileWorkerStatus workerStat = FW_STATUS_DONE;
103
104 4 U32 crcFromFile = 0;
105 4 U32 crcCalculated = 0;
106
1/1
✓ Branch 1 taken 4 times.
4 Utils::crc_stat_t crcStat = Utils::verify_checksum(fileName, crcFromFile, crcCalculated);
107
108
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (crcStat != Utils::PASSED_FILE_CRC_CHECK) {
109
1/1
✓ Branch 5 taken 2 times.
2 this->log_WARNING_HI_CrcFailed(crcStat);
110 2 workerStat = FW_STATUS_FAILED_CRC;
111 }
112
113
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (crc != crcCalculated) {
114 2 workerStat = FW_STATUS_FAILED_CRC;
115
1/1
✓ Branch 5 taken 2 times.
2 this->log_WARNING_LO_CrcVerificationError(crc, crcCalculated);
116 }
117
118 // Get filesize
119
1/1
✓ Branch 1 taken 4 times.
4 Os::FileSystem::Status fsStat = Os::FileSystem::getFileSize(fileName, fileSize);
120
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (fsStat != Os::FileSystem::OP_OK) {
121
1/1
✓ Branch 5 taken 2 times.
2 this->log_WARNING_HI_ReadFailedFileSize(fsStat);
122 2 workerStat = FW_STATUS_FAILED_FILE_SIZE;
123 }
124
125
1/1
✓ Branch 5 taken 4 times.
4 this->verifyDoneOut_out(0, workerStat, fileSize);
126 }
127
128 11 void FileWorker ::writeIn_handler(FwIndexType portNum,
129 const Fw::StringBase& path,
130 Fw::Buffer& buffer,
131 FwSizeType offsetBytes,
132 bool append) {
133 // Validate inputs before processing file
134
3/3
✓ Branch 11 taken 11 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 10 times.
11 if (path.length() == 0) {
135
3/3
✓ Branch 6 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 15 taken 1 times.
1 this->log_WARNING_HI_InvalidInput(Fw::LogStringArg("writeIn"), Fw::LogStringArg("empty path"));
136
1/1
✓ Branch 5 taken 1 times.
1 this->writeDoneOut_out(0, FW_STATUS_INVALID_INPUT, 0);
137 1 return;
138 }
139
3/3
✓ Branch 4 taken 10 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 9 times.
10 if (!buffer.isValid()) {
140
3/3
✓ Branch 6 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 15 taken 1 times.
1 this->log_WARNING_HI_InvalidInput(Fw::LogStringArg("writeIn"), Fw::LogStringArg("invalid buffer"));
141
1/1
✓ Branch 5 taken 1 times.
1 this->writeDoneOut_out(0, FW_STATUS_INVALID_INPUT, 0);
142 1 return;
143 }
144
3/3
✓ Branch 4 taken 9 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 8 times.
9 if (offsetBytes > buffer.getSize()) {
145
3/3
✓ Branch 6 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 15 taken 1 times.
1 this->log_WARNING_HI_InvalidInput(Fw::LogStringArg("writeIn"), Fw::LogStringArg("invalid offset"));
146
1/1
✓ Branch 5 taken 1 times.
1 this->writeDoneOut_out(0, FW_STATUS_INVALID_INPUT, 0);
147 1 return;
148 }
149
150 8 char fileName[FileNameStringSize];
151
152 // Make sure we are in IDLE state before proceeding
153
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 7 times.
8 if (this->m_state != FW_STATE_IDLE) {
154
2/3
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_NotInIdle(this->m_state);
155
1/1
✓ Branch 5 taken 1 times.
1 this->writeDoneOut_out(0, FW_STATUS_NOT_IDLE, 0);
156 1 return;
157 }
158
159 7 this->m_state = FW_STATE_WRITING;
160
161 // New write request overrides any leftover abort state
162 7 this->m_abort.store(false, std::memory_order_relaxed);
163
164 // Save file name
165 // NB: may count null terminator due to FPRIME/fprime-sw#57, but should still be less than FileNameStringSize in any
166 // case
167
2/2
✓ Branch 11 taken 7 times.
✓ Branch 14 taken 7 times.
7 FwSizeType length = Fw::StringUtils::string_length(path.toChar(), FileNameStringSize);
168
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
7 if (length >= FileNameStringSize || length >= sizeof(fileName)) {
169 // Path length is ground-controlled, so an oversized path is invalid input, not a coding error.
170 this->log_WARNING_HI_InvalidInput(Fw::LogStringArg("writeIn"), Fw::LogStringArg("path too long"));
171 this->writeDoneOut_out(0, FW_STATUS_INVALID_INPUT, 0);
172 this->m_state = FW_STATE_IDLE;
173 return;
174 }
175
176
2/2
✓ Branch 11 taken 7 times.
✓ Branch 14 taken 7 times.
7 (void)Fw::StringUtils::string_copy(fileName, path.toChar(), sizeof(fileName));
177 7 fileName[sizeof(fileName) - 1] = 0; // guarantee termination
178
179 // Write
180
1/1
✓ Branch 4 taken 7 times.
7 const bool isWrite = this->writeBufferToFile(buffer, fileName, offsetBytes, append);
181
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 if (isWrite) {
182
1/1
✓ Branch 4 taken 6 times.
6 this->writeBufferHashToFile(buffer, fileName, offsetBytes, append);
183 }
184
185 // Report the actual outcome of the write. A failed writeBufferToFile (open
186 // failure, permission denied, disk full, partial write) must not be reported
187 // to ground as a successful FW_STATUS_DONE_WRITE.
188
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 const FileWorkerStatus writeStatus = isWrite ? FW_STATUS_DONE_WRITE : FW_STATUS_FAILED_TO_WRITE;
189 // Report bytes actually written, which excludes the skipped offset. Reporting the full
190 // buffer size over-reports by offsetBytes, and reports a whole buffer for a zero-length
191 // write. offsetBytes <= buffer.getSize() is checked above, so this cannot underflow.
192
1/1
✓ Branch 4 taken 7 times.
7 const FwSizeType writtenBytes = buffer.getSize() - offsetBytes;
193
3/3
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 1 times.
✓ Branch 7 taken 7 times.
7 this->writeDoneOut_out(0, writeStatus, isWrite ? writtenBytes : 0);
194 7 this->m_state = FW_STATE_IDLE;
195 7 return;
196 }
197
198 // ----------------------------------------------------------------------
199 // Helper functions
200 // ----------------------------------------------------------------------
201
202 5 Svc ::FileWorkerStatus FileWorker ::readBufferFromFile(Fw::Buffer& buffer, const char* const fileName) {
203 5 FW_ASSERT(buffer.getData() != nullptr);
204 5 FW_ASSERT(fileName != nullptr);
205
206
1/1
✓ Branch 2 taken 5 times.
5 Fw::LogStringArg fileNameStr(fileName);
207
1/1
✓ Branch 2 taken 5 times.
5 Os::File file;
208
209 // Open file
210
1/1
✓ Branch 2 taken 5 times.
5 Os::File::Status fileStat = file.open(fileName, Os::File::OPEN_READ);
211
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (fileStat != Os::File::OP_OK) {
212
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_OpenFileError(fileNameStr, fileStat);
213 1 return FW_STATUS_FAILED_TO_OPEN;
214 }
215
216 // Get buffer data and size
217
1/1
✓ Branch 4 taken 4 times.
4 FwSizeType readSize = buffer.getSize();
218
219 // Read file
220
1/1
✓ Branch 5 taken 4 times.
4 this->log_ACTIVITY_LO_ReadBegin(readSize, fileNameStr);
221
1/1
✓ Branch 4 taken 4 times.
4 const FileWorkerReadStatus readStat = this->readFile(buffer, readSize, file, fileNameStr);
222
223
1/1
✓ Branch 5 taken 4 times.
4 this->log_ACTIVITY_LO_ReadCompleted(readSize, fileNameStr);
224
1/1
✓ Branch 2 taken 4 times.
4 file.close();
225
226 // Only a completed read is DONE_READ; error, abort, or timeout reports FAILED_TO_READ.
227
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 return (readStat == FileWorkerReadStatus::FW_READ_DONE) ? FileWorkerStatus::FW_STATUS_DONE_READ
228 4 : FileWorkerStatus::FW_STATUS_FAILED_TO_READ;
229 5 }
230
231 7 Svc ::FileWorkerReadStatus FileWorker ::readFile(Fw::Buffer& buffer,
232 FwSizeType size,
233 Os::File& file,
234 const Fw::LogStringArg& fileNameStr) {
235 7 FW_ASSERT(buffer.getData() != nullptr);
236 7 FW_ASSERT(size > 0);
237 7 FW_ASSERT(fileNameStr != nullptr);
238
239 7 FwSizeType bytesRead = 0;
240 7 FwSizeType numChunks = 0;
241 7 U64 timeout = 0;
242
243
3/3
✓ Branch 4 taken 7 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 6 times.
7 if (!file.isOpen()) {
244 1 return FileWorkerReadStatus::FW_READ_ERROR;
245 }
246
247
1/1
✓ Branch 4 taken 6 times.
6 FileWorkerReadStatus readStat = this->readFileBytes(buffer, size, file, bytesRead);
248
249
3/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
6 switch (readStat) {
250 2 case FW_READ_ERROR:
251 // Some read error
252
1/1
✓ Branch 8 taken 2 times.
2 this->log_WARNING_HI_ReadError(bytesRead, size, fileNameStr);
253 2 break;
254
255 2 case FW_READ_DONE:
256 2 break;
257
258 2 case FW_READ_ABORT:
259 // Abort command was sent
260
1/1
✓ Branch 8 taken 2 times.
2 this->log_WARNING_LO_ReadAborted(bytesRead, size, fileNameStr);
261 2 break;
262
263 case FW_READ_TIMEOUT:
264 // Determine true timeout
265 FW_ASSERT(this->m_chunkSize > 0);
266 numChunks = (size / this->m_chunkSize);
267 if (size % this->m_chunkSize > 0) {
268 numChunks += 1;
269 }
270 timeout = numChunks * TIMEOUT_MS;
271 this->log_WARNING_HI_ReadTimeout(bytesRead, size, fileNameStr, timeout);
272 break;
273
274 case FW_READ_UNKNOWN:
275 // The read loop ran out of iterations: a read larger than
276 // MAX_LOOP_ITERATIONS * BLOCK_SIZE_BYTES cannot complete
277 this->log_WARNING_HI_ReadError(bytesRead, size, fileNameStr);
278 break;
279
280 default:
281 FW_ASSERT(false, static_cast<FwAssertArgType>(readStat));
282 break;
283 }
284
285 6 return readStat;
286 }
287
288 9 Svc ::FileWorkerReadStatus FileWorker ::readFileBytes(Fw::Buffer& buffer,
289 FwSizeType size,
290 Os::File& file,
291 FwSizeType& bytesRead) {
292 9 FW_ASSERT(buffer.getData() != nullptr);
293 9 FW_ASSERT(size > 0);
294
295 // Determine true timeout
296 9 FW_ASSERT(this->m_chunkSize > 0);
297
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
9 FwSizeType numChunks = (size / this->m_chunkSize);
298
3/4
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 6 times.
9 if (size % this->m_chunkSize > 0) {
299 3 numChunks += 1;
300 }
301 9 U64 timeout = numChunks * TIMEOUT_MS;
302
303 // Read loop
304 9 bytesRead = 0;
305
1/1
✓ Branch 3 taken 9 times.
9 Fw::Time start = this->getTime();
306
307
1/2
✓ Branch 0 taken 1042 times.
✗ Branch 1 not taken.
1042 for (FwSizeType i = 0; i < numChunks; i++) {
308 1042 FwSizeType readAmt = FW_MIN(size - bytesRead, this->m_chunkSize);
309 1042 FwSizeType readAmtActual = readAmt;
310
2/2
✓ Branch 7 taken 1042 times.
✓ Branch 12 taken 1042 times.
1042 Os::File::Status ret = file.read(buffer.getData() + bytesRead, readAmtActual);
311
312
3/4
✓ Branch 0 taken 1039 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1039 times.
1042 if (Os::File::OP_OK != ret || readAmt != readAmtActual) {
313 // Count the bytes actually transferred so ReadError telemetry reports
314 // the true amount. A short read stays an error on purpose: FileWorker
315 // reads a fixed, caller-specified size and must not silently accept a
316 // file shorter than expected (e.g. truncated mid-read).
317 3 bytesRead += readAmtActual;
318 3 return FileWorkerReadStatus::FW_READ_ERROR;
319 }
320
321 1039 bool currAbort = this->m_abort.load(std::memory_order_relaxed);
322
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1036 times.
1039 if (currAbort) {
323 // Abort command was sent
324 3 return FileWorkerReadStatus::FW_READ_ABORT;
325 }
326
327
1/2
✓ Branch 0 taken 1036 times.
✗ Branch 1 not taken.
1036 if (timeout > 0) {
328 // Only check timeout if > 0
329
1/1
✓ Branch 3 taken 1036 times.
1036 Fw::Time now = this->getTime();
330
1/1
✓ Branch 2 taken 1036 times.
1036 Fw::Time diff = Fw::Time::sub(now, start);
331
2/2
✓ Branch 2 taken 1036 times.
✓ Branch 6 taken 1036 times.
1036 U64 elapsed = (diff.getSeconds() * 1000000) + diff.getUSeconds();
332
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1036 times.
1036 if (elapsed >= timeout) {
333 return FileWorkerReadStatus::FW_READ_TIMEOUT;
334 }
335 1036 }
336
337 1036 bytesRead += readAmt;
338
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1033 times.
1036 if (bytesRead >= size) {
339 // Finished, break out
340 3 return FileWorkerReadStatus::FW_READ_DONE;
341 }
342 }
343
344 return FileWorkerReadStatus::FW_READ_UNKNOWN;
345 9 }
346
347 8 bool FileWorker ::getHash(const char* const hashFileName,
348 Utils::Hash& hash,
349 Utils::HashBuffer& hashBuffer,
350 const U8* const data,
351 const FwSizeType size) {
352 8 FW_ASSERT(hashFileName != nullptr);
353 8 FW_ASSERT(data != nullptr);
354 8 FW_ASSERT(size > 0);
355
356 // Open file
357
1/1
✓ Branch 2 taken 8 times.
8 Os::File file;
358
1/1
✓ Branch 2 taken 8 times.
8 Os::File::Status stat = file.open(hashFileName, Os::File::OPEN_READ);
359
360 // Read value if it exists
361
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if (stat == Os::File::OP_OK) {
362 6 HASH_HANDLE_TYPE hashValue;
363 6 FwSizeType hashSize = sizeof(hashValue);
364 6 U8* hashValuePtr = reinterpret_cast<U8*>(&hashValue);
365 6 FW_ASSERT(hashValuePtr != nullptr);
366
367
1/1
✓ Branch 2 taken 6 times.
6 Os::File::Status readStat = file.read(hashValuePtr, hashSize);
368
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (readStat != Os::File::OP_OK) {
369
1/1
✓ Branch 2 taken 1 times.
1 Fw::LogStringArg s(hashFileName);
370
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_WriteValidationReadError(s, readStat);
371 1 return false;
372 1 }
373
1/1
✓ Branch 2 taken 5 times.
5 Utils::HashBuffer tmp(hashValuePtr, hashSize);
374
1/1
✓ Branch 2 taken 5 times.
5 hash.setHashValue(tmp);
375
1/1
✓ Branch 2 taken 5 times.
5 hash.update(data, size);
376
1/1
✓ Branch 2 taken 5 times.
5 hash.finalize(hashBuffer);
377
378
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
7 } else if (stat == Os::File::DOESNT_EXIST) {
379
1/1
✓ Branch 1 taken 1 times.
1 hash.hash(data, size, hashBuffer);
380
381 } else {
382
1/1
✓ Branch 2 taken 1 times.
1 Fw::LogStringArg s(hashFileName);
383
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_WriteValidationOpenError(s, stat);
384 1 return false;
385 1 }
386
387 6 return true;
388 8 }
389
390 8 bool FileWorker ::writeBufferToFile(Fw::Buffer& buffer, const char* fileName, FwSizeType offset, bool append) {
391 8 FW_ASSERT(buffer.getData() != nullptr);
392 8 FW_ASSERT(fileName != nullptr);
393
394
1/1
✓ Branch 2 taken 8 times.
8 Fw::LogStringArg logStringArg(fileName);
395
396 // Get buffer data and size, then apply offset
397
1/1
✓ Branch 4 taken 8 times.
8 FwSizeType size = buffer.getSize();
398
1/1
✓ Branch 4 taken 8 times.
8 U8* const data = reinterpret_cast<U8*>(buffer.getData());
399 8 FW_ASSERT(data != nullptr);
400 8 FW_ASSERT(offset <= size);
401 8 size -= offset;
402
403 // A zero-length write (offset == buffer size, a valid "nothing left to write" boundary
404 // permitted by writeIn_handler's offset check) is a successful no-op. Return before
405 // opening the file: this avoids reaching FW_ASSERT(size > 0) in writeToFile(), and avoids
406 // creating an empty file for a request that writes nothing, since both OPEN_WRITE and
407 // OPEN_APPEND pass O_CREAT. An existing file's contents are not at risk either way here:
408 // OPEN_WRITE overwrites in place and does not truncate; only OPEN_CREATE sets O_TRUNC.
409
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (size == 0) {
410
1/1
✓ Branch 5 taken 1 times.
1 this->log_ACTIVITY_LO_WriteCompleted(size, logStringArg);
411 1 return true;
412 }
413
414 7 U8* const dataFromOffset = reinterpret_cast<U8*>(data + offset);
415 7 FW_ASSERT(dataFromOffset != nullptr);
416
417
1/1
✓ Branch 2 taken 7 times.
7 Os::File file;
418 7 Os::File::Status stat = Os::File::OP_OK;
419
420 // Open file
421
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 if (!append) {
422
1/1
✓ Branch 2 taken 2 times.
2 stat = file.open(fileName, Os::File::Mode::OPEN_WRITE);
423 } else {
424
1/1
✓ Branch 2 taken 5 times.
5 stat = file.open(fileName, Os::File::Mode::OPEN_APPEND);
425 }
426
427
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 if (stat != Os::File::OP_OK) {
428
1/1
✓ Branch 5 taken 2 times.
2 this->log_WARNING_HI_OpenFileError(logStringArg, stat);
429 2 return false;
430 }
431
432 // Write file
433
1/1
✓ Branch 5 taken 5 times.
5 this->log_ACTIVITY_LO_WriteBegin(size, logStringArg);
434
1/1
✓ Branch 4 taken 5 times.
5 FwSizeType writtenSize = this->writeToFile(dataFromOffset, size, file, fileName);
435
436 // Check written size
437
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (writtenSize != size) {
438 return false;
439 }
440
441
1/1
✓ Branch 5 taken 5 times.
5 this->log_ACTIVITY_LO_WriteCompleted(size, logStringArg);
442 5 return true;
443 8 }
444
445 8 void FileWorker ::writeBufferHashToFile(Fw::Buffer& buffer, const char* fileName, FwSizeType offset, bool append) {
446 8 FW_ASSERT(buffer.getData() != nullptr);
447 8 FW_ASSERT(fileName != nullptr);
448
449 // Construct hash file name
450
1/1
✓ Branch 1 taken 8 times.
8 const char* ext = Utils::Hash::getFileExtensionString();
451 8 FW_ASSERT(ext != nullptr);
452 8 char hashFileName[FileNameStringSize];
453
1/1
✓ Branch 1 taken 8 times.
8 Fw::FormatStatus status = Fw::stringFormat(hashFileName, sizeof(hashFileName), "%s%s", fileName, ext);
454 8 FW_ASSERT(status == Fw::FormatStatus::SUCCESS);
455
456 // Compute hash
457
1/1
✓ Branch 2 taken 8 times.
8 Utils::HashBuffer hashBuffer;
458
1/1
✓ Branch 4 taken 8 times.
8 FwSizeType size = buffer.getSize();
459
1/1
✓ Branch 4 taken 8 times.
8 U8* const data = reinterpret_cast<U8*>(buffer.getData());
460 8 FW_ASSERT(data != nullptr);
461
462 // Apply offset
463 8 FW_ASSERT(offset <= size);
464 8 size -= offset; // checked by assert
465
466 // A zero-length write changed no file contents, so the hash must not change either. Skip
467 // generation entirely: on the append path this would otherwise trip FW_ASSERT(size > 0) in
468 // getHash(), and on the non-append path it would silently overwrite a valid hash file with
469 // the hash of zero bytes.
470
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (size == 0) {
471 1 return;
472 }
473
474 7 U8* const dataFromOffset = reinterpret_cast<U8*>(data + offset);
475 7 FW_ASSERT(dataFromOffset != nullptr);
476
477
1/1
✓ Branch 2 taken 7 times.
7 Utils::Hash hash;
478
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 if (!append) {
479
1/1
✓ Branch 1 taken 2 times.
2 hash.hash(dataFromOffset, size, hashBuffer);
480
481 } else {
482
1/1
✓ Branch 4 taken 5 times.
5 bool isHash = this->getHash(hashFileName, hash, hashBuffer, dataFromOffset, size);
483
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (!isHash) {
484 return;
485 }
486 }
487
488 // Open file
489
1/1
✓ Branch 2 taken 7 times.
7 Os::File file;
490
1/1
✓ Branch 2 taken 7 times.
7 Os::File::Status stat = file.open(hashFileName, Os::File::Mode::OPEN_WRITE);
491
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (stat != Os::File::OP_OK) {
492
1/1
✓ Branch 2 taken 1 times.
1 Fw::LogStringArg logStringArg(hashFileName);
493
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_OpenFileError(logStringArg, stat);
494 1 return;
495 1 }
496
497 // Write hash
498
2/2
✓ Branch 5 taken 6 times.
✓ Branch 10 taken 6 times.
6 FwSizeType writtenSize = this->writeToFile(hashBuffer.getBuffAddr(), hashBuffer.getSize(), file, hashFileName);
499
500 // Check written size
501
1/1
✓ Branch 2 taken 6 times.
6 FwSizeType hashSize = hashBuffer.getSize();
502
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (writtenSize != hashSize) {
503 Fw::LogStringArg logStringArg(hashFileName);
504 this->log_WARNING_LO_WriteValidationError(logStringArg, writtenSize, hashSize);
505 return;
506 }
507
508 6 return;
509 8 }
510
511 13 FwSizeType FileWorker ::writeToFile(const U8* data, FwSizeType size, Os::File& file, const char* fileName) {
512 13 FW_ASSERT(data != nullptr);
513 13 FW_ASSERT(size > 0);
514 13 FW_ASSERT(file.isOpen());
515 13 FW_ASSERT(fileName != nullptr);
516
517 // Determine true timeout
518 13 FW_ASSERT(this->m_chunkSize > 0);
519
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 13 times.
13 FwSizeType numChunks = (size / this->m_chunkSize);
520
2/4
✗ Branch 4 not taken.
✓ Branch 5 taken 13 times.
✓ Branch 6 taken 13 times.
✗ Branch 7 not taken.
13 if (size % this->m_chunkSize > 0) {
521 13 numChunks += 1;
522 }
523 13 U64 timeout = numChunks * TIMEOUT_MS;
524
525 // Write loop: legal short writes make progress but consume an iteration, so
526 // allow extra iterations beyond the chunk count before giving up
527 13 const FwSizeType maxIterations = numChunks + MAX_LOOP_ITERATIONS;
528 13 FwSizeType bytesWritten = 0;
529
1/1
✓ Branch 3 taken 13 times.
13 Fw::Time start = this->getTime();
530
3/4
✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✓ Branch 3 taken 11 times.
74 for (FwSizeType i = 0; (i < maxIterations) && (bytesWritten < size); i++) {
531 63 FwSizeType writeAmt = FW_MIN(size - bytesWritten, this->m_chunkSize);
532
1/1
✓ Branch 5 taken 63 times.
63 Os::File::Status ret = file.write(data + bytesWritten, writeAmt);
533
534
3/4
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 62 times.
63 if (Os::File::OP_OK != ret || writeAmt == 0) {
535
1/1
✓ Branch 2 taken 1 times.
1 Fw::LogStringArg logStringArg(fileName);
536
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_WriteFileError(bytesWritten, size, logStringArg, ret);
537 1 break;
538 1 }
539
540 62 bool currAbort = this->m_abort.load(std::memory_order_relaxed);
541
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 61 times.
62 if (currAbort) {
542 // Abort command was sent
543
1/1
✓ Branch 2 taken 1 times.
1 Fw::LogStringArg logStringArg(fileName);
544
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_LO_WriteAborted(bytesWritten, size, logStringArg);
545 1 break;
546 1 }
547
548
1/2
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
61 if (timeout > 0) {
549 // Only check timeout if > 0
550
1/1
✓ Branch 3 taken 61 times.
61 Fw::Time now = this->getTime();
551
1/1
✓ Branch 2 taken 61 times.
61 Fw::Time diff = Fw::Time::sub(now, start);
552
2/2
✓ Branch 2 taken 61 times.
✓ Branch 6 taken 61 times.
61 U64 elapsed = (diff.getSeconds() * 1000000) + diff.getUSeconds();
553
554
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
61 if (elapsed >= timeout) {
555 Fw::LogStringArg logStringArg(fileName);
556 this->log_WARNING_HI_WriteTimeout(bytesWritten, size, logStringArg, timeout);
557 break;
558 }
559 61 }
560
561 61 bytesWritten += writeAmt;
562 }
563
564 13 return bytesWritten;
565 13 }
566
567 } // namespace Svc
568