Logo Pico-Framework A web-first embedded framework for C++
Loading...
Searching...
No Matches
FatFsStorageManager.cpp
Go to the documentation of this file.
1
17#include "framework_config.h" // Must be included before DebugTrace.h to ensure framework_config.h is processed first
18#include "DebugTrace.h"
20
21#include "storage/FatFsStorageManager.h"
23#include <ff_utils.h>
24#include <ff_stdio.h>
25
28
31{
32 if (mounted)
33 return true;
34
35 TRACE("Mounting SD card at: %s\n", mountPoint.c_str());
36 mounted = ::mount(mountPoint.c_str());
37
38 if (mounted && !probeMountPoint())
39 {
40 TRACE("SD mount appears successful, but directory listing failed — treating as mount failure\n");
41 mounted = false;
42 }
43
44 if (!mounted)
45 {
46 TRACE("ERROR: SD mount FAILED for %s\n", mountPoint.c_str());
47 }
48 else
49 {
50 TRACE("SD card mounted successfully\n");
51 }
52
53 return mounted;
54}
55
58{
59 ::unmount(mountPoint.c_str());
60 mounted = false;
61 return true;
62}
63
65bool FatFsStorageManager::exists(const std::string &path)
66{
67 TRACE("[FatFs] Checking if path exists: %s\n", path.c_str());
68 if (!ensureMounted())
69 {
70 printf("[FatFs] SD card not mounted — cannot check path exists: %s\n", path.c_str());
71 return false;
72 }
73 TRACE("[FatFs] Checking if exists: %s\n", resolvePath(path).c_str());
74 FF_Stat_t xStat;
75
76 // Use ff_stat to check if the file or directory exists
77 TRACE("[FatFs] Calling ff_stat on path: %s\n",resolvePath(path).c_str());
78 int err = ff_stat(resolvePath(path).c_str(), &xStat);
79 TRACE("[FatFs] ff_stat returned: %d\n", err);
80 if (err == FF_ERR_NONE){
81 TRACE("[FatFs] Path exists: %s, size: %ld bytes\n", resolvePath(path).c_str(), xStat.st_size);
82 return true;
83 }
84 TRACE("Path does not exist: %s\n", path.c_str());
85 return false;
86}
87
89bool FatFsStorageManager::listDirectory(const std::string &path, std::vector<FileInfo> &out)
90{
91 if (!ensureMounted()) {
92 TRACE("SD card not mounted — cannot list directory: %s\n", path.c_str());
93 return false;
94 }
95 TRACE("[FatFs] Listing directory: %s\n", path.c_str());
96 FF_FindData_t xFindStruct{};
97 std::string searchPath = resolvePath(path.empty() ? "/" : path);
98 TRACE("[FatFs] Search path: %s\n", searchPath.c_str());
99 int result = ff_findfirst(searchPath.c_str(), &xFindStruct);
100
101 if (result != FF_ERR_NONE) {
102 printf("[FatFs] ff_findfirst failed with error: %d\n", result);
103 return false;
104 }
105
106 do {
107 if (xFindStruct.pcFileName && strlen(xFindStruct.pcFileName) > 0) {
108 FileInfo info;
109 info.name = xFindStruct.pcFileName;
110 info.isDirectory = xFindStruct.ucAttributes & FF_FAT_ATTR_DIR;
111 info.isReadOnly = xFindStruct.ucAttributes & FF_FAT_ATTR_READONLY;
112 info.size = static_cast<size_t>(xFindStruct.ulFileSize);
113 out.push_back(info);
114 }
115 } while (ff_findnext(&xFindStruct) == FF_ERR_NONE);
116
117 return true;
118}
119
120bool FatFsStorageManager::createDirectory(const std::string &path)
121{
122 if (!ensureMounted())
123 {
124 TRACE("SD card not mounted — cannot create directory: %s\n", path.c_str());
125 return false;
126 }
127 TRACE("[FatFs] Creating directory: %s\n", path.c_str());
128 return ff_mkdir(resolvePath(path).c_str()) == FF_ERR_NONE;
129}
130
131bool FatFsStorageManager::removeDirectory(const std::string &path)
132{
133 if (!ensureMounted())
134 {
135 TRACE("SD card not mounted — cannot remove directory: %s\n", path.c_str());
136 return false;
137 }
138 return ff_rmdir(resolvePath(path).c_str()) == FF_ERR_NONE;
139}
140
142bool FatFsStorageManager::readFile(const std::string &path, std::vector<uint8_t> &buffer)
143{
144 if (!ensureMounted())
145 {
146 TRACE("SD card not mounted — cannot read file: %s\n", path.c_str());
147 return false;
148 }
149 FF_FILE *file = ff_fopen(resolvePath(path).c_str(), "r");
150 if (!file)
151 return false;
152
153 ff_fseek(file, 0, SEEK_END);
154 long fileSize = ff_ftell(file);
155 ff_fseek(file, 0, SEEK_SET);
156
157 if (fileSize < 0)
158 {
159 ff_fclose(file);
160 return false;
161 }
162
163 buffer.resize(fileSize);
164 ff_fread(buffer.data(), 1, fileSize, file);
165 ff_fclose(file);
166 return true;
167}
168
170bool FatFsStorageManager::writeFile(const std::string &path, const std::vector<uint8_t> &data)
171{
172 if (!ensureMounted())
173 {
174 TRACE("SD card not mounted — cannot write to file: %s\n", path.c_str());
175 return false;
176 }
177 FF_FILE *file = ff_fopen(resolvePath(path).c_str(), "w");
178 if (!file)
179 return false;
180
181 ff_fwrite(data.data(), 1, data.size(), file);
182 ff_fclose(file);
183 return true;
184}
185
186bool FatFsStorageManager::writeFile(const std::string& path, const unsigned char* data, size_t size)
187{
188 if (!ensureMounted())
189 {
190 TRACE("SD card not mounted — cannot write to file: %s\n", path.c_str());
191 return false;
192 }
193
194 FF_FILE* file = ff_fopen(resolvePath(path).c_str(), "w");
195 if (!file)
196 return false;
197
198 size_t written = ff_fwrite(data, 1, size, file);
199 ff_fclose(file);
200
201 return (written == size);
202}
203
205bool FatFsStorageManager::remove(const std::string &path)
206{
207 if (!ensureMounted())
208 {
209 TRACE("SD card not mounted — cannot remove file: %s\n", path.c_str());
210 return false;
211 }
212 return ff_remove(resolvePath(path).c_str()) == FF_ERR_NONE;
213}
214
216bool FatFsStorageManager::rename(const std::string &from, const std::string &to)
217{
218 if (!ensureMounted())
219 {
220 TRACE("SD card not mounted — cannot rename file %s to file: %s\n", from.c_str(), to.c_str());
221 return false;
222 }
223 return ff_rename(resolvePath(from).c_str(), resolvePath(to).c_str(), false) == 0;
224}
225
227bool FatFsStorageManager::streamFile(const std::string &path, std::function<void(const uint8_t *, size_t)> chunkCallback)
228{
229 if (!ensureMounted())
230 {
231 TRACE("SD card not mounted — cannot stream file: %s\n", path.c_str());
232 return false;
233 }
234 FF_FILE *file = ff_fopen(resolvePath(path).c_str(), "r");
235 if (!file)
236 return false;
237
238 uint8_t buffer[HTTP_BUFFER_SIZE];
239 size_t bytes;
240 while ((bytes = ff_fread(buffer, 1, sizeof(buffer), file)) > 0)
241 {
242 chunkCallback(buffer, bytes);
243 }
244
245 ff_fclose(file);
246 return true;
247}
248
250size_t FatFsStorageManager::getFileSize(const std::string &path)
251{
252 if (!ensureMounted())
253 {
254 TRACE("SD card not mounted — cannot get file size: %s\n", path.c_str());
255 return false;
256 }
257 FF_FILE *file = ff_fopen(resolvePath(path).c_str(), "r");
258 if (!file)
259 return 0;
260
261 ff_fseek(file, 0, SEEK_END);
262 long size = ff_ftell(file);
263 ff_fclose(file);
264
265 return (size >= 0) ? static_cast<size_t>(size) : 0;
266}
267
269bool FatFsStorageManager::appendToFile(const std::string &path, const uint8_t *data, size_t size)
270{
271 if (!ensureMounted())
272 {
273 TRACE("SD card not mounted — cannot append to file: %s\n", path.c_str());
274 return false;
275 }
276 FF_FILE *file = ff_fopen(resolvePath(path).c_str(), "a");
277 if (!file)
278 return false;
279
280 size_t written = ff_fwrite(data, 1, size, file);
281 ff_fclose(file);
282 return written == size;
283}
284
286std::string FatFsStorageManager::resolvePath(const std::string &path) const
287{
288 std::string fullPath = "/" + mountPoint;
289 if (!path.empty())
290 {
291 if (path[0] != '/')
292 fullPath += "/";
293 fullPath += path;
294 }
295
296 // Normalize duplicate slashes
297 std::string normalized;
298 bool lastWasSlash = false;
299 for (char c : fullPath)
300 {
301 if (c == '/')
302 {
303 if (!lastWasSlash)
304 {
305 normalized += c;
306 lastWasSlash = true;
307 }
308 }
309 else
310 {
311 normalized += c;
312 lastWasSlash = false;
313 }
314 }
315
316 return normalized;
317}
318
320{
321 if (!mounted)
322 {
323 return mount();
324 }
325 return true;
326}
327
329{
330 return mounted;
331}
332
334{
335 if (mounted && !probeMountPoint())
336 {
337 TRACE("SD no longer accessible — marking as unmounted\n");
338 mounted = false;
339 }
340}
341
343{
344 FF_FindData_t xFindStruct = {};
345 std::string root = "/" + mountPoint;
346 int result = ff_findfirst(root.c_str(), &xFindStruct);
347 return result == FF_ERR_NONE;
348}
349
351{
352 if (!mounted) {
353 printf("[FatFs] Cannot format: card not mounted\n");
354 return false;
355 }
356
357 if (!format(mountPoint.c_str())) {
358 printf("[FatFs] Format failed for device: %s\n", mountPoint.c_str());
359 return false;
360 }
361
362 printf("[FatFs] Format successful for device: %s\n", mountPoint.c_str());
363
364 // Optionally re-mount to refresh filesystem state
365 unmount();
366 mount();
367
368 return mounted;
369}
370
371bool FatFsStorageManager::readFileString(const std::string &path, uint32_t startPosition, uint32_t length, std::string &buffer)
372{
373 if (!ensureMounted())
374 {
375 TRACE("SD card not mounted — cannot read file string: %s\n", path.c_str());
376 return false;
377 }
378
379 FF_FILE *file = ff_fopen(resolvePath(path).c_str(), "r");
380 if (!file)
381 {
382 TRACE("Failed to open file: %s\n", path.c_str());
383 return false;
384 }
385
386 ff_fseek(file, 0, SEEK_END);
387 size_t fileSize = ff_ftell(file);
388 if (startPosition >= fileSize)
389 {
390 ff_fclose(file);
391 buffer.clear();
392 return true;
393 }
394
395 size_t readLength = std::min<size_t>(length, fileSize - startPosition);
396 ff_fseek(file, startPosition, SEEK_SET);
397
398 buffer.resize(readLength);
399 size_t bytesRead = ff_fread(buffer.data(), 1, readLength, file);
400 ff_fclose(file);
401
402 if (bytesRead != readLength)
403 {
404 buffer.resize(bytesRead);
405 TRACE("Read only %zu bytes from file: %s\n", bytesRead, path.c_str());
406 }
407
408 return true;
409}
410
411
413
414std::unique_ptr<StorageFileReader> FatFsStorageManager::openReader(const std::string& path) {
415 if (!ensureMounted()) return nullptr;
416
417 auto reader = std::make_unique<FatFsFileReader>();
418 if (!reader->open(resolvePath(path))) return nullptr;
419 return reader;
420}
421
422
Macro-based debug trace system with optional SD file logging.
#define TRACE_INIT(MODULE_NAME)
Declare trace usage in a source file for a given module.
Definition DebugTrace.h:170
#define TRACE(...)
Default trace (INFO level).
Definition DebugTrace.h:187
Concrete implementation of StorageManager using FatFs.
bool rename(const std::string &from, const std::string &to) override
Rename a file or directory.
bool exists(const std::string &path) override
Check if the path exists.
bool formatStorage() override
Format the storage device.
bool unmount() override
Unmount the filesystem.
FatFsStorageManager()
Construct a new FatFsStorageManager object.
bool mount() override
Mount the filesystem at the specified mount point.
bool createDirectory(const std::string &path) override
Create a directory at the specified path.
bool writeFile(const std::string &path, const std::vector< uint8_t > &data) override
Write a memory buffer to a file.
bool mounted
Indicates if the filesystem is currently mounted.
bool readFileString(const std::string &path, uint32_t startPosition, uint32_t length, std::string &buffer)
Read a file string into a std::string.
bool streamFile(const std::string &path, std::function< void(const uint8_t *, size_t)> chunkCallback) override
Stream a file in chunks via callback.
std::string mountPoint
Default mount point.
size_t getFileSize(const std::string &path) override
Get the size of a file.
bool appendToFile(const std::string &path, const uint8_t *data, size_t size) override
Append data to a file.
bool removeDirectory(const std::string &path) override
Remove a directory at the specified path.
bool listDirectory(const std::string &path, std::vector< FileInfo > &out) override
List the contents of a directory.
std::string resolvePath(const std::string &path) const
Helper function to normalize full path based on mountPoint and relative path.
bool isMounted() const override
Check if the filesystem is currently mounted.
bool readFile(const std::string &path, std::vector< uint8_t > &buffer) override
Read a file into a memory buffer.
bool remove(const std::string &path) override
Remove a file or directory at the specified path.
std::unique_ptr< StorageFileReader > openReader(const std::string &path) override
open a file for streaming read access.
Delegates to user or system configuration.
#define HTTP_BUFFER_SIZE
Size of the HTTP buffer for request/response data.
Structure representing metadata for a file or directory.
bool isDirectory
True if item is a directory.
size_t size
Size in bytes.
bool isReadOnly
True if item is read-only.
std::string name
File or directory name.