GCC Code Coverage Report


Directory: ./
File: Os/FileSystem.cpp
Date: 2026-09-23 22:11:34
Exec Total Coverage
Lines: 43 201 21.4%
Functions: 11 29 37.9%
Branches: 12 93 12.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 1 taken 16 times.
✓ Branch 2 taken 1 times.
✓ Branch 4 taken 1 times.
17 FileSystem::FileSystem() : m_handle_storage(), m_delegate(*FileSystemInterface::getDelegate(m_handle_storage)) {
12 1 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
13 1 }
14
15 2 FileSystem::~FileSystem() {
16 2 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
17 2 m_delegate.~FileSystemInterface();
18 2 }
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 ✗ FileSystem::Status FileSystem::_removeDirectory(const char* path) {
26 ✗ FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
27 ✗ FW_ASSERT(path != nullptr);
28 ✗ return this->m_delegate._removeDirectory(path);
29 }
30
31 1 FileSystem::Status FileSystem::_removeFile(const char* path) {
32 1 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
33 1 FW_ASSERT(path != nullptr);
34 1 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 11 FileSystem::Status FileSystem::_getWorkingDirectory(char* path, FwSizeType bufferSize) {
51 11 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
52 11 FW_ASSERT(path != nullptr);
53 11 FW_ASSERT(bufferSize > 0); // because bufferSize=0 would trigger a malloc in some implementations (e.g. Posix)
54 11 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 241 FileSystem::Status FileSystem::_getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) {
64 241 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileSystemInterface*>(&this->m_handle_storage[0]));
65 241 FW_ASSERT(path != nullptr);
66 241 return this->m_delegate._getFreeSpace(path, totalBytes, freeBytes);
67 }
68
69 1 void FileSystem::init() {
70 // Force trigger on the fly singleton setup
71 1 (void)FileSystem::getSingleton();
72 1 }
73
74 254 FileSystem& FileSystem::getSingleton() {
75
4/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 253 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
254 static FileSystem s_singleton;
76 254 return s_singleton;
77 }
78
79 // ------------------------------------------------------------
80 // Static functions calling implementation-specific operations
81 // ------------------------------------------------------------
82
83 ✗ FileSystem::Status FileSystem::removeDirectory(const char* path) {
84 ✗ return FileSystem::getSingleton()._removeDirectory(path);
85 }
86
87 1 FileSystem::Status FileSystem::removeFile(const char* path) {
88 1 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 11 FileSystem::Status FileSystem::getWorkingDirectory(char* path, FwSizeType bufferSize) {
96 11 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 241 FileSystem::Status FileSystem::getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) {
104 241 return FileSystem::getSingleton()._getFreeSpace(path, totalBytes, freeBytes);
105 }
106
107 // ------------------------------------------------------------
108 // Additional functions built on top of OS-specific operations
109 // ------------------------------------------------------------
110
111 1 FileSystem::Status FileSystem::createDirectory(const char* path, bool errorIfAlreadyExists) {
112 1 FW_ASSERT(path != nullptr);
113 1 Status status = Status::OP_OK;
114
1/1
✓ Branch 1 taken 1 times.
1 Os::Directory dir;
115 // If errorIfAlreadyExists is true, use CREATE_EXCLUSIVE mode, otherwise use CREATE_IF_MISSING
116 1 Directory::OpenMode mode =
117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 errorIfAlreadyExists ? Directory::OpenMode::CREATE_EXCLUSIVE : Directory::OpenMode::CREATE_IF_MISSING;
118
1/1
✓ Branch 1 taken 1 times.
1 Directory::Status dirStatus = dir.open(path, mode);
119
1/1
✓ Branch 1 taken 1 times.
1 dir.close();
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (dirStatus != Directory::OP_OK) {
121 ✗ return FileSystem::handleDirectoryError(dirStatus);
122 }
123 1 return status;
124 1 }
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 ✗ FileSystem::Status FileSystem::getFileSize(const char* path, FwSizeType& size) {
228 ✗ Os::File file;
229 ✗ Os::File::Status status = file.open(path, Os::File::OPEN_READ);
230 ✗ if (status != File::Status::OP_OK) {
231 ✗ return FileSystem::handleFileError(status);
232 }
233 ✗ status = file.size(size);
234 ✗ if (status != File::Status::OP_OK) {
235 ✗ return FileSystem::handleFileError(status);
236 }
237 ✗ return FileSystem::OP_OK;
238 ✗ }
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