Skip to content

Commit cafc27f

Browse files
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 esp8266#6992
1 parent 8242d72 commit cafc27f

File tree

9 files changed

+158
-67
lines changed

9 files changed

+158
-67
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::getCreation() {
191+
if (!_p)
192+
return 0;
193+
194+
return _p->getCreation();
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::fileCreation() {
235+
if (!_impl)
236+
return 0;
237+
return _impl->fileCreation();
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 getCreation();
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 fileCreation();
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 getCreation() { 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 fileCreation() { 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+
fileCreation
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+
getCreation
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.getCreation();
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

+16-3
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ FileImplPtr LittleFSImpl::open(const char* path, OpenMode openMode, AccessMode a
5252
int flags = _getFlags(openMode, accessMode);
5353
auto fd = std::make_shared<lfs_file_t>();
5454

55-
if ((openMode && OM_CREATE) && strchr(path, '/')) {
55+
if ((openMode & OM_CREATE) && strchr(path, '/')) {
5656
// For file creation, silently make subdirs as needed. If any fail,
5757
// it will be caught by the real file open later on
5858
char *pathStr = strdup(path);
@@ -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

+46-20
Original file line numberDiff line numberDiff line change
@@ -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
}
@@ -420,12 +420,19 @@ class LittleFSFileImpl : public FileImpl
420420
_opened = false;
421421
DEBUGV("lfs_file_close: fd=%p\n", _getFD());
422422
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 getCreation() 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
@@ -537,23 +555,11 @@ class LittleFSDirImpl : public DirImpl
537555
}
538556

539557
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;
558+
return (time_t)_getAttr4('t');
559+
}
560+
561+
time_t fileCreation() override {
562+
return (time_t)_getAttr4('c');
557563
}
558564

559565

@@ -592,6 +598,26 @@ class LittleFSDirImpl : public DirImpl
592598
return _dir.get();
593599
}
594600

601+
uint32_t _getAttr4(char attr) {
602+
if (!_valid) {
603+
return 0;
604+
}
605+
int nameLen = 3; // Slashes, terminator
606+
nameLen += _dirPath.get() ? strlen(_dirPath.get()) : 0;
607+
nameLen += strlen(_dirent.name);
608+
char *tmpName = (char*)malloc(nameLen);
609+
if (!tmpName) {
610+
return 0;
611+
}
612+
snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name);
613+
time_t ftime = 0;
614+
int rc = lfs_getattr(_fs->getFS(), tmpName, attr, (void *)&ftime, sizeof(ftime));
615+
if (rc != sizeof(ftime))
616+
ftime = 0; // Error, so clear read value
617+
free(tmpName);
618+
return ftime;
619+
}
620+
595621
String _pattern;
596622
LittleFSImpl *_fs;
597623
std::shared_ptr<lfs_dir_t> _dir;

libraries/SDFS/src/SDFS.h

+23
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,18 @@ class SDFSFileImpl : public FileImpl
363363
return ftime;
364364
}
365365

366+
time_t getCreation() override {
367+
time_t ftime = 0;
368+
if (_opened && _fd) {
369+
sdfat::dir_t tmp;
370+
if (_fd.get()->dirEntry(&tmp)) {
371+
ftime = SDFSImpl::FatToTimeT(tmp.creationDate, tmp.creationTime);
372+
}
373+
}
374+
return ftime;
375+
}
376+
377+
366378

367379
protected:
368380
SDFSImpl* _fs;
@@ -426,6 +438,14 @@ class SDFSDirImpl : public DirImpl
426438
return _time;
427439
}
428440

441+
time_t fileCreation() override
442+
{
443+
if (!_valid) {
444+
return 0;
445+
}
446+
447+
return _creation;
448+
}
429449

430450
bool isFile() const override
431451
{
@@ -451,8 +471,10 @@ class SDFSDirImpl : public DirImpl
451471
sdfat::dir_t tmp;
452472
if (file.dirEntry(&tmp)) {
453473
_time = SDFSImpl::FatToTimeT(tmp.lastWriteDate, tmp.lastWriteTime);
474+
_creation = SDFSImpl::FatToTimeT(tmp.creationDate, tmp.creationTime);
454475
} else {
455476
_time = 0;
477+
_creation = 0;
456478
}
457479
file.getName(_lfn, sizeof(_lfn));
458480
file.close();
@@ -477,6 +499,7 @@ class SDFSDirImpl : public DirImpl
477499
bool _valid;
478500
char _lfn[64];
479501
time_t _time;
502+
time_t _creation;
480503
std::shared_ptr<char> _dirPath;
481504
uint32_t _size;
482505
bool _isFile;

0 commit comments

Comments
 (0)