F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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 // Create constants for the max limits of the signed types
33 // These constants are used for comparisons with complementary unsigned types to avoid sign-compare warning
34 using UnsignedOffT = std::make_unsigned<off_t>::type;
35 static const UnsignedOffT OFF_T_MAX_LIMIT = static_cast<UnsignedOffT>(std::numeric_limits<off_t>::max());
36 using UnsignedSSizeT = std::make_unsigned<ssize_t>::type;
37 static const UnsignedSSizeT SSIZE_T_MAX_LIMIT = static_cast<UnsignedSSizeT>(std::numeric_limits<ssize_t>::max());
38 
39 // Ensure size of FwSizeType is large enough to fit eh necessary range
40 static_assert(sizeof(FwSignedSizeType) >= sizeof(off_t),
41  "FwSignedSizeType is not large enough to store values of type off_t");
42 static_assert(sizeof(FwSignedSizeType) >= sizeof(ssize_t),
43  "FwSignedSizeType is not large enough to store values of type ssize_t");
44 static_assert(sizeof(FwSizeType) >= sizeof(size_t), "FwSizeType is not large enough to store values of type size_t");
45 
46 // Now check ranges of FwSizeType
47 static_assert(std::numeric_limits<FwSignedSizeType>::max() >= std::numeric_limits<off_t>::max(),
48  "Maximum value of FwSignedSizeType less than the maximum value of off_t. Configure a larger type.");
49 static_assert(std::numeric_limits<FwSizeType>::max() >= OFF_T_MAX_LIMIT,
50  "Maximum value of FwSizeType less than the maximum value of off_t. Configure a larger type.");
51 static_assert(std::numeric_limits<FwSignedSizeType>::max() >= std::numeric_limits<ssize_t>::max(),
52  "Maximum value of FwSignedSizeType less than the maximum value of ssize_t. Configure a larger type.");
54  "Minimum value of FwSignedSizeType larger than the minimum value of off_t. Configure a larger type.");
56  "Minimum value of FwSizeType larger than the minimum value of ssize_t. Configure a larger type.");
57 static_assert(std::numeric_limits<FwSizeType>::max() >= std::numeric_limits<size_t>::max(),
58  "Maximum value of FwSizeType less than the maximum value of size_t. Configure a larger type.");
59 
62  // Must properly duplicate the file handle
63  this->m_handle.m_file_descriptor = fcntl(other.m_handle.m_file_descriptor, F_DUPFD, 0);
64 }
65 
67  if (this != &other) {
68  this->m_handle.m_file_descriptor = fcntl(other.m_handle.m_file_descriptor, F_DUPFD, 0);
69  }
70  return *this;
71 }
72 
73 PosixFile::Status PosixFile::open(const char* filepath,
74  PosixFile::Mode requested_mode,
75  PosixFile::OverwriteType overwrite) {
76  int mode_flags = 0;
77  Status status = OP_OK;
78  switch (requested_mode) {
79  case OPEN_READ:
80  mode_flags = O_RDONLY;
81  break;
82  case OPEN_WRITE:
83  mode_flags = O_WRONLY | O_CREAT;
84  break;
85  case OPEN_SYNC_WRITE:
86  mode_flags = O_WRONLY | O_CREAT | O_SYNC;
87  break;
88  case OPEN_CREATE:
89  mode_flags =
90  O_WRONLY | O_CREAT | O_TRUNC | ((overwrite == PosixFile::OverwriteType::OVERWRITE) ? 0 : O_EXCL);
91  break;
92  case OPEN_APPEND:
93  mode_flags = O_WRONLY | O_CREAT | O_APPEND;
94  break;
95  default:
96  FW_ASSERT(0, requested_mode);
97  break;
98  }
99  int descriptor = ::open(filepath, mode_flags, USER_FLAGS);
100  if (PosixFileHandle::INVALID_FILE_DESCRIPTOR == descriptor) {
101  int errno_store = errno;
102  status = Os::Posix::errno_to_file_status(errno_store);
103  }
104  this->m_handle.m_file_descriptor = descriptor;
105  return status;
106 }
107 
109  // Only close file handles that are not open
111  (void)::close(this->m_handle.m_file_descriptor);
113  }
114 }
115 
117  FwSizeType current_position = 0;
118  Status status = this->position(current_position);
119  size_result = 0;
120  if (Os::File::Status::OP_OK == status) {
121  // Must be a coding error if current_position is larger than off_t max in Posix File
122  FW_ASSERT(current_position <= OFF_T_MAX_LIMIT);
123  // Seek to the end of the file to determine size
124  off_t end_of_file = ::lseek(this->m_handle.m_file_descriptor, 0, SEEK_END);
125  if (PosixFileHandle::ERROR_RETURN_VALUE == end_of_file) {
126  int errno_store = errno;
127  status = Os::Posix::errno_to_file_status(errno_store);
128  }
129  // Return to original position
130  (void)::lseek(this->m_handle.m_file_descriptor, static_cast<off_t>(current_position), SEEK_SET);
131  size_result = static_cast<FwSizeType>(end_of_file);
132  }
133  return status;
134 }
135 
137  Status status = OP_OK;
138  position_result = 0;
139  off_t actual = ::lseek(this->m_handle.m_file_descriptor, 0, SEEK_CUR);
140  if (PosixFileHandle::ERROR_RETURN_VALUE == actual) {
141  int errno_store = errno;
142  status = Os::Posix::errno_to_file_status(errno_store);
143  }
144  // Protected by static assertion (FwSizeType >= off_t)
145  position_result = static_cast<FwSizeType>(actual);
146  return status;
147 }
148 
150  PosixFile::Status status = Os::File::Status::NOT_SUPPORTED;
151  // Check for larger size than posix supports
152  if ((length > OFF_T_MAX_LIMIT) || (offset > OFF_T_MAX_LIMIT) ||
153  (std::numeric_limits<off_t>::max() - length) < offset) {
154  status = Os::File::Status::BAD_SIZE;
155  }
156  // posix_fallocate is only available with the posix C-API post version 200112L, however; it is not guaranteed that
157  // this call is properly implemented. This code starts with a status of "NOT_SUPPORTED". When the standard is met
158  // an attempt will be made to called posix_fallocate, and should that still return NOT_SUPPORTED then fallback
159  // code is engaged to synthesize this behavior.
160 #if _POSIX_C_SOURCE >= 200112L
161  else {
162  int errno_status =
163  ::posix_fallocate(this->m_handle.m_file_descriptor, static_cast<off_t>(offset), static_cast<off_t>(length));
164  status = Os::Posix::errno_to_file_status(errno_status);
165  }
166 #endif
167  // When the operation is not supported or posix-API is not sufficient, fallback to a slower algorithm
168  if (Os::File::Status::NOT_SUPPORTED == status) {
169  // Calculate size
170  FwSizeType file_size = 0;
171  status = this->size(file_size);
172  if (Os::File::Status::OP_OK == status) {
173  // Calculate current position
174  FwSizeType file_position = 0;
175  status = this->position(file_position);
176  // Check for overflow in seek calls
177  if (file_position > static_cast<FwSizeType>(std::numeric_limits<FwSignedSizeType>::max()) ||
178  file_size > static_cast<FwSizeType>(std::numeric_limits<FwSignedSizeType>::max())) {
179  status = Os::File::Status::BAD_SIZE;
180  }
181  // Only allocate when the file is smaller than the allocation
182  else if ((Os::File::Status::OP_OK == status) && (file_size < (offset + length))) {
183  const FwSizeType write_length = (offset + length) - file_size;
184  status = this->seek(static_cast<FwSignedSizeType>(file_size), PosixFile::SeekType::ABSOLUTE);
185  if (Os::File::Status::OP_OK == status) {
186  // Fill in zeros past size of file to ensure compatibility with fallocate
187  for (FwSizeType i = 0; i < write_length; i++) {
188  FwSizeType write_size = 1;
189  status =
190  this->write(reinterpret_cast<const U8*>("\0"), write_size, PosixFile::WaitType::NO_WAIT);
191  if (Status::OP_OK != status || write_size != 1) {
192  break;
193  }
194  }
195  // Return to original position
196  if (Os::File::Status::OP_OK == status) {
197  status =
198  this->seek(static_cast<FwSignedSizeType>(file_position), PosixFile::SeekType::ABSOLUTE);
199  }
200  }
201  }
202  }
203  }
204  return status;
205 }
206 
208  Status status = OP_OK;
209  if (offset > std::numeric_limits<off_t>::max()) {
210  status = BAD_SIZE;
211  } else {
212  off_t actual = ::lseek(this->m_handle.m_file_descriptor, static_cast<off_t>(offset),
213  (seekType == SeekType::ABSOLUTE) ? SEEK_SET : SEEK_CUR);
214  int errno_store = errno;
215  if (actual == PosixFileHandle::ERROR_RETURN_VALUE) {
216  status = Os::Posix::errno_to_file_status(errno_store);
217  } else if ((seekType == SeekType::ABSOLUTE) && (actual != offset)) {
219  }
220  }
221  return status;
222 }
223 
225  PosixFile::Status status = OP_OK;
226  if (PosixFileHandle::ERROR_RETURN_VALUE == ::fsync(this->m_handle.m_file_descriptor)) {
227  int errno_store = errno;
228  status = Os::Posix::errno_to_file_status(errno_store);
229  }
230  return status;
231 }
232 
234  Status status = OP_OK;
235  FwSizeType accumulated = 0;
236  // Loop up to 2 times for each by, bounded to prevent overflow
237  const FwSizeType maximum =
238  (size > (std::numeric_limits<FwSizeType>::max() / 2)) ? std::numeric_limits<FwSizeType>::max() : size * 2;
239  // POSIX APIs are implementation dependent when dealing with sizes larger than the signed return value
240  // thus we ensure a clear decision: BAD_SIZE
241  if (size > SSIZE_T_MAX_LIMIT) {
242  return BAD_SIZE;
243  }
244 
245  for (FwSizeType i = 0; i < maximum && accumulated < size; i++) {
246  // char* for some posix implementations
247  ssize_t read_size = ::read(this->m_handle.m_file_descriptor, reinterpret_cast<CHAR*>(&buffer[accumulated]),
248  static_cast<size_t>(size - accumulated));
249  // Non-interrupt error
250  if (PosixFileHandle::ERROR_RETURN_VALUE == read_size) {
251  int errno_store = errno;
252  // Interrupted w/o read, try again
253  if (EINTR != errno_store) {
254  continue;
255  }
256  status = Os::Posix::errno_to_file_status(errno_store);
257  break;
258  }
259  // End-of-file
260  else if (read_size == 0) {
261  break;
262  }
263  accumulated += static_cast<FwSizeType>(read_size);
264  // Stop looping when we had a good read and are not waiting
265  if (not wait) {
266  break;
267  }
268  }
269  size = accumulated;
270  return status;
271 }
272 
274  Status status = OP_OK;
275  FwSizeType accumulated = 0;
276  // Loop up to 2 times for each by, bounded to prevent overflow
277  const FwSizeType maximum =
278  (size > (std::numeric_limits<FwSizeType>::max() / 2)) ? std::numeric_limits<FwSizeType>::max() : size * 2;
279  // POSIX APIs are implementation dependent when dealing with sizes larger than the signed return value
280  // thus we ensure a clear decision: BAD_SIZE
281  if (size > SSIZE_T_MAX_LIMIT) {
282  return BAD_SIZE;
283  }
284 
285  for (FwSizeType i = 0; i < maximum && accumulated < size; i++) {
286  // char* for some posix implementations
287  ssize_t write_size =
288  ::write(this->m_handle.m_file_descriptor, reinterpret_cast<const CHAR*>(&buffer[accumulated]),
289  static_cast<size_t>(size - accumulated));
290  // Non-interrupt error
291  if (PosixFileHandle::ERROR_RETURN_VALUE == write_size || write_size < 0) {
292  int errno_store = errno;
293  // Interrupted w/o write, try again
294  if (EINTR != errno_store) {
295  continue;
296  }
297  status = Os::Posix::errno_to_file_status(errno_store);
298  break;
299  }
300  accumulated += static_cast<FwSizeType>(write_size);
301  }
302  size = accumulated;
303  // When waiting, sync to disk
304  if (wait) {
305  int fsync_return = ::fsync(this->m_handle.m_file_descriptor);
306  if (PosixFileHandle::ERROR_RETURN_VALUE == fsync_return) {
307  int errno_store = errno;
308  status = Os::Posix::errno_to_file_status(errno_store);
309  }
310  }
311  return status;
312 }
313 
315  return &this->m_handle;
316 }
317 
318 } // namespace File
319 } // namespace Posix
320 } // namespace Os
Status size(FwSizeType &size_result) override
get size of currently open file
Definition: File.cpp:116
base implementation of FileHandle
Definition: File.hpp:24
Status preallocate(FwSizeType offset, FwSizeType length) override
pre-allocate file storage
Definition: File.cpp:149
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:37
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
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:273
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:233
void close() override
close the file, if not opened then do nothing
Definition: File.cpp:108
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:66
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:207
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:36
static const UnsignedOffT OFF_T_MAX_LIMIT
Definition: File.cpp:35
Invalid size parameter.
Definition: File.hpp:44
Status flush() override
flush file contents to storage
Definition: File.cpp:224
FileHandle * getHandle() override
returns the raw file handle
Definition: File.cpp:314
Status position(FwSizeType &position_result) override
get file pointer position of the currently open file
Definition: File.cpp:136
std::make_unsigned< off_t >::type UnsignedOffT
Definition: File.cpp:34
Os::FileInterface::Status open(const char *path, Mode mode, OverwriteType overwrite) override
open file with supplied path and mode
Definition: File.cpp:73
#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