Skip to content

Commit bea64df

Browse files
Add fileCreation/getCreation create-time accessors (#7000)
* Add fileCreation/getCreation create-time accessors For SDFS and LittleFS, enable a creation time accessor for files and Dir iterators, similar to the existing fileTime/getLastWrite calls. Remove spurious Dir::getLastWrite method (the proper and only documented way is really Dir::fileTime). Update json to point to new mklittlefs which copies the creation date of files to the image. Fixes #6992 * Remove malloc(), use stack vars for temp names LFS filenames are limited in size and generally very small. Use a stack variable instead of a dynamic allocation when performing full-path computations. * Replace "Creation" w/"CreationTime" in FS accessor Per review, `getCreation` -> `getCreationTime`, `fileCreation` -> `fileCreationTime`. The names `fileTime()` and `getLastWrite()` are inherited from ESP32 implementation and unchanged. * Add creation time to listfiles SD example * Enable SdFat's sateTime callback for timestamping SdFat requries the dateTimeCallback call (global for everything) to update dates and times on created files. Because the callback signature doesn't have space for us to provide any parameters, we cannot get the the File, Dir, or FS object's dateTimeCB member. Instead, just go with `time(null)` as the callback function which is right in all but the most esoteric cases. * Correct DOS year/month offset in dateTime callback * Fix docs to match new xxxCreationTime() API names Co-authored-by: Develo <[email protected]>
1 parent 4eca62c commit bea64df

File tree

10 files changed

+176
-83
lines changed

10 files changed

+176
-83
lines changed

cores/esp8266/FS.cpp

+15-7
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ time_t File::getLastWrite() {
187187
return _p->getLastWrite();
188188
}
189189

190+
time_t File::getCreationTime() {
191+
if (!_p)
192+
return 0;
193+
194+
return _p->getCreationTime();
195+
}
196+
190197
void File::setTimeCallback(time_t (*cb)(void)) {
191198
if (!_p)
192199
return;
@@ -224,6 +231,12 @@ time_t Dir::fileTime() {
224231
return _impl->fileTime();
225232
}
226233

234+
time_t Dir::fileCreationTime() {
235+
if (!_impl)
236+
return 0;
237+
return _impl->fileCreationTime();
238+
}
239+
227240
size_t Dir::fileSize() {
228241
if (!_impl) {
229242
return 0;
@@ -262,17 +275,11 @@ bool Dir::rewind() {
262275
return _impl->rewind();
263276
}
264277

265-
time_t Dir::getLastWrite() {
266-
if (!_impl)
267-
return 0;
268-
269-
return _impl->getLastWrite();
270-
}
271-
272278
void Dir::setTimeCallback(time_t (*cb)(void)) {
273279
if (!_impl)
274280
return;
275281
_impl->setTimeCallback(cb);
282+
timeCallback = cb;
276283
}
277284

278285

@@ -289,6 +296,7 @@ bool FS::begin() {
289296
DEBUGV("#error: FS: no implementation");
290297
return false;
291298
}
299+
_impl->setTimeCallback(timeCallback);
292300
bool ret = _impl->begin();
293301
DEBUGV("%s\n", ret? "": "#error: FS could not start");
294302
return ret;

cores/esp8266/FS.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class File : public Stream
112112
String readString() override;
113113

114114
time_t getLastWrite();
115+
time_t getCreationTime();
115116
void setTimeCallback(time_t (*cb)(void));
116117

117118
protected:
@@ -120,7 +121,6 @@ class File : public Stream
120121
// Arduino SD class emulation
121122
std::shared_ptr<Dir> _fakeDir;
122123
FS *_baseFS;
123-
time_t (*timeCallback)(void) = nullptr;
124124
};
125125

126126
class Dir {
@@ -132,20 +132,19 @@ class Dir {
132132
String fileName();
133133
size_t fileSize();
134134
time_t fileTime();
135+
time_t fileCreationTime();
135136
bool isFile() const;
136137
bool isDirectory() const;
137138

138139
bool next();
139140
bool rewind();
140141

141-
time_t getLastWrite();
142142
void setTimeCallback(time_t (*cb)(void));
143143

144144
protected:
145145
DirImplPtr _impl;
146146
FS *_baseFS;
147147
time_t (*timeCallback)(void) = nullptr;
148-
149148
};
150149

151150
// Backwards compatible, <4GB filesystem usage

cores/esp8266/FSImpl.h

+6-5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ class FileImpl {
5151
// as the FS is allowed to return either the time of the last write() operation or the
5252
// time present in the filesystem metadata (often the last time the file was closed)
5353
virtual time_t getLastWrite() { return 0; } // Default is to not support timestamps
54+
// Same for creation time.
55+
virtual time_t getCreationTime() { return 0; } // Default is to not support timestamps
5456

5557
protected:
5658
time_t (*timeCallback)(void) = nullptr;
@@ -75,7 +77,11 @@ class DirImpl {
7577
virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0;
7678
virtual const char* fileName() = 0;
7779
virtual size_t fileSize() = 0;
80+
// Return the last written time for a file. Undefined when called on a writable file
81+
// as the FS is allowed to return either the time of the last write() operation or the
82+
// time present in the filesystem metadata (often the last time the file was closed)
7883
virtual time_t fileTime() { return 0; } // By default, FS doesn't report file times
84+
virtual time_t fileCreationTime() { return 0; } // By default, FS doesn't report file times
7985
virtual bool isFile() const = 0;
8086
virtual bool isDirectory() const = 0;
8187
virtual bool next() = 0;
@@ -86,11 +92,6 @@ class DirImpl {
8692
// same name. The default implementation simply returns time(&null)
8793
virtual void setTimeCallback(time_t (*cb)(void)) { timeCallback = cb; }
8894

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-
9495
protected:
9596
time_t (*timeCallback)(void) = nullptr;
9697
};

doc/filesystem.rst

+10
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,11 @@ fileTime
535535
Returns the time_t write time of the current file pointed
536536
to by the internal iterator.
537537

538+
fileCreationTime
539+
~~~~~~~~~~~~~~~~
540+
Returns the time_t creation time of the current file
541+
pointed to by the internal iterator.
542+
538543
isFile
539544
~~~~~~
540545

@@ -642,6 +647,11 @@ getLastWrite
642647
Returns the file last write time, and only valid for files opened in read-only
643648
mode. If a file is opened for writing, the returned time may be indeterminate.
644649

650+
getCreationTime
651+
~~~~~~~~~~~~~~~
652+
653+
Returns the file creation time, if available.
654+
645655
isFile
646656
~~~~~~
647657

libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino

+6-2
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,12 @@ void listDir(const char * dirname) {
5353
Serial.print(root.fileName());
5454
Serial.print(" SIZE: ");
5555
Serial.print(file.size());
56-
time_t t = file.getLastWrite();
57-
struct tm * tmstruct = localtime(&t);
56+
time_t cr = file.getCreationTime();
57+
time_t lw = file.getLastWrite();
5858
file.close();
59+
struct tm * tmstruct = localtime(&cr);
60+
Serial.printf(" CREATION: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
61+
tmstruct = localtime(&lw);
5962
Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
6063
}
6164
}
@@ -90,6 +93,7 @@ void writeFile(const char * path, const char * message) {
9093
} else {
9194
Serial.println("Write failed");
9295
}
96+
delay(2000); // Make sure the CREATE and LASTWRITE times are different
9397
file.close();
9498
}
9599

libraries/LittleFS/src/LittleFS.cpp

+15-2
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,26 @@ FileImplPtr LittleFSImpl::open(const char* path, OpenMode openMode, AccessMode a
6868
}
6969
free(pathStr);
7070
}
71+
72+
time_t creation = 0;
73+
if (timeCallback && (openMode & OM_CREATE)) {
74+
// O_CREATE means we *may* make the file, but not if it already exists.
75+
// See if it exists, and only if not update the creation time
76+
int rc = lfs_file_open(&_lfs, fd.get(), path, LFS_O_RDONLY);
77+
if (rc == 0) {
78+
lfs_file_close(&_lfs, fd.get()); // It exists, don't update create time
79+
} else {
80+
creation = timeCallback(); // File didn't exist or otherwise, so we're going to create this time
81+
}
82+
}
83+
7184
int rc = lfs_file_open(&_lfs, fd.get(), path, flags);
7285
if (rc == LFS_ERR_ISDIR) {
7386
// To support the SD.openNextFile, a null FD indicates to the LittleFSFile this is just
7487
// a directory whose name we are carrying around but which cannot be read or written
75-
return std::make_shared<LittleFSFileImpl>(this, path, nullptr, flags);
88+
return std::make_shared<LittleFSFileImpl>(this, path, nullptr, flags, creation);
7689
} else if (rc == 0) {
77-
return std::make_shared<LittleFSFileImpl>(this, path, fd, flags);
90+
return std::make_shared<LittleFSFileImpl>(this, path, fd, flags, creation);
7891
} else {
7992
DEBUGV("LittleFSDirImpl::openFile: rc=%d fd=%p path=`%s` openMode=%d accessMode=%d err=%d\n",
8093
rc, fd.get(), path, openMode, accessMode, rc);

libraries/LittleFS/src/LittleFS.h

+47-29
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class LittleFSImpl : public FSImpl
8989
DirImplPtr openDir(const char *path) override;
9090

9191
bool exists(const char* path) override {
92-
if ( !_mounted || !path || !path[0] ) {
92+
if (!_mounted || !path || !path[0]) {
9393
return false;
9494
}
9595
lfs_info info;
@@ -98,7 +98,7 @@ class LittleFSImpl : public FSImpl
9898
}
9999

100100
bool rename(const char* pathFrom, const char* pathTo) override {
101-
if (!_mounted || !pathFrom || !pathFrom[0] || !pathTo || !pathTo[0]) {
101+
if (!_mounted || !pathFrom || !pathFrom[0] || !pathTo || !pathTo[0]) {
102102
return false;
103103
}
104104
int rc = lfs_rename(&_lfs, pathFrom, pathTo);
@@ -323,7 +323,7 @@ class LittleFSImpl : public FSImpl
323323
class LittleFSFileImpl : public FileImpl
324324
{
325325
public:
326-
LittleFSFileImpl(LittleFSImpl* fs, const char *name, std::shared_ptr<lfs_file_t> fd, int flags) : _fs(fs), _fd(fd), _opened(true), _flags(flags) {
326+
LittleFSFileImpl(LittleFSImpl* fs, const char *name, std::shared_ptr<lfs_file_t> fd, int flags, time_t creation) : _fs(fs), _fd(fd), _opened(true), _flags(flags), _creation(creation) {
327327
_name = std::shared_ptr<char>(new char[strlen(name) + 1], std::default_delete<char[]>());
328328
strcpy(_name.get(), name);
329329
}
@@ -419,13 +419,20 @@ class LittleFSFileImpl : public FileImpl
419419
lfs_file_close(_fs->getFS(), _getFD());
420420
_opened = false;
421421
DEBUGV("lfs_file_close: fd=%p\n", _getFD());
422-
if (timeCallback && (_flags & LFS_O_WRONLY)) {
422+
if (timeCallback && (_flags & LFS_O_WRONLY)) {
423+
// If the file opened with O_CREAT, write the creation time attribute
424+
if (_creation) {
425+
int rc = lfs_setattr(_fs->getFS(), _name.get(), 'c', (const void *)&_creation, sizeof(_creation));
426+
if (rc < 0) {
427+
DEBUGV("Unable to set creation time on '%s' to %d\n", _name.get(), _creation);
428+
}
429+
}
423430
// Add metadata with last write time
424431
time_t now = timeCallback();
425432
int rc = lfs_setattr(_fs->getFS(), _name.get(), 't', (const void *)&now, sizeof(now));
426433
if (rc < 0) {
427-
DEBUGV("Unable to set time on '%s' to %d\n", _name.get(), now);
428-
}
434+
DEBUGV("Unable to set last write time on '%s' to %d\n", _name.get(), now);
435+
}
429436
}
430437
}
431438
}
@@ -440,6 +447,16 @@ class LittleFSFileImpl : public FileImpl
440447
return ftime;
441448
}
442449

450+
time_t getCreationTime() override {
451+
time_t ftime = 0;
452+
if (_opened && _fd) {
453+
int rc = lfs_getattr(_fs->getFS(), _name.get(), 'c', (void *)&ftime, sizeof(ftime));
454+
if (rc != sizeof(ftime))
455+
ftime = 0; // Error, so clear read value
456+
}
457+
return ftime;
458+
}
459+
443460
const char* name() const override {
444461
if (!_opened) {
445462
return nullptr;
@@ -484,6 +501,7 @@ class LittleFSFileImpl : public FileImpl
484501
std::shared_ptr<char> _name;
485502
bool _opened;
486503
int _flags;
504+
time_t _creation;
487505
};
488506

489507
class LittleFSDirImpl : public DirImpl
@@ -512,13 +530,9 @@ class LittleFSDirImpl : public DirImpl
512530
int nameLen = 3; // Slashes, terminator
513531
nameLen += _dirPath.get() ? strlen(_dirPath.get()) : 0;
514532
nameLen += strlen(_dirent.name);
515-
char *tmpName = (char*)malloc(nameLen);
516-
if (!tmpName) {
517-
return FileImplPtr();
518-
}
519-
snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name);
533+
char tmpName[nameLen];
534+
snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name);
520535
auto ret = _fs->open((const char *)tmpName, openMode, accessMode);
521-
free(tmpName);
522536
return ret;
523537
}
524538

@@ -537,23 +551,11 @@ class LittleFSDirImpl : public DirImpl
537551
}
538552

539553
time_t fileTime() override {
540-
if (!_valid) {
541-
return 0;
542-
}
543-
int nameLen = 3; // Slashes, terminator
544-
nameLen += _dirPath.get() ? strlen(_dirPath.get()) : 0;
545-
nameLen += strlen(_dirent.name);
546-
char *tmpName = (char*)malloc(nameLen);
547-
if (!tmpName) {
548-
return 0;
549-
}
550-
snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name);
551-
time_t ftime = 0;
552-
int rc = lfs_getattr(_fs->getFS(), tmpName, 't', (void *)&ftime, sizeof(ftime));
553-
if (rc != sizeof(ftime))
554-
ftime = 0; // Error, so clear read value
555-
free(tmpName);
556-
return ftime;
554+
return (time_t)_getAttr4('t');
555+
}
556+
557+
time_t fileCreationTime() override {
558+
return (time_t)_getAttr4('c');
557559
}
558560

559561

@@ -592,6 +594,22 @@ class LittleFSDirImpl : public DirImpl
592594
return _dir.get();
593595
}
594596

597+
uint32_t _getAttr4(char attr) {
598+
if (!_valid) {
599+
return 0;
600+
}
601+
int nameLen = 3; // Slashes, terminator
602+
nameLen += _dirPath.get() ? strlen(_dirPath.get()) : 0;
603+
nameLen += strlen(_dirent.name);
604+
char tmpName[nameLen];
605+
snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name);
606+
time_t ftime = 0;
607+
int rc = lfs_getattr(_fs->getFS(), tmpName, attr, (void *)&ftime, sizeof(ftime));
608+
if (rc != sizeof(ftime))
609+
ftime = 0; // Error, so clear read value
610+
return ftime;
611+
}
612+
595613
String _pattern;
596614
LittleFSImpl *_fs;
597615
std::shared_ptr<lfs_dir_t> _dir;

libraries/SD/examples/listfiles/listfiles.ino

+6-8
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ File root;
2929
void setup() {
3030
// Open serial communications and wait for port to open:
3131
Serial.begin(115200);
32-
while (!Serial) {
33-
; // wait for serial port to connect. Needed for Leonardo only
34-
}
3532

3633
Serial.print("Initializing SD card...");
3734

@@ -71,11 +68,12 @@ void printDirectory(File dir, int numTabs) {
7168
// files have sizes, directories do not
7269
Serial.print("\t\t");
7370
Serial.print(entry.size(), DEC);
74-
Serial.print("\t\t");
75-
time_t ft = entry.getLastWrite();
76-
struct tm *tm = localtime(&ft);
77-
// US format. Feel free to convert to your own locale...
78-
Serial.printf("%02d-%02d-%02d %02d:%02d:%02d\n", tm->tm_mon + 1, tm->tm_mday, tm->tm_year % 100, tm->tm_hour, tm->tm_min, tm->tm_sec);
71+
time_t cr = entry.getCreationTime();
72+
time_t lw = entry.getLastWrite();
73+
struct tm * tmstruct = localtime(&cr);
74+
Serial.printf("\tCREATION: %d-%02d-%02d %02d:%02d:%02d", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
75+
tmstruct = localtime(&lw);
76+
Serial.printf("\tLAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
7977
}
8078
entry.close();
8179
}

0 commit comments

Comments
 (0)