1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-21 10:26:06 +03:00

Add time to filesystem API (#6544)

* Add time to filesystem API

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.

This PR does not update SPIFFS, due to compatability concerns and a
possible massive rewrite which would make it possible to determine if an
old-style ot metadata enabled FS is present at mount time.

Includes an updated SD/listfiles and LittleFS_time example.

Replaces #6315

* Add links to new mklittlefs w/timestamp support

Include the update mklittlefs which generated 't' metadata on imported
files.
	../tools/sdk/lwip2/include/netif/lowpan6_opts.h

* Add explicit note about timestamp being local time

* Address review concerns

Clean up some awkward object instantiations.

Remove the _enableTime flag/setter from SPIFFS.

Clean up the FSConfig constructors using C++ style init lists.
This commit is contained in:
Earle F. Philhower, III 2019-10-31 06:09:52 -07:00 committed by david gauchard
parent b4c28e74d6
commit 72dd589599
12 changed files with 576 additions and 114 deletions

View File

@ -180,6 +180,19 @@ String File::readString()
return ret;
}
time_t File::getLastWrite() {
if (!_p)
return 0;
return _p->getLastWrite();
}
void File::setTimeCallback(time_t (*cb)(void)) {
if (!_p)
return;
_p->setTimeCallback(cb);
}
File Dir::openFile(const char* mode) {
if (!_impl) {
return File();
@ -192,7 +205,9 @@ File Dir::openFile(const char* mode) {
return File();
}
return File(_impl->openFile(om, am), _baseFS);
File f(_impl->openFile(om, am), _baseFS);
f.setTimeCallback(timeCallback);
return f;
}
String Dir::fileName() {
@ -203,6 +218,12 @@ String Dir::fileName() {
return _impl->fileName();
}
time_t Dir::fileTime() {
if (!_impl)
return 0;
return _impl->fileTime();
}
size_t Dir::fileSize() {
if (!_impl) {
return 0;
@ -241,6 +262,20 @@ 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);
}
bool FS::setConfig(const FSConfig &cfg) {
if (!_impl) {
return false;
@ -315,7 +350,9 @@ File FS::open(const char* path, const char* mode) {
DEBUGV("FS::open: invalid mode `%s`\r\n", mode);
return File();
}
return File(_impl->open(path, om, am), this);
File f(_impl->open(path, om, am), this);
f.setTimeCallback(timeCallback);
return f;
}
bool FS::exists(const char* path) {
@ -334,7 +371,9 @@ Dir FS::openDir(const char* path) {
return Dir();
}
DirImplPtr p = _impl->openDir(path);
return Dir(p, this);
Dir d(p, this);
d.setTimeCallback(timeCallback);
return d;
}
Dir FS::openDir(const String& path) {
@ -385,6 +424,11 @@ bool FS::rename(const String& pathFrom, const String& pathTo) {
return rename(pathFrom.c_str(), pathTo.c_str());
}
void FS::setTimeCallback(time_t (*cb)(void)) {
if (!_impl)
return;
_impl->setTimeCallback(cb);
}
static bool sflags(const char* mode, OpenMode& om, AccessMode& am) {

View File

@ -110,12 +110,16 @@ public:
String readString() override;
time_t getLastWrite();
void setTimeCallback(time_t (*cb)(void));
protected:
FileImplPtr _p;
// Arduino SD class emulation
std::shared_ptr<Dir> _fakeDir;
FS *_baseFS;
time_t (*timeCallback)(void) = nullptr;
};
class Dir {
@ -126,15 +130,21 @@ public:
String fileName();
size_t fileSize();
time_t fileTime();
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
@ -161,12 +171,10 @@ struct FSInfo64 {
class FSConfig
{
public:
FSConfig(bool autoFormat = true) {
_type = FSConfig::fsid::FSId;
_autoFormat = autoFormat;
}
static constexpr uint32_t FSId = 0x00000000;
FSConfig(uint32_t type = FSId, bool autoFormat = true) : _type(type), _autoFormat(autoFormat) { }
enum fsid { FSId = 0x00000000 };
FSConfig setAutoFormat(bool val = true) {
_autoFormat = val;
return *this;
@ -179,17 +187,17 @@ public:
class SPIFFSConfig : public FSConfig
{
public:
SPIFFSConfig(bool autoFormat = true) {
_type = SPIFFSConfig::fsid::FSId;
_autoFormat = autoFormat;
}
enum fsid { FSId = 0x53504946 };
static constexpr uint32_t FSId = 0x53504946;
SPIFFSConfig(bool autoFormat = true) : FSConfig(FSId, autoFormat) { }
// Inherit _type and _autoFormat
// nothing yet, enableTime TBD when SPIFFS has metadate
};
class FS
{
public:
FS(FSImplPtr impl) : _impl(impl) { }
FS(FSImplPtr impl) : _impl(impl) { timeCallback = _defaultTimeCB; }
bool setConfig(const FSConfig &cfg);
@ -225,10 +233,14 @@ public:
bool gc();
bool check();
void setTimeCallback(time_t (*cb)(void));
friend class ::SDClass; // More of a frenemy, but SD needs internal implementation to get private FAT bits
protected:
FSImplPtr _impl;
FSImplPtr getImpl() { return _impl; }
time_t (*timeCallback)(void);
static time_t _defaultTimeCB(void) { return time(NULL); }
};
} // namespace fs

View File

@ -41,6 +41,19 @@ public:
virtual const char* fullName() const = 0;
virtual bool isFile() const = 0;
virtual bool isDirectory() const = 0;
// Filesystems *may* support a timestamp per-file, so allow the user to override with
// their own callback for *this specific* file (as opposed to the FSImpl call of the
// 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;
};
enum OpenMode {
@ -62,10 +75,24 @@ public:
virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0;
virtual const char* fileName() = 0;
virtual size_t fileSize() = 0;
virtual time_t fileTime() { return 0; } // By default, FS doesn't report file times
virtual bool isFile() const = 0;
virtual bool isDirectory() const = 0;
virtual bool next() = 0;
virtual bool rewind() = 0;
// Filesystems *may* support a timestamp per-file, so allow the user to override with
// their own callback for *this specific* file (as opposed to the FSImpl call of the
// 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;
};
class FSImpl {
@ -86,6 +113,14 @@ public:
virtual bool rmdir(const char* path) = 0;
virtual bool gc() { return true; } // May not be implemented in all file systems.
virtual bool check() { return true; } // May not be implemented in all file systems.
// Filesystems *may* support a timestamp per-file, so allow the user to override with
// their own callback for all files on this FS. The default implementation simply
// returns the present time as reported by time(&null)
virtual void setTimeCallback(time_t (*cb)(void)) { timeCallback = cb; }
protected:
time_t (*timeCallback)(void) = nullptr;
};
} // namespace fs

View File

@ -162,7 +162,7 @@ public:
bool setConfig(const FSConfig &cfg) override
{
if ((cfg._type != SPIFFSConfig::fsid::FSId) || (SPIFFS_mounted(&_fs) != 0)) {
if ((cfg._type != SPIFFSConfig::FSId) || (SPIFFS_mounted(&_fs) != 0)) {
return false;
}
_cfg = *static_cast<const SPIFFSConfig *>(&cfg);

View File

@ -92,6 +92,27 @@ and ``SPIFFS.open()`` to ``LittleFS.open()`` with the rest of the
code remaining untouched.
SDFS and SD
-----------
FAT filesystems are supported on the ESP8266 using the old Arduino wrapper
"SD.h" which wraps the "SDFS.h" filesystem transparently.
Any commands discussed below pertaining to SPIFFS or LittleFS are
applicable to SD/SDFS.
For legacy applications, the classic SD filesystem may continue to be used,
but for new applications, directly accessing the SDFS filesystem is
recommended as it may expose additional functionality that the old Arduino
SD filesystem didn't have.
Note that in earlier releases of the core, using SD and SPIFFS in the same
sketch was complicated and required the use of ``NO_FS_GLOBALS``. The
current design makes SD, SDFS, SPIFFS, and LittleFS fully source compatible
and so please remove any ``NO_FS_GLOBALS`` definitions in your projects
when updgrading core versions.
SPIFFS file system limitations
------------------------------
@ -198,8 +219,8 @@ use esptool.py.
- To upload a LittleFS filesystem use Tools > ESP8266 LittleFS Data Upload
File system object (SPIFFS/LittleFS)
------------------------------------
File system object (SPIFFS/LittleFS/SD/SDFS)
--------------------------------------------
setConfig
~~~~~~~~~
@ -369,41 +390,6 @@ rename
Renames file from ``pathFrom`` to ``pathTo``. Paths must be absolute.
Returns *true* if file was renamed successfully.
info
~~~~
.. code:: cpp
FSInfo fs_info;
SPIFFS.info(fs_info);
or LittleFS.info(fs_info);
Fills `FSInfo structure <#filesystem-information-structure>`__ with
information about the file system. Returns ``true`` is successful,
``false`` otherwise.
Filesystem information structure
--------------------------------
.. code:: cpp
struct FSInfo {
size_t totalBytes;
size_t usedBytes;
size_t blockSize;
size_t pageSize;
size_t maxOpenFiles;
size_t maxPathLength;
};
This is the structure which may be filled using FS::info method. -
``totalBytes`` — total size of useful data on the file system -
``usedBytes`` — number of bytes used by files - ``blockSize`` — filesystem
block size - ``pageSize`` — filesystem logical page size - ``maxOpenFiles``
— max number of files which may be open simultaneously -
``maxPathLength`` — max file name length (including one byte for zero
termination)
gc
~~
@ -429,6 +415,81 @@ Only implemented in SPIFFS. Performs an in-depth check of the filesystem metada
correct what is repairable. Not normally needed, and not guaranteed to actually fix
anything should there be corruption.
info
~~~~
.. code:: cpp
FSInfo fs_info;
SPIFFS.info(fs_info);
or LittleFS.info(fs_info);
Fills `FSInfo structure <#filesystem-information-structure>`__ with
information about the file system. Returns ``true`` if successful,
``false`` otherwise.
Filesystem information structure
--------------------------------
.. code:: cpp
struct FSInfo {
size_t totalBytes;
size_t usedBytes;
size_t blockSize;
size_t pageSize;
size_t maxOpenFiles;
size_t maxPathLength;
};
This is the structure which may be filled using FS::info method. -
``totalBytes`` — total size of useful data on the file system -
``usedBytes`` — number of bytes used by files - ``blockSize`` — filesystem
block size - ``pageSize`` — filesystem logical page size - ``maxOpenFiles``
— max number of files which may be open simultaneously -
``maxPathLength`` — max file name length (including one byte for zero
termination)
info64
~~~~~~
.. code:: cpp
FSInfo64 fsinfo;
SD.info(fsinfo);
or LittleFS(fsinfo);
Performs the same operation as ``info`` but allows for reporting greater than
4GB for filesystem size/used/etc. Should be used with the SD and SDFS
filesystems since most SD cards today are greater than 4GB in size.
setTimeCallback(time_t (*cb)(void))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: cpp
time_t myTimeCallback() {
return 1455451200; // UNIX timestamp
}
void setup () {
LittleFS.setTimeCallback(myTimeCallback);
...
// Any files will now be made with Pris' incept date
}
The SD, SDFS, and LittleFS filesystems support a file timestamp, updated when the file is
opened for writing. By default, the ESP8266 will use the internal time returned from
``time(NULL)`` (i.e. local time, not UTC, to conform to the existing FAT filesystem), but this
can be overridden to GMT or any other standard you'd like by using ``setTimeCallback()``.
If your app sets the system time using NTP before file operations, then
you should not need to use this function. However, if you need to set a specific time
for a file, or the system clock isn't correct and you need to read the time from an external
RTC or use a fixed time, this call allows you do to so.
In general use, with a functioning ``time()`` call, user applications should not need
to use this function.
Directory object (Dir)
----------------------
@ -468,6 +529,12 @@ fileSize
Returns the size of the current file pointed to
by the internal iterator.
fileTime
~~~~~~~~
Returns the time_t write time of the current file pointed
to by the internal iterator.
isFile
~~~~~~
@ -491,6 +558,13 @@ rewind
Resets the internal pointer to the start of the directory.
setTimeCallback(time_t (*cb)(void))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets the time callback for any files accessed from this Dir object via openNextFile.
Note that the SD and SDFS filesystems only support a filesystem-wide callback and
calls to ``Dir::setTimeCallback`` may produce unexpected behavior.
File object
-----------
@ -562,6 +636,12 @@ fullName
Returns the full path file name as a ``const char*``.
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.
isFile
~~~~~~
@ -616,3 +696,10 @@ rewindDirectory (compatibiity method, not recommended for new code)
Resets the ``openNextFile`` pointer to the top of the directory. Only
valid when ``File.isDirectory() == true``.
setTimeCallback(time_t (*cb)(void))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets the time callback for this specific file. Note that the SD and
SDFS filesystems only support a filesystem-wide callback and calls to
``Dir::setTimeCallback`` may produce unexpected behavior.

View File

@ -0,0 +1,181 @@
/* Example showing timestamp support in LittleFS */
/* Released into the public domain. */
/* Earle F. Philhower, III <earlephilhower@yahoo.com> */
#include <FS.h>
#include <LittleFS.h>
#include <time.h>
#include <ESP8266WiFi.h>
#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK "your-password"
#endif
const char *ssid = STASSID;
const char *pass = STAPSK;
long timezone = 2;
byte daysavetime = 1;
bool getLocalTime(struct tm * info, uint32_t ms) {
uint32_t count = ms / 10;
time_t now;
time(&now);
localtime_r(&now, info);
if (info->tm_year > (2016 - 1900)) {
return true;
}
while (count--) {
delay(10);
time(&now);
localtime_r(&now, info);
if (info->tm_year > (2016 - 1900)) {
return true;
}
}
return false;
}
void listDir(const char * dirname) {
Serial.printf("Listing directory: %s\n", dirname);
Dir root = LittleFS.openDir(dirname);
while (root.next()) {
File file = root.openFile("r");
Serial.print(" FILE: ");
Serial.print(root.fileName());
Serial.print(" SIZE: ");
Serial.print(file.size());
time_t t = file.getLastWrite();
struct tm * tmstruct = localtime(&t);
file.close();
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);
}
}
void readFile(const char * path) {
Serial.printf("Reading file: %s\n", path);
File file = LittleFS.open(path, "r");
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while (file.available()) {
Serial.write(file.read());
}
file.close();
}
void writeFile(const char * path, const char * message) {
Serial.printf("Writing file: %s\n", path);
File file = LittleFS.open(path, "w");
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file.print(message)) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
file.close();
}
void appendFile(const char * path, const char * message) {
Serial.printf("Appending to file: %s\n", path);
File file = LittleFS.open(path, "a");
if (!file) {
Serial.println("Failed to open file for appending");
return;
}
if (file.print(message)) {
Serial.println("Message appended");
} else {
Serial.println("Append failed");
}
file.close();
}
void renameFile(const char * path1, const char * path2) {
Serial.printf("Renaming file %s to %s\n", path1, path2);
if (LittleFS.rename(path1, path2)) {
Serial.println("File renamed");
} else {
Serial.println("Rename failed");
}
}
void deleteFile(const char * path) {
Serial.printf("Deleting file: %s\n", path);
if (LittleFS.remove(path)) {
Serial.println("File deleted");
} else {
Serial.println("Delete failed");
}
}
void setup() {
Serial.begin(115200);
// We start by connecting to a WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("Contacting Time Server");
configTime(3600 * timezone, daysavetime * 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
struct tm tmstruct ;
delay(2000);
tmstruct.tm_year = 0;
getLocalTime(&tmstruct, 5000);
Serial.printf("\nNow is : %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);
Serial.println("");
Serial.printf("Formatting LittleFS filesystem\n");
LittleFS.format();
listDir("/");
deleteFile("/hello.txt");
writeFile("/hello.txt", "Hello ");
appendFile("/hello.txt", "World!\n");
listDir("/");
Serial.printf("The timestamp should be valid above\n");
Serial.printf("Now unmount and remount and perform the same operation.\n");
Serial.printf("Timestamp should be valid, data should be good.\n");
LittleFS.end();
Serial.printf("Now mount it\n");
if (!LittleFS.begin()) {
Serial.println("LittleFS mount failed");
return;
}
readFile("/hello.txt");
listDir("/");
}
void loop() { }

View File

@ -47,11 +47,8 @@ class LittleFSDirImpl;
class LittleFSConfig : public FSConfig
{
public:
LittleFSConfig(bool autoFormat = true) {
_type = LittleFSConfig::fsid::FSId;
_autoFormat = autoFormat;
}
enum fsid { FSId = 0x4c495454 };
static constexpr uint32_t FSId = 0x4c495454;
LittleFSConfig(bool autoFormat = true) : FSConfig(FSId, autoFormat) { }
};
class LittleFSImpl : public FSImpl
@ -176,7 +173,7 @@ public:
}
bool setConfig(const FSConfig &cfg) override {
if ((cfg._type != LittleFSConfig::fsid::FSId) || _mounted) {
if ((cfg._type != LittleFSConfig::FSId) || _mounted) {
return false;
}
_cfg = *static_cast<const LittleFSConfig *>(&cfg);
@ -422,8 +419,26 @@ public:
lfs_file_close(_fs->getFS(), _getFD());
_opened = false;
DEBUGV("lfs_file_close: fd=%p\n", _getFD());
if (timeCallback) {
// 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);
}
}
}
}
time_t getLastWrite() override {
time_t ftime = 0;
if (_opened && _fd) {
int rc = lfs_getattr(_fs->getFS(), _name.get(), 't', (void *)&ftime, sizeof(ftime));
if (rc != sizeof(ftime))
ftime = 0; // Error, so clear read value
}
return ftime;
}
const char* name() const override {
if (!_opened) {
@ -520,6 +535,27 @@ public:
return _dirent.size;
}
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;
}
bool isFile() const override {
return _valid && (_dirent.type == LFS_TYPE_REG);
}

View File

@ -28,14 +28,14 @@ File root;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
Serial.print("Initializing SD card...");
if (!SD.begin(4)) {
if (!SD.begin(SS)) {
Serial.println("initialization failed!");
return;
}
@ -70,11 +70,13 @@ void printDirectory(File dir, int numTabs) {
} else {
// files have sizes, directories do not
Serial.print("\t\t");
Serial.println(entry.size(), DEC);
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);
}
entry.close();
}
}

View File

@ -3,3 +3,5 @@
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD)
SDClass SD;
#endif
void (*__SD__userDateTimeCB)(uint16_t*, uint16_t*) = nullptr;

View File

@ -29,6 +29,7 @@
#undef FILE_WRITE
#define FILE_WRITE (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT | sdfat::O_APPEND)
class SDClass {
public:
boolean begin(uint8_t csPin, SPISettings cfg = SPI_HALF_SPEED) {
@ -137,6 +138,17 @@ public:
return ((uint64_t)clusterSize() * (uint64_t)totalClusters());
}
void setTimeCallback(time_t (*cb)(void)) {
SDFS.setTimeCallback(cb);
}
// Wrapper to allow obsolete datetimecallback use, silently convert to time_t in wrappertimecb
void dateTimeCallback(void (*cb)(uint16_t*, uint16_t*)) {
extern void (*__SD__userDateTimeCB)(uint16_t*, uint16_t*);
__SD__userDateTimeCB = cb;
SDFS.setTimeCallback(wrapperTimeCB);
}
private:
const char *getMode(uint8_t mode) {
bool read = (mode & sdfat::O_READ) ? true : false;
@ -150,8 +162,46 @@ private:
else { return "r"; }
}
static time_t wrapperTimeCB(void) {
extern void (*__SD__userDateTimeCB)(uint16_t*, uint16_t*);
if (__SD__userDateTimeCB) {
uint16_t d, t;
__SD__userDateTimeCB(&d, &t);
return sdfs::SDFSImpl::FatToTimeT(d, t);
}
return time(nullptr);
}
};
// Expose FatStructs.h helpers for MSDOS date/time for use with dateTimeCallback
static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) {
return (year - 1980) << 9 | month << 5 | day;
}
static inline uint16_t FAT_YEAR(uint16_t fatDate) {
return 1980 + (fatDate >> 9);
}
static inline uint8_t FAT_MONTH(uint16_t fatDate) {
return (fatDate >> 5) & 0XF;
}
static inline uint8_t FAT_DAY(uint16_t fatDate) {
return fatDate & 0X1F;
}
static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) {
return hour << 11 | minute << 5 | second >> 1;
}
static inline uint8_t FAT_HOUR(uint16_t fatTime) {
return fatTime >> 11;
}
static inline uint8_t FAT_MINUTE(uint16_t fatTime) {
return (fatTime >> 5) & 0X3F;
}
static inline uint8_t FAT_SECOND(uint16_t fatTime) {
return 2*(fatTime & 0X1F);
}
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD)
extern SDClass SD;
#endif

