Skip to content

Commit aa0b7ae

Browse files
Add time to filesystems, new API calls
Support the ESP32 File::getLastWrite() call and setting the time on all filesystems automatically (assuming the system clock has been set properly and time(NULL) returns the proper time!). Adds Dir::fileTime() to get the time of a file being listed, similar to Dir::fileName() and Dir::fileSize(). Adds ::setTimeCallback(time_t (*cb)()) to File, Dir, and FS, allowing users to override the default timestamp on a per-file, directory, or filesystem basis. By default, a simple callback returning time(nullptr) is implemented. LittleFS uses the 't' attribute and should be backwards compatible. SD/SDFS work and include wrappers for obsolete SdFat timestamp callbacks using the MSDOS time. SPIFFS has been updated to support either the existing on-flash format without any timestamps, or a new on-flash format with a flag area and a timestamp metadata element added. SPIFFS will try and mount any existing filesystem as non-timestamp, and continue to write them in the old format. New filesystems may be formatted to include timestamps (default) or in the old format, configurable via the SPIFFS.setConfig call. The SPIFFS work took as starting point all of work of @luc-github in #3730 and extended it into a backwards compatible mode by doing much surgery on the SPIFFS library itself and the on-flash format. Includes an updated SD/listfiles and SPIFFS_time example.
1 parent d968435 commit aa0b7ae

File tree

19 files changed

+784
-94
lines changed

19 files changed

+784
-94
lines changed

cores/esp8266/FS.cpp

+47-3
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,19 @@ String File::readString()
180180
return ret;
181181
}
182182

