feat: real stat metadata across all filesystems, plus mkdir and rmdir
This commit is contained in:
+71
-1
@@ -11,6 +11,7 @@
|
||||
#include <Libraries/Memory.hpp>
|
||||
#include <Memory/PageFrameAllocator.hpp>
|
||||
#include <CppLib/Vector.hpp>
|
||||
#include <Timekeeping/Time.hpp>
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
@@ -107,6 +108,13 @@ namespace Fs::Fat32 {
|
||||
// Location of the SFN entry on disk (for write support)
|
||||
uint64_t sfnPartSector;
|
||||
uint32_t sfnOffInSector;
|
||||
// Packed FAT date/time words straight from the SFN entry, decoded
|
||||
// on demand by Stat. Zero means "not recorded".
|
||||
uint16_t writeTime;
|
||||
uint16_t writeDate;
|
||||
uint16_t createTime;
|
||||
uint16_t createDate;
|
||||
uint16_t accessDate;
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
@@ -853,6 +861,13 @@ namespace Fs::Fat32 {
|
||||
out->firstCluster = ((uint32_t)clHi << 16) | (uint32_t)clLo;
|
||||
memcpy(&out->fileSize, e + 28, 4);
|
||||
out->attributes = attr;
|
||||
// SFN timestamp words: creation at 14/16, last access
|
||||
// date at 18, last write at 22/24.
|
||||
memcpy(&out->createTime, e + 14, 2);
|
||||
memcpy(&out->createDate, e + 16, 2);
|
||||
memcpy(&out->accessDate, e + 18, 2);
|
||||
memcpy(&out->writeTime, e + 22, 2);
|
||||
memcpy(&out->writeDate, e + 24, 2);
|
||||
int j = 0;
|
||||
while (entryName[j] && j < MaxNameLen - 1) {
|
||||
out->name[j] = entryName[j]; j++;
|
||||
@@ -888,6 +903,12 @@ namespace Fs::Fat32 {
|
||||
out->attributes = ATTR_DIRECTORY;
|
||||
out->name[0] = '/';
|
||||
out->name[1] = '\0';
|
||||
// The root has no directory entry, so it has no timestamps.
|
||||
out->writeTime = 0;
|
||||
out->writeDate = 0;
|
||||
out->createTime = 0;
|
||||
out->createDate = 0;
|
||||
out->accessDate = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1578,6 +1599,54 @@ namespace Fs::Fat32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Stat — metadata for a single path
|
||||
// =========================================================================
|
||||
|
||||
// Decode a packed FAT date/time pair into a Unix timestamp.
|
||||
// date: bits 15-9 year since 1980, 8-5 month (1-12), 4-0 day (1-31)
|
||||
// time: bits 15-11 hour, 10-5 minute, 4-0 seconds/2
|
||||
// FAT stores local time with no recorded offset, so it is taken as UTC.
|
||||
// A zero date means the field was never written; report 0 rather than
|
||||
// inventing 1980-00-00.
|
||||
static int64_t FatDateTimeToEpoch(uint16_t date, uint16_t time) {
|
||||
if (date == 0) return 0;
|
||||
|
||||
int year = 1980 + ((date >> 9) & 0x7F);
|
||||
int month = (date >> 5) & 0x0F;
|
||||
int day = date & 0x1F;
|
||||
|
||||
int hour = (time >> 11) & 0x1F;
|
||||
int minute = (time >> 5) & 0x3F;
|
||||
int second = (time & 0x1F) * 2;
|
||||
|
||||
return Timekeeping::DateToUnixTimestamp(year, month, day, hour, minute, second);
|
||||
}
|
||||
|
||||
static int StatImpl(int inst, const char* path, Vfs::StatInfo* out) {
|
||||
if (out == nullptr) return -1;
|
||||
if (InstanceAt(inst) == nullptr) return -1;
|
||||
|
||||
ParsedEntry entry;
|
||||
if (!TraversePath(inst, path, &entry)) return -1;
|
||||
|
||||
bool isDir = (entry.attributes & ATTR_DIRECTORY) != 0;
|
||||
|
||||
out->size = isDir ? 0 : entry.fileSize;
|
||||
out->mtime = FatDateTimeToEpoch(entry.writeDate, entry.writeTime);
|
||||
out->ctime = FatDateTimeToEpoch(entry.createDate, entry.createTime);
|
||||
// Access date has no time-of-day component on FAT.
|
||||
out->atime = FatDateTimeToEpoch(entry.accessDate, 0);
|
||||
// FAT carries no POSIX permissions; synthesize them from the
|
||||
// read-only attribute so callers see a plausible mode.
|
||||
uint32_t perms = (entry.attributes & ATTR_READ_ONLY)
|
||||
? (isDir ? 0555u : 0444u)
|
||||
: (isDir ? 0755u : 0644u);
|
||||
out->mode = (isDir ? Vfs::ModeDir : Vfs::ModeReg) | perms;
|
||||
out->isDir = isDir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Rename — atomic directory entry move
|
||||
// =========================================================================
|
||||
@@ -1821,6 +1890,7 @@ namespace Fs::Fat32 {
|
||||
static int DrvMkdir(void* c, const char* p) { return MkdirImpl(CtxToInst(c), p); }
|
||||
static int DrvRename(void* c, const char* o, const char* n) { return RenameImpl(CtxToInst(c), o, n); }
|
||||
static const char* DrvGetLabel(void* c) { return GetLabelImpl(CtxToInst(c)); }
|
||||
static int DrvStat(void* c, const char* p, Vfs::StatInfo* o) { return StatImpl(CtxToInst(c), p, o); }
|
||||
|
||||
// Release everything the mount owns. The driver is either not registered
|
||||
// yet, or the VFS has deactivated its drive and drained dispatches.
|
||||
@@ -2028,7 +2098,7 @@ namespace Fs::Fat32 {
|
||||
.Rename = DrvRename,
|
||||
.GetLabel = DrvGetLabel,
|
||||
.ReadDirAt = DrvReadDirAt,
|
||||
.Stat = nullptr,
|
||||
.Stat = DrvStat,
|
||||
.Unmount = DrvUnmount,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user