F´ Flight Software - C/C++ Documentation  devel
A framework for building embedded system applications to NASA flight quality standards.
FileSystem.cpp
Go to the documentation of this file.
1 #include <FpConfig.hpp>
2 #include <Fw/Types/Assert.hpp>
3 #include <Os/File.hpp>
4 #include <Os/FileSystem.hpp>
5 
6 #include <dirent.h>
7 #include <sys/stat.h>
8 #include <sys/statvfs.h>
9 #include <unistd.h>
10 #include <cerrno>
11 #include <cstdio> // Needed for rename
12 #include <cstring>
13 #include <limits>
14 
15 namespace Os {
16 
17 namespace FileSystem {
18 
19 Status createDirectory(const char* path) {
20  Status stat = OP_OK;
21 
22 #ifdef __VXWORKS__
23  int mkStat = ::mkdir(path);
24 #else
25  int mkStat = ::mkdir(path, S_IRWXU);
26 #endif
27 
28  if (-1 == mkStat) {
29  switch (errno) {
30  case EACCES:
31  case EPERM:
32  case EROFS:
33  case EFAULT:
34  stat = NO_PERMISSION;
35  break;
36  case EEXIST:
37  stat = ALREADY_EXISTS;
38  break;
39  case ELOOP:
40  case ENOENT:
41  case ENAMETOOLONG:
42  stat = INVALID_PATH;
43  break;
44  case ENOTDIR:
45  stat = NOT_DIR;
46  break;
47  case ENOSPC:
48  case EDQUOT:
49  stat = NO_SPACE;
50  break;
51  case EMLINK:
52  stat = FILE_LIMIT;
53  break;
54  default:
55  stat = OTHER_ERROR;
56  break;
57  }
58  }
59 
60  return stat;
61 } // end createDirectory
62 
63 Status removeDirectory(const char* path) {
64  Status stat = OP_OK;
65 
66  if (::rmdir(path) == -1) {
67  switch (errno) {
68  case EACCES:
69  case EPERM:
70  case EROFS:
71  case EFAULT:
72  stat = NO_PERMISSION;
73  break;
74  case ENOTEMPTY:
75  case EEXIST:
76  stat = NOT_EMPTY;
77  break;
78  case EINVAL:
79  case ELOOP:
80  case ENOENT:
81  case ENAMETOOLONG:
82  stat = INVALID_PATH;
83  break;
84  case ENOTDIR:
85  stat = NOT_DIR;
86  break;
87  case EBUSY:
88  stat = BUSY;
89  break;
90  default:
91  stat = OTHER_ERROR;
92  break;
93  }
94  }
95 
96  return stat;
97 } // end removeDirectory
98 
99 Status readDirectory(const char* path, const U32 maxNum, Fw::String fileArray[], U32& numFiles) {
100  Status dirStat = OP_OK;
101  DIR* dirPtr = nullptr;
102  struct dirent* direntData = nullptr;
103 
104  FW_ASSERT(fileArray != nullptr);
105  FW_ASSERT(path != nullptr);
106 
107  // Open directory failed:
108  if ((dirPtr = ::opendir(path)) == nullptr) {
109  switch (errno) {
110  case EACCES:
111  dirStat = NO_PERMISSION;
112  break;
113  case ENOENT:
114  dirStat = INVALID_PATH;
115  break;
116  case ENOTDIR:
117  dirStat = NOT_DIR;
118  break;
119  default:
120  dirStat = OTHER_ERROR;
121  break;
122  }
123  return dirStat;
124  }
125 
126  // Set errno to 0 so we know why we exited readdir
127  errno = 0;
128  U32 arrayIdx = 0;
129  U32 limitCount = 0;
130  const U32 loopLimit = std::numeric_limits<U32>::max();
131 
132  // Read the directory contents and store in passed in array:
133  while (arrayIdx < maxNum && limitCount < loopLimit) {
134  ++limitCount;
135 
136  if ((direntData = ::readdir(dirPtr)) != nullptr) {
137  // We are only care about regular files
138  if (direntData->d_type == DT_REG) {
139  FW_ASSERT(arrayIdx < maxNum, static_cast<NATIVE_INT_TYPE>(arrayIdx),
140  static_cast<NATIVE_INT_TYPE>(maxNum));
141 
142  Fw::String str(direntData->d_name);
143  fileArray[arrayIdx++] = str;
144  }
145  } else {
146  // readdir failed, did it error or did we run out of files?
147  if (errno != 0) {
148  // Only error from readdir is EBADF
149  dirStat = OTHER_ERROR;
150  }
151  break;
152  }
153  }
154 
155  if (limitCount == loopLimit) {
156  dirStat = FILE_LIMIT;
157  }
158 
159  if (::closedir(dirPtr) == -1) {
160  // Only error from closedir is EBADF
161  dirStat = OTHER_ERROR;
162  }
163 
164  numFiles = arrayIdx;
165 
166  return dirStat;
167 }
168 
169 Status removeFile(const char* path) {
170  Status stat = OP_OK;
171 
172  if (::unlink(path) == -1) {
173  switch (errno) {
174  case EACCES:
175  case EPERM:
176  case EROFS:
177  stat = NO_PERMISSION;
178  break;
179  case EISDIR:
180  stat = IS_DIR;
181  break;
182  case ELOOP:
183  case ENOENT:
184  case ENAMETOOLONG:
185  stat = INVALID_PATH;
186  break;
187  case ENOTDIR:
188  stat = NOT_DIR;
189  break;
190  case EBUSY:
191  stat = BUSY;
192  break;
193  default:
194  stat = OTHER_ERROR;
195  break;
196  }
197  }
198 
199  return stat;
200 } // end removeFile
201 
202 Status moveFile(const char* originPath, const char* destPath) {
203  Status stat = OP_OK;
204 
205  if (::rename(originPath, destPath) == -1) {
206  switch (errno) {
207  case EACCES:
208  case EPERM:
209  case EROFS:
210  case EFAULT:
211  stat = NO_PERMISSION;
212  break;
213  case EDQUOT:
214  case ENOSPC:
215  stat = NO_SPACE;
216  break;
217  case ELOOP:
218  case ENAMETOOLONG:
219  case ENOENT:
220  case EINVAL:
221  stat = INVALID_PATH;
222  break;
223  case ENOTDIR:
224  case EISDIR: // Old path is not a dir
225  stat = NOT_DIR;
226  break;
227  case ENOTEMPTY:
228  case EEXIST:
229  stat = NOT_EMPTY;
230  break;
231  case EMLINK:
232  stat = FILE_LIMIT;
233  break;
234  case EBUSY:
235  stat = BUSY;
236  break;
237  default:
238  stat = OTHER_ERROR;
239  break;
240  }
241  }
242  return stat;
243 
244 } // end moveFile
245 
246 Status handleFileError(File::Status fileStatus) {
247  Status fileSystemStatus = OTHER_ERROR;
248 
249  switch (fileStatus) {
250  case File::NO_SPACE:
251  fileSystemStatus = NO_SPACE;
252  break;
253  case File::NO_PERMISSION:
254  fileSystemStatus = NO_PERMISSION;
255  break;
256  case File::DOESNT_EXIST:
257  fileSystemStatus = INVALID_PATH;
258  break;
259  default:
260  fileSystemStatus = OTHER_ERROR;
261  }
262  return fileSystemStatus;
263 } // end handleFileError
264 
272 Status initAndCheckFileStats(const char* filePath, struct stat* fileInfo = nullptr) {
273  FileSystem::Status fs_status;
274  struct stat local_info;
275  if (!fileInfo) {
276  // No external struct given, so use the local one
277  fileInfo = &local_info;
278  }
279 
280  if (::stat(filePath, fileInfo) == -1) {
281  switch (errno) {
282  case EACCES:
283  fs_status = NO_PERMISSION;
284  break;
285  case ELOOP:
286  case ENOENT:
287  case ENAMETOOLONG:
288  fs_status = INVALID_PATH;
289  break;
290  case ENOTDIR:
291  fs_status = NOT_DIR;
292  break;
293  default:
294  fs_status = OTHER_ERROR;
295  break;
296  }
297  return fs_status;
298  }
299 
300  // Make sure the origin is a regular file
301  if (!S_ISREG(fileInfo->st_mode)) {
302  return INVALID_PATH;
303  }
304 
305  return OP_OK;
306 }
307 
320 Status copyFileData(File& source, File& destination, FwSignedSizeType size) {
321  static_assert(FILE_SYSTEM_CHUNK_SIZE != 0, "FILE_SYSTEM_CHUNK_SIZE must be >0");
322  U8 fileBuffer[FILE_SYSTEM_CHUNK_SIZE];
323  File::Status file_status;
324 
325  // Set loop limit
326  const FwSignedSizeType copyLoopLimit = (size / FILE_SYSTEM_CHUNK_SIZE) + 2;
327 
328  FwSignedSizeType loopCounter = 0;
329  FwSignedSizeType chunkSize;
330  while (loopCounter < copyLoopLimit) {
331  chunkSize = FILE_SYSTEM_CHUNK_SIZE;
332  file_status = source.read(fileBuffer, chunkSize, Os::File::WaitType::NO_WAIT);
333  if (file_status != File::OP_OK) {
334  return handleFileError(file_status);
335  }
336 
337  if (chunkSize == 0) {
338  // file has been successfully copied
339  break;
340  }
341 
342  file_status = destination.write(fileBuffer, chunkSize, Os::File::WaitType::WAIT);
343  if (file_status != File::OP_OK) {
344  return handleFileError(file_status);
345  }
346  loopCounter++;
347  }
348  FW_ASSERT(loopCounter < copyLoopLimit);
349 
350  return FileSystem::OP_OK;
351 } // end copyFileData
352 
353 Status copyFile(const char* originPath, const char* destPath) {
354  FileSystem::Status fs_status;
355  File::Status file_status;
356 
357  FwSignedSizeType fileSize = 0;
358 
359  File source;
360  File destination;
361 
362  fs_status = initAndCheckFileStats(originPath);
363  if (FileSystem::OP_OK != fs_status) {
364  return fs_status;
365  }
366 
367  // Get the file size:
368  fs_status =
369  FileSystem::getFileSize(originPath, fileSize);
370  if (FileSystem::OP_OK != fs_status) {
371  return fs_status;
372  }
373 
374  file_status = source.open(originPath, File::OPEN_READ);
375  if (file_status != File::OP_OK) {
376  return handleFileError(file_status);
377  }
378 
379  file_status = destination.open(destPath, File::OPEN_WRITE);
380  if (file_status != File::OP_OK) {
381  return handleFileError(file_status);
382  }
383 
384  fs_status = copyFileData(source, destination, fileSize);
385 
386  (void)source.close();
387  (void)destination.close();
388 
389  return fs_status;
390 } // end copyFile
391 
392 Status appendFile(const char* originPath, const char* destPath, bool createMissingDest) {
393  FileSystem::Status fs_status;
394  File::Status file_status;
395  FwSignedSizeType fileSize = 0;
396 
397  File source;
398  File destination;
399 
400  fs_status = initAndCheckFileStats(originPath);
401  if (FileSystem::OP_OK != fs_status) {
402  return fs_status;
403  }
404 
405  // Get the file size:
406  fs_status =
407  FileSystem::getFileSize(originPath, fileSize);
408  if (FileSystem::OP_OK != fs_status) {
409  return fs_status;
410  }
411 
412  file_status = source.open(originPath, File::OPEN_READ);
413  if (file_status != File::OP_OK) {
414  return handleFileError(file_status);
415  }
416 
417  // If needed, check if destination file exists (and exit if not)
418  if (!createMissingDest) {
419  fs_status = initAndCheckFileStats(destPath);
420  if (FileSystem::OP_OK != fs_status) {
421  return fs_status;
422  }
423  }
424 
425  file_status = destination.open(destPath, File::OPEN_APPEND);
426  if (file_status != File::OP_OK) {
427  return handleFileError(file_status);
428  }
429 
430  fs_status = copyFileData(source, destination, fileSize);
431 
432  (void)source.close();
433  (void)destination.close();
434 
435  return fs_status;
436 } // end appendFile
437 
438 Status getFileSize(const char* path, FwSignedSizeType& size) {
439  Status fileStat = OP_OK;
440  struct stat fileStatStruct;
441 
442  fileStat = initAndCheckFileStats(path, &fileStatStruct);
443  if (FileSystem::OP_OK == fileStat) {
444  // Only check size if struct was initialized successfully
445  size = fileStatStruct.st_size;
446  if (static_cast<off_t>(size) != fileStatStruct.st_size) {
448  }
449  }
450 
451  return fileStat;
452 } // end getFileSize
453 
454 Status changeWorkingDirectory(const char* path) {
455  Status stat = OP_OK;
456 
457  if (::chdir(path) == -1) {
458  switch (errno) {
459  case EACCES:
460  case EFAULT:
461  stat = NO_PERMISSION;
462  break;
463  case ENOTEMPTY:
464  stat = NOT_EMPTY;
465  break;
466  case ENOENT:
467  case ELOOP:
468  case ENAMETOOLONG:
469  stat = INVALID_PATH;
470  break;
471  case ENOTDIR:
472  stat = NOT_DIR;
473  break;
474  default:
475  stat = OTHER_ERROR;
476  break;
477  }
478  }
479 
480  return stat;
481 } // end changeWorkingDirectory
482 
483 Status getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) {
484  Status stat = OP_OK;
485 
486  struct statvfs fsStat;
487  int ret = statvfs(path, &fsStat);
488  if (ret) {
489  switch (errno) {
490  case EACCES:
491  stat = NO_PERMISSION;
492  break;
493  case ELOOP:
494  case ENOENT:
495  case ENAMETOOLONG:
496  stat = INVALID_PATH;
497  break;
498  case ENOTDIR:
499  stat = NOT_DIR;
500  break;
501  default:
502  stat = OTHER_ERROR;
503  break;
504  }
505  return stat;
506  }
507 
508  const FwSizeType block_size = static_cast<FwSizeType>(fsStat.f_frsize);
509  const FwSizeType free_blocks = static_cast<FwSizeType>(fsStat.f_bfree);
510  const FwSizeType total_blocks = static_cast<FwSizeType>(fsStat.f_blocks);
511 
512  // Check for casting and type error
513  if (((block_size <= 0) || (static_cast<unsigned long>(block_size) != fsStat.f_frsize)) ||
514  ((free_blocks <= 0) || (static_cast<fsblkcnt_t>(free_blocks) != fsStat.f_bfree)) ||
515  ((total_blocks <= 0) || (static_cast<fsblkcnt_t>(block_size) != fsStat.f_blocks))) {
516  return OTHER_ERROR;
517  }
518  // Check for overflow in multiplication
519  if (free_blocks > (std::numeric_limits<FwSizeType>::max() / block_size) ||
520  total_blocks > (std::numeric_limits<FwSizeType>::max() / block_size)) {
521  return OTHER_ERROR;
522  }
523  freeBytes = free_blocks * block_size;
524  totalBytes = total_blocks * block_size;
525  return stat;
526 }
527 
528 // Public function to get the file count for a given directory.
529 Status getFileCount(const char* directory, U32& fileCount) {
530  Status dirStat = OP_OK;
531  DIR* dirPtr = nullptr;
532  struct dirent* direntData = nullptr;
533  U32 limitCount;
534  const U32 loopLimit = std::numeric_limits<U32>::max();
535 
536  fileCount = 0;
537  if ((dirPtr = ::opendir(directory)) == nullptr) {
538  switch (errno) {
539  case EACCES:
540  dirStat = NO_PERMISSION;
541  break;
542  case ENOENT:
543  dirStat = INVALID_PATH;
544  break;
545  case ENOTDIR:
546  dirStat = NOT_DIR;
547  break;
548  default:
549  dirStat = OTHER_ERROR;
550  break;
551  }
552  return dirStat;
553  }
554 
555  // Set errno to 0 so we know why we exited readdir
556  errno = 0;
557  for (limitCount = 0; limitCount < loopLimit; limitCount++) {
558  if ((direntData = ::readdir(dirPtr)) != nullptr) {
559  // We are only counting regular files
560  if (direntData->d_type == DT_REG) {
561  fileCount++;
562  }
563  } else {
564  // readdir failed, did it error or did we run out of files?
565  if (errno != 0) {
566  // Only error from readdir is EBADF
567  dirStat = OTHER_ERROR;
568  }
569  break;
570  }
571  }
572  if (limitCount == loopLimit) {
573  dirStat = FILE_LIMIT;
574  }
575 
576  if (::closedir(dirPtr) == -1) {
577  // Only error from closedir is EBADF
578  dirStat = OTHER_ERROR;
579  }
580 
581  return dirStat;
582 } // end getFileCount
583 
584 } // namespace FileSystem
585 
586 } // namespace Os
#define FW_ASSERT(...)
Definition: Assert.hpp:14
PlatformIntType NATIVE_INT_TYPE
Definition: BasicTypes.h:51
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:26
#define FILE_SYSTEM_CHUNK_SIZE
Definition: FileSystem.hpp:7
PlatformSignedSizeType FwSignedSizeType
Definition: FpConfig.h:25
PlatformSizeType FwSizeType
Definition: FpConfig.h:30
C++-compatible configuration header for fprime configuration.
Status read(U8 *buffer, FwSignedSizeType &size)
read data from this file into supplied buffer bounded by size
Definition: File.cpp:142
void close() override
close the file, if not opened then do nothing
Definition: File.cpp:69
Os::FileInterface::Status open(const char *path, Mode mode)
open file with supplied path and mode
Definition: File.cpp:45
Status write(const U8 *buffer, FwSignedSizeType &size)
write data to this file from the supplied buffer bounded by size
Definition: File.cpp:162
@ NO_SPACE
No space left.
Definition: File.hpp:32
@ NO_PERMISSION
No permission to read/write file.
Definition: File.hpp:33
@ OP_OK
Operation was successful.
Definition: File.hpp:30
@ DOESNT_EXIST
File doesn't exist (for read)
Definition: File.hpp:31
@ OPEN_WRITE
Open file for writing.
Definition: File.hpp:23
@ OPEN_READ
Open file for reading.
Definition: File.hpp:21
@ OPEN_APPEND
Open file for appending.
Definition: File.hpp:25
Status getFileCount(const char *directory, U32 &fileCount)
counts the number of files in the given directory
Definition: FileSystem.cpp:46
Status getFreeSpace(const char *path, FwSizeType &totalBytes, FwSizeType &freeBytes)
get FS free and total space in bytes on filesystem containing path
Definition: FileSystem.cpp:52
Status handleFileError(File::Status fileStatus)
Definition: FileSystem.cpp:30
Status createDirectory(const char *path)
create a new directory at location path
Definition: FileSystem.cpp:8
Status moveFile(const char *originPath, const char *destPath)
Definition: FileSystem.cpp:25
Status getFileSize(const char *path, FwSizeType &size)
Definition: FileSystem.cpp:38
Status changeWorkingDirectory(const char *path)
move current directory to path
Definition: FileSystem.cpp:42
Status copyFileData(File &source, File &destination, FwSignedSizeType size)
Definition: FileSystem.cpp:320
Status appendFile(const char *originPath, const char *destPath, bool createMissingDest)
copies a file from origin to destination
Definition: FileSystem.cpp:49
@ OP_OK
Operation was successful.
Definition: FileSystem.hpp:15
@ NOT_DIR
Path is not a directory.
Definition: FileSystem.hpp:19
@ ALREADY_EXISTS
File already exists.
Definition: FileSystem.hpp:16
@ NO_PERMISSION
No permission to write.
Definition: FileSystem.hpp:18
@ IS_DIR
Path is a directory.
Definition: FileSystem.hpp:20
@ INVALID_PATH
Path is too long, too many sym links, doesn't exist, ect.
Definition: FileSystem.hpp:22
@ FILE_LIMIT
Too many files or links.
Definition: FileSystem.hpp:23
@ OTHER_ERROR
other OS-specific error
Definition: FileSystem.hpp:25
@ NOT_EMPTY
directory is not empty
Definition: FileSystem.hpp:21
@ NO_SPACE
No space left.
Definition: FileSystem.hpp:17
@ BUSY
Operand is in use by the system or by a process.
Definition: FileSystem.hpp:24
Status copyFile(const char *originPath, const char *destPath)
moves a file from origin to destination
Definition: FileSystem.cpp:34
Status removeDirectory(const char *path)
remove a directory at location path
Definition: FileSystem.cpp:12
Status removeFile(const char *path)
removes a file at location path
Definition: FileSystem.cpp:21
Status initAndCheckFileStats(const char *filePath, struct stat *fileInfo=nullptr)
Definition: FileSystem.cpp:272
Status readDirectory(const char *path, const U32 maxNum, Fw::String fileArray[], U32 &numFiles)
read the contents of a directory. Size of fileArray should be maxNum. Cleaner implementation found in...
Definition: FileSystem.cpp:16