Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fileCreation/getCreation create-time accessors #7000

Merged
merged 12 commits into from
Feb 22, 2020
Merged
22 changes: 15 additions & 7 deletions cores/esp8266/FS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}


Expand All @@ -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;
Expand Down
5 changes: 2 additions & 3 deletions cores/esp8266/FS.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ class File : public Stream
String readString() override;

time_t getLastWrite();
time_t getCreationTime();
void setTimeCallback(time_t (*cb)(void));

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

class Dir {
Expand All @@ -132,20 +132,19 @@ class Dir {
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
Expand Down
11 changes: 6 additions & 5 deletions cores/esp8266/FSImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 getCreationTime() { return 0; } // Default is to not support timestamps

protected:
time_t (*timeCallback)(void) = nullptr;
Expand All @@ -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 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;
Expand All @@ -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;
};
Expand Down
10 changes: 10 additions & 0 deletions doc/filesystem.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
~~~~~~

Expand Down Expand Up @@ -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
~~~~~~

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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();
}

Expand Down
17 changes: 15 additions & 2 deletions libraries/LittleFS/src/LittleFS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<LittleFSFileImpl>(this, path, nullptr, flags);
return std::make_shared<LittleFSFileImpl>(this, path, nullptr, flags, creation);
} else if (rc == 0) {
return std::make_shared<LittleFSFileImpl>(this, path, fd, flags);
return std::make_shared<LittleFSFileImpl>(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);
Expand Down
76 changes: 47 additions & 29 deletions libraries/LittleFS/src/LittleFS.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class LittleFSImpl : public FSImpl
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;
Expand All @@ -98,7 +98,7 @@ class LittleFSImpl : public FSImpl
}

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);
Expand Down Expand Up @@ -323,7 +323,7 @@ class LittleFSImpl : public FSImpl
class LittleFSFileImpl : public FileImpl
{
public:
LittleFSFileImpl(LittleFSImpl* fs, const char *name, std::shared_ptr<lfs_file_t> fd, int flags) : _fs(fs), _fd(fd), _opened(true), _flags(flags) {
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) {
_name = std::shared_ptr<char>(new char[strlen(name) + 1], std::default_delete<char[]>());
strcpy(_name.get(), name);
}
Expand Down Expand Up @@ -419,13 +419,20 @@ class LittleFSFileImpl : public FileImpl
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);
}
}
}
}
Expand All @@ -440,6 +447,16 @@ class LittleFSFileImpl : public FileImpl
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;
Expand Down Expand Up @@ -484,6 +501,7 @@ class LittleFSFileImpl : public FileImpl
std::shared_ptr<char> _name;
bool _opened;
int _flags;
time_t _creation;
};

class LittleFSDirImpl : public DirImpl
Expand Down Expand Up @@ -512,13 +530,9 @@ class LittleFSDirImpl : public DirImpl
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;
}

Expand All @@ -537,23 +551,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 fileCreationTime() override {
return (time_t)_getAttr4('c');
}


Expand Down Expand Up @@ -592,6 +594,22 @@ 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[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<lfs_dir_t> _dir;
Expand Down
14 changes: 6 additions & 8 deletions libraries/SD/examples/listfiles/listfiles.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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...");

Expand Down Expand Up @@ -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();
}
Expand Down
Loading