F´ Flight Software - C/C++ Documentation  devel
A framework for building embedded system applications to NASA flight quality standards.
File.cpp
Go to the documentation of this file.
1 #include <FpConfig.hpp>
2 #include <Os/File.hpp>
3 #include <Fw/Types/Assert.hpp>
4 
5 #ifdef __cplusplus
6 extern "C" {
7 #endif // __cplusplus
8 
9 #include <Utils/Hash/libcrc/lib_crc.h> // borrow CRC
10 
11 #ifdef __cplusplus
12 }
13 #endif // __cplusplus
14 
15 #include <cerrno>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <limits>
21 #include <cstring>
22 #include <cstdio>
23 
24 //#define DEBUG_PRINT(...) printf(##__VA_ARGS__); fflush(stdout)
25 #define DEBUG_PRINT(...)
26 
27 namespace Os {
28 
29  File::File() :m_fd(-1),m_mode(OPEN_NO_MODE),m_lastError(0) {
30  }
31 
32  File::~File() {
33  if (this->m_mode != OPEN_NO_MODE) {
34  this->close();
35  }
36  }
37 
38  File::Status File::open(const char* fileName, File::Mode mode) {
39  return this->open(fileName, mode, true);
40  }
41  File::Status File::open(const char* fileName, File::Mode mode, bool include_excl) {
42 
43  NATIVE_INT_TYPE flags = 0;
44  Status stat = OP_OK;
45 
46  switch (mode) {
47  case OPEN_READ:
48  flags = O_RDONLY;
49  break;
50  case OPEN_WRITE:
51  flags = O_WRONLY | O_CREAT;
52  break;
53  case OPEN_SYNC_WRITE:
54  flags = O_WRONLY | O_CREAT | O_SYNC;
55  break;
56 #ifndef TGT_OS_TYPE_RTEMS
57  case OPEN_SYNC_DIRECT_WRITE:
58  flags = O_WRONLY | O_CREAT | O_DSYNC
59 #ifdef __linux__
60  | O_DIRECT;
61 #else
62  ;
63 #endif
64  break;
65 #endif
66  case OPEN_CREATE:
67  flags = O_WRONLY | O_CREAT | O_TRUNC;
68  if(include_excl) {
69  flags |= O_EXCL;
70  }
71  break;
72  case OPEN_APPEND:
73  flags = O_WRONLY | O_CREAT | O_APPEND;
74  break;
75  default:
76  FW_ASSERT(0, mode);
77  break;
78  }
79 
80  NATIVE_INT_TYPE userFlags =
81 #ifdef __VXWORKS__
82  0;
83 #else
84  S_IRUSR|S_IWRITE;
85 #endif
86  NATIVE_INT_TYPE fd = ::open(fileName,flags,userFlags);
87 
88  if (-1 == fd) {
89  this->m_lastError = errno;
90  switch (errno) {
91  case ENOSPC:
92  stat = NO_SPACE;
93  break;
94  case ENOENT:
95  stat = DOESNT_EXIST;
96  break;
97  case EACCES:
98  stat = NO_PERMISSION;
99  break;
100  case EEXIST:
101  stat = FILE_EXISTS;
102  break;
103  default:
104  stat = OTHER_ERROR;
105  break;
106  }
107  }
108 
109  this->m_mode = mode;
110  this->m_fd = fd;
111  return stat;
112  }
113 
114  bool File::isOpen() {
115  return this->m_fd > 0;
116  }
117 
118  File::Status File::prealloc(NATIVE_INT_TYPE offset, NATIVE_INT_TYPE len) {
119  // make sure it has been opened
120  if (OPEN_NO_MODE == this->m_mode) {
121  return NOT_OPENED;
122  }
123 
124  File::Status fileStatus = OP_OK;
125 
126 #ifdef __linux__
127  NATIVE_INT_TYPE stat = posix_fallocate(this->m_fd, offset, len);
128 
129  if (stat) {
130  switch (stat) {
131  case ENOSPC:
132  case EFBIG:
133  fileStatus = NO_SPACE;
134  break;
135  case EBADF:
136  fileStatus = DOESNT_EXIST;
137  break;
138  default:
139  fileStatus = OTHER_ERROR;
140  break;
141  }
142  }
143 #endif
144 
145  return fileStatus;
146  }
147 
148  File::Status File::seek(NATIVE_INT_TYPE offset, bool absolute) {
149 
150  // make sure it has been opened
151  if (OPEN_NO_MODE == this->m_mode) {
152  return NOT_OPENED;
153  }
154 
155  Status stat = OP_OK;
156  NATIVE_INT_TYPE whence = absolute?SEEK_SET:SEEK_CUR;
157 
158  off_t act_off = ::lseek(this->m_fd,offset,whence);
159 
160  // No error would be a normal one for this simple
161  // class, so return other error
162  if (act_off != offset) {
163  if (-1 == act_off) {
164  this->m_lastError = errno;
165  stat = OTHER_ERROR;
166  } else {
167  stat = BAD_SIZE;
168  }
169  }
170 
171  return stat;
172  }
173 
174  File::Status File::read(void * buffer, NATIVE_INT_TYPE &size, bool waitForFull) {
175 
176  FW_ASSERT(buffer);
177 
178  // make sure it has been opened
179  if (OPEN_NO_MODE == this->m_mode) {
180  size = 0;
181  return NOT_OPENED;
182  }
183  // Validate read size before entering reading loop. Linux's read call expects size_t, which
184  // is defined as an unsigned value. Thus 0 and negative values rejected.
185  if (size <= 0) {
186  size = 0;
187  return BAD_SIZE;
188  }
189 
190  NATIVE_INT_TYPE accSize = 0; // accumulated size
191 
192  Status stat = OP_OK;
193 
194  NATIVE_INT_TYPE maxIters = size*2; // loop limit; couldn't block more times than number of bytes
195 
196  while (maxIters > 0) {
197 
198  ssize_t readSize = ::read(this->m_fd,
199 #ifdef __VXWORKS__
200  static_cast<char*>(buffer)
201 #else
202  buffer
203 #endif
204  ,size-accSize);
205 
206  if (readSize != size-accSize) {
207  // could be an error
208  if (-1 == readSize) {
209  switch (errno) {
210  case EINTR: // read was interrupted
211  maxIters--; // decrement loop count
212  continue;
213  default:
214  stat = OTHER_ERROR;
215  break;
216  }
217  this->m_lastError = errno;
218  accSize = 0;
219  break; // break out of while loop
220  } else if (0 == readSize) { // end of file
221  break;
222  } else { // partial read so adjust read point and size
223  accSize += readSize;
224  if (not waitForFull) {
225  break; // break out of while loop
226  } else {
227  // in order to move the pointer ahead, we need to cast it
228  U8* charPtr = static_cast<U8*>(buffer);
229  charPtr = &charPtr[readSize];
230  buffer = static_cast<void*>(charPtr);
231  }
232  maxIters--; // decrement loop count
233  }
234 
235  } else { // got number we wanted
236  accSize += readSize;
237  break; // break out of while loop
238  }
239 
240  maxIters--; // decrement loop count
241 
242  } // end read while loop
243 
244  // make sure we didn't exceed loop count
245  FW_ASSERT(maxIters > 0);
246 
247  size = accSize;
248 
249  return stat;
250  }
251 
252  File::Status File::write(const void * buffer, NATIVE_INT_TYPE &size, bool waitForDone) {
253 
254  // make sure it has been opened
255  if (OPEN_NO_MODE == this->m_mode) {
256  size = 0;
257  return NOT_OPENED;
258  }
259  // Validate read size before entering reading loop. Linux's read call expects size_t, which
260  // is defined as an unsigned value. Thus 0 and negative values rejected.
261  if (size <= 0) {
262  size = 0;
263  return BAD_SIZE;
264  }
265 
266  Status stat = OP_OK;
267  // just check for EINTR
268  NATIVE_INT_TYPE maxIters = size*2; // loop limit; couldn't block more times than number of bytes
269  while (maxIters > 0) {
270  NATIVE_INT_TYPE writeSize = ::write(this->m_fd,
271 #ifdef __VXWORKS__
272  static_cast<char*>(const_cast<void*>(buffer)) // Ugly, but that's how VxWorks likes to roll...
273 #else
274  buffer
275 #endif
276  ,size);
277 
278  if (-1 == writeSize) {
279  switch (errno) {
280  case EINTR: // write was interrupted
281  maxIters--; // decrement loop count
282  continue;
283  case ENOSPC:
284  stat = NO_SPACE;
285  break;
286  default:
287  DEBUG_PRINT("Error %d during write of 0x%p, addrMod %d, size %d, sizeMod %d\n",
288  errno, buffer, static_cast<POINTER_CAST>(buffer) % 512, size, size % 512);
289  stat = OTHER_ERROR;
290  break;
291  }
292  this->m_lastError = errno;
293  break; // break out of while loop
294  } else {
295  size = writeSize;
296 
297 #ifdef __linux__
298  if ((OPEN_SYNC_DIRECT_WRITE != this->m_mode) && (waitForDone)) {
299  NATIVE_UINT_TYPE position = lseek(this->m_fd, 0, SEEK_CUR);
300  sync_file_range(this->m_fd, position - writeSize, writeSize,
301  SYNC_FILE_RANGE_WAIT_BEFORE
302  | SYNC_FILE_RANGE_WRITE
303  | SYNC_FILE_RANGE_WAIT_AFTER);
304  }
305 #endif
306 
307  break; // break out of while loop
308  }
309  }
310 
311  return stat;
312  }
313 
314  // NOTE(mereweth) - see http://lkml.iu.edu/hypermail/linux/kernel/1005.2/01845.html
315  // recommendation from Linus Torvalds, but doesn't seem to be that fast
316  File::Status File::bulkWrite(const void * buffer, NATIVE_UINT_TYPE &totalSize,
317  NATIVE_INT_TYPE chunkSize) {
318 
319  // make sure it has been opened
320  if (OPEN_NO_MODE == this->m_mode) {
321  totalSize = 0;
322  return NOT_OPENED;
323  }
324  // Validate read size before entering reading loop. Linux's read call expects size_t, which
325  // is defined as an unsigned value. Thus 0 and negative values rejected.
326  if (totalSize == 0) {
327  totalSize = 0;
328  return BAD_SIZE;
329  }
330  else if (chunkSize <= 0) {
331  totalSize = 0;
332  return BAD_SIZE;
333  }
334 
335 #ifdef __linux__
336  const NATIVE_UINT_TYPE startPosition = lseek(this->m_fd, 0, SEEK_CUR);
337 #endif
338  NATIVE_UINT_TYPE newBytesWritten = 0;
339 
340  for (NATIVE_UINT_TYPE idx = 0; idx < totalSize; idx += chunkSize) {
341  NATIVE_INT_TYPE size = chunkSize;
342  // if we're on the last iteration and length isn't a multiple of chunkSize
343  if (idx + chunkSize > totalSize) {
344  size = totalSize - idx;
345  }
346  const NATIVE_INT_TYPE toWrite = size;
347  FW_ASSERT(idx + size <= totalSize, idx + size);
348  const Os::File::Status fileStatus = this->write(static_cast<const U8*>(buffer) + idx, size, false);
349  if (!(fileStatus == Os::File::OP_OK
350  && size == static_cast<NATIVE_INT_TYPE>(toWrite))) {
351  totalSize = newBytesWritten;
352  return fileStatus;
353  }
354 
355 #ifdef __linux__
356  sync_file_range(this->m_fd,
357  startPosition + newBytesWritten,
358  chunkSize,
359  SYNC_FILE_RANGE_WRITE);
360 
361  if (newBytesWritten) {
362  sync_file_range(this->m_fd,
363  startPosition + newBytesWritten - chunkSize,
364  chunkSize,
365  SYNC_FILE_RANGE_WAIT_BEFORE
366  | SYNC_FILE_RANGE_WRITE
367  | SYNC_FILE_RANGE_WAIT_AFTER);
368 
369  posix_fadvise(this->m_fd,
370  startPosition + newBytesWritten - chunkSize,
371  chunkSize, POSIX_FADV_DONTNEED);
372  }
373 #endif
374 
375  newBytesWritten += toWrite;
376  }
377 
378  totalSize = newBytesWritten;
379  return OP_OK;
380  }
381 
383  // make sure it has been opened
384  if (OPEN_NO_MODE == this->m_mode) {
385  return NOT_OPENED;
386  }
387 
388  File::Status stat = OP_OK;
389 
390  if (-1 == fsync(this->m_fd)) {
391  switch (errno) {
392  case ENOSPC:
393  stat = NO_SPACE;
394  break;
395  default:
396  stat = OTHER_ERROR;
397  break;
398  }
399  }
400 
401  return stat;
402  }
403 
404  void File::close() {
405  if ((this->m_fd != -1) and (this->m_mode != OPEN_NO_MODE)) {
406  (void)::close(this->m_fd);
407  this->m_fd = -1;
408  }
409  this->m_mode = OPEN_NO_MODE;
410  }
411 
412  NATIVE_INT_TYPE File::getLastError() {
413  return this->m_lastError;
414  }
415 
416  const char* File::getLastErrorString() {
417  return strerror(this->m_lastError);
418  }
419 
420  File::Status File::calculateCRC32(U32 &crc)
421  {
422 
423  // make sure it has been opened
424  if (OPEN_NO_MODE == this->m_mode) {
425  crc = 0;
426  return NOT_OPENED;
427  }
428 
429  const U32 maxChunkSize = 32;
430  const U32 initialSeed = 0xFFFFFFFF;
431 
432  // Seek to beginning of file
433  Status status = seek(0, true);
434  if (status != OP_OK) {
435  crc = 0;
436  return status;
437  }
438 
439  U8 file_buffer[maxChunkSize];
440 
441  bool endOfFile = false;
442 
443  U32 seed = initialSeed;
444  const U32 maxIters = std::numeric_limits<U32>::max(); // loop limit
445  U32 numIters = 0;
446 
447  while (!endOfFile && numIters < maxIters) {
448 
449  ++numIters;
450  int chunkSize = maxChunkSize;
451 
452  status = read(file_buffer, chunkSize, false);
453  if (status == OP_OK) {
454  // chunkSize modified by file.read
455 
456  if (chunkSize == 0) {
457  endOfFile = true;
458  continue;
459  }
460 
461  int chunkIdx = 0;
462 
463  while (chunkIdx < chunkSize) {
464  seed = update_crc_32(seed, file_buffer[chunkIdx]);
465  chunkIdx++;
466  }
467 
468  } else {
469  crc = 0;
470  return status;
471  }
472  }
473 
474  if (!endOfFile) {
475  crc = 0;
476  return OTHER_ERROR;
477  }
478  else {
479  crc = seed;
480  return OP_OK;
481  }
482  }
483 }
#define FW_ASSERT(...)
Definition: Assert.hpp:14
PlatformPointerCastType POINTER_CAST
Definition: BasicTypes.h:53
PlatformIntType NATIVE_INT_TYPE
Definition: BasicTypes.h:51
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:26
PlatformUIntType NATIVE_UINT_TYPE
Definition: BasicTypes.h:52
C++-compatible configuration header for fprime configuration.
#define DEBUG_PRINT(...)
Definition: File.cpp:25
Status seek(FwSignedSizeType offset, SeekType seekType) override
seek the file pointer to the given offset
Definition: File.cpp:117
File()
constructor
Definition: File.cpp:13
Status read(U8 *buffer, FwSignedSizeType &size)
read data from this file into supplied buffer bounded by size
Definition: File.cpp:142
Status flush() override
flush file contents to storage
Definition: File.cpp:130
Status size(FwSignedSizeType &size_result) override
get size of currently open file
Definition: File.cpp:84
void close() override
close the file, if not opened then do nothing
Definition: File.cpp:69
~File() final
destructor
Definition: File.cpp:17
Os::FileInterface::Status open(const char *path, Mode mode)
open file with supplied path and mode
Definition: File.cpp:45
Status position(FwSignedSizeType &position_result) override
get file pointer position of the currently open file
Definition: File.cpp:93
bool isOpen() const
determine if the file is open
Definition: File.cpp:78
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
@ NOT_OPENED
file hasn't been opened yet
Definition: File.hpp:35
@ OTHER_ERROR
A catch-all for other errors. Have to look in implementation-specific code.
Definition: File.hpp:40
@ BAD_SIZE
Invalid size parameter.
Definition: File.hpp:34
@ OP_OK
Operation was successful.
Definition: File.hpp:30
@ DOESNT_EXIST
File doesn't exist (for read)
Definition: File.hpp:31
@ FILE_EXISTS
file already exist (for CREATE with O_EXCL enabled)
Definition: File.hpp:36
@ OPEN_NO_MODE
File mode not yet selected.
Definition: File.hpp:20
@ OPEN_WRITE
Open file for writing.
Definition: File.hpp:23
@ OPEN_CREATE
Open file for writing and truncates file if it exists, ie same flags as creat()
Definition: File.hpp:22
@ OPEN_READ
Open file for reading.
Definition: File.hpp:21
@ OPEN_APPEND
Open file for appending.
Definition: File.hpp:25
@ OPEN_SYNC_WRITE
Open file for writing; writes don't return until data is on disk.
Definition: File.hpp:24
unsigned long update_crc_32(unsigned long crc, char c)
Definition: lib_crc.c:271