183+
time_t File::getLastWrite() {
184+
if (!_p)
185+
return 0;
186+
187+
return _p->getLastWrite();
188+
}
189+
190+
void File::setTimeCallback(time_t (*cb)(void)) {
191+
if (!_p)
192+
return;
193+
_p->setTimeCallback(cb);
194+
}
195+
183196
File Dir::openFile(const char* mode) {
184197
if (!_impl) {
185198
return File();
@@ -192,7 +205,9 @@ File Dir::openFile(const char* mode) {
192205
return File();
193206
}
194207

195-
return File(_impl->openFile(om, am), _baseFS);
208+
auto f = File(_impl->openFile(om, am), _baseFS);
209+
f.setTimeCallback(timeCallback);
210+
return f;
196211
}
197212

198213
String Dir::fileName() {
@@ -203,6 +218,12 @@ String Dir::fileName() {
203218
return _impl->fileName();
204219
}
205220

221+
time_t Dir::fileTime() {
222+
if (!_impl)
223+
return 0;
224+
return _impl->fileTime();
225+
}
226+
206227
size_t Dir::fileSize() {
207228
if (!_impl) {
208229
return 0;
@@ -241,6 +262,20 @@ bool Dir::rewind() {
241262
return _impl->rewind();
242263
}
243264

265+
time_t Dir::getLastWrite() {
266+
if (!_impl)
267+
return 0;
268+
269+
return _impl->getLastWrite();
270+
}
271+
272+
void Dir::setTimeCallback(time_t (*cb)(void)) {
273+
if (!_impl)
274+
return;
275+
_impl->setTimeCallback(cb);
276+
}
277+
278+
244279
bool FS::setConfig(const FSConfig &cfg) {
245280
if (!_impl) {
246281
return false;
@@ -308,7 +343,9 @@ File FS::open(const char* path, const char* mode) {
308343
DEBUGV("FS::open: invalid mode `%s`\r\n", mode);
309344
return File();
310345
}
311-
return File(_impl->open(path, om, am), this);
346+
auto f = File(_impl->open(path, om, am), this);
347+
f.setTimeCallback(timeCallback);
348+
return f;
312349
}
313350

314351
bool FS::exists(const char* path) {
@@ -327,7 +364,9 @@ Dir FS::openDir(const char* path) {
327364
return Dir();
328365
}
329366
DirImplPtr p = _impl->openDir(path);
330-
return Dir(p, this);
367+
auto d = Dir(p, this);
368+
d.setTimeCallback(timeCallback);
369+
return d;
331370
}
332371

333372
Dir FS::openDir(const String& path) {
@@ -378,6 +417,11 @@ bool FS::rename(const String& pathFrom, const String& pathTo) {
378417
return rename(pathFrom.c_str(), pathTo.c_str());
379418
}
380419

420+
void FS::setTimeCallback(time_t (*cb)(void)) {
421+
if (!_impl)
422+
return;
423+
_impl->setTimeCallback(cb);
424+
}
381425

382426

383427
static bool sflags(const char* mode, OpenMode& om, AccessMode& am) {

cores/esp8266/FS.h

+26-2
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,16 @@ class File : public Stream
110110

111111
String readString() override;
112112

113+
time_t getLastWrite();
114+
void setTimeCallback(time_t (*cb)(void));
115+
113116
protected:
114117
FileImplPtr _p;
115118

116119
// Arduino SD class emulation
117120
std::shared_ptr<Dir> _fakeDir;
118121
FS *_baseFS;
122+
time_t (*timeCallback)(void) = nullptr;
119123
};
120124

121125
class Dir {
@@ -126,15 +130,21 @@ class Dir {
126130

127131
String fileName();
128132
size_t fileSize();
133+
time_t fileTime();
129134
bool isFile() const;
130135
bool isDirectory() const;
131136

132137
bool next();
133138
bool rewind();
134139

140+
time_t getLastWrite();
141+
void setTimeCallback(time_t (*cb)(void));
142+
135143
protected:
136144
DirImplPtr _impl;
137145
FS *_baseFS;
146+
time_t (*timeCallback)(void) = nullptr;
147+
138148
};
139149

140150
// Backwards compatible, <4GB filesystem usage
@@ -179,17 +189,26 @@ class FSConfig
179189
class SPIFFSConfig : public FSConfig
180190
{
181191
public:
182-
SPIFFSConfig(bool autoFormat = true) {
192+
SPIFFSConfig(bool autoFormat = true, bool enableTime = true) {
183193
_type = SPIFFSConfig::fsid::FSId;
184194
_autoFormat = autoFormat;
195+
_enableTime = enableTime;
185196
}
186197
enum fsid { FSId = 0x53504946 };
198+
199+
SPIFFSConfig setEnableTime(bool val = true) {
200+
_enableTime = val;
201+
return *this;
202+
}
203+
204+
// Inherit _type and _autoFormat
205+
bool _enableTime;
187206
};
188207

189208
class FS
190209
{
191210
public:
192-
FS(FSImplPtr impl) : _impl(impl) { }
211+
FS(FSImplPtr impl) : _impl(impl) { timeCallback = _defaultTimeCB; }
193212

194213
bool setConfig(const FSConfig &cfg);
195214

@@ -223,10 +242,14 @@ class FS
223242

224243
bool gc();
225244

245+
void setTimeCallback(time_t (*cb)(void));
246+
226247
friend class ::SDClass; // More of a frenemy, but SD needs internal implementation to get private FAT bits
227248
protected:
228249
FSImplPtr _impl;
229250
FSImplPtr getImpl() { return _impl; }
251+
time_t (*timeCallback)(void);
252+
static time_t _defaultTimeCB(void) { return time(NULL); }
230253
};
231254

232255
} // namespace fs
@@ -241,6 +264,7 @@ using fs::SeekCur;
241264
using fs::SeekEnd;
242265
using fs::FSInfo;
243266
using fs::FSConfig;
267+
using fs::SPIFFSConfig;
244268
#endif //FS_NO_GLOBALS
245269

246270
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SPIFFS)

cores/esp8266/FSImpl.h

+35
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ class FileImpl {
4141
virtual const char* fullName() const = 0;
4242
virtual bool isFile() const = 0;
4343
virtual bool isDirectory() const = 0;
44+
45+
// Filesystems *may* support a timestamp per-file, so allow the user to override with
46+
// their own callback for *this specific* file (as opposed to the FSImpl call of the
47+
// same name. The default implementation simply returns time(&null)
48+
virtual void setTimeCallback(time_t (*cb)(void)) { timeCallback = cb; }
49+
50+
// Return the last written time for a file. Undefined when called on a writable file
51+
// as the FS is allowed to return either the time of the last write() operation or the
52+
// time present in the filesystem metadata (often the last time the file was closed)
53+
virtual time_t getLastWrite() { return 0; } // Default is to not support timestamps
54+
55+
protected:
56+
time_t (*timeCallback)(void) = nullptr;
4457
};
4558

4659
enum OpenMode {
@@ -62,10 +75,24 @@ class DirImpl {
6275
virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0;
6376
virtual const char* fileName() = 0;
6477
virtual size_t fileSize() = 0;
78+
virtual time_t fileTime() { return 0; } // By default, FS doesn't report file times
6579
virtual bool isFile() const = 0;
6680
virtual bool isDirectory() const = 0;
6781
virtual bool next() = 0;
6882
virtual bool rewind() = 0;
83+
84+
// Filesystems *may* support a timestamp per-file, so allow the user to override with
85+
// their own callback for *this specific* file (as opposed to the FSImpl call of the
86+
// same name. The default implementation simply returns time(&null)
87+
virtual void setTimeCallback(time_t (*cb)(void)) { timeCallback = cb; }
88+
89+
// Return the last written time for a file. Undefined when called on a writable file
90+
// as the FS is allowed to return either the time of the last write() operation or the
91+
// time present in the filesystem metadata (often the last time the file was closed)
92+
virtual time_t getLastWrite() { return 0; } // Default is to not support timestamps
93+
94+
protected:
95+
time_t (*timeCallback)(void) = nullptr;
6996
};
7097

7198
class FSImpl {
@@ -85,6 +112,14 @@ class FSImpl {
85112
virtual bool mkdir(const char* path) = 0;
86113
virtual bool rmdir(const char* path) = 0;
87114
virtual bool gc() { return true; } // May not be implemented in all file systems.
115+
116+
// Filesystems *may* support a timestamp per-file, so allow the user to override with
117+
// their own callback for all files on this FS. The default implementation simply
118+
// returns the present time as reported by time(&null)
119+
virtual void setTimeCallback(time_t (*cb)(void)) { timeCallback = cb; }
120+
121+
protected:
122+
time_t (*timeCallback)(void) = nullptr;
88123
};
89124

90125
} // namespace fs

cores/esp8266/spiffs/spiffs.h

+4-8
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,7 @@ typedef struct {
300300
spiffs_obj_type type;
301301
spiffs_page_ix pix;
302302
u8_t name[SPIFFS_OBJ_NAME_LEN];
303-
#if SPIFFS_OBJ_META_LEN
304-
u8_t meta[SPIFFS_OBJ_META_LEN];
305-
#endif
303+
u8_t meta[4]; // Set to the largest supported metadata len
306304
} spiffs_stat;
307305

308306
struct spiffs_dirent {
@@ -311,9 +309,7 @@ struct spiffs_dirent {
311309
spiffs_obj_type type;
312310
u32_t size;
313311
spiffs_page_ix pix;
314-
#if SPIFFS_OBJ_META_LEN
315-
u8_t meta[SPIFFS_OBJ_META_LEN];
316-
#endif
312+
u8_t meta[4]; // Set to the largest supported metadata len
317313
};
318314

319315
typedef struct {
@@ -534,7 +530,7 @@ s32_t SPIFFS_close(spiffs *fs, spiffs_file fh);
534530
*/
535531
s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath);
536532

537-
#if SPIFFS_OBJ_META_LEN
533+
538534
/**
539535
* Updates file's metadata
540536
* @param fs the file system struct
@@ -550,7 +546,7 @@ s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta);
550546
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
551547
*/
552548
s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta);
553-
#endif
549+
554550

555551
/**
556552
* Returns last error of last file operation.

cores/esp8266/spiffs/spiffs_check.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static s32_t spiffs_object_get_data_page_index_reference(
6464
u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix);
6565
if (objix_spix == 0) {
6666
// get referenced page from object index header
67-
addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix);
67+
addr += sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN + data_spix * sizeof(spiffs_page_ix);
6868
} else {
6969
// get referenced page from object index
7070
addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix);
@@ -136,7 +136,7 @@ static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_
136136

137137
// rewrite in mem
138138
if (objix_spix == 0) {
139-
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix;
139+
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[data_spix] = new_data_pix;
140140
} else {
141141
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix;
142142
}
@@ -574,7 +574,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
574574
// object header page index
575575
entries = SPIFFS_OBJ_HDR_IX_LEN(fs);
576576
data_spix_offset = 0;
577-
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header));
577+
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN);
578578
} else {
579579
// object page index
580580
entries = SPIFFS_OBJ_IX_LEN(fs);

cores/esp8266/spiffs/spiffs_config.h

+16
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@ typedef uint16_t u16_t;
2525
typedef int8_t s8_t;
2626
typedef uint8_t u8_t;
2727

28+
29+
// Enable supporting both timestamped and non-timestamped FS images by making
30+
// the meta-len a global variable here and by changing the logic of SPIFFS.begin.
31+
extern int __SPIFFS_obj_meta_len;
32+
#define SPIFFS_OBJ_META_LEN (__SPIFFS_obj_meta_len)
33+
#define SPIFFS_MAX_META (4) // Maximum metadata size allowed at runtime
34+
// Because SPIFFS reads binary images of blocks, we need to be sure to actually ensure there is space to
35+
// keep the metadata (since 0 bytes are allocated in the struct itself). Use an anonymous union to ensure
36+
// that there is at least MAX_META bytes after the end of the struct.
37+
#define alloc_spiffs_page_object_ix_header(name) \
38+
union { \
39+
spiffs_page_object_ix_header name; \
40+
char xxx[sizeof(spiffs_page_object_ix_header) + SPIFFS_MAX_META]; \
41+
};
42+
43+
2844
#ifndef SEEK_SET
2945
#define SEEK_SET 0 /* set file offset to offset */
3046
#endif

cores/esp8266/spiffs/spiffs_gc.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
467467
// update memory representation of object index page with new data page
468468
if (gc.cur_objix_spix == 0) {
469469
// update object index header page
470-
((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
470+
((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[p_hdr.span_ix] = new_data_pix;
471471
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page " _SPIPRIpg" to objix_hdr entry " _SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
472472
} else {
473473
// update object index page

0 commit comments

Comments
 (0)