GCC Code Coverage Report


Directory: ./
File: Os/FileSystem.cpp
Date: 2026-09-23 21:11:01
Exec Total Coverage
Lines: 169 203 83.3%
Functions: 29 29 100.0%
Branches: 67 95 70.5%

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 352 times.
✓ Branch 11 taken 22 times.
✓ Branch 17 taken 22 times.
374 FileSystem::FileSystem() : m_handle_storage(), m_delegate(*FileSystemInterface::getDelegate(m_handle_storage)) {
12 22 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
13 22 }
14
15 44 FileSystem::~FileSystem() {
16 44 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
17 44 m_delegate.~FileSystemInterface();
18 44 }
19
20 1 FileSystemHandle* FileSystem::getHandle() {
21 1 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
22 1 return this->m_delegate.getHandle();
23 }
24
25 184 FileSystem::Status FileSystem::_removeDirectory(const char* path) {
26 184 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
27 184 FW_ASSERT(path != nullptr);
28 184 return this->m_delegate._removeDirectory(path);
29 }
30
31 13491 FileSystem::Status FileSystem::_removeFile(const char* path) {
32 13491 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
33 13491 FW_ASSERT(path != nullptr);
34 13491 return this->m_delegate._removeFile(path);
35 }
36
37 144 FileSystem::Status FileSystem::_rename(const char* sourcePath, const char* destPath) {
38 144 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
39 144 FW_ASSERT(sourcePath != nullptr);
40 144 FW_ASSERT(destPath != nullptr);
41 144 return this->m_delegate._rename(sourcePath, destPath);
42 }
43
44 1821 FileSystem::Status FileSystem::_getPathType(const char* path, PathType& pathType) {
45 1821 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
46 1821 FW_ASSERT(path != nullptr);
47 1821 return this->m_delegate._getPathType(path, pathType);
48 }
49
50 209 FileSystem::Status FileSystem::_getWorkingDirectory(char* path, FwSizeType bufferSize) {
51 209 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
52 209 FW_ASSERT(path != nullptr);
53 209 FW_ASSERT(bufferSize > 0); // because bufferSize=0 would trigger a malloc in some implementations (e.g. Posix)
54 209 return this->m_delegate._getWorkingDirectory(path, bufferSize);
55 }
56
57 149 FileSystem::Status FileSystem::_changeWorkingDirectory(const char* path) {
58 149 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
59 149 FW_ASSERT(path != nullptr);
60 149 return this->m_delegate._changeWorkingDirectory(path);
61 }
62
63 62 FileSystem::Status FileSystem::_getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) {
64 62 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
65 62 FW_ASSERT(path != nullptr);
66 62 return this->m_delegate._getFreeSpace(path, totalBytes, freeBytes);
67 }
68
69 3 void FileSystem::init() {
70 // Force trigger on the fly singleton setup
71 3 (void)FileSystem::getSingleton();
72 3 }
73
74 18904 FileSystem& FileSystem::getSingleton() {
75
4/7
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 18882 times.
✓ Branch 3 taken 22 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 22 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
18904 static FileSystem s_singleton;
76 18904 return s_singleton;
77 }
78
79 // ------------------------------------------------------------
80 // Static functions calling implementation-specific operations
81 // ------------------------------------------------------------
82
83 184 FileSystem::Status FileSystem::removeDirectory(const char* path) {
84 184 return FileSystem::getSingleton()._removeDirectory(path);
85 }
86
87 13491 FileSystem::Status FileSystem::removeFile(const char* path) {
88 13491 return FileSystem::getSingleton()._removeFile(path);
89 }
90
91 144 FileSystem::Status FileSystem::rename(const char* sourcePath, const char* destPath) {
92 144 return FileSystem::getSingleton()._rename(sourcePath, destPath);
93 }
94
95 209 FileSystem::Status FileSystem::getWorkingDirectory(char* path, FwSizeType bufferSize) {
96 209 return FileSystem::getSingleton()._getWorkingDirectory(path, bufferSize);
97 }
98
99 149 FileSystem::Status FileSystem::changeWorkingDirectory(const char* path) {
100 149 return FileSystem::getSingleton()._changeWorkingDirectory(path);
101 }
102
103 62 FileSystem::Status FileSystem::getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) {
104 62 return FileSystem::getSingleton()._getFreeSpace(path, totalBytes, freeBytes);
105 }
106
107 // ------------------------------------------------------------
108 // Additional functions built on top of OS-specific operations
109 // ------------------------------------------------------------
110
111 355 FileSystem::Status FileSystem::createDirectory(const char* path, bool errorIfAlreadyExists) {
112 355 FW_ASSERT(path != nullptr);
113 355 Status status = Status::OP_OK;
114
1/1
✓ Branch 2 taken 355 times.
355 Os::Directory dir;
115 // If errorIfAlreadyExists is true, use CREATE_EXCLUSIVE mode, otherwise use CREATE_IF_MISSING
116 355 Directory::OpenMode mode =
117
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 351 times.
355 errorIfAlreadyExists ? Directory::OpenMode::CREATE_EXCLUSIVE : Directory::OpenMode::CREATE_IF_MISSING;
118
1/1
✓ Branch 2 taken 355 times.
355 Directory::Status dirStatus = dir.open(path, mode);
119
1/1
✓ Branch 2 taken 355 times.
355 dir.close();
120
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 354 times.
355 if (dirStatus != Directory::OP_OK) {
121 1 return FileSystem::handleDirectoryError(dirStatus);
122 }
123 354 return status;
124 355 }
125
126 70 FileSystem::Status FileSystem::touch(const char* path) {
127 70 FW_ASSERT(path != nullptr);
128 70 Status status = Status::OP_OK;
129
1/1
✓ Branch 2 taken 70 times.
70 Os::File file;
130
1/1
✓ Branch 2 taken 70 times.
70 File::Status file_status = file.open(path, Os::File::OPEN_WRITE);
131
1/1
✓ Branch 2 taken 70 times.
70 file.close();
132
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (file_status != File::OP_OK) {
133 ✗ status = FileSystem::handleFileError(file_status);
134 }
135 70 return status;
136 70 }
137
138 1821 FileSystem::PathType FileSystem::getPathType(const char* path) {
139 1821 FW_ASSERT(path != nullptr);
140 1821 PathType pathType;
141
2/2
✓ Branch 1 taken 1821 times.
✓ Branch 7 taken 1821 times.
1821 Status status = getSingleton()._getPathType(path, pathType);
142
2/2
✓ Branch 0 taken 702 times.
✓ Branch 1 taken 1119 times.
1821 if (status != Status::OP_OK) {
143 702 return PathType::NOT_EXIST;
144 }
145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1119 times.
1119 return pathType;
146 } // end getPathType
147
148 1804 bool FileSystem::exists(const char* path) {
149 1804 return FileSystem::getPathType(path) != PathType::NOT_EXIST;
150 } // end exists
151
152 261 FileSystem::Status FileSystem::copyFile(const char* sourcePath, const char* destPath) {
153 261 FW_ASSERT(sourcePath != nullptr);
154 261 FW_ASSERT(destPath != nullptr);
155
1/1
✓ Branch 2 taken 261 times.
261 Os::File source;
156
1/1
✓ Branch 2 taken 261 times.
261 Os::File destination;
157
1/1
✓ Branch 2 taken 261 times.
261 Os::File::Status fileStatus = source.open(sourcePath, Os::File::OPEN_READ);
158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 261 times.
261 if (fileStatus != Os::File::OP_OK) {
159 ✗ return FileSystem::handleFileError(fileStatus);
160 }
161
1/1
✓ Branch 2 taken 261 times.
261 fileStatus = destination.open(destPath, Os::File::OPEN_WRITE);
162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 261 times.
261 if (fileStatus != Os::File::OP_OK) {
163 ✗ return FileSystem::handleFileError(fileStatus);
164 }
165
166 261 FwSizeType sourceFileSize = 0;
167
1/1
✓ Branch 1 taken 261 times.
261 FileSystem::Status fs_status = FileSystem::getFileSize(sourcePath, sourceFileSize);
168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 261 times.
261 if (fs_status != FileSystem::Status::OP_OK) {
169 ✗ return fs_status;
170 }
171
172
1/1
✓ Branch 1 taken 261 times.
261 fs_status = FileSystem::copyFileData(source, destination, sourceFileSize);
173
174 261 return fs_status;
175 261 } // end copyFile
176
177 145 FileSystem::Status FileSystem::appendFile(const char* sourcePath, const char* destPath, bool createMissingDest) {
178
1/1
✓ Branch 2 taken 145 times.
145 Os::File source;
179
1/1
✓ Branch 2 taken 145 times.
145 Os::File destination;
180
181 // If requested, check if destination file exists and exit if does not exist
182
1/1
✓ Branch 1 taken 145 times.
145 const bool destExists = FileSystem::exists(destPath);
183
3/4
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 61 times.
145 if (not createMissingDest and not destExists) {
184 ✗ return Status::DOESNT_EXIST;
185 }
186
187
1/1
✓ Branch 2 taken 145 times.
145 Os::File::Status fileStatus = source.open(sourcePath, Os::File::OPEN_READ);
188
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 144 times.
145 if (fileStatus != Os::File::OP_OK) {
189 1 return FileSystem::handleFileError(fileStatus);
190 }
191
1/1
✓ Branch 2 taken 144 times.
144 fileStatus = destination.open(destPath, Os::File::OPEN_APPEND);
192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 if (fileStatus != Os::File::OP_OK) {
193 ✗ return FileSystem::handleFileError(fileStatus);
194 }
195
196 144 FileSystem::Status fs_status = FileSystem::OP_OK;
197
198 144 FwSizeType sourceFileSize = 0;
199
1/1
✓ Branch 1 taken 144 times.
144 fs_status = FileSystem::getFileSize(sourcePath, sourceFileSize);
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 if (fs_status != FileSystem::Status::OP_OK) {
201 ✗ return fs_status;
202 }
203
204
1/1
✓ Branch 1 taken 144 times.
144 fs_status = FileSystem::copyFileData(source, destination, sourceFileSize);
205
206 144 return fs_status;
207 145 } // end appendFile
208
209 77 FileSystem::Status FileSystem::moveFile(const char* source, const char* destination) {
210 77 Status status = Status::OP_OK;
211
212 // Try to rename the file
213 77 status = FileSystem::rename(source, destination);
214
215 // If rename fails because of cross-device rename, attempt to copy and remove instead
216
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 77 times.
77 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 77 return status;
225 }
226
227 7045 FileSystem::Status FileSystem::getFileSize(const char* path, FwSizeType& size) {
228
1/1
✓ Branch 2 taken 7045 times.
7045 Os::File file;
229
1/1
✓ Branch 2 taken 7045 times.
7045 Os::File::Status status = file.open(path, Os::File::OPEN_READ);
230
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 7015 times.
7045 if (status != File::Status::OP_OK) {
231 30 return FileSystem::handleFileError(status);
232 }
233
1/1
✓ Branch 2 taken 7015 times.
7015 status = file.size(size);
234
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7015 times.
7015 if (status != File::Status::OP_OK) {
235 ✗ return FileSystem::handleFileError(status);
236 }
237 7015 return FileSystem::OP_OK;
238 7045 }
239
240 // ------------------------------------------------------------
241 // Internal helper functions
242 // ------------------------------------------------------------
243
244 31 FileSystem::Status FileSystem::handleFileError(File::Status fileStatus) {
245 31 FileSystem::Status status = FileSystem::OTHER_ERROR;
246
247
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 3 times.
31 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 28 case File::DOESNT_EXIST:
255 28 status = FileSystem::DOESNT_EXIST;
256 28 break;
257 3 default:
258 3 status = FileSystem::OTHER_ERROR;
259 }
260 31 return status;
261 } // end handleFileError
262
263 1 FileSystem::Status FileSystem::handleDirectoryError(Directory::Status dirStatus) {
264 1 FileSystem::Status status = FileSystem::OTHER_ERROR;
265
266
1/5
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1 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 1 case Directory::ALREADY_EXISTS:
274 1 status = FileSystem::ALREADY_EXISTS;
275 1 break;
276 ✗ case Directory::NOT_SUPPORTED:
277 ✗ status = FileSystem::NOT_SUPPORTED;
278 ✗ break;
279 ✗ default:
280 ✗ status = FileSystem::OTHER_ERROR;
281 }
282 1 return status;
283 } // end handleFileError
284
285 405 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 405 U8 fileBuffer[FILE_SYSTEM_FILE_CHUNK_SIZE];
288 File::Status file_status;
289
290 405 FwSizeType copiedSize = 0;
291 405 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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 405 times.
405 (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 405 FwSizeType i = 0;
299
3/4
✓ Branch 0 taken 330 times.
✓ Branch 1 taken 405 times.
✓ Branch 2 taken 330 times.
✗ Branch 3 not taken.
735 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 330 chunkSize = std::min(static_cast<FwSizeType>(FILE_SYSTEM_FILE_CHUNK_SIZE), size - copiedSize);
304 330 FwSizeType readSize = chunkSize;
305
1/1
✓ Branch 4 taken 330 times.
330 file_status = source.read(fileBuffer, readSize, Os::File::WaitType::WAIT);
306
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 330 times.
330 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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 330 times.
330 if (readSize == 0) {
311 ✗ break;
312 }
313 330 FwSizeType writeSize = readSize;
314
1/1
✓ Branch 4 taken 330 times.
330 file_status = destination.write(fileBuffer, writeSize, Os::File::WaitType::WAIT);
315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 330 times.
330 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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 330 times.
330 if (writeSize != readSize) {
320 ✗ return FileSystem::OTHER_ERROR;
321 }
322 330 copiedSize += writeSize;
323 }
324
325 // A copy that did not transfer the full expected size must not report success
326
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 405 times.
405 if (copiedSize != size) {
327 ✗ return FileSystem::OTHER_ERROR;
328 }
329
330 405 return FileSystem::OP_OK;
331 } // end copyFileData
332
333 } // namespace Os
334