View File

@ -45,22 +45,9 @@ class SDFSDirImpl;
class SDFSConfig : public FSConfig
{
public:
SDFSConfig() {
_type = SDFSConfig::fsid::FSId;
_autoFormat = false;
_csPin = 4;
_spiSettings = SD_SCK_MHZ(10);
_part = 0;
}
SDFSConfig(uint8_t csPin, SPISettings spi) {
_type = SDFSConfig::fsid::FSId;
_autoFormat = false;
_csPin = csPin;
_spiSettings = spi;
_part = 0;
}
static constexpr uint32_t FSId = 0x53444653;
enum fsid { FSId = 0x53444653 };
SDFSConfig(uint8_t csPin = 4, SPISettings spi = SD_SCK_MHZ(10)) : FSConfig(FSId, false), _csPin(csPin), _part(0), _spiSettings(spi) { }
SDFSConfig setAutoFormat(bool val = true) {
_autoFormat = val;
@ -152,7 +139,7 @@ public:
bool setConfig(const FSConfig &cfg) override
{
if ((cfg._type != SDFSConfig::fsid::FSId) || _mounted) {
if ((cfg._type != SDFSConfig::FSId) || _mounted) {
DEBUGV("SDFS::setConfig: invalid config or already mounted\n");
return false;
}
@ -203,6 +190,20 @@ public:
return (clusterSize() * totalClusters());
}
// Helper function, takes FAT and makes standard time_t
static time_t FatToTimeT(uint16_t d, uint16_t t) {
struct tm tiempo;
memset(&tiempo, 0, sizeof(tiempo));
tiempo.tm_sec = (((int)t) << 1) & 0x3e;
tiempo.tm_min = (((int)t) >> 5) & 0x3f;
tiempo.tm_hour = (((int)t) >> 11) & 0x1f;
tiempo.tm_mday = (int)(d & 0x1f);
tiempo.tm_mon = ((int)(d >> 5) & 0x0f) - 1;
tiempo.tm_year = ((int)(d >> 9) & 0x7f) + 80;
tiempo.tm_isdst = -1;
return mktime(&tiempo);
}
protected:
friend class SDFileImpl;
friend class SDFSDirImpl;
@ -212,6 +213,7 @@ protected:
return &_fs;
}
static uint8_t _getFlags(OpenMode openMode, AccessMode accessMode) {
uint8_t mode = 0;
if (openMode & OM_CREATE) {
@ -350,6 +352,17 @@ public:
return _opened ? _fd->isDirectory() : false;
}
time_t getLastWrite() override {
time_t ftime = 0;
if (_opened && _fd) {
sdfat::dir_t tmp;
if (_fd.get()->dirEntry(&tmp)) {
ftime = SDFSImpl::FatToTimeT(tmp.lastWriteDate, tmp.lastWriteTime);
}
}
return ftime;
}
protected:
SDFSImpl* _fs;
@ -425,6 +438,12 @@ public:
_size = file.fileSize();
_isFile = file.isFile();
_isDirectory = file.isDirectory();
sdfat::dir_t tmp;
if (file.dirEntry(&tmp)) {
_time = SDFSImpl::FatToTimeT(tmp.lastWriteDate, tmp.lastWriteTime);
} else {
_time = 0;
}
file.getName(_lfn, sizeof(_lfn));
file.close();
} else {
@ -447,6 +466,7 @@ protected:
std::shared_ptr<sdfat::File> _dir;
bool _valid;
char _lfn[64];
time_t _time;
std::shared_ptr<char> _dirPath;
uint32_t _size;
bool _isFile;

View File

@ -121,7 +121,7 @@
},
{
"packager": "esp8266",
"version": "2.5.0-4-b40a506",
"version": "2.5.0-4-69bd9e6",
"name": "mklittlefs"
},
{
@ -302,57 +302,50 @@
]
},
{
"version": "2.5.0-4-b40a506",
"version": "2.5.0-4-69bd9e6",
"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-7f77f2b.1563313032.tar.gz",
"archiveFileName": "aarch64-linux-gnu.mklittlefs-7f77f2b.1563313032.tar.gz",
"checksum": "SHA-256:25c4dcf818d175e19c3cc22bc0388c61fa3d9bdf82a1fad388323cef34caa169",
"size": "44059"
"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"
},
{
"host": "arm-linux-gnueabihf",
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/arm-linux-gnueabihf.mklittlefs-7f77f2b.1563313032.tar.gz",
"archiveFileName": "arm-linux-gnueabihf.mklittlefs-7f77f2b.1563313032.tar.gz",
"checksum": "SHA-256:75a284f4e8c54d302b1880df46dd48e18857f69c21baa0977b1e6efc404caf18",
"size": "36567"
},
{
"host": "i686-pc-linux-gnu",
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/i686-linux-gnu.mklittlefs-7f77f2b.1563313032.tar.gz",
"archiveFileName": "i686-linux-gnu.mklittlefs-7f77f2b.1563313032.tar.gz",
"checksum": "SHA-256:022c96df4d110f957d43f6d23e9c5e8b699a66d8ab041056dd5da7411a8ade42",
"size": "47544"
"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"
},
{
"host": "i686-mingw32",
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/i686-w64-mingw32.mklittlefs-7f77f2b.1563313032.zip",
"archiveFileName": "i686-w64-mingw32.mklittlefs-7f77f2b.1563313032.zip",
"checksum": "SHA-256:7778209e9df8c8c5f5da82660ff9a95b866defee3c9eb5c22371e0fd84b1addc",
"size": "332057"
"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"
},
{
"host": "x86_64-apple-darwin",
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-apple-darwin14.mklittlefs-7f77f2b.1563313032.tar.gz",
"archiveFileName": "x86_64-apple-darwin14.mklittlefs-7f77f2b.1563313032.tar.gz",
"checksum": "SHA-256:c465da766026c6c66d731442b741fb5a7f8b741e9473d181e6c5e588c541f588",
"size": "362014"
"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"
},
{
"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-7f77f2b.1563313032.tar.gz",
"archiveFileName": "x86_64-linux-gnu.mklittlefs-7f77f2b.1563313032.tar.gz",
"checksum": "SHA-256:6a358716d4c780fa459b4c774723302431b3ad5e1ee3f7edae62be331541615c",
"size": "46164"
"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"
},
{
"host": "x86_64-mingw32",
"url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/x86_64-w64-mingw32.mklittlefs-7f77f2b.1563313032.zip",
"archiveFileName": "x86_64-w64-mingw32.mklittlefs-7f77f2b.1563313032.zip",
"checksum": "SHA-256:d5d44b5f21681a831318a23b31957bc9368c50f0766964ead409c3d2fe4747d2",
"size": "344578"
"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"
}
]
}