GCC Code Coverage Report


Directory: Os/
File: FileSystem.cpp
Date: 2026-09-03 21:15:10
Exec Total Coverage
Lines: 48 203 23.6%
Functions: 11 29 37.9%
Branches: 17 95 17.9%

Line Branch Exec Source
1 // ======================================================================
2 // \title Os/FileSystem.cpp
3 // \brief common function implementation for Os::FileSystem
4 // ======================================================================
5 #include <Fw/Types/Assert.hpp>
6 #include <Os/FileSystem.hpp>
7 #include <algorithm>
8
9 namespace Os {
10
11
3/3
✓ Branch 10 taken 48 times.
✓ Branch 11 taken 3 times.
✓ Branch 17 taken 3 times.
51 FileSystem::FileSystem() : m_handle_storage(), m_delegate(*FileSystemInterface::getDelegate(m_handle_storage)) {
12 3 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
13 3 }
14
15 6 FileSystem::~FileSystem() {
16 6 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
17 6 m_delegate.~FileSystemInterface();
18 6 }
19
20 FileSystemHandle* FileSystem::getHandle() {
21 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
22 return this->m_delegate.getHandle();
23 }
24
25 9 FileSystem::Status FileSystem::_removeDirectory(const char* path) {
26 9 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
27 9 FW_ASSERT(path != nullptr);
28 9 return this->m_delegate._removeDirectory(path);
29 }
30
31 10 FileSystem::Status FileSystem::_removeFile(const char* path) {
32 10 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
33 10 FW_ASSERT(path != nullptr);
34 10 return this->m_delegate._removeFile(path);
35 }
36
37 FileSystem::Status FileSystem::_rename(const char* sourcePath, const char* destPath) {
38 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
39 FW_ASSERT(sourcePath != nullptr);
40 FW_ASSERT(destPath != nullptr);
41 return this->m_delegate._rename(sourcePath, destPath);
42 }
43
44 FileSystem::Status FileSystem::_getPathType(const char* path, PathType& pathType) {
45 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
46 FW_ASSERT(path != nullptr);
47 return this->m_delegate._getPathType(path, pathType);
48 }
49
50 2 FileSystem::Status FileSystem::_getWorkingDirectory(char* path, FwSizeType bufferSize) {
51 2 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
52 2 FW_ASSERT(path != nullptr);
53 2 FW_ASSERT(bufferSize > 0); // because bufferSize=0 would trigger a malloc in some implementations (e.g. Posix)
54 2 return this->m_delegate._getWorkingDirectory(path, bufferSize);
55 }
56
57 FileSystem::Status FileSystem::_changeWorkingDirectory(const char* path) {
58 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
59 FW_ASSERT(path != nullptr);
60 return this->m_delegate._changeWorkingDirectory(path);
61 }
62
63 FileSystem::Status FileSystem::_getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) {
64 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
65 FW_ASSERT(path != nullptr);
66 return this->m_delegate._getFreeSpace(path, totalBytes, freeBytes);
67 }
68
69 void FileSystem::init() {
70 // Force trigger on the fly singleton setup
71 (void)FileSystem::getSingleton();
72 }
73
74 21 FileSystem& FileSystem::getSingleton() {
75
4/7
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 18 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
21 static FileSystem s_singleton;
76 21 return s_singleton;
77 }
78
79 // ------------------------------------------------------------
80 // Static functions calling implementation-specific operations
81 // ------------------------------------------------------------
82
83 9 FileSystem::Status FileSystem::removeDirectory(const char* path) {
84 9 return FileSystem::getSingleton()._removeDirectory(path);
85 }
86
87 10 FileSystem::Status FileSystem::removeFile(const char* path) {
88 10 return FileSystem::getSingleton()._removeFile(path);
89 }
90
91 FileSystem::Status FileSystem::rename(const char* sourcePath, const char* destPath) {
92 return FileSystem::getSingleton()._rename(sourcePath, destPath);
93 }
94
95 2 FileSystem::Status FileSystem::getWorkingDirectory(char* path, FwSizeType bufferSize) {
96 2 return FileSystem::getSingleton()._getWorkingDirectory(path, bufferSize);
97 }
98
99 FileSystem::Status FileSystem::changeWorkingDirectory(const char* path) {
100 return FileSystem::getSingleton()._changeWorkingDirectory(path);
101 }
102
103 FileSystem::Status FileSystem::getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) {
104 return FileSystem::getSingleton()._getFreeSpace(path, totalBytes, freeBytes);
105 }
106
107 // ------------------------------------------------------------
108 // Additional functions built on top of OS-specific operations
109 // ------------------------------------------------------------
110
111 9 FileSystem::Status FileSystem::createDirectory(const char* path, bool errorIfAlreadyExists) {
112 9 FW_ASSERT(path != nullptr);
113 9 Status status = Status::OP_OK;
114
1/1
✓ Branch 2 taken 9 times.
9 Os::Directory dir;
115 // If errorIfAlreadyExists is true, use CREATE_EXCLUSIVE mode, otherwise use CREATE_IF_MISSING
116 9 Directory::OpenMode mode =
117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 errorIfAlreadyExists ? Directory::OpenMode::CREATE_EXCLUSIVE : Directory::OpenMode::CREATE_IF_MISSING;
118
1/1
✓ Branch 2 taken 9 times.
9 Directory::Status dirStatus = dir.open(path, mode);
119
1/1
✓ Branch 2 taken 9 times.
9 dir.close();
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (dirStatus != Directory::OP_OK) {
121 return FileSystem::handleDirectoryError(dirStatus);
122 }
123 9 return status;
124 9 }
125
126 FileSystem::Status FileSystem::touch(const char* path) {
127 FW_ASSERT(path != nullptr);
128 Status status = Status::OP_OK;
129 Os::File file;
130 File::Status file_status = file.open(path, Os::File::OPEN_WRITE);
131 file.close();
132 if (file_status != File::OP_OK) {
133 status = FileSystem::handleFileError(file_status);
134 }
135 return status;
136 }
137
138 FileSystem::PathType FileSystem::getPathType(const char* path) {
139 FW_ASSERT(path != nullptr);
140 PathType pathType;
141 Status status = getSingleton()._getPathType(path, pathType);
142 if (status != Status::OP_OK) {
143 return PathType::NOT_EXIST;
144 }
145 return pathType;
146 } // end getPathType
147
148 bool FileSystem::exists(const char* path) {
149 return FileSystem::getPathType(path) != PathType::NOT_EXIST;
150 } // end exists
151
152 FileSystem::Status FileSystem::copyFile(const char* sourcePath, const char* destPath) {
153 FW_ASSERT(sourcePath != nullptr);
154 FW_ASSERT(destPath != nullptr);
155 Os::File source;
156 Os::File destination;
157 Os::File::Status fileStatus = source.open(sourcePath, Os::File::OPEN_READ);
158 if (fileStatus != Os::File::OP_OK) {
159 return FileSystem::handleFileError(fileStatus);
160 }
161 fileStatus = destination.open(destPath, Os::File::OPEN_WRITE);
162 if (fileStatus != Os::File::OP_OK) {
163 return FileSystem::handleFileError(fileStatus);
164 }
165
166 FwSizeType sourceFileSize = 0;
167 FileSystem::Status fs_status = FileSystem::getFileSize(sourcePath, sourceFileSize);
168 if (fs_status != FileSystem::Status::OP_OK) {
169 return fs_status;
170 }
171
172 fs_status = FileSystem::copyFileData(source, destination, sourceFileSize);
173
174 return fs_status;
175 } // end copyFile
176
177 FileSystem::Status FileSystem::appendFile(const char* sourcePath, const char* destPath, bool createMissingDest) {
178 Os::File source;
179 Os::File destination;
180
181 // If requested, check if destination file exists and exit if does not exist
182 const bool destExists = FileSystem::exists(destPath);
183 if (not createMissingDest and not destExists) {
184 return Status::DOESNT_EXIST;
185 }
186
187 Os::File::Status fileStatus = source.open(sourcePath, Os::File::OPEN_READ);
188 if (fileStatus != Os::File::OP_OK) {
189 return FileSystem::handleFileError(fileStatus);
190 }
191 fileStatus = destination.open(destPath, Os::File::OPEN_APPEND);
192 if (fileStatus != Os::File::OP_OK) {
193 return FileSystem::handleFileError(fileStatus);
194 }
195
196 FileSystem::Status fs_status = FileSystem::OP_OK;
197
198 FwSizeType sourceFileSize = 0;
199 fs_status = FileSystem::getFileSize(sourcePath, sourceFileSize);
200 if (fs_status != FileSystem::Status::OP_OK) {
201 return fs_status;
202 }
203
204 fs_status = FileSystem::copyFileData(source, destination, sourceFileSize);
205
206 return fs_status;
207 } // end appendFile
208
209 FileSystem::Status FileSystem::moveFile(const char* source, const char* destination) {
210 Status status = Status::OP_OK;
211
212 // Try to rename the file
213 status = FileSystem::rename(source, destination);
214
215 // If rename fails because of cross-device rename, attempt to copy and remove instead
216 if (status == Status::EXDEV_ERROR) {
217 status = FileSystem::copyFile(source, destination);
218 if (status != Status::OP_OK) {
219 return status;
220 }
221 status = FileSystem::removeFile(source);
222 }
223
224 return status;
225 }
226
227 5 FileSystem::Status FileSystem::getFileSize(const char* path, FwSizeType& size) {
228
1/1
✓ Branch 2 taken 5 times.
5 Os::File file;
229
1/1
✓ Branch 2 taken 5 times.
5 Os::File::Status status = file.open(path, Os::File::OPEN_READ);
230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (status != File::Status::OP_OK) {
231 return FileSystem::handleFileError(status);
232 }
233
1/1
✓ Branch 2 taken 5 times.
5 status = file.size(size);
234
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (status != File::Status::OP_OK) {
235 return FileSystem::handleFileError(status);
236 }
237 5 return FileSystem::OP_OK;
238 5 }
239
240 // ------------------------------------------------------------
241 // Internal helper functions
242 // ------------------------------------------------------------
243
244 FileSystem::Status FileSystem::handleFileError(File::Status fileStatus) {
245 FileSystem::Status status = FileSystem::OTHER_ERROR;
246
247 switch (fileStatus) {
248 case File::NO_SPACE:
249 status = FileSystem::NO_SPACE;
250 break;
251 case File::NO_PERMISSION:
252 status = FileSystem::NO_PERMISSION;
253 break;
254 case File::DOESNT_EXIST:
255 status = FileSystem::DOESNT_EXIST;
256 break;
257 default:
258 status = FileSystem::OTHER_ERROR;
259 }
260 return status;
261 } // end handleFileError
262
263 FileSystem::Status FileSystem::handleDirectoryError(Directory::Status dirStatus) {
264 FileSystem::Status status = FileSystem::OTHER_ERROR;
265
266 switch (dirStatus) {
267 case Directory::DOESNT_EXIST:
268 status = FileSystem::DOESNT_EXIST;
269 break;
270 case Directory::NO_PERMISSION:
271 status = FileSystem::NO_PERMISSION;
272 break;
273 case Directory::ALREADY_EXISTS:
274 status = FileSystem::ALREADY_EXISTS;
275 break;
276 case Directory::NOT_SUPPORTED:
277 status = FileSystem::NOT_SUPPORTED;
278 break;
279 default:
280 status = FileSystem::OTHER_ERROR;
281 }
282 return status;
283 } // end handleFileError
284
285 FileSystem::Status FileSystem::copyFileData(File& source, File& destination, FwSizeType size) {
286 static_assert(FILE_SYSTEM_FILE_CHUNK_SIZE != 0, "FILE_SYSTEM_FILE_CHUNK_SIZE must be >0");
287 U8 fileBuffer[FILE_SYSTEM_FILE_CHUNK_SIZE];
288 File::Status file_status;
289
290 FwSizeType copiedSize = 0;
291 FwSizeType chunkSize = FILE_SYSTEM_FILE_CHUNK_SIZE;
292
293 // Loop up to 2 times for each by, bounded to prevent infinite loop
294 const FwSizeType maximum =
295 (size > (std::numeric_limits<FwSizeType>::max() / 2)) ? std::numeric_limits<FwSizeType>::max() : size * 2;
296
297 // Copy the file in chunks - loop until all data is copied
298 FwSizeType i = 0;
299 for (copiedSize = 0; (copiedSize < size) && (i < maximum); i++) {
300 // chunkSize is FILE_SYSTEM_FILE_CHUNK_SIZE unless size-copiedSize is less than that
301 // in which case chunkSize is size-copiedSize, ensuring the last chunk reads the remaining data
302 // static_cast needed to avoid ODR linker issue with static constexpr passed by reference to std::min
303 chunkSize = std::min(static_cast<FwSizeType>(FILE_SYSTEM_FILE_CHUNK_SIZE), size - copiedSize);
304 FwSizeType readSize = chunkSize;
305 file_status = source.read(fileBuffer, readSize, Os::File::WaitType::WAIT);
306 if (file_status != File::OP_OK) {
307 return FileSystem::handleFileError(file_status);
308 }
309 // A zero-byte read means the source ended before the expected size (e.g. truncated mid-copy)
310 if (readSize == 0) {
311 break;
312 }
313 FwSizeType writeSize = readSize;
314 file_status = destination.write(fileBuffer, writeSize, Os::File::WaitType::WAIT);
315 if (file_status != File::OP_OK) {
316 return FileSystem::handleFileError(file_status);
317 }
318 // A short write means the destination did not receive all the data (e.g. device full)
319 if (writeSize != readSize) {
320 return FileSystem::OTHER_ERROR;
321 }
322 copiedSize += writeSize;
323 }
324
325 // A copy that did not transfer the full expected size must not report success
326 if (copiedSize != size) {
327 return FileSystem::OTHER_ERROR;
328 }
329
330 return FileSystem::OP_OK;
331 } // end copyFileData
332
333 } // namespace Os
334