F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
FileManager.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title FileManager.hpp
3 // \author bocchino
4 // \brief hpp file for FileManager component implementation class
5 //
6 // \copyright
7 // Copyright 2009-2015, by the California Institute of Technology.
8 // ALL RIGHTS RESERVED. United States Government Sponsorship
9 // acknowledged.
10 //
11 // ======================================================================
12 
13 #include <cstdio>
14 #include <cstdlib>
15 
16 #include <Fw/FPrimeBasicTypes.hpp>
17 #include "Fw/Types/Assert.hpp"
19 #include "Fw/Types/StringUtils.hpp"
20 #include "Os/Directory.hpp"
21 #include "Os/FilePathUtils.hpp"
23 #include "config/FileManagerConfig.hpp"
24 
25 namespace Svc {
26 
27 // ----------------------------------------------------------------------
28 // Construction, initialization, and destruction
29 // ----------------------------------------------------------------------
30 
31 FileManager ::FileManager(const char* const compName
32  )
33  : FileManagerComponentBase(compName),
34  commandCount(0),
35  errorCount(0),
36  m_sandboxDir(""),
37  m_sandboxConfigured(false),
38  m_listState(IDLE),
39  m_totalEntries(0),
40  m_currentOpCode(0),
41  m_currentCmdSeq(0),
42  m_runQueued(false),
43  m_dpState(DP_IDLE),
44  m_dpFileSize(0),
45  m_dpOffset(0),
46  m_dpChunkSize(0),
47  m_dpEndOffset(0),
48  m_dpPriority(0),
49  m_dpChunkCount(0),
50  m_dpOpCode(0),
51  m_dpCmdSeq(0),
52  m_dpBuffer{} {}
53 
55 
56 void FileManager ::configure(const char* sandboxDir) {
57  FW_ASSERT(sandboxDir != nullptr);
58 
59  // Resolve the sandbox directory (relative paths resolve against CWD)
61  const Os::FilePathUtils::Status resolveStatus =
62  Os::FilePathUtils::resolveFromCwd(sandboxDir, resolved, sizeof(resolved));
63  FW_ASSERT(resolveStatus == Os::FilePathUtils::VALID, static_cast<FwAssertArgType>(resolveStatus));
64 
65  // Ensure trailing '/'
67  FW_ASSERT(resolvedLen > 0);
69  if (resolved[resolvedLen - 1] != '/') {
71  resolved[resolvedLen] = '/';
72  resolved[resolvedLen + 1] = '\0';
73  }
74 
75  this->m_sandboxDir = resolved;
76  this->m_sandboxConfigured = true;
77 }
78 
79 bool FileManager ::resolveInSandbox(const Fw::CmdStringArg& path,
80  Fw::FileNameString& resolved,
81  const FwOpcodeType opCode,
82  const U32 cmdSeq) {
83  bool accepted = false;
84  if (this->m_sandboxConfigured) {
85  char resolvedBuffer[Os::FilePathUtils::MAX_PATH_LENGTH];
86  const Os::FilePathUtils::Status resolveStatus =
87  Os::FilePathUtils::resolveFromCwd(path.toChar(), resolvedBuffer, sizeof(resolvedBuffer));
88  if ((resolveStatus == Os::FilePathUtils::VALID) &&
89  (Os::FilePathUtils::checkContainment(resolvedBuffer, this->m_sandboxDir.toChar()) ==
91  resolved = resolvedBuffer;
92  accepted = true;
93  }
94  }
95  if (!accepted) {
97  Fw::LogStringArg(this->m_sandboxDir.toChar()));
98  ++this->errorCount;
99  this->tlmWrite_Errors(this->errorCount);
100  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
101  }
102  return accepted;
103 }
104 
105 // ----------------------------------------------------------------------
106 // Command handler implementations
107 // ----------------------------------------------------------------------
108 
109 void FileManager ::CreateDirectory_cmdHandler(const FwOpcodeType opCode,
110  const U32 cmdSeq,
111  const Fw::CmdStringArg& dirName) {
112  Fw::FileNameString resolvedDirName;
113  if (!this->resolveInSandbox(dirName, resolvedDirName, opCode, cmdSeq)) {
114  return;
115  }
116  Fw::LogStringArg logStringDirName(dirName.toChar());
117  this->log_ACTIVITY_HI_CreateDirectoryStarted(logStringDirName);
118  bool errorIfDirExists = true;
119  const Os::FileSystem::Status status = Os::FileSystem::createDirectory(resolvedDirName.toChar(), errorIfDirExists);
120  if (status != Os::FileSystem::OP_OK) {
121  this->log_WARNING_HI_DirectoryCreateError(logStringDirName, status);
122  } else {
123  this->log_ACTIVITY_HI_CreateDirectorySucceeded(logStringDirName);
124  }
125  this->emitTelemetry(status);
126  this->sendCommandResponse(opCode, cmdSeq, status);
127 }
128 
129 void FileManager ::RemoveFile_cmdHandler(const FwOpcodeType opCode,
130  const U32 cmdSeq,
131  const Fw::CmdStringArg& fileName,
132  const bool ignoreErrors) {
133  Fw::FileNameString resolvedFileName;
134  if (!this->resolveInSandbox(fileName, resolvedFileName, opCode, cmdSeq)) {
135  return;
136  }
137  Fw::LogStringArg logStringFileName(fileName.toChar());
138  this->log_ACTIVITY_HI_RemoveFileStarted(logStringFileName);
139  const Os::FileSystem::Status status = Os::FileSystem::removeFile(resolvedFileName.toChar());
140  if (status != Os::FileSystem::OP_OK) {
141  this->log_WARNING_HI_FileRemoveError(logStringFileName, status);
142  if (ignoreErrors == true) {
143  ++this->errorCount;
144  this->tlmWrite_Errors(this->errorCount);
145  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
146  return;
147  }
148  } else {
149  this->log_ACTIVITY_HI_RemoveFileSucceeded(logStringFileName);
150  }
151  this->emitTelemetry(status);
152  this->sendCommandResponse(opCode, cmdSeq, status);
153 }
154 
155 void FileManager ::MoveFile_cmdHandler(const FwOpcodeType opCode,
156  const U32 cmdSeq,
157  const Fw::CmdStringArg& sourceFileName,
158  const Fw::CmdStringArg& destFileName) {
159  Fw::FileNameString resolvedSource;
160  Fw::FileNameString resolvedDest;
161  if (!this->resolveInSandbox(sourceFileName, resolvedSource, opCode, cmdSeq) ||
162  !this->resolveInSandbox(destFileName, resolvedDest, opCode, cmdSeq)) {
163  return;
164  }
165  Fw::LogStringArg logStringSource(sourceFileName.toChar());
166  Fw::LogStringArg logStringDest(destFileName.toChar());
167  this->log_ACTIVITY_HI_MoveFileStarted(logStringSource, logStringDest);
168  const Os::FileSystem::Status status = Os::FileSystem::moveFile(resolvedSource.toChar(), resolvedDest.toChar());
169  if (status != Os::FileSystem::OP_OK) {
170  this->log_WARNING_HI_FileMoveError(logStringSource, logStringDest, status);
171  } else {
172  this->log_ACTIVITY_HI_MoveFileSucceeded(logStringSource, logStringDest);
173  }
174  this->emitTelemetry(status);
175  this->sendCommandResponse(opCode, cmdSeq, status);
176 }
177 
178 void FileManager ::RemoveDirectory_cmdHandler(const FwOpcodeType opCode,
179  const U32 cmdSeq,
180  const Fw::CmdStringArg& dirName) {
181  Fw::FileNameString resolvedDirName;
182  if (!this->resolveInSandbox(dirName, resolvedDirName, opCode, cmdSeq)) {
183  return;
184  }
185  Fw::LogStringArg logStringDirName(dirName.toChar());
186  this->log_ACTIVITY_HI_RemoveDirectoryStarted(logStringDirName);
187  const Os::FileSystem::Status status = Os::FileSystem::removeDirectory(resolvedDirName.toChar());
188  if (status != Os::FileSystem::OP_OK) {
189  this->log_WARNING_HI_DirectoryRemoveError(logStringDirName, status);
190  } else {
191  this->log_ACTIVITY_HI_RemoveDirectorySucceeded(logStringDirName);
192  }
193  this->emitTelemetry(status);
194  this->sendCommandResponse(opCode, cmdSeq, status);
195 }
196 
197 void FileManager ::AppendFile_cmdHandler(const FwOpcodeType opCode,
198  const U32 cmdSeq,
199  const Fw::CmdStringArg& source,
200  const Fw::CmdStringArg& target) {
201  Fw::FileNameString resolvedSource;
202  Fw::FileNameString resolvedTarget;
203  if (!this->resolveInSandbox(source, resolvedSource, opCode, cmdSeq) ||
204  !this->resolveInSandbox(target, resolvedTarget, opCode, cmdSeq)) {
205  return;
206  }
207  Fw::LogStringArg logStringSource(source.toChar());
208  Fw::LogStringArg logStringTarget(target.toChar());
209  this->log_ACTIVITY_HI_AppendFileStarted(logStringSource, logStringTarget);
210 
211  Os::FileSystem::Status status;
212  status = Os::FileSystem::appendFile(resolvedSource.toChar(), resolvedTarget.toChar(), true);
213  if (status != Os::FileSystem::OP_OK) {
214  this->log_WARNING_HI_AppendFileFailed(logStringSource, logStringTarget, status);
215  } else {
216  this->log_ACTIVITY_HI_AppendFileSucceeded(logStringSource, logStringTarget);
217  }
218 
219  this->emitTelemetry(status);
220  this->sendCommandResponse(opCode, cmdSeq, status);
221 }
222 
223 void FileManager ::FileSize_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, const Fw::CmdStringArg& fileName) {
224  Fw::FileNameString resolvedFileName;
225  if (!this->resolveInSandbox(fileName, resolvedFileName, opCode, cmdSeq)) {
226  return;
227  }
228  Fw::LogStringArg logStringFileName(fileName.toChar());
229  this->log_ACTIVITY_HI_FileSizeStarted(logStringFileName);
230 
231  FwSizeType size_arg;
232  const Os::FileSystem::Status status = Os::FileSystem::getFileSize(resolvedFileName.toChar(), size_arg);
233  if (status != Os::FileSystem::OP_OK) {
234  this->log_WARNING_HI_FileSizeError(logStringFileName, status);
235  } else {
236  this->log_ACTIVITY_HI_FileSizeSucceeded(logStringFileName, size_arg);
237  }
238  this->emitTelemetry(status);
239  this->sendCommandResponse(opCode, cmdSeq, status);
240 }
241 
242 void FileManager ::ListDirectory_cmdHandler(const FwOpcodeType opCode,
243  const U32 cmdSeq,
244  const Fw::CmdStringArg& dirName) {
245  // Check if we're already listing a directory
246  if (m_listState == LISTING_IN_PROGRESS) {
247  this->log_WARNING_HI_ListDirectoryError(dirName, static_cast<U32>(Os::Directory::OTHER_ERROR));
248  this->emitTelemetry(Os::FileSystem::OTHER_ERROR);
249  this->sendCommandResponse(opCode, cmdSeq, Os::FileSystem::OTHER_ERROR);
250  return;
251  }
252 
253  Fw::FileNameString resolvedDirName;
254  if (!this->resolveInSandbox(dirName, resolvedDirName, opCode, cmdSeq)) {
255  return;
256  }
257 
259 
260  // Open the directory for reading
261  Os::Directory::Status status = m_currentDir.open(resolvedDirName.toChar(), Os::Directory::OpenMode::READ);
262 
263  if (status != Os::Directory::OP_OK) {
264  this->log_WARNING_HI_ListDirectoryError(dirName, static_cast<U32>(status));
265  this->emitTelemetry(Os::FileSystem::OTHER_ERROR);
266  this->sendCommandResponse(opCode, cmdSeq, Os::FileSystem::OTHER_ERROR);
267  return;
268  }
269 
270  // Initialize state machine for asynchronous processing
271  m_listState = LISTING_IN_PROGRESS;
272  m_currentDirName = dirName;
273  m_currentOpCode = opCode;
274  m_currentCmdSeq = cmdSeq;
275  m_totalEntries = 0;
276 
277  // Directory listing will be processed asynchronously by the rate group.
278  // The schedIn_handler will process FILES_PER_RATE_TICK directory entries per rate tick to
279  // prevent event flooding while maintaining configurable performance.
280  // Command response will be sent when listing completes.
281 }
282 
283 void FileManager ::CalculateCrc_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdStringArg& filename) {
284  Os::File file;
285  U32 crcValue = 0;
286  Fw::FileNameString resolvedFilename;
287  if (!this->resolveInSandbox(filename, resolvedFilename, opCode, cmdSeq)) {
288  return;
289  }
290  this->log_ACTIVITY_HI_CalculateCrcStarted(filename);
291 
292  Os::File::Status status = file.open(resolvedFilename.toChar(), Os::File::OPEN_READ);
293  if (status == Os::File::OP_OK) {
294  status = file.calculateCrc(crcValue);
295  }
296 
297  if (status == Os::File::OP_OK) {
298  this->log_ACTIVITY_HI_CalculateCrcSucceeded(filename, crcValue);
299  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
300  } else {
301  this->log_WARNING_HI_CalculateCrcFailed(filename, status);
302  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
303  }
304  file.close();
305 }
306 
307 void FileManager ::GenerateDp_cmdHandler(FwOpcodeType opCode,
308  U32 cmdSeq,
309  const Fw::CmdStringArg& fileName,
310  U32 chunkSize,
311  U64 beginOffset,
312  U64 endOffset,
313  U32 priority,
314  const FileManager_GenerateDpMode& mode) {
315  Fw::LogStringArg logFileName(fileName.toChar());
316 
317  // Reject a second request while one is already running
318  if (this->m_dpState != DP_IDLE) {
320  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
321  return;
322  }
323 
324  // Data products must be available
326  this->log_WARNING_HI_GenerateDpBufferFailed(logFileName);
327  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
328  return;
329  }
330 
331  // Clamp the requested chunk size to the configured read buffer
332  U32 effectiveChunkSize = chunkSize;
333  if ((effectiveChunkSize == 0) || (effectiveChunkSize > FileManagerConfig::GENERATE_DP_MAX_CHUNK_SIZE)) {
335  }
336 
337  Fw::FileNameString resolvedFileName;
338  if (!this->resolveInSandbox(fileName, resolvedFileName, opCode, cmdSeq)) {
339  return;
340  }
341 
342  Os::File::Status status = this->m_dpFile.open(resolvedFileName.toChar(), Os::File::OPEN_READ);
343  if (status != Os::File::OP_OK) {
344  this->log_WARNING_HI_GenerateDpFailed(logFileName, FileManager_GenerateDpStage::OPEN, static_cast<U32>(status));
345  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
346  return;
347  }
348 
349  FwSizeType fileSize = 0;
350  status = this->m_dpFile.size(fileSize);
351  if (status != Os::File::OP_OK) {
352  this->m_dpFile.close();
353  this->log_WARNING_HI_GenerateDpFailed(logFileName, FileManager_GenerateDpStage::SIZE, static_cast<U32>(status));
354  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
355  return;
356  }
357 
358  // An end offset of zero, or one past the end of the file, means the end of
359  // the file. Ranges let an operator retransmit part of a file or spread the
360  // downlink over several commands.
361  U64 effectiveEnd = endOffset;
362  if ((effectiveEnd == 0) || (effectiveEnd > static_cast<U64>(fileSize))) {
363  effectiveEnd = static_cast<U64>(fileSize);
364  }
365 
366  const bool emptyFile = (fileSize == 0);
367  const bool badRange = (beginOffset > static_cast<U64>(fileSize)) || (!emptyFile && (beginOffset >= effectiveEnd));
368  if (badRange) {
369  this->m_dpFile.close();
370  this->log_WARNING_HI_GenerateDpInvalidRange(logFileName, beginOffset, endOffset, static_cast<U64>(fileSize));
371  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
372  return;
373  }
374 
375  // Position the file at the start of the requested range
376  if (beginOffset > 0) {
377  status = this->m_dpFile.seek(static_cast<FwSignedSizeType>(beginOffset), Os::File::SeekType::ABSOLUTE);
378  if (status != Os::File::OP_OK) {
379  this->m_dpFile.close();
381  static_cast<U32>(status));
382  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
383  return;
384  }
385  }
386 
387  this->m_dpFileName = Fw::String(fileName.toChar());
388  this->m_dpFileSize = fileSize;
389  this->m_dpOffset = beginOffset;
390  this->m_dpEndOffset = effectiveEnd;
391  this->m_dpChunkSize = effectiveChunkSize;
392  this->m_dpChunkCount = 0;
393  this->m_dpOpCode = opCode;
394  this->m_dpCmdSeq = cmdSeq;
395  // A priority of zero reverts to the configured default
396  this->m_dpPriority = (priority == 0) ? static_cast<FwDpPriorityType>(FileManagerCfg::DEFAULT_DP_PRIORITY)
397  : static_cast<FwDpPriorityType>(priority);
398  this->m_dpState = DP_IN_PROGRESS;
399 
400  // Report the number of bytes that will be written, which is the requested
401  // range rather than the size of the whole file
402  this->log_ACTIVITY_HI_GenerateDpStarted(logFileName, this->m_dpEndOffset - this->m_dpOffset);
403 
404  // An empty range produces no chunks, so complete immediately
405  if (this->m_dpOffset >= this->m_dpEndOffset) {
406  this->log_ACTIVITY_HI_GenerateDpComplete(logFileName, this->m_dpChunkCount);
407  this->finishDpGeneration();
408  return;
409  }
410 
411  // In immediate mode the whole range is emitted here, so that a project that
412  // wants the file out quickly is not limited by the rate group. In paced
413  // mode the rate group meters the work out and the response is deferred.
415  this->processDpChunks(0);
416  }
417 }
418 
419 void FileManager ::processDpChunks(U32 chunkLimit) {
420  Fw::LogStringArg logFileName(this->m_dpFileName.toChar());
421 
422  // A limit of zero means emit the whole remaining range in this call
423  const bool paced = (chunkLimit > 0);
424 
425  for (U32 chunk = 0; !paced || (chunk < chunkLimit); chunk++) {
426  // Number of bytes remaining in the requested range. The loop returns as
427  // soon as the range is exhausted, so this is always non-zero here.
428  const FwSizeType remaining = static_cast<FwSizeType>(this->m_dpEndOffset - this->m_dpOffset);
429 
430  const FwSizeType requestedSize = (remaining < static_cast<FwSizeType>(this->m_dpChunkSize))
431  ? remaining
432  : static_cast<FwSizeType>(this->m_dpChunkSize);
433 
434  // The file size is known, so a short read means the file changed underneath us
435  FwSizeType readSize = requestedSize;
436  const Os::File::Status status = this->m_dpFile.read(this->m_dpBuffer, readSize);
437  if ((status != Os::File::OP_OK) || (readSize != requestedSize)) {
439  static_cast<U32>(status));
440  this->finishDpGeneration();
441  return;
442  }
443 
444  // Request a container large enough for this chunk's header and data
446  DpContainer container;
447  const Fw::Success::T dpStatus = this->dpGet_FileDpContainer(dpSize, container);
448  if (dpStatus != Fw::Success::SUCCESS) {
449  this->log_WARNING_HI_GenerateDpBufferFailed(logFileName);
450  this->finishDpGeneration();
451  return;
452  }
453  container.setPriority(this->m_dpPriority);
454 
455  // Each chunk is a metadata record followed by a data record, so that
456  // ground tools can reassemble the file from any number of containers
457  const FileManager_FileChunkHeader header(this->m_dpFileName, this->m_dpOffset, static_cast<U32>(readSize));
458  Fw::SerializeStatus serializeStatus = container.serializeRecord_FileChunkHeaderRecord(header);
459  if (serializeStatus == Fw::FW_SERIALIZE_OK) {
460  serializeStatus = container.serializeRecord_FileChunkDataRecord(this->m_dpBuffer, readSize);
461  }
462  if (serializeStatus != Fw::FW_SERIALIZE_OK) {
464  static_cast<U32>(serializeStatus));
465  this->finishDpGeneration();
466  return;
467  }
468 
469  this->dpSend(container);
470 
471  this->m_dpOffset += static_cast<U64>(readSize);
472  this->m_dpChunkCount++;
473 
474  // Last chunk of the requested range
475  if (this->m_dpOffset >= this->m_dpEndOffset) {
476  this->log_ACTIVITY_HI_GenerateDpComplete(logFileName, this->m_dpChunkCount);
477  this->finishDpGeneration();
478  return;
479  }
480  }
481 }
482 
483 void FileManager ::finishDpGeneration() {
484  this->m_dpFile.close();
485  this->m_dpState = DP_IDLE;
486  this->m_dpOffset = 0;
487  this->m_dpEndOffset = 0;
488  this->m_dpFileSize = 0;
489  // Failures emit a warning event but still respond with OK, so that a bad
490  // file name or a transient resource problem does not stop a whole sequence
491  this->cmdResponse_out(this->m_dpOpCode, this->m_dpCmdSeq, Fw::CmdResponse::OK);
492 }
493 
494 void FileManager ::pingIn_handler(const FwIndexType portNum, U32 key) {
495  // return key
496  this->pingOut_out(0, key);
497 }
498 
499 void FileManager ::schedIn_handler(const FwIndexType portNum, U32 context) {
500  bool isQueued = false;
501  // m_runQueued will be compared to isQueued (false). When equal (i.e. m_runQueued is false) the atomic will be
502  // set to true and the function will return true indicating that a run was successfully marked as queued and thus
503  // the internal handler should be invoked.
504  bool expects_enqueue = this->m_runQueued.compare_exchange_strong(isQueued, true);
505  if (expects_enqueue) {
507  }
508 }
509 
510 void FileManager ::run_internalInterfaceHandler() {
511  FW_ASSERT(this->m_runQueued);
512  this->m_runQueued = false; // Run is not queued anymore (we are running)
513  // Data product generation is paced the same way as directory listing
514  if (this->m_dpState == DP_IN_PROGRESS) {
515  this->processDpChunks(FileManagerConfig::CHUNKS_PER_RATE_TICK);
516  }
517 
518  // Only process if we're in the middle of a directory listing
519  if (m_listState == LISTING_IN_PROGRESS) {
520  // Process multiple files per rate tick based on configuration
521  for (U32 fileCount = 0; fileCount < Svc::FileManagerConfig::FILES_PER_RATE_TICK; fileCount++) {
522  Fw::String filename;
523  Os::Directory::Status status = m_currentDir.read(filename);
524 
525  if (status == Os::Directory::NO_MORE_FILES) {
526  // We're done listing - close directory and send response
527  m_currentDir.close();
528  m_listState = IDLE;
529 
530  this->log_ACTIVITY_HI_ListDirectorySucceeded(m_currentDirName, m_totalEntries);
531  this->emitTelemetry(Os::FileSystem::OP_OK);
532  this->sendCommandResponse(m_currentOpCode, m_currentCmdSeq, Os::FileSystem::OP_OK);
533  break; // Exit the loop since we're done
534 
535  } else if (status == Os::Directory::OP_OK) {
536  // Construct full path for type checking
537  Fw::String fullPath;
538  Fw::FormatStatus formatStatus = fullPath.format("%s/%s", m_currentDirName.toChar(), filename.toChar());
539 
540  // Determine entry type
541  Os::FileSystem::PathType pathType = (formatStatus == Fw::FormatStatus::SUCCESS)
542  ? Os::FileSystem::getPathType(fullPath.toChar())
544 
545  if (formatStatus != Fw::FormatStatus::SUCCESS) {
546  // Cannot determine the type of an entry whose path did not format
547  this->log_WARNING_HI_FileNameFormatError(filename,
548  static_cast<Fw::StringFormatStatus::T>(formatStatus));
549  } else if (pathType == Os::FileSystem::FILE) {
550  // Regular file: get size and emit file event
551  FwSizeType fileSize;
552  Os::FileSystem::Status sizeStatus = Os::FileSystem::getFileSize(fullPath.toChar(), fileSize);
554  m_currentDirName, filename,
555  (sizeStatus == Os::FileSystem::OP_OK) ? fileSize : static_cast<FwSizeType>(0));
556  } else if (pathType == Os::FileSystem::DIRECTORY) {
557  // Subdirectory: emit subdirectory event
558  this->log_ACTIVITY_HI_DirectoryListingSubdir(m_currentDirName, filename);
559  } else {
560  // Special file or inaccessible: treat as file with 0 size
561  this->log_ACTIVITY_HI_DirectoryListing(m_currentDirName, filename, static_cast<FwSizeType>(0));
562  }
563 
564  m_totalEntries++;
565 
566  } else {
567  // Error reading directory - close and send error response
568  m_currentDir.close();
569  m_listState = IDLE;
570 
571  this->log_WARNING_HI_ListDirectoryError(m_currentDirName, static_cast<U32>(status));
572  this->emitTelemetry(Os::FileSystem::OTHER_ERROR);
573  this->sendCommandResponse(m_currentOpCode, m_currentCmdSeq, Os::FileSystem::OTHER_ERROR);
574  break; // Exit the loop since we had an error
575  }
576  }
577  }
578 }
579 
580 // ----------------------------------------------------------------------
581 // Helper methods
582 // ----------------------------------------------------------------------
583 
584 void FileManager ::emitTelemetry(const Os::FileSystem::Status status) {
585  if (status == Os::FileSystem::OP_OK) {
586  ++this->commandCount;
587  this->tlmWrite_CommandsExecuted(this->commandCount);
588  } else {
589  ++this->errorCount;
590  this->tlmWrite_Errors(this->errorCount);
591  }
592 }
593 
594 void FileManager ::sendCommandResponse(const FwOpcodeType opCode,
595  const U32 cmdSeq,
596  const Os::FileSystem::Status status) {
597  this->cmdResponse_out(opCode, cmdSeq,
599 }
600 
601 } // namespace Svc
bool isConnected_productSendOut_OutputPort(FwIndexType portNum) const
Serialization/Deserialization operation was successful.
A request arrived while another was in progress.
void log_WARNING_HI_ListDirectoryError(const Fw::StringBase &dirName, U32 status) const
void log_ACTIVITY_HI_DirectoryListingSubdir(const Fw::StringBase &dirName, const Fw::StringBase &subdirName) const
void log_ACTIVITY_HI_CreateDirectoryStarted(const Fw::StringBase &dirName) const
static constexpr FwSizeType SIZE_OF_FileChunkDataRecord_RECORD(FwSizeType arraySize)
Record sizes.
FwIdType FwOpcodeType
The type of a command opcode.
Fw::Success::T dpGet_FileDpContainer(FwSizeType dataSize, DpContainer &container)
Status calculateCrc(U32 &crc)
calculate the CRC32 of the entire file
Definition: File.cpp:237
void log_ACTIVITY_HI_RemoveDirectorySucceeded(const Fw::StringBase &dirName) const
void log_ACTIVITY_HI_DirectoryListing(const Fw::StringBase &dirName, const Fw::StringBase &fileName, FwSizeType fileSize) const
Representing success.
PlatformSizeType FwSizeType
static Status moveFile(const char *sourcePath, const char *destPath)
Move a file from sourcePath to destPath.
Definition: FileSystem.cpp:209
U32 FwDpPriorityType
The type of a data product priority.
void log_ACTIVITY_HI_CalculateCrcStarted(const Fw::StringBase &fileName) const
static constexpr U32 CHUNKS_PER_RATE_TICK
void log_WARNING_HI_FileSizeError(const Fw::StringBase &fileName, U32 status) const
void run_internalInterfaceInvoke()
Internal interface base-class function for run.
FileManager(const char *const compName)
Definition: FileManager.cpp:31
void log_ACTIVITY_HI_GenerateDpStarted(const Fw::StringBase &fileName, U64 bytesToWrite) const
static Status removeDirectory(const char *path)
Remove a directory at the specified path.
Definition: FileSystem.cpp:83
StringTemplate< FW_FIXED_LENGTH_STRING_SIZE > String
Definition: String.hpp:14
void log_ACTIVITY_HI_FileSizeSucceeded(const Fw::StringBase &fileName, FwSizeType size) const
static Status appendFile(const char *sourcePath, const char *destPath, bool createMissingDest=false)
Append the source file to the destination file.
Definition: FileSystem.cpp:177
void log_ACTIVITY_HI_ListDirectorySucceeded(const Fw::StringBase &dirName, U32 fileCount) const
Auto-generated base for FileManager component.
Status size(FwSizeType &size_result) override
get size of currently open file
Definition: File.cpp:111
bool isConnected_productGetOut_OutputPort(FwIndexType portNum) const
void log_ACTIVITY_HI_CalculateCrcSucceeded(const Fw::StringBase &fileName, U32 crc) const
Status checkContainment(const char *resolvedPath, const char *allowedDirectory)
Check whether an already-resolved path is within an allowed directory.
void log_ACTIVITY_HI_ListDirectoryStarted(const Fw::StringBase &dirName) const
void log_ACTIVITY_HI_MoveFileStarted(const Fw::StringBase &sourceFileName, const Fw::StringBase &destFileName) const
void log_WARNING_HI_GenerateDpInvalidRange(const Fw::StringBase &fileName, U64 beginOffset, U64 endOffset, U64 fileSize) const
void pingOut_out(FwIndexType portNum, U32 key) const
Invoke output port pingOut.
void log_ACTIVITY_HI_AppendFileSucceeded(const Fw::StringBase &source, const Fw::StringBase &target) const
SerializeStatus
forward declaration for string
void log_WARNING_HI_PathOutsideSandbox(const Fw::StringBase &path, const Fw::StringBase &sandboxDir) const
void configure(const char *sandboxDir)
Definition: FileManager.cpp:56
void log_WARNING_HI_GenerateDpBufferFailed(const Fw::StringBase &fileName) const
void log_ACTIVITY_HI_CreateDirectorySucceeded(const Fw::StringBase &dirName) const
Os::FileInterface::Status open(const char *path, Mode mode)
open file with supplied path and mode
Definition: File.cpp:50
A catch-all for other errors. Have to look in implementation-specific code.
Definition: Directory.hpp:32
void log_WARNING_HI_GenerateDpFailed(const Fw::StringBase &fileName, const Svc::FileManager_GenerateDpStage &stage, U32 status) const
void log_ACTIVITY_HI_RemoveDirectoryStarted(const Fw::StringBase &dirName) const
void close() override
Close directory.
Definition: Directory.cpp:74
Status seek(FwSignedSizeType offset, SeekType seekType) override
seek the file pointer to the given offset
Definition: File.cpp:142
static constexpr FwSizeType MAX_PATH_LENGTH
Maximum supported path length for resolution buffers.
Status read(char *fileNameBuffer, FwSizeType buffSize) override
Get next filename from directory stream.
Definition: Directory.cpp:54
T
The raw enum type.
static constexpr U32 GENERATE_DP_MAX_CHUNK_SIZE
void close() override
close the file, if not opened then do nothing
Definition: File.cpp:97
void cmdResponse_out(FwOpcodeType opCode, U32 cmdSeq, Fw::CmdResponse response)
Emit command response.
Directory stream has no more files.
Definition: Directory.hpp:27
const char * toChar() const
Convert to a C-style char*.
void log_ACTIVITY_HI_FileSizeStarted(const Fw::StringBase &fileName) const
void log_WARNING_HI_FileNameFormatError(const Fw::StringBase &fileName, const Fw::StringFormatStatus &status) const
FormatStatus format(const CHAR *formatString,...)
write formatted string to buffer
Definition: StringBase.cpp:58
Command successfully executed.
void log_ACTIVITY_HI_GenerateDpComplete(const Fw::StringBase &fileName, U32 chunks) const
void log_ACTIVITY_HI_MoveFileSucceeded(const Fw::StringBase &sourceFileName, const Fw::StringBase &destFileName) const
static Status getFileSize(const char *path, FwSizeType &size)
Get the size of the file (in bytes) at the specified path.
Definition: FileSystem.cpp:227
static Status createDirectory(const char *path, bool errorIfAlreadyExists=false)
Create a new directory at the specified path.
Definition: FileSystem.cpp:111
static constexpr FwSizeType SIZE_OF_FileChunkHeaderRecord_RECORD
Status read(U8 *buffer, FwSizeType &size)
read data from this file into supplied buffer bounded by size
Definition: File.cpp:194
Path is valid and within the allowed directory.
void log_WARNING_HI_DirectoryRemoveError(const Fw::StringBase &dirName, U32 status) const
Command had execution error.
static PathType getPathType(const char *path)
Return the type of the path (file, directory, or doesn&#39;t exist)
Definition: FileSystem.cpp:138
other OS-specific error
Definition: FileSystem.hpp:40
void log_ACTIVITY_HI_AppendFileStarted(const Fw::StringBase &source, const Fw::StringBase &target) const
Operation was successful.
Definition: File.hpp:42
Status resolveFromCwd(const char *path, char *resolvedOut, FwSizeType resolvedSize)
Resolve a path using CWD as the base for relative paths.
void tlmWrite_Errors(U32 arg, Fw::Time _tlmTime=Fw::Time()) const
void log_ACTIVITY_HI_RemoveFileStarted(const Fw::StringBase &fileName) const
PlatformIndexType FwIndexType
Operation was successful.
Definition: Directory.hpp:22
Open file for reading.
Definition: File.hpp:33
void log_WARNING_HI_AppendFileFailed(const Fw::StringBase &source, const Fw::StringBase &target, U32 status) const
Status open(const char *path, OpenMode mode) override
Open or create a directory.
Definition: Directory.cpp:31
Command failed validation.
RateGroupDivider component implementation.
Emit all chunks in the command handler, completing immediately.
void log_WARNING_HI_CalculateCrcFailed(const Fw::StringBase &fileName, U32 status) const
Operation was successful.
Definition: FileSystem.hpp:24
static constexpr U32 FILES_PER_RATE_TICK
void log_WARNING_HI_DirectoryCreateError(const Fw::StringBase &dirName, U32 status) const
void log_ACTIVITY_HI_RemoveFileSucceeded(const Fw::StringBase &fileName) const
void log_WARNING_HI_FileMoveError(const Fw::StringBase &sourceFileName, const Fw::StringBase &destFileName, U32 status) const
void tlmWrite_CommandsExecuted(U32 arg, Fw::Time _tlmTime=Fw::Time()) const
#define FW_ASSERT(...)
Definition: Assert.hpp:14
static Status removeFile(const char *path)
Remove a file at the specified path.
Definition: FileSystem.cpp:87
void log_WARNING_HI_FileRemoveError(const Fw::StringBase &fileName, U32 status) const
void dpSend(DpContainer &container, Fw::Time timeTag=Fw::ZERO_TIME)
Send a data product.
FwSizeType string_length(const CHAR *source, FwSizeType buffer_size)
get the length of the source string
Definition: StringUtils.cpp:32
FormatStatus
status of string format calls
Definition: format.hpp:18