F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
FileWorker.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title FileWorker.cpp
3 // \author racheljt
4 // \brief cpp file for FileWorker component implementation class
5 // ======================================================================
6 
8 
9 namespace Svc {
10 
11 // ----------------------------------------------------------------------
12 // Component construction and destruction
13 // ----------------------------------------------------------------------
14 
15 FileWorker ::FileWorker(const char* const compName)
16  : FileWorkerComponentBase(compName), m_state(FileWorkerState::FW_STATE_IDLE), m_abort(false), m_chunkSize(0) {}
17 
18 void FileWorker ::configure(U64 chunkSize) {
19  FW_ASSERT(chunkSize > 0);
20  this->m_chunkSize = chunkSize;
21 }
22 
24 
25 // ----------------------------------------------------------------------
26 // Handler implementations for typed input ports
27 // ----------------------------------------------------------------------
28 
29 void FileWorker ::cancelIn_handler(FwIndexType portNum) {
30  this->m_abort.store(true, std::memory_order_relaxed);
31 }
32 
33 void FileWorker ::readIn_handler(FwIndexType portNum, const Fw::StringBase& path, Fw::Buffer& buffer) {
34  FW_ASSERT(path != nullptr);
35  FW_ASSERT(path.length() > 0);
36  FW_ASSERT(buffer.getData() != nullptr);
37 
38  const char* const fileName = path.toChar();
39  FwSizeType fileSize = 0;
40 
41  if (this->m_state != FW_STATE_IDLE) {
42  this->log_WARNING_HI_NotInIdle(this->m_state);
44  return;
45  }
46 
47  // New read request overrides any leftover abort state
48  this->m_abort.store(false, std::memory_order_relaxed);
49 
50  this->m_state = FW_STATE_READING;
51 
52  // Check CRC
53  U32 crcFromFile = 0;
54  U32 crcCalculated = 0;
55  Utils::crc_stat_t crcStat = Utils::verify_checksum(fileName, crcFromFile, crcCalculated);
56  if (crcStat != Utils::PASSED_FILE_CRC_CHECK) {
57  this->log_WARNING_HI_CrcFailed(crcStat);
59  this->m_state = FW_STATE_IDLE;
60  return;
61  }
62 
63  // Get filesize
64  Os::FileSystem::Status fsStat = Os::FileSystem::getFileSize(fileName, fileSize);
65  FW_ASSERT(fsStat == Os::FileSystem::OP_OK, fsStat); // file size checked with checksum
66 
67  // Start reading
68  FileWorkerStatus workerStat = this->readBufferFromFile(buffer, fileName);
69 
70  // Signal done and pass U8* buffer with data
71  this->readDoneOut_out(0, workerStat, fileSize);
72  this->m_state = FW_STATE_IDLE;
73 }
74 
75 void FileWorker ::verifyIn_handler(FwIndexType portNum, const Fw::StringBase& path, U32 crc) {
76  FW_ASSERT(path != nullptr);
77  FW_ASSERT(path.length() > 0);
78 
79  const char* const fileName = path.toChar();
80  FwSizeType fileSize = 0;
81  FileWorkerStatus workerStat = FW_STATUS_DONE;
82 
83  U32 crcFromFile = 0;
84  U32 crcCalculated = 0;
85  Utils::crc_stat_t crcStat = Utils::verify_checksum(fileName, crcFromFile, crcCalculated);
86 
87  if (crcStat != Utils::PASSED_FILE_CRC_CHECK) {
88  this->log_WARNING_HI_CrcFailed(crcStat);
89  workerStat = FW_STATUS_FAILED_CRC;
90  }
91 
92  if (crc != crcFromFile) {
93  workerStat = FW_STATUS_FAILED_CRC;
94  this->log_WARNING_LO_CrcVerificationError(crc, crcCalculated);
95  }
96 
97  // Get filesize
98  Os::FileSystem::Status fsStat = Os::FileSystem::getFileSize(fileName, fileSize);
99  if (fsStat != Os::FileSystem::OP_OK) {
100  this->log_WARNING_HI_ReadFailedFileSize(fsStat);
101  workerStat = FW_STATUS_FAILED_FILE_SIZE;
102  }
103 
104  this->verifyDoneOut_out(0, workerStat, fileSize);
105 }
106 
107 void FileWorker ::writeIn_handler(FwIndexType portNum,
108  const Fw::StringBase& path,
109  Fw::Buffer& buffer,
110  FwSizeType offsetBytes,
111  bool append) {
112  FW_ASSERT(path != nullptr);
113  FW_ASSERT(path.length() > 0);
114  FW_ASSERT(buffer.getData() != nullptr);
115  FW_ASSERT(offsetBytes <= buffer.getSize());
116 
117  char fileName[FileNameStringSize];
118 
119  // Make sure we are in IDLE state before proceeding
120  if (this->m_state != FW_STATE_IDLE) {
121  this->log_WARNING_HI_NotInIdle(this->m_state);
123  return;
124  }
125 
126  this->m_state = FW_STATE_WRITING;
127 
128  // New write request overrides any leftover abort state
129  this->m_abort.store(false, std::memory_order_relaxed);
130 
131  // Save file name
132  // NB: may count null terminator due to FPRIME/fprime-sw#57, but should still be less than FileNameStringSize in any
133  // case
135  FW_ASSERT(length < FileNameStringSize && length < sizeof(fileName));
136 
137  (void)Fw::StringUtils::string_copy(fileName, path.toChar(), sizeof(fileName));
138  fileName[sizeof(fileName) - 1] = 0; // guarantee termination
139 
140  // Write
141  bool isWrite = this->writeBufferToFile(buffer, fileName, offsetBytes, append);
142  if (isWrite) {
143  this->writeBufferHashToFile(buffer, fileName, offsetBytes, append);
144  }
145 
146  this->writeDoneOut_out(0, FW_STATUS_DONE_WRITE, buffer.getSize());
147  this->m_state = FW_STATE_IDLE;
148  return;
149 }
150 
151 // ----------------------------------------------------------------------
152 // Helper functions
153 // ----------------------------------------------------------------------
154 
155 Svc ::FileWorkerStatus FileWorker ::readBufferFromFile(Fw::Buffer& buffer, const char* const fileName) {
156  FW_ASSERT(buffer.getData() != nullptr);
157  FW_ASSERT(fileName != nullptr);
158 
159  Fw::LogStringArg fileNameStr(fileName);
160  Os::File file;
161 
162  // Open file
163  Os::File::Status fileStat = file.open(fileName, Os::File::OPEN_READ);
164  if (fileStat != Os::File::OP_OK) {
165  this->log_WARNING_HI_OpenFileError(fileNameStr, fileStat);
167  }
168 
169  // Get buffer data and size
170  FwSizeType readSize = buffer.getSize();
171 
172  // Read file
173  this->log_ACTIVITY_LO_ReadBegin(readSize, fileNameStr);
174  this->readFile(buffer, readSize, file, fileNameStr);
175 
176  this->log_ACTIVITY_LO_ReadCompleted(readSize, fileNameStr);
177  file.close();
178 
180 }
181 
182 void FileWorker ::readFile(Fw::Buffer& buffer, FwSizeType size, Os::File& file, const Fw::LogStringArg& fileNameStr) {
183  FW_ASSERT(buffer.getData() != nullptr);
184  FW_ASSERT(size > 0);
185  FW_ASSERT(fileNameStr != nullptr);
186 
187  FwSizeType bytesRead = 0;
188  FwSizeType numChunks = 0;
189  U64 timeout = 0;
190 
191  if (!file.isOpen()) {
192  return;
193  }
194 
195  FileWorkerReadStatus readStat = this->readFileBytes(buffer, size, file, bytesRead);
196 
197  switch (readStat) {
198  case FW_READ_ERROR:
199  // Some read error
200  this->log_WARNING_HI_ReadError(bytesRead, size, fileNameStr);
201  break;
202 
203  case FW_READ_DONE:
204  break;
205 
206  case FW_READ_ABORT:
207  // Abort command was sent
208  this->log_WARNING_LO_ReadAborted(bytesRead, size, fileNameStr);
209  break;
210 
211  case FW_READ_TIMEOUT:
212  // Determine true timeout
213  static_assert(BLOCK_SIZE_BYTES > 0, "Divide by 0 error");
214  numChunks = (size / BLOCK_SIZE_BYTES);
215  if (size % BLOCK_SIZE_BYTES > 0) {
216  numChunks += 1;
217  }
218  timeout = numChunks * TIMEOUT_MS;
219  this->log_WARNING_HI_ReadTimeout(bytesRead, size, fileNameStr, timeout);
220  break;
221 
222  default:
223  FW_ASSERT(0); // Should not get here
224  break;
225  }
226 
227  return;
228 }
229 
230 Svc ::FileWorkerReadStatus FileWorker ::readFileBytes(Fw::Buffer& buffer,
231  FwSizeType size,
232  Os::File& file,
233  FwSizeType& bytesRead) {
234  FW_ASSERT(buffer.getData() != nullptr);
235  FW_ASSERT(size > 0);
236 
237  // Determine true timeout
238  static_assert(BLOCK_SIZE_BYTES > 0, "Divide by 0 error");
239  FwSizeType numChunks = (size / BLOCK_SIZE_BYTES);
240  if (size % BLOCK_SIZE_BYTES > 0) {
241  numChunks += 1;
242  }
243  U64 timeout = numChunks * TIMEOUT_MS;
244 
245  // Read loop
246  bytesRead = 0;
247  Fw::Time start = this->getTime();
248 
249  for (U32 i = 0; i < MAX_LOOP_ITERATIONS; i++) {
250  FwSizeType readAmt = FW_MIN(size - bytesRead, BLOCK_SIZE_BYTES);
251  FwSizeType readAmtActual = readAmt;
252  Os::File::Status ret = file.read(buffer.getData() + bytesRead, readAmtActual);
253 
254  if (Os::File::OP_OK != ret || readAmt != readAmtActual) {
256  }
257 
258  bool currAbort = this->m_abort.load(std::memory_order_relaxed);
259  if (currAbort) {
260  // Abort command was sent
262  }
263 
264  if (timeout > 0) {
265  // Only check timeout if > 0
266  Fw::Time now = this->getTime();
267  Fw::Time diff = Fw::Time::sub(now, start);
268  U64 elapsed = (diff.getSeconds() * 1000000) + diff.getUSeconds();
269  if (elapsed >= timeout) {
271  }
272  }
273 
274  bytesRead += readAmt;
275  if (bytesRead >= size) {
276  // Finished, break out
278  }
279  }
280 
282 }
283 
284 bool FileWorker ::getHash(const char* const hashFileName,
285  Utils::Hash& hash,
286  Utils::HashBuffer& hashBuffer,
287  const U8* const data,
288  const FwSizeType size) {
289  FW_ASSERT(hashFileName != nullptr);
290  FW_ASSERT(data != nullptr);
291  FW_ASSERT(size > 0);
292 
293  // Open file
294  Os::File file;
295  Os::File::Status stat = file.open(hashFileName, Os::File::OPEN_READ);
296 
297  // Read value if it exists
298  if (stat == Os::File::OP_OK) {
299  HASH_HANDLE_TYPE hashValue;
300  FwSizeType hashSize = sizeof(hashValue);
301  U8* hashValuePtr = reinterpret_cast<U8*>(&hashValue);
302  FW_ASSERT(hashValuePtr != nullptr);
303 
304  Os::File::Status readStat = file.read(hashValuePtr, hashSize);
305  if (readStat != Os::File::OP_OK) {
306  Fw::LogStringArg s(hashFileName);
307  this->log_WARNING_HI_WriteValidationReadError(s, readStat);
308  return false;
309  }
310  Utils::HashBuffer tmp(hashValuePtr, hashSize);
311  hash.setHashValue(tmp);
312  hash.update(data, size);
313  hash.finalize(hashBuffer);
314 
315  } else if (stat == Os::File::DOESNT_EXIST) {
316  hash.hash(data, size, hashBuffer);
317 
318  } else {
319  Fw::LogStringArg s(hashFileName);
321  return false;
322  }
323 
324  return true;
325 }
326 
327 bool FileWorker ::writeBufferToFile(Fw::Buffer& buffer, const char* fileName, FwSizeType offset, bool append) {
328  FW_ASSERT(buffer.getData() != nullptr);
329  FW_ASSERT(fileName != nullptr);
330 
331  Fw::LogStringArg logStringArg(fileName);
332  Os::File file;
334 
335  // Open file
336  if (!append) {
337  stat = file.open(fileName, Os::File::Mode::OPEN_WRITE);
338  } else {
339  stat = file.open(fileName, Os::File::Mode::OPEN_APPEND);
340  }
341 
342  if (stat != Os::File::OP_OK) {
343  this->log_WARNING_HI_OpenFileError(logStringArg, stat);
344  return false;
345  }
346 
347  // Get buffer data and size
348  FwSizeType size = buffer.getSize();
349  U8* const data = reinterpret_cast<U8*>(buffer.getData());
350  FW_ASSERT(data != nullptr);
351 
352  // Apply offset
353  FW_ASSERT(offset <= size);
354  size -= offset;
355  U8* const dataFromOffset = reinterpret_cast<U8*>(data + offset);
356  FW_ASSERT(dataFromOffset != nullptr);
357 
358  // Write file
359  this->log_ACTIVITY_LO_WriteBegin(size, logStringArg);
360  FwSizeType writtenSize = this->writeToFile(dataFromOffset, size, file, fileName);
361 
362  // Check written size
363  if (writtenSize != size) {
364  return false;
365  }
366 
367  this->log_ACTIVITY_LO_WriteCompleted(size, logStringArg);
368  return true;
369 }
370 
371 void FileWorker ::writeBufferHashToFile(Fw::Buffer& buffer, const char* fileName, FwSizeType offset, bool append) {
372  FW_ASSERT(buffer.getData() != nullptr);
373  FW_ASSERT(fileName != nullptr);
374 
375  // Construct hash file name
376  const char* ext = Utils::Hash::getFileExtensionString();
377  FW_ASSERT(ext != nullptr);
378  char hashFileName[FileNameStringSize];
379  Fw::FormatStatus status = Fw::stringFormat(hashFileName, sizeof(hashFileName), "%s%s", fileName, ext);
381 
382  // Compute hash
383  Utils::HashBuffer hashBuffer;
384  FwSizeType size = buffer.getSize();
385  U8* const data = reinterpret_cast<U8*>(buffer.getData());
386  FW_ASSERT(data != nullptr);
387 
388  // Apply offset
389  FW_ASSERT(offset <= size);
390  size -= offset; // checked by assert
391  U8* const dataFromOffset = reinterpret_cast<U8*>(data + offset);
392  FW_ASSERT(dataFromOffset != nullptr);
393 
394  Utils::Hash hash;
395  if (!append) {
396  hash.hash(dataFromOffset, size, hashBuffer);
397 
398  } else {
399  bool isHash = this->getHash(hashFileName, hash, hashBuffer, dataFromOffset, size);
400  if (!isHash) {
401  return;
402  }
403  }
404 
405  // Open file
406  Os::File file;
407  Os::File::Status stat = file.open(hashFileName, Os::File::Mode::OPEN_WRITE);
408  if (stat != Os::File::OP_OK) {
409  Fw::LogStringArg logStringArg(hashFileName);
410  this->log_WARNING_HI_OpenFileError(logStringArg, stat);
411  return;
412  }
413 
414  // Write hash
415  FwSizeType writtenSize = this->writeToFile(hashBuffer.getBuffAddr(), hashBuffer.getSize(), file, hashFileName);
416 
417  // Check written size
418  FwSizeType hashSize = hashBuffer.getSize();
419  if (writtenSize != hashSize) {
420  Fw::LogStringArg logStringArg(hashFileName);
421  this->log_WARNING_LO_WriteValidationError(logStringArg, writtenSize, hashSize);
422  return;
423  }
424 
425  return;
426 }
427 
428 FwSizeType FileWorker ::writeToFile(const U8* data, FwSizeType size, Os::File& file, const char* fileName) {
429  FW_ASSERT(data != nullptr);
430  FW_ASSERT(size > 0);
431  FW_ASSERT(file.isOpen());
432  FW_ASSERT(fileName != nullptr);
433 
434  // Determine true timeout
435  static_assert(BLOCK_SIZE_BYTES > 0, "Divide by 0 error");
436  FwSizeType numChunks = (size / BLOCK_SIZE_BYTES);
437  if (size % BLOCK_SIZE_BYTES > 0) {
438  numChunks += 1;
439  }
440  U64 timeout = numChunks * TIMEOUT_MS;
441 
442  // Write loop
443  FwSizeType bytesWritten = 0;
444  Fw::Time start = this->getTime();
445  for (U32 i = 0; i < MAX_LOOP_ITERATIONS; i++) {
446  FwSizeType writeAmt = FW_MIN(size - bytesWritten, BLOCK_SIZE_BYTES);
447  Os::File::Status ret = file.write(data + bytesWritten, writeAmt);
448 
449  if (Os::File::OP_OK != ret || writeAmt == 0) {
450  Fw::LogStringArg logStringArg(fileName);
451  this->log_WARNING_HI_WriteFileError(bytesWritten, size, logStringArg, ret);
452  break;
453  }
454 
455  bool currAbort = this->m_abort.load(std::memory_order_relaxed);
456  if (currAbort) {
457  // Abort command was sent
458  Fw::LogStringArg logStringArg(fileName);
459  this->log_WARNING_LO_WriteAborted(bytesWritten, size, logStringArg);
460  break;
461  }
462 
463  if (timeout > 0) {
464  // Only check timeout if > 0
465  Fw::Time now = this->getTime();
466  Fw::Time diff = Fw::Time::sub(now, start);
467  U64 elapsed = (diff.getSeconds() * 1000000) + diff.getUSeconds();
468 
469  if (elapsed >= timeout) {
470  Fw::LogStringArg logStringArg(fileName);
471  this->log_WARNING_HI_WriteTimeout(bytesWritten, size, logStringArg, timeout);
472  break;
473  }
474  }
475 
476  bytesWritten += writeAmt;
477  if (bytesWritten >= size) {
478  // Finished, break out
479  break;
480  }
481  }
482 
483  return bytesWritten;
484 }
485 
486 } // namespace Svc
void verifyDoneOut_out(FwIndexType portNum, U32 status, FwSizeType sizeBytes)
Invoke output port verifyDoneOut.
void update(const void *const data, const FwSizeType len)
Definition: CRC32.cpp:45
void log_WARNING_HI_ReadFailedFileSize(U32 fsStat)
U8 * getBuffAddr() override
PlatformSizeType FwSizeType
Serializable::SizeType getSize() const override
Get current buffer size.
void log_WARNING_HI_WriteValidationOpenError(const Fw::StringBase &hashFileName, I32 status)
void log_WARNING_HI_WriteTimeout(FwSizeType bytesWritten, FwSizeType writeSize, const Fw::StringBase &fileName, U64 timeout) const
U8 * getData() const
Definition: Buffer.cpp:56
virtual const CHAR * toChar() const =0
Convert to a C-style char*.
void log_WARNING_LO_ReadAborted(FwSizeType bytesRead, FwSizeType readSize, const Fw::StringBase &fileName) const
Os::FileInterface::Status open(const char *path, Mode mode)
open file with supplied path and mode
void log_ACTIVITY_LO_WriteBegin(FwSizeType writeSize, const Fw::StringBase &fileName) const
#define HASH_HANDLE_TYPE
Definition: CRC32.hpp:12
static Time sub(const Time &minuend, const Time &subtrahend)
Definition: Time.cpp:187
void log_ACTIVITY_LO_ReadCompleted(FwSizeType fileSize, const Fw::StringBase &fileName) const
void finalize(HashBuffer &buffer)
Definition: CRC32.cpp:54
#define FW_MIN(a, b)
MIN macro (deprecated in C++, use std::min)
Definition: BasicTypes.h:94
void log_WARNING_HI_OpenFileError(const Fw::StringBase &fileName, U32 fsStat)
File doesn&#39;t exist (for read)
Definition: File.hpp:41
void readDoneOut_out(FwIndexType portNum, U32 status, FwSizeType sizeBytes)
Invoke output port readDoneOut.
void log_ACTIVITY_LO_WriteCompleted(FwSizeType writeSize, const Fw::StringBase &fileName) const
char * string_copy(char *destination, const char *source, FwSizeType num)
copy string with null-termination guaranteed
Definition: StringUtils.cpp:7
void setHashValue(HashBuffer &value)
Definition: CRC32.cpp:68
U32 getSeconds() const
Definition: Time.cpp:118
void writeDoneOut_out(FwIndexType portNum, U32 status, FwSizeType sizeBytes)
Invoke output port writeDoneOut.
void close() override
close the file, if not opened then do nothing
Definition: File.cpp:71
Status write(const U8 *buffer, FwSizeType &size)
write data to this file from the supplied buffer bounded by size
Definition: File.cpp:188
A generic interface for creating and comparing hash values.
Definition: Hash.hpp:24
static const char * getFileExtensionString()
Definition: HashCommon.cpp:5
~FileWorker()
Destroy FileWorker object.
Definition: FileWorker.cpp:23
crc_stat_t verify_checksum(const char *const fname, U32 &expected, U32 &actual)
Definition: CRCChecker.cpp:133
void log_WARNING_HI_WriteFileError(FwSizeType bytesWritten, FwSizeType writeSize, const Fw::StringBase &fileName, I32 status)
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:53
void log_WARNING_HI_NotInIdle(U32 currState)
void log_WARNING_LO_WriteAborted(FwSizeType bytesWritten, FwSizeType writeSize, const Fw::StringBase &fileName) const
void log_ACTIVITY_LO_ReadBegin(FwSizeType fileSize, const Fw::StringBase &fileName) const
static Status getFileSize(const char *path, FwSizeType &size)
Get the size of the file (in bytes) at the specified path.
Definition: FileSystem.cpp:226
Status read(U8 *buffer, FwSizeType &size)
read data from this file into supplied buffer bounded by size
Definition: File.cpp:169
FwSizeType getSize() const
Definition: Buffer.cpp:60
static void hash(const void *data, const FwSizeType len, HashBuffer &buffer)
Definition: CRC32.cpp:25
void log_WARNING_HI_WriteValidationReadError(const Fw::StringBase &hashFileName, I32 status)
Operation was successful.
Definition: File.hpp:40
U32 getUSeconds() const
Definition: Time.cpp:122
PlatformIndexType FwIndexType
void log_WARNING_LO_CrcVerificationError(U32 crcExp, U32 crcCalculated) const
void configure(U64 chunkSize)
Definition: FileWorker.cpp:18
Open file for reading.
Definition: File.hpp:31
A container class for holding a hash buffer.
Definition: HashBuffer.hpp:26
FormatStatus stringFormat(char *destination, const FwSizeType maximumSize, const char *formatString,...)
format a c-string
FileWorker(const char *const compName)
Construct FileWorker object.
Definition: FileWorker.cpp:15
RateGroupDivider component implementation.
virtual SizeType length() const
Get the length of the string.
FileWorkerReadStatus
void log_WARNING_LO_WriteValidationError(const Fw::StringBase &hashFileName, FwSizeType bytesWritten, FwSizeType hashSize) const
Operation was successful.
Definition: FileSystem.hpp:24
void start(FwTaskPriorityType priority=Os::Task::TASK_PRIORITY_DEFAULT, FwSizeType stackSize=Os::Task::TASK_DEFAULT, FwSizeType cpuAffinity=Os::Task::TASK_DEFAULT, FwTaskIdType identifier=static_cast< FwTaskIdType >(Os::Task::TASK_DEFAULT))
called by instantiator when task is to be started
void log_WARNING_HI_ReadError(FwSizeType bytesRead, FwSizeType readSize, const Fw::StringBase &fileName) const
#define FW_ASSERT(...)
Definition: Assert.hpp:14
bool isOpen() const
determine if the file is open
Definition: File.cpp:80
void log_WARNING_HI_ReadTimeout(FwSizeType bytesRead, FwSizeType readSize, const Fw::StringBase &fileName, U64 timeout) const
Auto-generated base for FileWorker component.
FwSizeType string_length(const CHAR *source, FwSizeType buffer_size)
get the length of the source string
Definition: StringUtils.cpp:24
FormatStatus
status of string format calls
Definition: format.hpp:18
#define U64(C)
Definition: sha.h:181