Skip to content

Commit

Permalink
Add fileCreation/getCreation create-time accessors
Browse files Browse the repository at this point in the history
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
  • Loading branch information
earlephilhower committed Jan 8, 2020
1 parent 8242d72 commit cafc27f
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 67 deletions.
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::getCreation() {
if (!_p)
return 0;

return _p->getCreation();
}

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::fileCreation() {
if (!_impl)
return 0;
return _impl->fileCreation();
}

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 getCreation();
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 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
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 getCreation() { 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 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;
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.

fileCreation
~~~~~~~~~~~~
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.

getCreation
~~~~~~~~~~~

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.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);
}
}
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
19 changes: 16 additions & 3 deletions libraries/LittleFS/src/LittleFS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ FileImplPtr LittleFSImpl::open(const char* path, OpenMode openMode, AccessMode a
int flags = _getFlags(openMode, accessMode);
auto fd = std::make_shared<lfs_file_t>();

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);
Expand All @@ -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
66 changes: 46 additions & 20 deletions libraries/LittleFS/src/LittleFS.h
Original file line number Diff line number Diff line change
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 @@ -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);
}
}
}
}
Expand All @@ -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;
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 @@ -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');
}


Expand Down Expand Up @@ -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<lfs_dir_t> _dir;
Expand Down
23 changes: 23 additions & 0 deletions libraries/SDFS/src/SDFS.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -426,6 +438,14 @@ class SDFSDirImpl : public DirImpl
return _time;
}

time_t fileCreation() override
{
if (!_valid) {
return 0;
}

return _creation;
}

bool isFile() const override
{
Expand All @@ -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();
Expand All @@ -477,6 +499,7 @@ class SDFSDirImpl : public DirImpl
bool _valid;
char _lfn[64];
time_t _time;
time_t _creation;
std::shared_ptr<char> _dirPath;
uint32_t _size;
bool _isFile;
Expand Down
Loading

0 comments on commit cafc27f

Please sign in to comment.