From bea64dfa6987655ad9c51fda0949fdd4ab2a7d01 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Sat, 22 Feb 2020 12:49:08 -0800 Subject: [PATCH] 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 --- cores/esp8266/FS.cpp | 22 ++++-- cores/esp8266/FS.h | 5 +- cores/esp8266/FSImpl.h | 11 +-- doc/filesystem.rst | 10 +++ .../LittleFS_Timestamp/LittleFS_Timestamp.ino | 8 +- libraries/LittleFS/src/LittleFS.cpp | 17 ++++- libraries/LittleFS/src/LittleFS.h | 76 ++++++++++++------- libraries/SD/examples/listfiles/listfiles.ino | 14 ++-- libraries/SDFS/src/SDFS.h | 35 +++++++++ .../package_esp8266com_index.template.json | 61 ++++++++------- 10 files changed, 176 insertions(+), 83 deletions(-) diff --git a/cores/esp8266/FS.cpp b/cores/esp8266/FS.cpp index fa841c64f..392955d49 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::getCreationTime() { + if (!_p) + return 0; + + return _p->getCreationTime(); +} + void File::setTimeCallback(time_t (*cb)(void)) { if (!_p) return; @@ -224,6 +231,12 @@ time_t Dir::fileTime() { return _impl->fileTime(); } +time_t Dir::fileCreationTime() { + if (!_impl) + return 0; + return _impl->fileCreationTime(); +} + 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 23d05bad8..aa4752781 100644 --- a/cores/esp8266/FS.h +++ b/cores/esp8266/FS.h @@ -112,6 +112,7 @@ public: String readString() override; time_t getLastWrite(); + time_t getCreationTime(); void setTimeCallback(time_t (*cb)(void)); protected: @@ -120,7 +121,6 @@ protected: // Arduino SD class emulation std::shared_ptr _fakeDir; FS *_baseFS; - time_t (*timeCallback)(void) = nullptr; }; class Dir { @@ -132,20 +132,19 @@ public: String fileName(); size_t fileSize(); time_t fileTime(); + time_t fileCreationTime(); 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 9715c65a8..1a3566f2c 100644 --- a/cores/esp8266/FSImpl.h +++ b/cores/esp8266/FSImpl.h @@ -51,6 +51,8 @@ public: // 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 getCreationTime() { return 0; } // Default is to not support timestamps protected: time_t (*timeCallback)(void) = nullptr; @@ -75,7 +77,11 @@ public: 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 fileCreationTime() { 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 @@ public: // 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 fecd84074..abf47a7ea 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. +fileCreationTime +~~~~~~~~~~~~~~~~ +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. +getCreationTime +~~~~~~~~~~~~~~~ + +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 ce85de034..dfec9ee30 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.getCreationTime(); + 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 83041d222..b85075112 100644 --- a/libraries/LittleFS/src/LittleFS.cpp +++ b/libraries/LittleFS/src/LittleFS.cpp @@ -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 e332fd9ac..4d417905e 100644 --- a/libraries/LittleFS/src/LittleFS.h +++ b/libraries/LittleFS/src/LittleFS.h @@ -89,7 +89,7 @@ public: DirImplPtr openDir(const char *path) override; bool exists(const char* path) override { - if ( !_mounted || !path || !path[0] ) { + if (!_mounted || !path || !path[0]) { return false; } lfs_info info; @@ -98,7 +98,7 @@ public: } bool rename(const char* pathFrom, const char* pathTo) override { - if (!_mounted || !pathFrom || !pathFrom[0] || !pathTo || !pathTo[0]) { + if (!_mounted || !pathFrom || !pathFrom[0] || !pathTo || !pathTo[0]) { return false; } int rc = lfs_rename(&_lfs, pathFrom, pathTo); @@ -323,7 +323,7 @@ protected: 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); } @@ -419,13 +419,20 @@ public: lfs_file_close(_fs->getFS(), _getFD()); _opened = false; DEBUGV("lfs_file_close: fd=%p\n", _getFD()); - if (timeCallback && (_flags & LFS_O_WRONLY)) { + 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 @@ public: return ftime; } + time_t getCreationTime() 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 @@ protected: std::shared_ptr _name; bool _opened; int _flags; + time_t _creation; }; class LittleFSDirImpl : public DirImpl @@ -512,13 +530,9 @@ public: int nameLen = 3; // Slashes, terminator nameLen += _dirPath.get() ? strlen(_dirPath.get()) : 0; nameLen += strlen(_dirent.name); - char *tmpName = (char*)malloc(nameLen); - if (!tmpName) { - return FileImplPtr(); - } - snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name); + char tmpName[nameLen]; + snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name); auto ret = _fs->open((const char *)tmpName, openMode, accessMode); - free(tmpName); return ret; } @@ -537,23 +551,11 @@ public: } 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 fileCreationTime() override { + return (time_t)_getAttr4('c'); } @@ -592,6 +594,22 @@ protected: 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[nameLen]; + 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 + return ftime; + } + String _pattern; LittleFSImpl *_fs; std::shared_ptr _dir; diff --git a/libraries/SD/examples/listfiles/listfiles.ino b/libraries/SD/examples/listfiles/listfiles.ino index fd35d0c33..55478d708 100644 --- a/libraries/SD/examples/listfiles/listfiles.ino +++ b/libraries/SD/examples/listfiles/listfiles.ino @@ -29,9 +29,6 @@ File root; void setup() { // Open serial communications and wait for port to open: Serial.begin(115200); - while (!Serial) { - ; // wait for serial port to connect. Needed for Leonardo only - } Serial.print("Initializing SD card..."); @@ -71,11 +68,12 @@ void printDirectory(File dir, int numTabs) { // files have sizes, directories do not Serial.print("\t\t"); Serial.print(entry.size(), DEC); - Serial.print("\t\t"); - time_t ft = entry.getLastWrite(); - struct tm *tm = localtime(&ft); - // US format. Feel free to convert to your own locale... - 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); + time_t cr = entry.getCreationTime(); + time_t lw = entry.getLastWrite(); + struct tm * tmstruct = localtime(&cr); + 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); + tmstruct = localtime(&lw); + 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); } entry.close(); } diff --git a/libraries/SDFS/src/SDFS.h b/libraries/SDFS/src/SDFS.h index ee772cd5c..a884552ff 100644 --- a/libraries/SDFS/src/SDFS.h +++ b/libraries/SDFS/src/SDFS.h @@ -156,6 +156,7 @@ public: format(); _mounted = _fs.begin(_cfg._csPin, _cfg._spiSettings); } + sdfat::SdFile::dateTimeCallback(dateTimeCB); return _mounted; } @@ -204,6 +205,17 @@ public: return mktime(&tiempo); } + // Because SdFat has a single, global setting for this we can only use a + // static member of our class to return the time/date. However, since + // this is static, we can't see the time callback variable. Punt for now, + // using time(NULL) as the best we can do. + static void dateTimeCB(uint16_t *dosYear, uint16_t *dosTime) { + time_t now = time(nullptr); + struct tm *tiempo = localtime(&now); + *dosYear = ((tiempo->tm_year - 80) << 9) | ((tiempo->tm_mon + 1) << 5) | tiempo->tm_mday; + *dosTime = (tiempo->tm_hour << 11) | (tiempo->tm_min << 5) | tiempo->tm_sec; + } + protected: friend class SDFileImpl; friend class SDFSDirImpl; @@ -363,6 +375,18 @@ public: return ftime; } + time_t getCreationTime() 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 +450,14 @@ public: return _time; } + time_t fileCreationTime() override + { + if (!_valid) { + return 0; + } + + return _creation; + } bool isFile() const override { @@ -451,8 +483,10 @@ public: 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 +511,7 @@ protected: 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 847eb4a12..c541cd192 100644 --- a/package/package_esp8266com_index.template.json +++ b/package/package_esp8266com_index.template.json @@ -130,7 +130,7 @@ }, { "packager": "esp8266", - "version": "2.5.0-4-69bd9e6", + "version": "2.5.0-4-fe5bb56", "name": "mklittlefs" }, { @@ -311,22 +311,29 @@ ] }, { - "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-pc-linux-gnu", @@ -337,35 +344,35 @@ }, { "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 +}