diff --git a/cores/esp8266/FS.cpp b/cores/esp8266/FS.cpp
index fa841c64fa..c0a13a7614 100644
--- a/cores/esp8266/FS.cpp
+++ b/cores/esp8266/FS.cpp
@@ -187,6 +187,13 @@ time_t File::getLastWrite() {
return _p->getLastWrite();
}
+time_t File::getCreation() {
+ if (!_p)
+ return 0;
+
+ return _p->getCreation();
+}
+
void File::setTimeCallback(time_t (*cb)(void)) {
if (!_p)
return;
@@ -224,6 +231,12 @@ time_t Dir::fileTime() {
return _impl->fileTime();
}
+time_t Dir::fileCreation() {
+ if (!_impl)
+ return 0;
+ return _impl->fileCreation();
+}
+
size_t Dir::fileSize() {
if (!_impl) {
return 0;
@@ -262,17 +275,11 @@ bool Dir::rewind() {
return _impl->rewind();
}
-time_t Dir::getLastWrite() {
- if (!_impl)
- return 0;
-
- return _impl->getLastWrite();
-}
-
void Dir::setTimeCallback(time_t (*cb)(void)) {
if (!_impl)
return;
_impl->setTimeCallback(cb);
+ timeCallback = cb;
}
@@ -289,6 +296,7 @@ bool FS::begin() {
DEBUGV("#error: FS: no implementation");
return false;
}
+ _impl->setTimeCallback(timeCallback);
bool ret = _impl->begin();
DEBUGV("%s\n", ret? "": "#error: FS could not start");
return ret;
diff --git a/cores/esp8266/FS.h b/cores/esp8266/FS.h
index 23d05bad83..a652b7511e 100644
--- a/cores/esp8266/FS.h
+++ b/cores/esp8266/FS.h
@@ -112,6 +112,7 @@ class File : public Stream
String readString() override;
time_t getLastWrite();
+ time_t getCreation();
void setTimeCallback(time_t (*cb)(void));
protected:
@@ -120,7 +121,6 @@ class File : public Stream
// Arduino SD class emulation
std::shared_ptr
_fakeDir;
FS *_baseFS;
- time_t (*timeCallback)(void) = nullptr;
};
class Dir {
@@ -132,20 +132,19 @@ class Dir {
String fileName();
size_t fileSize();
time_t fileTime();
+ time_t fileCreation();
bool isFile() const;
bool isDirectory() const;
bool next();
bool rewind();
- time_t getLastWrite();
void setTimeCallback(time_t (*cb)(void));
protected:
DirImplPtr _impl;
FS *_baseFS;
time_t (*timeCallback)(void) = nullptr;
-
};
// Backwards compatible, <4GB filesystem usage
diff --git a/cores/esp8266/FSImpl.h b/cores/esp8266/FSImpl.h
index 9715c65a8b..3dea6f9424 100644
--- a/cores/esp8266/FSImpl.h
+++ b/cores/esp8266/FSImpl.h
@@ -51,6 +51,8 @@ class FileImpl {
// as the FS is allowed to return either the time of the last write() operation or the
// time present in the filesystem metadata (often the last time the file was closed)
virtual time_t getLastWrite() { return 0; } // Default is to not support timestamps
+ // Same for creation time.
+ virtual time_t getCreation() { return 0; } // Default is to not support timestamps
protected:
time_t (*timeCallback)(void) = nullptr;
@@ -75,7 +77,11 @@ class DirImpl {
virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0;
virtual const char* fileName() = 0;
virtual size_t fileSize() = 0;
+ // Return the last written time for a file. Undefined when called on a writable file
+ // as the FS is allowed to return either the time of the last write() operation or the
+ // time present in the filesystem metadata (often the last time the file was closed)
virtual time_t fileTime() { return 0; } // By default, FS doesn't report file times
+ virtual time_t fileCreation() { return 0; } // By default, FS doesn't report file times
virtual bool isFile() const = 0;
virtual bool isDirectory() const = 0;
virtual bool next() = 0;
@@ -86,11 +92,6 @@ class DirImpl {
// same name. The default implementation simply returns time(&null)
virtual void setTimeCallback(time_t (*cb)(void)) { timeCallback = cb; }
- // Return the last written time for a file. Undefined when called on a writable file
- // as the FS is allowed to return either the time of the last write() operation or the
- // time present in the filesystem metadata (often the last time the file was closed)
- virtual time_t getLastWrite() { return 0; } // Default is to not support timestamps
-
protected:
time_t (*timeCallback)(void) = nullptr;
};
diff --git a/doc/filesystem.rst b/doc/filesystem.rst
index fecd840742..3c2fb72664 100644
--- a/doc/filesystem.rst
+++ b/doc/filesystem.rst
@@ -535,6 +535,11 @@ fileTime
Returns the time_t write time of the current file pointed
to by the internal iterator.
+fileCreation
+~~~~~~~~~~~~
+Returns the time_t creation time of the current file
+pointed to by the internal iterator.
+
isFile
~~~~~~
@@ -642,6 +647,11 @@ getLastWrite
Returns the file last write time, and only valid for files opened in read-only
mode. If a file is opened for writing, the returned time may be indeterminate.
+getCreation
+~~~~~~~~~~~
+
+Returns the file creation time, if available.
+
isFile
~~~~~~
diff --git a/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino b/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino
index ce85de0340..b76f5f320a 100644
--- a/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino
+++ b/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino
@@ -53,9 +53,12 @@ void listDir(const char * dirname) {
Serial.print(root.fileName());
Serial.print(" SIZE: ");
Serial.print(file.size());
- time_t t = file.getLastWrite();
- struct tm * tmstruct = localtime(&t);
+ time_t cr = file.getCreation();
+ time_t lw = file.getLastWrite();
file.close();
+ struct tm * tmstruct = localtime(&cr);
+ 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);
+ tmstruct = localtime(&lw);
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);
}
}
@@ -90,6 +93,7 @@ void writeFile(const char * path, const char * message) {
} else {
Serial.println("Write failed");
}
+ delay(2000); // Make sure the CREATE and LASTWRITE times are different
file.close();
}
diff --git a/libraries/LittleFS/src/LittleFS.cpp b/libraries/LittleFS/src/LittleFS.cpp
index 518ef663de..b85075112e 100644
--- a/libraries/LittleFS/src/LittleFS.cpp
+++ b/libraries/LittleFS/src/LittleFS.cpp
@@ -52,7 +52,7 @@ FileImplPtr LittleFSImpl::open(const char* path, OpenMode openMode, AccessMode a
int flags = _getFlags(openMode, accessMode);
auto fd = std::make_shared();
- if ((openMode && OM_CREATE) && strchr(path, '/')) {
+ if ((openMode & OM_CREATE) && strchr(path, '/')) {
// For file creation, silently make subdirs as needed. If any fail,
// it will be caught by the real file open later on
char *pathStr = strdup(path);
@@ -68,13 +68,26 @@ FileImplPtr LittleFSImpl::open(const char* path, OpenMode openMode, AccessMode a
}
free(pathStr);
}
+
+ time_t creation = 0;
+ if (timeCallback && (openMode & OM_CREATE)) {
+ // O_CREATE means we *may* make the file, but not if it already exists.
+ // See if it exists, and only if not update the creation time
+ int rc = lfs_file_open(&_lfs, fd.get(), path, LFS_O_RDONLY);
+ if (rc == 0) {
+ lfs_file_close(&_lfs, fd.get()); // It exists, don't update create time
+ } else {
+ creation = timeCallback(); // File didn't exist or otherwise, so we're going to create this time
+ }
+ }
+
int rc = lfs_file_open(&_lfs, fd.get(), path, flags);
if (rc == LFS_ERR_ISDIR) {
// To support the SD.openNextFile, a null FD indicates to the LittleFSFile this is just
// a directory whose name we are carrying around but which cannot be read or written
- return std::make_shared(this, path, nullptr, flags);
+ return std::make_shared(this, path, nullptr, flags, creation);
} else if (rc == 0) {
- return std::make_shared(this, path, fd, flags);
+ return std::make_shared(this, path, fd, flags, creation);
} else {
DEBUGV("LittleFSDirImpl::openFile: rc=%d fd=%p path=`%s` openMode=%d accessMode=%d err=%d\n",
rc, fd.get(), path, openMode, accessMode, rc);
diff --git a/libraries/LittleFS/src/LittleFS.h b/libraries/LittleFS/src/LittleFS.h
index e332fd9acb..556558ed7d 100644
--- a/libraries/LittleFS/src/LittleFS.h
+++ b/libraries/LittleFS/src/LittleFS.h
@@ -323,7 +323,7 @@ class LittleFSImpl : public FSImpl
class LittleFSFileImpl : public FileImpl
{
public:
- LittleFSFileImpl(LittleFSImpl* fs, const char *name, std::shared_ptr fd, int flags) : _fs(fs), _fd(fd), _opened(true), _flags(flags) {
+ LittleFSFileImpl(LittleFSImpl* fs, const char *name, std::shared_ptr fd, int flags, time_t creation) : _fs(fs), _fd(fd), _opened(true), _flags(flags), _creation(creation) {
_name = std::shared_ptr(new char[strlen(name) + 1], std::default_delete());
strcpy(_name.get(), name);
}
@@ -420,12 +420,19 @@ class LittleFSFileImpl : public FileImpl
_opened = false;
DEBUGV("lfs_file_close: fd=%p\n", _getFD());
if (timeCallback && (_flags & LFS_O_WRONLY)) {
+ // If the file opened with O_CREAT, write the creation time attribute
+ if (_creation) {
+ int rc = lfs_setattr(_fs->getFS(), _name.get(), 'c', (const void *)&_creation, sizeof(_creation));
+ if (rc < 0) {
+ DEBUGV("Unable to set creation time on '%s' to %d\n", _name.get(), _creation);
+ }
+ }
// Add metadata with last write time
time_t now = timeCallback();
int rc = lfs_setattr(_fs->getFS(), _name.get(), 't', (const void *)&now, sizeof(now));
if (rc < 0) {
- DEBUGV("Unable to set time on '%s' to %d\n", _name.get(), now);
- }
+ DEBUGV("Unable to set last write time on '%s' to %d\n", _name.get(), now);
+ }
}
}
}
@@ -440,6 +447,16 @@ class LittleFSFileImpl : public FileImpl
return ftime;
}
+ time_t getCreation() override {
+ time_t ftime = 0;
+ if (_opened && _fd) {
+ int rc = lfs_getattr(_fs->getFS(), _name.get(), 'c', (void *)&ftime, sizeof(ftime));
+ if (rc != sizeof(ftime))
+ ftime = 0; // Error, so clear read value
+ }
+ return ftime;
+ }
+
const char* name() const override {
if (!_opened) {
return nullptr;
@@ -484,6 +501,7 @@ class LittleFSFileImpl : public FileImpl
std::shared_ptr _name;
bool _opened;
int _flags;
+ time_t _creation;
};
class LittleFSDirImpl : public DirImpl
@@ -537,23 +555,11 @@ class LittleFSDirImpl : public DirImpl
}
time_t fileTime() override {
- if (!_valid) {
- return 0;
- }
- int nameLen = 3; // Slashes, terminator
- nameLen += _dirPath.get() ? strlen(_dirPath.get()) : 0;
- nameLen += strlen(_dirent.name);
- char *tmpName = (char*)malloc(nameLen);
- if (!tmpName) {
- return 0;
- }
- snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name);
- time_t ftime = 0;
- int rc = lfs_getattr(_fs->getFS(), tmpName, 't', (void *)&ftime, sizeof(ftime));
- if (rc != sizeof(ftime))
- ftime = 0; // Error, so clear read value
- free(tmpName);
- return ftime;
+ return (time_t)_getAttr4('t');
+ }
+
+ time_t fileCreation() override {
+ return (time_t)_getAttr4('c');
}
@@ -592,6 +598,26 @@ class LittleFSDirImpl : public DirImpl
return _dir.get();
}
+ uint32_t _getAttr4(char attr) {
+ if (!_valid) {
+ return 0;
+ }
+ int nameLen = 3; // Slashes, terminator
+ nameLen += _dirPath.get() ? strlen(_dirPath.get()) : 0;
+ nameLen += strlen(_dirent.name);
+ char *tmpName = (char*)malloc(nameLen);
+ if (!tmpName) {
+ return 0;
+ }
+ snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name);
+ time_t ftime = 0;
+ int rc = lfs_getattr(_fs->getFS(), tmpName, attr, (void *)&ftime, sizeof(ftime));
+ if (rc != sizeof(ftime))
+ ftime = 0; // Error, so clear read value
+ free(tmpName);
+ return ftime;
+ }
+
String _pattern;
LittleFSImpl *_fs;
std::shared_ptr _dir;
diff --git a/libraries/SDFS/src/SDFS.h b/libraries/SDFS/src/SDFS.h
index ee772cd5cb..43c743a5cd 100644
--- a/libraries/SDFS/src/SDFS.h
+++ b/libraries/SDFS/src/SDFS.h
@@ -363,6 +363,18 @@ class SDFSFileImpl : public FileImpl
return ftime;
}
+ time_t getCreation() override {
+ time_t ftime = 0;
+ if (_opened && _fd) {
+ sdfat::dir_t tmp;
+ if (_fd.get()->dirEntry(&tmp)) {
+ ftime = SDFSImpl::FatToTimeT(tmp.creationDate, tmp.creationTime);
+ }
+ }
+ return ftime;
+ }
+
+
protected:
SDFSImpl* _fs;
@@ -426,6 +438,14 @@ class SDFSDirImpl : public DirImpl
return _time;
}
+ time_t fileCreation() override
+ {
+ if (!_valid) {
+ return 0;
+ }
+
+ return _creation;
+ }
bool isFile() const override
{
@@ -451,8 +471,10 @@ class SDFSDirImpl : public DirImpl
sdfat::dir_t tmp;
if (file.dirEntry(&tmp)) {
_time = SDFSImpl::FatToTimeT(tmp.lastWriteDate, tmp.lastWriteTime);
+ _creation = SDFSImpl::FatToTimeT(tmp.creationDate, tmp.creationTime);
} else {
_time = 0;
+ _creation = 0;
}
file.getName(_lfn, sizeof(_lfn));
file.close();
@@ -477,6 +499,7 @@ class SDFSDirImpl : public DirImpl
bool _valid;
char _lfn[64];
time_t _time;
+ time_t _creation;
std::shared_ptr _dirPath;
uint32_t _size;
bool _isFile;
diff --git a/package/package_esp8266com_index.template.json b/package/package_esp8266com_index.template.json
index eb6dca7312..2f338a5bb2 100644
--- a/package/package_esp8266com_index.template.json
+++ b/package/package_esp8266com_index.template.json
@@ -121,7 +121,7 @@
},
{
"packager": "esp8266",
- "version": "2.5.0-4-69bd9e6",
+ "version": "2.5.0-4-fe5bb56",
"name": "mklittlefs"
},
{
@@ -302,54 +302,61 @@
]
},
{
- "version": "2.5.0-4-69bd9e6",
+ "version": "2.5.0-4-fe5bb56",
"name": "mklittlefs",
"systems": [
{
"host": "aarch64-linux-gnu",
- "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/aarch64-linux-gnu-mklittlefs-69bd9e6.tar.gz",
- "archiveFileName": "aarch64-linux-gnu-mklittlefs-69bd9e6.tar.gz",
- "checksum": "SHA-256:74d938f15a3fb8ac20aeb0f938ace2c6759f622451419c09446aa79866302e18",
- "size": "44342"
+ "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/aarch64-linux-gnu.mklittlefs-fe5bb56.1578453304.tar.gz",
+ "archiveFileName": "aarch64-linux-gnu.mklittlefs-fe5bb56.1578453304.tar.gz",
+ "checksum": "SHA-256:ac50bae3b580053ba98a181ae3700fafd2b2f8a37ed9c16bc22a5d7c1659388e",
+ "size": "44433"
},
{
"host": "arm-linux-gnueabihf",
- "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/arm-linux-gnueabihf-mklittlefs-69bd9e6.tar.gz",
- "archiveFileName": "arm-linux-gnueabihf-mklittlefs-69bd9e6.tar.gz",
- "checksum": "SHA-256:926cca1c1f8f732a8ac79809ce0a52cabe283ab4137aa3237bca0fcca6bc2236",
- "size": "36871"
+ "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/arm-linux-gnueabihf.mklittlefs-fe5bb56.1578453304.tar.gz",
+ "archiveFileName": "arm-linux-gnueabihf.mklittlefs-fe5bb56.1578453304.tar.gz",
+ "checksum": "SHA-256:092555612e7e229fbe622df75db70560896c3aea8d0ac2e5fa16d92dc16857cf",
+ "size": "36917"
+ },
+ {
+ "host": "i686-pc-linux-gnu",
+ "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/i686-linux-gnu.mklittlefs-fe5bb56.1578453304.tar.gz",
+ "archiveFileName": "i686-linux-gnu.mklittlefs-fe5bb56.1578453304.tar.gz",
+ "checksum": "SHA-256:060e2525223269d2a5d01055542ff36837f0b19598d78cb02d58563aeda441cd",
+ "size": "47833"
},
{
"host": "i686-mingw32",
- "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/i686-w64-mingw32-mklittlefs-69bd9e6.zip",
- "archiveFileName": "i686-w64-mingw32-mklittlefs-69bd9e6.zip",
- "checksum": "SHA-256:da916c66f70e162f4aec22dbcb4542dd8b8187d12c35c915d563e2262cfe6fbd",
- "size": "332325"
+ "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/i686-w64-mingw32.mklittlefs-fe5bb56.1578453304.zip",
+ "archiveFileName": "i686-w64-mingw32.mklittlefs-fe5bb56.1578453304.zip",
+ "checksum": "SHA-256:2e570bed4ec59a9ecc73290e16c31ed53ee15e3abd8c82cb038b2148596d112e",
+ "size": "332329"
},
{
"host": "x86_64-apple-darwin",
- "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-apple-darwin14-mklittlefs-69bd9e6.tar.gz",
- "archiveFileName": "x86_64-apple-darwin14-mklittlefs-69bd9e6.tar.gz",
- "checksum": "SHA-256:35610be5f725121eaa9baea83c686693f340742e61739af6789d00feff4e90ba",
- "size": "362366"
+ "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-apple-darwin14.mklittlefs-fe5bb56.1578453304.tar.gz",
+ "archiveFileName": "x86_64-apple-darwin14.mklittlefs-fe5bb56.1578453304.tar.gz",
+ "checksum": "SHA-256:fcb57ff58eceac79e988cc26a9e009a11ebda68d4ae97e44fed8e7c6d98a35b5",
+ "size": "362389"
},
{
"host": "x86_64-pc-linux-gnu",
- "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-linux-gnu-mklittlefs-69bd9e6.tar.gz",
- "archiveFileName": "x86_64-linux-gnu-mklittlefs-69bd9e6.tar.gz",
- "checksum": "SHA-256:e4ce7cc80eceab6a9a2e620f2badfb1ef09ee88f7af529f290c65b4b72f19358",
- "size": "46518"
+ "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-linux-gnu.mklittlefs-fe5bb56.1578453304.tar.gz",
+ "archiveFileName": "x86_64-linux-gnu.mklittlefs-fe5bb56.1578453304.tar.gz",
+ "checksum": "SHA-256:5ef79d76e8e76f8287dc70d10c33f020d4cf5320354571adf666665eeef2e2de",
+ "size": "46580"
},
{
"host": "x86_64-mingw32",
- "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-w64-mingw32-mklittlefs-69bd9e6.zip",
- "archiveFileName": "x86_64-w64-mingw32-mklittlefs-69bd9e6.zip",
- "checksum": "SHA-256:c65ee1ee38f65ce67f664bb3118301ee6e93bec38a7a7efaf8e1d8455c6a4a18",
- "size": "344780"
+ "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-w64-mingw32.mklittlefs-fe5bb56.1578453304.zip",
+ "archiveFileName": "x86_64-w64-mingw32.mklittlefs-fe5bb56.1578453304.zip",
+ "checksum": "SHA-256:a460f410a22a59e23d7f862b8d08d6b7dfbc93aa558f8161a3d640d4df2ab86f",
+ "size": "344792"
}
]
}
]
}
]
-}
\ No newline at end of file
+}