19 return reinterpret_cast<Dir*
>(portion->after + offset);
30 for (Portion* portion : m_Portions) {
31 delete[] portion->before;
32 delete[] portion->after;
38 contents.directory = directory;
39 uint32_t cluster = directory->getInode();
40 for (
size_t visited = 0; visited <= m_Filesystem->
m_ClusterCount; ++visited) {
41 if ((cluster == 0 && m_Filesystem->
m_Type == FAT32) ||
42 (cluster && (cluster < 2 || cluster >= m_Filesystem->
m_ClusterCount + 2)))
44 for (
const Slot& slot : contents.slots) {
45 if (!slot.offset && slot.portion->cluster == cluster)
48 Portion* portion = read(cluster);
51 append(contents, portion);
52 if (!cluster && m_Filesystem->
m_Type != FAT32)
55 if (m_Filesystem->
isEof(next))
62 bool locate(Contents& contents,
const String& name,
File* file,
size_t& index) {
70 auto match = [](
void* opaque,
const ScannedEntry& entry, uint64_t, uint64_t) {
71 Identity&
identity = *
static_cast<Identity*
>(opaque);
74 const uint32_t cluster = LITTLE_TO_HOST16(entry.entry.DIR_FstClusLO) |
75 (uint32_t(LITTLE_TO_HOST16(entry.entry.DIR_FstClusHI)) << 16);
76 uint32_t expectedCluster = 0, expectedOffset = 0;
79 expectedCluster = file->getDirCluster();
80 expectedOffset = file->getDirOffset();
81 }
else if (
identity.file->isSymlink()) {
83 expectedCluster = file->getDirCluster();
84 expectedOffset = file->getDirOffset();
87 expectedCluster = file->getDirCluster();
88 expectedOffset = file->getDirOffset();
91 entry.directoryCluster == expectedCluster &&
92 entry.directoryOffset == expectedOffset;
93 identity.cluster = entry.directoryCluster;
94 identity.offset = entry.directoryOffset;
98 const ReadStatus status = contents.directory->scanDirectory(cookie, match, &
identity);
100 syscallError(status == ReadStatus::IoError ? Error::IoError : Error::DoesNotExist);
103 for (index = 0; index < contents.slots.count(); ++index) {
104 const Slot& slot = contents.slots[index];
105 if (slot.portion->cluster ==
identity.cluster && slot.offset ==
identity.offset)
111 void erase(Contents& contents,
size_t index) {
112 Dir* entry = contents.slots[index].entry();
113 const uint8_t checksum = nameChecksum(entry->DIR_Name);
114 entry->DIR_Name[0] = 0xE5;
115 for (
size_t ordinal = 1; index && ordinal <= 20; ++ordinal) {
118 if ((previous->LDIR_Attr & ATTR_LONG_NAME_MASK) != ATTR_LONG_NAME ||
119 previous->LDIR_Chksum != checksum || (previous->LDIR_Ord & 0x1F) != ordinal ||
120 previous->LDIR_Type || previous->LDIR_FstClusLO)
122 const bool last = previous->LDIR_Ord & 0x40;
123 previous->LDIR_Ord = 0xE5;
129 bool insert(Contents& contents,
const String& name,
const Dir& metadata, Slot& result) {
131 if (!contents.directory->encodeEntrySet(name, metadata, entries))
133 if (!uniqueShortName(contents, entries))
135 size_t index = 0, freeCount = 0;
136 bool pastEnd =
false;
138 for (; index < contents.slots.count(); ++index) {
139 const uint8_t first = contents.slots[index].entry()->DIR_Name[0];
140 pastEnd = pastEnd || !first;
141 freeCount = pastEnd || first == 0xE5 ? freeCount + 1 : 0;
142 if (freeCount == entries.
count()) {
143 const size_t begin = index + 1 - entries.
count();
144 for (
size_t i = 0; i < entries.
count(); ++i)
145 *contents.slots[begin + i].entry() = entries[i];
146 if (pastEnd && index + 1 < contents.slots.count())
147 contents.slots[index + 1].entry()->DIR_Name[0] = 0;
148 result = contents.slots[index];
157 bool updateParent(Contents& contents, uint32_t oldParent, uint32_t newParent) {
158 if (contents.slots.count() < 2)
160 Dir* entry = contents.slots[1].entry();
161 if (MemoryCompare(entry->DIR_Name,
".. ", 11) || !(entry->DIR_Attr & ATTR_DIRECTORY))
163 const uint32_t recorded = LITTLE_TO_HOST16(entry->DIR_FstClusLO) |
164 (uint32_t(LITTLE_TO_HOST16(entry->DIR_FstClusHI)) << 16);
165 const uint32_t root = m_Filesystem->
m_pRoot->getInode();
166 if (recorded != oldParent && !(oldParent == root && !recorded))
168 if (newParent == root)
170 entry->DIR_FstClusLO = HOST_TO_LITTLE16(newParent & 0xFFFF);
171 entry->DIR_FstClusHI = HOST_TO_LITTLE16(newParent >> 16);
175 bool commit(
const Contents* destination =
nullptr) {
178 for (
const Slot& slot : destination->slots) {
183 for (Portion* portion : m_Portions) {
185 for (Portion* existing : ordered)
186 found = found || existing == portion;
190 for (
size_t i = 0; i < ordered.
count(); ++i) {
191 Portion* portion = ordered[i];
192 if (!MemoryCompare(portion->before, portion->after, portion->size))
197 for (
size_t rollback = i + 1; rollback; --rollback) {
198 Portion* original = ordered[rollback - 1];
200 m_Filesystem->m_IoFailed =
true;
202 ERROR(
"FAT namespace rollback failed; filesystem is now read-only");
213 SYSCALL_ERROR(IoError);
216 static uint8_t nameChecksum(
const uint8_t* name) {
217 uint8_t checksum = 0;
218 for (
size_t i = 0; i < 11; ++i)
219 checksum = uint8_t(((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + name[i]);
222 Portion* read(uint32_t cluster) {
223 for (Portion* portion : m_Portions) {
224 if (portion->cluster == cluster)
229 : m_Filesystem->m_RootDirCount * m_Filesystem->
m_Superblock.BPB_BytsPerSec;
230 if (!size || size %
sizeof(
Dir)) {
239 auto* after =
new uint8_t[size];
240 MemoryCopy(after, before, size);
241 Portion* portion =
new Portion{cluster, size, before, after};
242 m_Portions.pushBack(portion);
245 void append(Contents& contents, Portion* portion) {
246 for (
size_t offset = 0; offset < portion->size; offset +=
sizeof(
Dir))
247 contents.slots.pushBack(Slot{portion, uint32_t(offset)});
248 contents.tail = portion->cluster;
250 bool grow(Contents& contents) {
251 if (!contents.tail) {
252 SYSCALL_ERROR(NoSpaceLeftOnDevice);
258 auto* zero =
new uint8_t[m_Filesystem->
m_BlockSize];
260 const bool cleared = m_Filesystem->
writeCluster(cluster,
reinterpret_cast<uintptr_t
>(zero));
271 m_Filesystem->m_IoFailed =
true;
273 ERROR(
"FAT directory extension rollback failed; filesystem is now read-only");
278 Portion* portion = read(cluster);
281 append(contents, portion);
284 bool uniqueShortName(
const Contents& contents,
Vector<Dir>& entries) {
285 Dir& target = entries[entries.
count() - 1];
286 uint8_t original[11];
287 MemoryCopy(original, target.DIR_Name, 11);
288 for (uint32_t suffix = 0; suffix < 1000000; ++suffix) {
289 bool occupied =
false;
290 for (
const Slot& slot : contents.slots) {
291 const Dir* entry = slot.entry();
292 if (!entry->DIR_Name[0])
294 if (entry->DIR_Name[0] != 0xE5 &&
295 (entry->DIR_Attr & ATTR_LONG_NAME_MASK) != ATTR_LONG_NAME &&
296 !MemoryCompare(entry->DIR_Name, target.DIR_Name, 11)) {
302 const uint8_t checksum = nameChecksum(target.DIR_Name);
303 for (
size_t i = 0; i + 1 < entries.
count(); ++i)
304 reinterpret_cast<DirLongFilename*
>(&entries[i])->LDIR_Chksum = checksum;
309 for (uint32_t number = suffix + 1; number; number /= 10)
310 digits[length++] =
'0' + number % 10;
311 MemoryCopy(target.DIR_Name, original, 11);
312 const size_t prefix = 7 - length;
313 for (
size_t i = 0; i < prefix; ++i) {
314 if (target.DIR_Name[i] ==
' ')
315 target.DIR_Name[i] =
'_';
317 target.DIR_Name[prefix] =
'~';
318 for (
size_t i = 0; i < length; ++i)
319 target.DIR_Name[prefix + 1 + i] = digits[length - 1 - i];
321 SYSCALL_ERROR(NoSpaceLeftOnDevice);
330 const bool special = filename ==
"." || filename ==
"..";
335 auto* filesystem =
static_cast<FatFilesystem*
>(m_pFilesystem);
337 if (filesystem->isReadOnly()) {
338 SYSCALL_ERROR(ReadOnlyFilesystem);
342 SYSCALL_ERROR(DoesNotExist);
345 struct ExistingName {
349 auto match = [](
void* opaque,
const ScannedEntry& entry, uint64_t, uint64_t) {
350 auto& existing = *
static_cast<ExistingName*
>(opaque);
351 existing.found = entry.name == existing.name;
352 return !existing.found;
356 const ReadStatus status = scanDirectory(cookie, match, &existing);
357 if (existing.found || status == ReadStatus::IoError) {
358 syscallError(existing.found ? Error::FileExists : Error::IoError);
364 if (!edit.load(
this, contents))
367 metadata.DIR_Attr = type ? ATTR_DIRECTORY : 0;
368 uint32_t cluster = file->getInode();
369 if (filename ==
".." && filesystem->m_pRoot && cluster == filesystem->m_pRoot->getInode())
371 metadata.DIR_FstClusLO = HOST_TO_LITTLE16(cluster & 0xFFFF);
372 metadata.DIR_FstClusHI = HOST_TO_LITTLE16(cluster >> 16);
373 metadata.DIR_FileSize = HOST_TO_LITTLE32(type ? 0 : file->getSize());
374 filesystem->writeEntryAttributes(file, &metadata,
true);
376 if (!edit.insert(contents, filename, metadata, location) || !edit.commit())
378 filesystem->moveNode(file, location.portion->cluster, location.offset);
379 if (publish && !special) {
383 reservation.complete(LookupStatus::Found);
413 auto* oldDirectory =
static_cast<FatDirectory*
>(oldParent);
414 auto* newDirectory =
static_cast<FatDirectory*
>(newParent);
418 const bool oldFirst =
419 reinterpret_cast<uintptr_t
>(oldDirectory) <
reinterpret_cast<uintptr_t
>(newDirectory);
420 FatDirectory* first = oldFirst ? oldDirectory : newDirectory;
421 FatDirectory* second = oldFirst ? newDirectory : oldDirectory;
427 if (oldDirectory->isDetached() || newDirectory->isDetached() || (moved && moved->isDetached()) ||
428 (victim && victim->isDetached())) {
429 SYSCALL_ERROR(DoesNotExist);
434 Edit::Contents oldContents, newContents, movedContents;
435 Edit::Contents* destination = oldDirectory == newDirectory ? &oldContents : &newContents;
436 if (!edit.load(oldDirectory, oldContents) ||
437 (destination == &newContents && !edit.load(newDirectory, newContents)))
439 size_t oldIndex = 0, replacedIndex = 0;
440 if (!edit.locate(oldContents, oldName, source, oldIndex) ||
441 (replaced && !edit.locate(*destination, newName, replaced, replacedIndex)))
443 Dir metadata = *oldContents.slots[oldIndex].entry();
444 metadata.DIR_FileSize = HOST_TO_LITTLE32(moved ? 0 : source->getSize());
445 metadata.DIR_FstClusLO = HOST_TO_LITTLE16(source->getInode() & 0xFFFF);
446 metadata.DIR_FstClusHI = HOST_TO_LITTLE16(source->getInode() >> 16);
447 edit.erase(oldContents, oldIndex);
449 edit.erase(*destination, replacedIndex);
450 String diskName = newName;
452 diskName += FatDirectory::symlinkSuffix();
454 if (!edit.insert(*destination, diskName, metadata, location))
456 if (moved && oldDirectory != newDirectory &&
457 (!edit.load(moved, movedContents) ||
458 !edit.updateParent(movedContents, oldDirectory->getInode(), newDirectory->getInode())))
460 if (!edit.commit(destination))
463 unlinkNode(replaced);
464 moveNode(source, location.portion->cluster, location.offset);