GCC Code Coverage Report


Directory: ./
File: Os/FileSystem.cpp
Date: 2026-09-03 21:13:48
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 320 times.
✓ Branch 11 taken 20 times.
✓ Branch 17 taken 20 times.
340 FileSystem::FileSystem() : m_handle_storage(), m_delegate(*FileSystemInterface::getDelegate(m_handle_storage)) {
12 20 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
13 20 }
14
15 40 FileSystem::~FileSystem() {
16 40 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
17 40 m_delegate.~FileSystemInterface();
18 40 }
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 202 FileSystem::Status FileSystem::_removeDirectory(const char* path) {
26 202 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
27 202 FW_ASSERT(path != nullptr);
28 202 return this->m_delegate._removeDirectory(path);
29 }
30
31 12914 FileSystem::Status FileSystem::_removeFile(const char* path) {
32 12914 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
33 12914 FW_ASSERT(path != nullptr);
34 12914 return this->m_delegate._removeFile(path);
35 }
36
37 147 FileSystem::Status FileSystem::_rename(const char* sourcePath, const char* destPath) {
38 147 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
39 147 FW_ASSERT(sourcePath != nullptr);
40 147 FW_ASSERT(destPath != nullptr);
41 147 return this->m_delegate._rename(sourcePath, destPath);
42 }
43
44 1881 FileSystem::Status FileSystem::_getPathType(const char* path, PathType& pathType) {
45 1881 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
46 1881 FW_ASSERT(path != nullptr);
47 1881 return this->m_delegate._getPathType(path, pathType);
48 }
49
50 149 FileSystem::Status FileSystem::_getWorkingDirectory(char* path, FwSizeType bufferSize) {
51 149 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
52 149 FW_ASSERT(path != nullptr);
53 149 FW_ASSERT(bufferSize > 0); // because bufferSize=0 would trigger a malloc in some implementations (e.g. Posix)
54 149 return this->m_delegate._getWorkingDirectory(path, bufferSize);
55 }
56
57 129 FileSystem::Status FileSystem::_changeWorkingDirectory(const char* path) {
58 129 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
59 129 FW_ASSERT(path != nullptr);
60 129 return this->m_delegate._changeWorkingDirectory(path);
61 }
62
63 76 FileSystem::Status FileSystem::_getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) {
64 76 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
65 76 FW_ASSERT(path != nullptr);
66 76 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 18367 FileSystem& FileSystem::getSingleton() {
75
4/7
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 18347 times.
✓ Branch 3 taken 20 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 20 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
18367 static FileSystem s_singleton;
76 18367 return s_singleton;
77 }
78
79 // ------------------------------------------------------------
80 // Static functions calling implementation-specific operations
81 // ------------------------------------------------------------
82
83 202 FileSystem::Status FileSystem::removeDirectory(const char* path) {
84 202 return FileSystem::getSingleton()._removeDirectory(path);
85 }
86
87 12914 FileSystem::Status FileSystem::removeFile(const char* path) {
88 12914 return FileSystem::getSingleton()._removeFile(path);
89 }
90
91 147 FileSystem::Status FileSystem::rename(const char* sourcePath, const char* destPath) {
92 147 return FileSystem::getSingleton()._rename(sourcePath, destPath);
93 }
94
95 149 FileSystem::Status FileSystem::getWorkingDirectory(char* path, FwSizeType bufferSize) {
96 149 return FileSystem::getSingleton()._getWorkingDirectory(path, bufferSize);
97 }
98
99 129 FileSystem::Status FileSystem::changeWorkingDirectory(const char* path) {
100 129 return FileSystem::getSingleton()._changeWorkingDirectory(path);
101 }
102
103 76 FileSystem::Status FileSystem::getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) {
104 76 return FileSystem::getSingleton()._getFreeSpace(path, totalBytes, freeBytes);
105 }
106
107 // ------------------------------------------------------------
108 // Additional functions built on top of OS-specific operations
109 // ------------------------------------------------------------
110
111 370 FileSystem::Status FileSystem::createDirectory(const char* path, bool errorIfAlreadyExists) {
112 370 FW_ASSERT(path != nullptr);
113 370 Status status = Status::OP_OK;
114
1/1
✓ Branch 2 taken 370 times.
370 Os::Directory dir;
115 // If errorIfAlreadyExists is true, use CREATE_EXCLUSIVE mode, otherwise use CREATE_IF_MISSING
116 370 Directory::OpenMode mode =
117
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 368 times.
370 errorIfAlreadyExists ? Directory::OpenMode::CREATE_EXCLUSIVE : Directory::OpenMode::CREATE_IF_MISSING;
118
1/1
✓ Branch 2 taken 370 times.
370 Directory::Status dirStatus = dir.open(path, mode);
119
1/1
✓ Branch 2 taken 370 times.
370 dir.close();
120
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 369 times.
370 if (dirStatus != Directory::OP_OK) {
121 1 return FileSystem::handleDirectoryError(dirStatus);
122 }
123 369 return status;
124 370 }
125
126 80 FileSystem::Status FileSystem::touch(const char* path) {
127 80 FW_ASSERT(path != nullptr);
128 80 Status status = Status::OP_OK;
129
1/1
✓ Branch 2 taken 80 times.
80 Os::File file;
130
1/1
✓ Branch 2 taken 80 times.
80 File::Status file_status = file.open(path, Os::File::OPEN_WRITE);
131
1/1
✓ Branch 2 taken 80 times.
80 file.close();
132
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if (file_status != File::OP_OK) {
133 status = FileSystem::handleFileError(file_status);
134 }
135 80 return status;
136 80 }
137
138 1881 FileSystem::PathType FileSystem::getPathType(const char* path) {
139 1881 FW_ASSERT(path != nullptr);
140 1881 PathType pathType;
141
2/2
✓ Branch 1 taken 1881 times.
✓ Branch 7 taken 1881 times.
1881 Status status = getSingleton()._getPathType(path, pathType);
142
2/2
✓ Branch 0 taken 694 times.
✓ Branch 1 taken 1187 times.
1881 if (status != Status::OP_OK) {
143 694 return PathType::NOT_EXIST;
144 }
145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1187 times.
1187 return pathType;
146 } // end getPathType
147
148 1864 bool FileSystem::exists(const char* path) {
149 1864 return FileSystem::getPathType(path) != PathType::NOT_EXIST;
150 } // end exists
151
152 65 FileSystem::Status FileSystem::copyFile(const char* sourcePath, const char* destPath) {
153 65 FW_ASSERT(sourcePath != nullptr);
154 65 FW_ASSERT(destPath != nullptr);
155
1/1
✓ Branch 2 taken 65 times.
65 Os::File source;
156
1/1
✓ Branch 2 taken 65 times.
65 Os::File destination;
157
1/1
✓ Branch 2 taken 65 times.
65 Os::File::Status fileStatus = source.open(sourcePath, Os::File::OPEN_READ);
158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65 times.
65 if (fileStatus != Os::File::OP_OK) {
159 return FileSystem::handleFileError(fileStatus);
160 }
161
1/1
✓ Branch 2 taken 65 times.
65 fileStatus = destination.open(destPath, Os::File::OPEN_WRITE);
162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65 times.
65 if (fileStatus != Os::File::OP_OK) {
163 return FileSystem::handleFileError(fileStatus);
164 }
165
166 65 FwSizeType sourceFileSize = 0;
167
1/1
✓ Branch 1 taken 65 times.
65 FileSystem::Status fs_status = FileSystem::getFileSize(sourcePath, sourceFileSize);
168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65 times.
65 if (fs_status != FileSystem::Status::OP_OK) {
169 return fs_status;
170 }
171
172
1/1
✓ Branch 1 taken 65 times.
65 fs_status = FileSystem::copyFileData(source, destination, sourceFileSize);
173
174 65 return fs_status;
175 65 } // end copyFile
176
177 153 FileSystem::Status FileSystem::appendFile(const char* sourcePath, const char* destPath, bool createMissingDest) {
178
1/1
✓ Branch 2 taken 153 times.
153 Os::File source;
179
1/1
✓ Branch 2 taken 153 times.
153 Os::File destination;
180
181 // If requested, check if destination file exists and exit if does not exist
182
1/1
✓ Branch 1 taken 153 times.
153 const bool destExists = FileSystem::exists(destPath);
183
3/4
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 79 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 74 times.
153 if (not createMissingDest and not destExists) {
184 return Status::DOESNT_EXIST;
185 }
186
187
1/1
✓ Branch 2 taken 153 times.
153 Os::File::Status fileStatus = source.open(sourcePath, Os::File::OPEN_READ);
188
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 152 times.
153 if (fileStatus != Os::File::OP_OK) {
189 1 return FileSystem::handleFileError(fileStatus);
190 }
191
1/1
✓ Branch 2 taken 152 times.
152 fileStatus = destination.open(destPath, Os::File::OPEN_APPEND);
192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
152 if (fileStatus != Os::File::OP_OK) {
193 return FileSystem::handleFileError(fileStatus);
194 }
195
196 152 FileSystem::Status fs_status = FileSystem::OP_OK;
197
198 152 FwSizeType sourceFileSize = 0;
199
1/1
✓ Branch 1 taken 152 times.
152 fs_status = FileSystem::getFileSize(sourcePath, sourceFileSize);
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
152 if (fs_status != FileSystem::Status::OP_OK) {
201 return fs_status;
202 }
203
204
1/1
✓ Branch 1 taken 152 times.
152 fs_status = FileSystem::copyFileData(source, destination, sourceFileSize);
205
206 152 return fs_status;
207 153 } // end appendFile
208
209 80 FileSystem::Status FileSystem::moveFile(const char* source, const char* destination) {
210 80 Status status = Status::OP_OK;
211
212 // Try to rename the file
213 80 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 80 times.
80 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 80 return status;
225 }
226
227 6718 FileSystem::Status FileSystem::getFileSize(const char* path, FwSizeType& size) {
228
1/1
✓ Branch 2 taken 6718 times.
6718 Os::File file;
229
1/1
✓ Branch 2 taken 6718 times.
6718 Os::File::Status status = file.open(path, Os::File::OPEN_READ);
230
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 6688 times.
6718 if (status != File::Status::OP_OK) {
231 30 return FileSystem::handleFileError(status);
232 }
233
1/1
✓ Branch 2 taken 6688 times.
6688 status = file.size(size);
234
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6688 times.
6688 if (status != File::Status::OP_OK) {
235 return FileSystem::handleFileError(status);
236 }
237 6688 return FileSystem::OP_OK;
238 6718 }
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 217 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 217 U8 fileBuffer[FILE_SYSTEM_FILE_CHUNK_SIZE];
288 File::Status file_status;
289
290 217 FwSizeType copiedSize = 0;
291 217 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 217 times.
217 (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 217 FwSizeType i = 0;
299
3/4
✓ Branch 0 taken 163 times.
✓ Branch 1 taken 217 times.
✓ Branch 2 taken 163 times.
✗ Branch 3 not taken.
380 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 163 chunkSize = std::min(static_cast<FwSizeType>(FILE_SYSTEM_FILE_CHUNK_SIZE), size - copiedSize);
304 163 FwSizeType readSize = chunkSize;
305
1/1
✓ Branch 4 taken 163 times.
163 file_status = source.read(fileBuffer, readSize, Os::File::WaitType::WAIT);
306
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 163 times.
163 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 163 times.
163 if (readSize == 0) {
311 break;
312 }
313 163 FwSizeType writeSize = readSize;
314
1/1
✓ Branch 4 taken 163 times.
163 file_status = destination.write(fileBuffer, writeSize, Os::File::WaitType::WAIT);
315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 163 times.
163 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 163 times.
163 if (writeSize != readSize) {
320 return FileSystem::OTHER_ERROR;
321 }
322 163 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 217 times.
217 if (copiedSize != size) {
327 return FileSystem::OTHER_ERROR;
328 }
329
330 217 return FileSystem::OP_OK;
331 } // end copyFileData
332
333 } // namespace Os
334