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