8#include "pedigree/kernel/Log.h"
9#include "pedigree/kernel/machine/Disk.h"
10#include "pedigree/kernel/syscallError.h"
12#include "FatFilesystem.h"
13#include "FatSymlink.h"
14#include "system/kernel/machine/mach_pc/RtcTimeAccounting.h"
17constexpr Time::Timestamp FatEpoch = 315532800;
18constexpr Time::Timestamp FatLastSecond = 4354819199ULL;
19constexpr Time::Timestamp SecondsPerDay = 86400;
21Time::Timestamp fatTimestamp(Time::Timestamp timestamp) {
22 if (timestamp < FatEpoch)
24 if (timestamp > FatLastSecond)
30 timestamp = fatTimestamp(timestamp);
32 uint64_t days = (timestamp - FatEpoch) / SecondsPerDay;
33 while (days >= (RtcTimeAccounting::isLeapYear(civil.year) ? 366U : 365U)) {
34 days -= RtcTimeAccounting::isLeapYear(civil.year) ? 366U : 365U;
37 while (days >= RtcTimeAccounting::daysInMonth(civil.year, civil.month)) {
38 days -= RtcTimeAccounting::daysInMonth(civil.year, civil.month);
42 civil.hour = (timestamp % SecondsPerDay) / 3600;
43 civil.minute = (timestamp % 3600) / 60;
44 civil.second = timestamp % 60;
50 const uint8_t signature =
m_Type == FAT32 ? m_Superblock32.BS_BootSig : m_Superblock16.BS_BootSig;
51 if (signature != 0x28 && signature != 0x29) {
55 const uint32_t volumeId =
56 LITTLE_TO_HOST32(
m_Type == FAT32 ? m_Superblock32.BS_VolID : m_Superblock16.BS_VolID);
57 uuid.Format(
"%04X-%04X", volumeId >> 16, volumeId & 0xFFFF);
61uint16_t FatFilesystem::getFatDate(Time::Timestamp timestamp)
const {
62 const auto civil = fatCivilTime(timestamp);
63 return static_cast<uint16_t
>(((civil.year - 1980) << 9) | (civil.month << 5) | civil.day);
66uint16_t FatFilesystem::getFatTime(Time::Timestamp timestamp)
const {
67 const auto civil = fatCivilTime(timestamp);
68 return static_cast<uint16_t
>((civil.hour << 11) | (civil.minute << 5) | (civil.second / 2));
71Time::Timestamp FatFilesystem::getUnixTimestamp(uint16_t time, uint16_t date)
const {
72 time = LITTLE_TO_HOST16(time);
73 date = LITTLE_TO_HOST16(date);
74 const size_t year = 1980 + (date >> 9);
75 const uint8_t month = (date >> 5) & 15;
76 const uint8_t day = date & 31;
77 const uint8_t hour = time >> 11;
78 const uint8_t minute = (time >> 5) & 63;
79 const uint8_t second = (time & 31) * 2;
80 if (month < 1 || month > 12 || day < 1 || day > RtcTimeAccounting::daysInMonth(year, month) ||
81 hour > 23 || minute > 59 || second > 59)
84 uint64_t days = day - 1;
85 for (
size_t precedingYear = 1980; precedingYear < year; ++precedingYear)
86 days += RtcTimeAccounting::isLeapYear(precedingYear) ? 366 : 365;
87 for (uint8_t precedingMonth = 1; precedingMonth < month; ++precedingMonth)
88 days += RtcTimeAccounting::daysInMonth(year, precedingMonth);
89 return FatEpoch + days * SecondsPerDay + hour * 3600 + minute * 60 + second;
92void FatFilesystem::writeEntryAttributes(
File* file,
Dir* entry,
bool creating) {
94 encodeEntryAttributes(file->File::getAttributes(), entry, creating);
99 entry->DIR_WrtDate = HOST_TO_LITTLE16(getFatDate(attributes.modified));
100 entry->DIR_WrtTime = HOST_TO_LITTLE16(getFatTime(attributes.modified));
101 entry->DIR_LstAccDate = HOST_TO_LITTLE16(getFatDate(attributes.accessed));
102 if (creating || !entry->DIR_CrtDate) {
104 const Time::Timestamp created = attributes.changed ? attributes.changed : Time::getTime();
105 entry->DIR_CrtDate = HOST_TO_LITTLE16(getFatDate(created));
106 entry->DIR_CrtTime = HOST_TO_LITTLE16(getFatTime(created));
107 const auto civil = fatCivilTime(created);
108 entry->DIR_CrtTimeTenth = (civil.second % 2) * 100;
112bool FatFilesystem::writePendingAttributes(
const PendingAttributes& pending) {
113 const uint32_t cluster = pending.slot >> 32;
114 const uint32_t offset = pending.slot;
118 if (entry->DIR_Name[0] == 0 || entry->DIR_Name[0] == 0xE5 ||
119 (entry->DIR_Attr & ATTR_LONG_NAME_MASK) == ATTR_LONG_NAME) {
123 encodeEntryAttributes(pending.attributes, entry);
129bool FatFilesystem::syncNodeAttributes(
File* file) {
132 if (file ==
m_pRoot || isNodeUnlinked(file))
134 uint32_t cluster = 0, offset = 0;
137 cluster = directory->getDirCluster();
138 offset = directory->getDirOffset();
140 auto* symlink =
static_cast<FatSymlink*
>(file);
141 cluster = symlink->getDirCluster();
142 offset = symlink->getDirOffset();
144 auto* regular =
static_cast<FatFile*
>(file);
145 cluster = regular->getDirCluster();
146 offset = regular->getDirOffset();
148 if (cluster == 0xdeadbeef || offset == 0xbeefdead)
150 const uint64_t slot = (uint64_t(cluster) << 32) | offset;
151 PendingAttributes* pending = m_PendingAttributes;
152 while (pending && pending->slot != slot)
153 pending = pending->next;
155 pending =
new PendingAttributes;
158 SYSCALL_ERROR(OutOfMemory);
161 pending->slot = slot;
162 pending->next = m_PendingAttributes;
163 m_PendingAttributes = pending;
165 pending->attributes = file->File::getAttributes();
166 if (!pending->attributes.changed)
167 pending->attributes.changed = Time::getTime();
168 if (!writePendingAttributes(*pending))
170 discardPendingAttributes(cluster, offset);
174bool FatFilesystem::syncPendingAttributes() {
176 return !m_IoFailed && !m_PendingAttributes;
177 bool succeeded = !m_IoFailed;
178 PendingAttributes** link = &m_PendingAttributes;
180 PendingAttributes* pending = *link;
181 if (!writePendingAttributes(*pending)) {
183 link = &pending->next;
186 *link = pending->next;
192void FatFilesystem::movePendingAttributes(uint32_t oldCluster, uint32_t oldOffset,
193 uint32_t newCluster, uint32_t newOffset) {
194 const uint64_t oldSlot = (uint64_t(oldCluster) << 32) | oldOffset;
195 const uint64_t newSlot = (uint64_t(newCluster) << 32) | newOffset;
196 if (oldSlot == newSlot)
198 discardPendingAttributes(newCluster, newOffset);
199 for (PendingAttributes* pending = m_PendingAttributes; pending; pending = pending->next) {
200 if (pending->slot == oldSlot) {
201 pending->slot = newSlot;
207void FatFilesystem::discardPendingAttributes(uint32_t cluster, uint32_t offset) {
208 const uint64_t slot = (uint64_t(cluster) << 32) | offset;
209 PendingAttributes** link = &m_PendingAttributes;
211 PendingAttributes* pending = *link;
212 if (pending->slot == slot) {
213 *link = pending->next;
217 link = &pending->next;
221void FatFilesystem::clearPendingAttributes() {
222 while (m_PendingAttributes) {
223 PendingAttributes* pending = m_PendingAttributes;
224 m_PendingAttributes = pending->next;
229void FatFilesystem::fileAttributeChanged(
File* file) {
232 auto* regular =
static_cast<FatFile*
>(file);
233 const auto attributes = file->File::getAttributes();
234 const auto* state = regular->m_State;
235 const bool changed = fatTimestamp(state->accessed) / SecondsPerDay !=
236 fatTimestamp(attributes.accessed) / SecondsPerDay ||
237 fatTimestamp(state->modified) / 2 != fatTimestamp(attributes.modified) / 2;
238 regular->m_State->accessed = attributes.accessed;
239 regular->m_State->modified = attributes.modified;
240 regular->m_State->changed = attributes.changed;
243 for (
auto* alias = regular->m_State->aliases; alias; alias = alias->m_NextAlias)
244 alias->copyStateAttributes();
246 regular->m_MetadataDirty = regular->m_MetadataDirty || changed;
249 if (!syncNodeAttributes(file))
250 WARNING(
"FAT: unable to persist file attributes");
257void FatFile::fileAttributeChanged() {
258 static_cast<FatFilesystem*
>(m_pFilesystem)->fileAttributeChanged(
this);
265void FatSymlink::fileAttributeChanged() {
266 static_cast<FatFilesystem*
>(m_pFilesystem)->fileAttributeChanged(
this);
269bool FatFilesystem::syncNode(
File* file) {
273 const bool metadata = syncNodeAttributes(file);
274 const bool allocation = syncFat();
279 return static_cast<FatFilesystem*
>(m_pFilesystem)->syncNode(
this);
283 return static_cast<FatFilesystem*
>(m_pFilesystem)->syncNode(
this);
virtual MUST_USE_RESULT bool syncData()
void fileAttributeChanged() override
uint64_t maximumFileSize() const override
Dir * getDirectoryEntry(uint32_t clus, uint32_t offset) const
bool getUuid(String &) const override
bool writeDirectoryEntry(Dir *dir, uint32_t clus, uint32_t offset)
virtual bool isDirectory()