F´ Flight Software - C/C++ Documentation devel
A framework for building embedded system applications to NASA flight quality standards.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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
15namespace Os {
16
17namespace FileSystem {
18
19Status 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
63Status 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
99Status 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
169Status 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
202Status 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
247 Status fileSystemStatus = OTHER_ERROR;
248
249 switch (fileStatus) {
250 case File::NO_SPACE:
251 fileSystemStatus = NO_SPACE;
252 break;
254 fileSystemStatus = NO_PERMISSION;
255 break;
257 fileSystemStatus = INVALID_PATH;
258 break;
259 default:
260 fileSystemStatus = OTHER_ERROR;
261 }
262 return fileSystemStatus;
263} // end handleFileError
264
272Status 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
320Status copyFileData(File source, File destination, FwSizeType 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 FwSizeType copyLoopLimit = (size / FILE_SYSTEM_CHUNK_SIZE) + 2;
327
328 FwSizeType loopCounter = 0;
329 NATIVE_INT_TYPE chunkSize;
330 while (loopCounter < copyLoopLimit) {
331 chunkSize = FILE_SYSTEM_CHUNK_SIZE;
332 file_status = source.read(&fileBuffer, chunkSize, false);
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, true);
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
353Status copyFile(const char* originPath, const char* destPath) {
354 FileSystem::Status fs_status;
355 File::Status file_status;
356
357 FwSizeType 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
392Status appendFile(const char* originPath, const char* destPath, bool createMissingDest) {
393 FileSystem::Status fs_status;
394 File::Status file_status;
395 FwSizeType 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
438Status getFileSize(const char* path, FwSizeType& 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
454Status 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
483Status 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.
529Status 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:7
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
PlatformSizeType FwSizeType
Definition FpConfig.h:18
C++-compatible configuration header for fprime configuration.
void close()
close file
Definition File.cpp:36
@ DOESNT_EXIST
File doesn't exist (for read)
Definition File.hpp:25
@ NO_PERMISSION
No permission to read/write file.
Definition File.hpp:27
@ OP_OK
Operation was successful.
Definition File.hpp:24
@ NO_SPACE
No space left.
Definition File.hpp:26
Status write(const void *buffer, NATIVE_INT_TYPE &size, bool waitForDone=true)
write size; will return amount written or errno
Definition File.cpp:32
@ OPEN_APPEND
Open file for appending.
Definition File.hpp:20
@ OPEN_WRITE
Open file for writing.
Definition File.hpp:16
@ OPEN_READ
Open file for reading.
Definition File.hpp:15
Status open(const char *fileName, Mode mode)
open file. Writing creates file if it doesn't exist
Definition File.cpp:12
Status read(void *buffer, NATIVE_INT_TYPE &size, bool waitForFull=true)
waitForFull = true to wait for all bytes to be read
Definition File.cpp:28
Status getFileCount(const char *directory, U32 &fileCount)
counts the number of files in the given directory
Status getFreeSpace(const char *path, FwSizeType &totalBytes, FwSizeType &freeBytes)
get FS free and total space in bytes on filesystem containing path
Status handleFileError(File::Status fileStatus)
Status createDirectory(const char *path)
create a new directory at location path
Definition FileSystem.cpp:8
Status moveFile(const char *originPath, const char *destPath)
Status getFileSize(const char *path, FwSizeType &size)
append file origin to destination file. If boolean true, creates a brand new file if the destination ...
Status changeWorkingDirectory(const char *path)
move current directory to path
Status appendFile(const char *originPath, const char *destPath, bool createMissingDest)
copies a file from origin to destination
@ OP_OK
Operation was successful.
@ NOT_DIR
Path is not a directory.
@ ALREADY_EXISTS
File already exists.
@ NO_PERMISSION
No permission to write.
@ IS_DIR
Path is a directory.
@ INVALID_PATH
Path is too long, too many sym links, doesn't exist, ect.
@ FILE_LIMIT
Too many files or links.
@ OTHER_ERROR
other OS-specific error
@ NOT_EMPTY
directory is not empty
@ NO_SPACE
No space left.
@ BUSY
Operand is in use by the system or by a process.
Status copyFile(const char *originPath, const char *destPath)
moves a file from origin to destination
Status removeDirectory(const char *path)
remove a directory at location path
Status readDirectory(const char *path, const U32 maxNum, Fw::String fileArray[])
Status removeFile(const char *path)
removes a file at location path
Status initAndCheckFileStats(const char *filePath, struct stat *fileInfo=nullptr)
Status copyFileData(File source, File destination, FwSizeType size)
Definition File.cpp:6