F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
File.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title Os/Posix/File.cpp
3 // \brief posix implementation for Os::File
4 // ======================================================================
5 #include <fcntl.h>
6 #include <unistd.h>
7 #include <cerrno>
8 #include <limits>
9 #include <type_traits>
10 
11 #include <Fw/Types/Assert.hpp>
12 #include <Os/File.hpp>
13 #include <Os/Posix/File.hpp>
14 #include <Os/Posix/error.hpp>
15 
16 namespace Os {
17 namespace Posix {
18 namespace File {
19 
20 // Sets up the default file permission as user read + user write
21 // Some posix systems (e.g. Darwin) use the older S_IREAD and S_IWRITE flags while other systems (e.g. Linux) use the
22 // newer S_IRUSR and S_IWUSR flags, and some don't support these flags at all. Hence, we look if flags are defined then
23 // set USER_FLAGS to be the set of flags supported or 0 in the case neither is defined.
24 #if defined(S_IREAD) && defined(S_IWRITE)
25 #define USER_FLAGS (S_IREAD | S_IWRITE)
26 #elif defined(S_IRUSR) && defined(S_IWUSR)
27 #define USER_FLAGS (S_IRUSR | S_IWUSR)
28 #else
29 #define USER_FLAGS (0)
30 #endif
31 
32 // O_SYNC is not defined on every system. This will set up the SYNC_FLAGS variable to be O_SYNC when defined and
33 // (0) when not defined. This allows OPEN_SYNC_WRITE to fall-back to OPEN_WRITE on those systems.
34 #if defined(O_SYNC)
35 #define SYNC_FLAGS O_SYNC
36 #else
37 #define SYNC_FLAGS (0)
38 #endif
39 
40 // Create constants for the max limits of the signed types
41 // These constants are used for comparisons with complementary unsigned types to avoid sign-compare warning
42 using UnsignedOffT = std::make_unsigned<off_t>::type;
43 static const UnsignedOffT OFF_T_MAX_LIMIT = static_cast<UnsignedOffT>(std::numeric_limits<off_t>::max());
44 using UnsignedSSizeT = std::make_unsigned<ssize_t>::type;
45 static const UnsignedSSizeT SSIZE_T_MAX_LIMIT = static_cast<UnsignedSSizeT>(std::numeric_limits<ssize_t>::max());
46 
47 // Ensure size of FwSizeType is large enough to fit eh necessary range
48 static_assert(sizeof(FwSignedSizeType) >= sizeof(off_t),
49  "FwSignedSizeType is not large enough to store values of type off_t");
50 static_assert(sizeof(FwSignedSizeType) >= sizeof(ssize_t),
51  "FwSignedSizeType is not large enough to store values of type ssize_t");
52 static_assert(sizeof(FwSizeType) >= sizeof(size_t), "FwSizeType is not large enough to store values of type size_t");
53 
54 // Now check ranges of FwSizeType
55 static_assert(std::numeric_limits<FwSignedSizeType>::max() >= std::numeric_limits<off_t>::max(),
56  "Maximum value of FwSignedSizeType less than the maximum value of off_t. Configure a larger type.");
57 static_assert(std::numeric_limits<FwSizeType>::max() >= OFF_T_MAX_LIMIT,
58  "Maximum value of FwSizeType less than the maximum value of off_t. Configure a larger type.");
59 static_assert(std::numeric_limits<FwSignedSizeType>::max() >= std::numeric_limits<ssize_t>::max(),
60  "Maximum value of FwSignedSizeType less than the maximum value of ssize_t. Configure a larger type.");
62  "Minimum value of FwSignedSizeType larger than the minimum value of off_t. Configure a larger type.");
64  "Minimum value of FwSizeType larger than the minimum value of ssize_t. Configure a larger type.");
65 static_assert(std::numeric_limits<FwSizeType>::max() >= std::numeric_limits<size_t>::max(),
66  "Maximum value of FwSizeType less than the maximum value of size_t. Configure a larger type.");
67 
70  // Must properly duplicate the file handle
71  this->m_handle.m_file_descriptor = fcntl(other.m_handle.m_file_descriptor, F_DUPFD, 0);
72 }
73 
75  if (this != &other) {
76  this->m_handle.m_file_descriptor = fcntl(other.m_handle.m_file_descriptor, F_DUPFD, 0);
77  }
78  return *this;
79 }
80 
81 PosixFile::Status PosixFile::open(const char* filepath,
82  PosixFile::Mode requested_mode,
83  PosixFile::OverwriteType overwrite) {
84  int mode_flags = 0;
85  Status status = OP_OK;
86  switch (requested_mode) {
87  case OPEN_READ:
88  mode_flags = O_RDONLY;
89  break;
90  case OPEN_WRITE:
91  mode_flags = O_WRONLY | O_CREAT;
92  break;
93  case OPEN_SYNC_WRITE:
94  mode_flags = O_WRONLY | O_CREAT | SYNC_FLAGS;
95  break;
96  case OPEN_CREATE:
97  mode_flags =
98  O_WRONLY | O_CREAT | O_TRUNC | ((overwrite == PosixFile::OverwriteType::OVERWRITE) ? 0 : O_EXCL);
99  break;
100  case OPEN_APPEND:
101  mode_flags = O_WRONLY | O_CREAT | O_APPEND;
102  break;
103  default:
104  FW_ASSERT(0, requested_mode);
105  break;
106  }
107  int descriptor = ::open(filepath, mode_flags, USER_FLAGS);
108  if (PosixFileHandle::INVALID_FILE_DESCRIPTOR == descriptor) {
109  int errno_store = errno;
110  status = Os::Posix::errno_to_file_status(errno_store);
111  }
112  this->m_handle.m_file_descriptor = descriptor;
113  return status;
114 }
115 
117  // Only close file handles that are not open
119  (void)::close(this->m_handle.m_file_descriptor);
121  }
122 }
123 
125  FwSizeType current_position = 0;
126  Status status = this->position(current_position);
127  size_result = 0;
128  if (Os::File::Status::OP_OK == status) {
129  // Must be a coding error if current_position is larger than off_t max in Posix File
130  FW_ASSERT(current_position <= OFF_T_MAX_LIMIT);
131  // Seek to the end of the file to determine size
132  off_t end_of_file = ::lseek(this->m_handle.m_file_descriptor, 0, SEEK_END);
133  if (PosixFileHandle::ERROR_RETURN_VALUE == end_of_file) {
134  int errno_store = errno;
135  status = Os::Posix::errno_to_file_status(errno_store);
136  }
137  // Return to original position
138  (void)::lseek(this->m_handle.m_file_descriptor, static_cast<off_t>(current_position), SEEK_SET);
139  size_result = static_cast<FwSizeType>(end_of_file);
140  }
141  return status;
142 }
143 
145  Status status = OP_OK;
146  position_result = 0;
147  off_t actual = ::lseek(this->m_handle.m_file_descriptor, 0, SEEK_CUR);
148  if (PosixFileHandle::ERROR_RETURN_VALUE == actual) {
149  int errno_store = errno;
150  status = Os::Posix::errno_to_file_status(errno_store);
151  }
152  // Protected by static assertion (FwSizeType >= off_t)
153  position_result = static_cast<FwSizeType>(actual);
154  return status;
155 }
156 
158  PosixFile::Status status = Os::File::Status::NOT_SUPPORTED;
159  // Check for larger size than posix supports
160  if ((length > OFF_T_MAX_LIMIT) || (offset > OFF_T_MAX_LIMIT) ||
161  (std::numeric_limits<off_t>::max() - length) < offset) {
162  status = Os::File::Status::BAD_SIZE;
163  }
164  // posix_fallocate is only available with the posix C-API post version 200112L, however; it is not guaranteed that
165  // this call is properly implemented. This code starts with a status of "NOT_SUPPORTED". When the standard is met
166  // an attempt will be made to called posix_fallocate, and should that still return NOT_SUPPORTED then fallback
167  // code is engaged to synthesize this behavior.
168 #if _POSIX_C_SOURCE >= 200112L && !(defined(FPRIME_SYNTHETIC_FALLOCATE) && FPRIME_SYNTHETIC_FALLOCATE)
169  else {
170  int errno_status =
171  ::posix_fallocate(this->m_handle.m_file_descriptor, static_cast<off_t>(offset), static_cast<off_t>(length));
172  status = Os::Posix::errno_to_file_status(errno_status);
173  }
174 #endif
175  // When the operation is not supported or posix-API is not sufficient, fallback to a slower algorithm
176  if (Os::File::Status::NOT_SUPPORTED == status) {
177  // Calculate size
178  FwSizeType file_size = 0;
179  status = this->size(file_size);
180  if (Os::File::Status::OP_OK == status) {
181  // Calculate current position
182  FwSizeType file_position = 0;
183  status = this->position(file_position);
184  // Check for overflow in seek calls
185  if (file_position > static_cast<FwSizeType>(std::numeric_limits<FwSignedSizeType>::max()) ||
186  file_size > static_cast<FwSizeType>(std::numeric_limits<FwSignedSizeType>::max())) {
187  status = Os::File::Status::BAD_SIZE;
188  }
189  // Only allocate when the file is smaller than the allocation
190  else if ((Os::File::Status::OP_OK == status) && (file_size < (offset + length))) {
191  const FwSizeType write_length = (offset + length) - file_size;
192  status = this->seek(static_cast<FwSignedSizeType>(file_size), PosixFile::SeekType::ABSOLUTE);
193  if (Os::File::Status::OP_OK == status) {
194  // Fill in zeros past size of file to ensure compatibility with fallocate
195  for (FwSizeType i = 0; i < write_length; i++) {
196  FwSizeType write_size = 1;
197  status =
198  this->write(reinterpret_cast<const U8*>("\0"), write_size, PosixFile::WaitType::NO_WAIT);
199  if (Status::OP_OK != status || write_size != 1) {
200  break;
201  }
202  }
203  // Return to original position
204  if (Os::File::Status::OP_OK == status) {
205  status =
206  this->seek(static_cast<FwSignedSizeType>(file_position), PosixFile::SeekType::ABSOLUTE);
207  }
208  }
209  }
210  }
211  }
212  return status;
213 }
214 
216  Status status = OP_OK;
217  if (offset > std::numeric_limits<off_t>::max()) {
218  status = BAD_SIZE;
219  } else {
220  off_t actual = ::lseek(this->m_handle.m_file_descriptor, static_cast<off_t>(offset),
221  (seekType == SeekType::ABSOLUTE) ? SEEK_SET : SEEK_CUR);
222  int errno_store = errno;
223  if (actual == PosixFileHandle::ERROR_RETURN_VALUE) {
224  status = Os::Posix::errno_to_file_status(errno_store);
225  } else if ((seekType == SeekType::ABSOLUTE) && (actual != offset)) {
227  }
228  }
229  return status;
230 }
231 
233  PosixFile::Status status = OP_OK;
234  if (PosixFileHandle::ERROR_RETURN_VALUE == ::fsync(this->m_handle.m_file_descriptor)) {
235  int errno_store = errno;
236  status = Os::Posix::errno_to_file_status(errno_store);
237  }
238  return status;
239 }
240 
242  Status status = OP_OK;
243  FwSizeType accumulated = 0;
244  // Loop up to 2 times for each by, bounded to prevent overflow
245  const FwSizeType maximum =
246  (size > (std::numeric_limits<FwSizeType>::max() / 2)) ? std::numeric_limits<FwSizeType>::max() : size * 2;
247  // POSIX APIs are implementation dependent when dealing with sizes larger than the signed return value
248  // thus we ensure a clear decision: BAD_SIZE
249  if (size > SSIZE_T_MAX_LIMIT) {
250  return BAD_SIZE;
251  }
252 
253  for (FwSizeType i = 0; i < maximum && accumulated < size; i++) {
254  // char* for some posix implementations
255  ssize_t read_size = ::read(this->m_handle.m_file_descriptor, reinterpret_cast<CHAR*>(&buffer[accumulated]),
256  static_cast<size_t>(size - accumulated));
257  // Non-interrupt error
258  if (PosixFileHandle::ERROR_RETURN_VALUE == read_size) {
259  int errno_store = errno;
260  // Interrupted w/o read, try again
261  if (EINTR != errno_store) {
262  continue;
263  }
264  status = Os::Posix::errno_to_file_status(errno_store);
265  break;
266  }
267  // End-of-file
268  else if (read_size == 0) {
269  break;
270  }
271  accumulated += static_cast<FwSizeType>(read_size);
272  // Stop looping when we had a good read and are not waiting
273  if (not wait) {
274  break;
275  }
276  }
277  size = accumulated;
278  return status;
279 }
280 
282  Status status = OP_OK;
283  FwSizeType accumulated = 0;
284  // Loop up to 2 times for each by, bounded to prevent overflow
285  const FwSizeType maximum =
286  (size > (std::numeric_limits<FwSizeType>::max() / 2)) ? std::numeric_limits<FwSizeType>::max() : size * 2;
287  // POSIX APIs are implementation dependent when dealing with sizes larger than the signed return value
288  // thus we ensure a clear decision: BAD_SIZE
289  if (size > SSIZE_T_MAX_LIMIT) {
290  return BAD_SIZE;
291  }
292 
293  for (FwSizeType i = 0; i < maximum && accumulated < size; i++) {
294  // char* for some posix implementations
295  ssize_t write_size =
296  ::write(this->m_handle.m_file_descriptor, reinterpret_cast<const CHAR*>(&buffer[accumulated]),
297  static_cast<size_t>(size - accumulated));
298  // Non-interrupt error
299  if (PosixFileHandle::ERROR_RETURN_VALUE == write_size || write_size < 0) {
300  int errno_store = errno;
301  // Interrupted w/o write, try again
302  if (EINTR != errno_store) {
303  continue;
304  }
305  status = Os::Posix::errno_to_file_status(errno_store);
306  break;
307  }
308  accumulated += static_cast<FwSizeType>(write_size);
309  }
310  size = accumulated;
311  // When waiting, sync to disk
312  if (wait) {
313  int fsync_return = ::fsync(this->m_handle.m_file_descriptor);
314  if (PosixFileHandle::ERROR_RETURN_VALUE == fsync_return) {
315  int errno_store = errno;
316  status = Os::Posix::errno_to_file_status(errno_store);
317  }
318  }
319  return status;
320 }
321 
323  return &this->m_handle;
324 }
325 
326 } // namespace File
327 } // namespace Posix
328 } // namespace Os
Status size(FwSizeType &size_result) override
get size of currently open file
Definition: File.cpp:124
base implementation of FileHandle
Definition: File.hpp:24
Status preallocate(FwSizeType offset, FwSizeType length) override
pre-allocate file storage
Definition: File.cpp:157
Operation succeeded.
Definition: Os.hpp:26
A catch-all for other errors. Have to look in implementation-specific code.
PlatformSizeType FwSizeType
PosixFile()=default
constructor
static const UnsignedSSizeT SSIZE_T_MAX_LIMIT
Definition: File.cpp:45
Open file for writing; writes don&#39;t return until data is on disk.
Definition: File.hpp:34
int m_file_descriptor
Posix file descriptor.
Definition: File.hpp:20
Open file for writing.
Definition: File.hpp:33
PlatformSignedSizeType FwSignedSizeType
#define SYNC_FLAGS
Definition: File.cpp:37
posix implementation of Os::File
Definition: File.hpp:29
Status write(const U8 *buffer, FwSizeType &size, WaitType wait) override
read data from this file into supplied buffer bounded by size
Definition: File.cpp:281
Open file for appending.
Definition: File.hpp:35
static constexpr int ERROR_RETURN_VALUE
Definition: File.hpp:17
static constexpr int INVALID_FILE_DESCRIPTOR
Definition: File.hpp:16
Status read(U8 *buffer, FwSizeType &size, WaitType wait) override
read data from this file into supplied buffer bounded by size
Definition: File.cpp:241
void close() override
close the file, if not opened then do nothing
Definition: File.cpp:116
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:53
PosixFile & operator=(const PosixFile &other)
assignment operator that copies the internal representation
Definition: File.cpp:74
static U32 min(const U32 a, const U32 b)
Definition: Checksum.cpp:16
Operation was successful.
Definition: File.hpp:40
Status seek(FwSignedSizeType offset, SeekType seekType) override
seek the file pointer to the given offset
Definition: File.cpp:215
Open file for reading.
Definition: File.hpp:31
File::Status errno_to_file_status(int errno_input)
Definition: error.cpp:11
#define USER_FLAGS
Definition: File.cpp:29
std::make_unsigned< ssize_t >::type UnsignedSSizeT
Definition: File.cpp:44
static const UnsignedOffT OFF_T_MAX_LIMIT
Definition: File.cpp:43
Invalid size parameter.
Definition: File.hpp:44
Status flush() override
flush file contents to storage
Definition: File.cpp:232
FileHandle * getHandle() override
returns the raw file handle
Definition: File.cpp:322
Status position(FwSizeType &position_result) override
get file pointer position of the currently open file
Definition: File.cpp:144
std::make_unsigned< off_t >::type UnsignedOffT
Definition: File.cpp:42
Os::FileInterface::Status open(const char *path, Mode mode, OverwriteType overwrite) override
open file with supplied path and mode
Definition: File.cpp:81
#define FW_ASSERT(...)
Definition: Assert.hpp:14
Open file for writing and truncates file if it exists, ie same flags as creat()
Definition: File.hpp:32