The Pedigree Project 0.1
FatDirectory.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "FatDirectory.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/syscallError.h"
24#include "pedigree/kernel/time/Time.h"
25#include "pedigree/kernel/utilities/PointerGuard.h"
26#include "pedigree/kernel/utilities/utility.h"
27
28#include "FatFile.h"
29#include "FatFilesystem.h"
30#include "FatSymlink.h"
31#include "fat.h"
32#include "modules/system/vfs/File.h"
33
34class Filesystem;
35
36namespace {
37constexpr size_t MaxLongFilenameEntries = 20;
38constexpr size_t LongFilenameCharactersPerEntry = 13;
39constexpr size_t LongFilenameStorageCharacters =
40 MaxLongFilenameEntries * LongFilenameCharactersPerEntry;
41constexpr size_t MaxLongFilenameCharacters = 255;
42
43uint8_t shortFilenameChecksum(const uint8_t* name) {
44 uint8_t checksum = 0;
45 for (size_t i = 0; i < 11; ++i)
46 checksum = static_cast<uint8_t>(((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + name[i]);
47 return checksum;
48}
49
50void writeLongFilenameCharacter(uint8_t* entry, size_t character, uint16_t value) {
51 static const uint8_t offsets[LongFilenameCharactersPerEntry] = {1, 3, 5, 7, 9, 14, 16,
52 18, 20, 22, 24, 28, 30};
53 const size_t offset = offsets[character];
54 entry[offset] = value & 0xFF;
55 entry[offset + 1] = value >> 8;
56}
57
58bool encodeLongFilename(const String& filename, uint16_t* characters, size_t& characterCount,
59 bool& tooLong) {
60 characterCount = 0;
61 tooLong = false;
62 for (size_t i = 0; i < filename.length();) {
63 const uint8_t first = static_cast<uint8_t>(filename[i]);
64 uint32_t character = 0;
65 size_t sequenceLength = 0;
66 if (first < 0x80) {
67 character = first;
68 sequenceLength = 1;
69 } else if (first >= 0xC2 && first <= 0xDF) {
70 character = first & 0x1F;
71 sequenceLength = 2;
72 } else if (first >= 0xE0 && first <= 0xEF) {
73 character = first & 0x0F;
74 sequenceLength = 3;
75 } else if (first >= 0xF0 && first <= 0xF4) {
76 character = first & 0x07;
77 sequenceLength = 4;
78 } else {
79 return false;
80 }
81
82 if ((i + sequenceLength) > filename.length())
83 return false;
84 for (size_t continuation = 1; continuation < sequenceLength; ++continuation) {
85 const uint8_t value = static_cast<uint8_t>(filename[i + continuation]);
86 if ((value & 0xC0) != 0x80)
87 return false;
88 character = (character << 6) | (value & 0x3F);
89 }
90
91 if ((sequenceLength == 3 && character < 0x800) ||
92 (sequenceLength == 4 && character < 0x10000) || character > 0x10FFFF ||
93 (character >= 0xD800 && character <= 0xDFFF)) {
94 return false;
95 }
96
97 if (character <= 0xFFFF) {
98 if (characterCount >= MaxLongFilenameCharacters) {
99 tooLong = true;
100 return false;
101 }
102 characters[characterCount++] = character;
103 } else {
104 if ((characterCount + 2) > MaxLongFilenameCharacters) {
105 tooLong = true;
106 return false;
107 }
108 character -= 0x10000;
109 characters[characterCount++] = 0xD800 | (character >> 10);
110 characters[characterCount++] = 0xDC00 | (character & 0x3FF);
111 }
112 i += sequenceLength;
113 }
114 return true;
115}
116} // namespace
117
118FatDirectory::FatDirectory(String name, uintptr_t inode_num, FatFilesystem* pFs, File* pParent,
119 FatFileInfo& info, uint32_t dirClus, uint32_t dirOffset)
120 : Directory(name, info.accessedTime, info.modifiedTime, info.creationTime, inode_num,
121 static_cast<Filesystem*>(pFs), 0, pParent),
122 m_DirClus(dirClus),
123 m_DirOffset(dirOffset),
124 m_Unlinked(false),
125 m_Type(FAT16),
126 m_BlockSize(0),
127 m_bRootDir(false),
128 m_Lock(),
129 m_DirBlockSize(0) {
130 uint32_t permissions = 0777;
131
132 setPermissionsOnly(permissions);
133 setUidOnly(0);
134 setGidOnly(0);
135
136 m_BlockSize = pFs->m_BlockSize;
137 m_Type = pFs->m_Type;
138
139 setInode(inode_num);
140 pFs->registerNode(this);
141}
142
143bool FatDirectory::encodeEntrySet(const String& filename, const Dir& metadata,
144 Vector<Dir>& entries) {
145 const bool special = filename == "." || filename == "..";
146 uint16_t characters[LongFilenameStorageCharacters];
147 size_t count = 0;
148 bool tooLong = false;
149 for (size_t i = 0; i < filename.length(); ++i) {
150 const uint8_t character = filename[i];
151 if (character < 0x20 || character == '"' || character == '*' || character == '/' ||
152 character == ':' || character == '<' || character == '>' || character == '?' ||
153 character == '\\' || character == '|') {
154 SYSCALL_ERROR(InvalidArgument);
155 return false;
156 }
157 }
158 if (!special &&
159 (!filename.length() || !encodeLongFilename(filename, characters, count, tooLong))) {
160 syscallError(tooLong ? Error::NameTooLong : Error::InvalidArgument);
161 return false;
162 }
163 const size_t longCount =
164 special ? 0 : (count + LongFilenameCharactersPerEntry - 1) / LongFilenameCharactersPerEntry;
165 FatFilesystem* filesystem = static_cast<FatFilesystem*>(m_pFilesystem);
166 const String shortName = filesystem->convertFilenameTo(filename);
167 if (shortName.length() < 11) {
168 SYSCALL_ERROR(InvalidArgument);
169 return false;
170 }
171 const uint8_t checksum =
172 shortFilenameChecksum(reinterpret_cast<const uint8_t*>(shortName.cstr()));
173 for (size_t i = 0; i < longCount; ++i) {
174 Dir raw;
175 ByteSet(&raw, 0xFF, sizeof(raw));
176 DirLongFilename* entry = reinterpret_cast<DirLongFilename*>(&raw);
177 const size_t ordinal = longCount - i;
178 entry->LDIR_Ord = ordinal | (i == 0 ? 0x40 : 0);
179 entry->LDIR_Attr = ATTR_LONG_NAME;
180 entry->LDIR_Type = 0;
181 entry->LDIR_Chksum = checksum;
182 entry->LDIR_FstClusLO = 0;
183 for (size_t character = 0; character < LongFilenameCharactersPerEntry; ++character) {
184 const size_t index = (ordinal - 1) * LongFilenameCharactersPerEntry + character;
185 writeLongFilenameCharacter(reinterpret_cast<uint8_t*>(entry), character,
186 index < count ? characters[index]
187 : index == count ? 0
188 : 0xFFFF);
189 }
190 entries.pushBack(raw);
191 }
192 Dir shortEntry = metadata;
193 MemoryCopy(shortEntry.DIR_Name, shortName.cstr(), 11);
194 entries.pushBack(shortEntry);
195 return true;
196}
197
199 static_cast<FatFilesystem*>(m_pFilesystem)->releaseNode(this);
200}
201
202File::Attributes FatDirectory::getAttributes() const {
203 FatFilesystem* filesystem = static_cast<FatFilesystem*>(m_pFilesystem);
204 LockGuard<Mutex> guard(filesystem->m_FileMutationLock);
205 Attributes attributes = File::getAttributes();
206 attributes.blocks = filesystem->allocatedBlocks(const_cast<FatDirectory*>(this));
207 return attributes;
208}
209
210void FatDirectory::setInode(uintptr_t inode) {
211 FatFilesystem* pFs = static_cast<FatFilesystem*>(m_pFilesystem);
212 m_Inode = inode;
213 uintptr_t clus = m_Inode;
214
215 m_DirBlockSize = m_BlockSize;
216
217 m_bRootDir = false;
218 if (clus == 0 && m_Type != FAT32) {
219 m_DirBlockSize = pFs->m_RootDirCount * pFs->m_Superblock.BPB_BytsPerSec;
220 m_bRootDir = true;
221 } else if (pFs->m_Type == FAT32)
222 if (clus == pFs->m_Superblock32.BPB_RootClus)
223 m_bRootDir = true;
224}
225
226namespace {
227struct LongFilenameState {
228 uint16_t characters[LongFilenameStorageCharacters];
229 uint8_t expectedOrdinal;
230 uint8_t entryCount;
231 uint8_t checksum;
232 bool valid;
233};
234
235void resetLongFilename(LongFilenameState& state) {
236 ByteSet(state.characters, 0xFF, sizeof(state.characters));
237 state.expectedOrdinal = 0;
238 state.entryCount = 0;
239 state.checksum = 0;
240 state.valid = false;
241}
242
243uint16_t readLongFilenameCharacter(const uint8_t* p) {
244 return static_cast<uint16_t>(p[0]) | (static_cast<uint16_t>(p[1]) << 8);
245}
246
247bool consumeLongFilenameEntry(LongFilenameState& state, const DirLongFilename& entry) {
248 const uint8_t ordinal = entry.LDIR_Ord & 0x1F;
249 const bool last = (entry.LDIR_Ord & 0x40) != 0;
250 if (entry.LDIR_Ord & 0xA0 || !ordinal || ordinal > MaxLongFilenameEntries || entry.LDIR_Type ||
251 entry.LDIR_FstClusLO) {
252 resetLongFilename(state);
253 return false;
254 }
255
256 if (last) {
257 resetLongFilename(state);
258 state.valid = true;
259 state.expectedOrdinal = ordinal;
260 state.entryCount = ordinal;
261 state.checksum = entry.LDIR_Chksum;
262 }
263
264 if (!state.valid || state.expectedOrdinal != ordinal || state.checksum != entry.LDIR_Chksum) {
265 resetLongFilename(state);
266 return false;
267 }
268
269 const uint8_t* raw = reinterpret_cast<const uint8_t*>(&entry);
270 size_t character = (ordinal - 1) * LongFilenameCharactersPerEntry;
271 for (size_t offset = 1; offset < 11; offset += 2)
272 state.characters[character++] = readLongFilenameCharacter(raw + offset);
273 for (size_t offset = 14; offset < 26; offset += 2)
274 state.characters[character++] = readLongFilenameCharacter(raw + offset);
275 for (size_t offset = 28; offset < 32; offset += 2)
276 state.characters[character++] = readLongFilenameCharacter(raw + offset);
277
278 state.expectedOrdinal = ordinal - 1;
279 return true;
280}
281
282String longFilename(const LongFilenameState& state) {
283 String result;
284 result.reserve((state.entryCount * LongFilenameCharactersPerEntry * 3) + 1);
285 const size_t characterCount =
286 pedigree_std::min(static_cast<size_t>(state.entryCount) * LongFilenameCharactersPerEntry,
287 MaxLongFilenameCharacters);
288 for (size_t i = 0; i < characterCount; ++i) {
289 uint32_t character = state.characters[i];
290 if (!character || character == 0xFFFF)
291 break;
292
293 if (character >= 0xD800 && character <= 0xDBFF) {
294 if ((i + 1) < characterCount && state.characters[i + 1] >= 0xDC00 &&
295 state.characters[i + 1] <= 0xDFFF) {
296 character = 0x10000 + ((character - 0xD800) << 10) + (state.characters[++i] - 0xDC00);
297 } else {
298 character = '?';
299 }
300 } else if (character >= 0xDC00 && character <= 0xDFFF) {
301 character = '?';
302 }
303
304 char utf8[5] = {};
305 size_t length = String::Utf32ToUtf8(character, utf8);
306 if (!length) {
307 utf8[0] = '?';
308 length = 1;
309 }
310 result += String(utf8, length, true);
311 }
312 return result;
313}
314
315uint64_t directoryCookie(uint32_t cluster, uint32_t offset) {
316 return ((static_cast<uint64_t>(cluster) << 32) | offset) + 1;
317}
318} // namespace
319
321
322Directory::ReadStatus FatDirectory::scanDirectory(uint64_t& cookie, ScanEmitter emitter,
323 void* context) {
324 if (!emitter)
325 return ReadStatus::IoError;
326
327 FatFilesystem* pFs = static_cast<FatFilesystem*>(m_pFilesystem);
328 const uint64_t startingCookie = cookie;
329 uint32_t requestedCluster = static_cast<uint32_t>(m_Inode);
330 uint32_t offset = 0;
331 if (cookie) {
332 const uint64_t packed = cookie - 1;
333 requestedCluster = static_cast<uint32_t>(packed >> 32);
334 offset = static_cast<uint32_t>(packed);
335 }
336
337 const uint32_t portionSize = m_bRootDir && m_Type != FAT32 ? m_DirBlockSize : m_BlockSize;
338 if (!portionSize || (portionSize % sizeof(Dir)) || (offset % sizeof(Dir)) || offset > portionSize)
339 return ReadStatus::IoError;
340
341 uint32_t cluster = static_cast<uint32_t>(m_Inode);
342 size_t visitedClusters = 1;
343 if (m_bRootDir && m_Type != FAT32) {
344 if (requestedCluster)
345 return ReadStatus::IoError;
346 } else {
347 if (cluster < 2 || requestedCluster < 2 || cluster >= (pFs->m_ClusterCount + 2) ||
348 requestedCluster >= (pFs->m_ClusterCount + 2)) {
349 return ReadStatus::IoError;
350 }
351
352 while (cluster != requestedCluster) {
353 if (visitedClusters++ >= pFs->m_ClusterCount)
354 return ReadStatus::IoError;
355 cluster = pFs->getClusterEntry(cluster);
356 if (!cluster || pFs->isEof(cluster) || cluster < 2 || cluster >= (pFs->m_ClusterCount + 2)) {
357 return ReadStatus::IoError;
358 }
359 }
360 }
361
362 uint8_t* buffer = new uint8_t[portionSize];
363 PointerGuard<uint8_t> bufferGuard(buffer, true);
364 if (!pFs->readDirectoryPortion(cluster, reinterpret_cast<uintptr_t>(buffer)))
365 return ReadStatus::IoError;
366
367 LongFilenameState lfn;
368 resetLongFilename(lfn);
369 uint64_t recordCookie = startingCookie;
370 bool initialSlot = true;
371
372 while (true) {
373 if (offset == portionSize) {
374 if (m_bRootDir && m_Type != FAT32)
375 return ReadStatus::Complete;
376
377 const uint32_t nextCluster = pFs->getClusterEntry(cluster);
378 if (!nextCluster)
379 return ReadStatus::IoError;
380 if (pFs->isEof(nextCluster))
381 return ReadStatus::Complete;
382 if (nextCluster < 2 || nextCluster >= (pFs->m_ClusterCount + 2) ||
383 visitedClusters++ >= pFs->m_ClusterCount) {
384 return ReadStatus::IoError;
385 }
386
387 cluster = nextCluster;
388 offset = 0;
389 if (!pFs->readCluster(cluster, reinterpret_cast<uintptr_t>(buffer)))
390 return ReadStatus::IoError;
391 if (!lfn.valid)
392 recordCookie = directoryCookie(cluster, 0);
393 initialSlot = false;
394 }
395
396 const uint64_t slotCookie =
397 initialSlot && !startingCookie ? 0 : directoryCookie(cluster, offset);
398 initialSlot = false;
399 const uint64_t nextCookie = directoryCookie(cluster, offset + sizeof(Dir));
400 const Dir* entry = reinterpret_cast<const Dir*>(buffer + offset);
401 offset += sizeof(Dir);
402
403 const uint8_t firstCharacter = entry->DIR_Name[0];
404 if (!firstCharacter)
405 return ReadStatus::Complete;
406
407 if (firstCharacter == 0xE5) {
408 resetLongFilename(lfn);
409 recordCookie = nextCookie;
410 continue;
411 }
412
413 if ((entry->DIR_Attr & ATTR_LONG_NAME_MASK) == ATTR_LONG_NAME) {
414 if (entry->DIR_Name[0] & 0x40)
415 recordCookie = slotCookie;
416 if (!consumeLongFilenameEntry(lfn, *reinterpret_cast<const DirLongFilename*>(entry)))
417 recordCookie = nextCookie;
418 continue;
419 }
420
421 String filename;
422 if (lfn.valid && !lfn.expectedOrdinal &&
423 lfn.checksum == shortFilenameChecksum(entry->DIR_Name)) {
424 filename = longFilename(lfn);
425 }
426 if (!filename.length()) {
427 uint8_t shortNameBytes[11];
428 MemoryCopy(shortNameBytes, entry->DIR_Name, sizeof(shortNameBytes));
429 if (shortNameBytes[0] == 0x05)
430 shortNameBytes[0] = 0xE5;
431 const String shortName(reinterpret_cast<const char*>(shortNameBytes), 11, true);
432 filename = pFs->convertFilenameFrom(shortName);
433 }
434
435 const uint64_t currentCookie = recordCookie;
436 resetLongFilename(lfn);
437 recordCookie = nextCookie;
438
439 if ((entry->DIR_Attr & ATTR_VOLUME_ID) || filename.compare(".", 1) ||
440 filename.compare("..", 2)) {
441 continue;
442 }
443
444 EntryType type = EntryType::Regular;
445 if (entry->DIR_Attr & ATTR_DIRECTORY) {
446 type = EntryType::Directory;
447 } else if (filename.endswith(symlinkSuffix())) {
448 filename.rtrim(symlinkSuffix().length());
449 type = EntryType::Symlink;
450 }
451
452 ScannedEntry scanned;
453 scanned.name = pedigree_std::move(filename);
454 MemoryCopy(&scanned.entry, entry, sizeof(Dir));
455 scanned.directoryCluster = cluster;
456 scanned.directoryOffset = offset - sizeof(Dir);
457 scanned.type = type;
458
459 if (!emitter(context, scanned, currentCookie, nextCookie))
460 return ReadStatus::Stopped;
461 cookie = nextCookie;
462 }
463}
464
465File* FatDirectory::materialize(const ScannedEntry& scanned) {
466 FatFilesystem* pFs = static_cast<FatFilesystem*>(m_pFilesystem);
467 const Dir& entry = scanned.entry;
468 const uint32_t fileCluster = LITTLE_TO_HOST16(entry.DIR_FstClusLO) |
469 (static_cast<uint32_t>(LITTLE_TO_HOST16(entry.DIR_FstClusHI)) << 16);
470 const Time::Timestamp writeTime = pFs->getUnixTimestamp(entry.DIR_WrtTime, entry.DIR_WrtDate);
471 const Time::Timestamp accessTime = pFs->getUnixTimestamp(0, entry.DIR_LstAccDate);
472 Time::Timestamp creationTime = pFs->getUnixTimestamp(entry.DIR_CrtTime, entry.DIR_CrtDate);
473 if (creationTime && entry.DIR_CrtTimeTenth >= 100 && entry.DIR_CrtTimeTenth < 200)
474 ++creationTime;
475
476 if (scanned.type == EntryType::Directory) {
477 FatFileInfo info;
478 info.accessedTime = accessTime;
479 info.modifiedTime = writeTime;
480 info.creationTime = creationTime;
481 return new FatDirectory(scanned.name, fileCluster, pFs, this, info, scanned.directoryCluster,
482 scanned.directoryOffset);
483 }
484
485 const uint32_t size = LITTLE_TO_HOST32(entry.DIR_FileSize);
486 if (scanned.type == EntryType::Symlink) {
487 return new FatSymlink(scanned.name, accessTime, writeTime, creationTime, fileCluster, pFs, size,
488 scanned.directoryCluster, scanned.directoryOffset, this);
489 }
490 return new FatFile(scanned.name, accessTime, writeTime, creationTime, fileCluster, pFs, size,
491 scanned.directoryCluster, scanned.directoryOffset, this);
492}
493
494Directory::LookupStatus FatDirectory::resolveChild(const StringView& name, File*& child) {
495 child = nullptr;
496 struct Context {
497 StringView name;
498 ScannedEntry entry;
499 bool found;
500 } context = {name, ScannedEntry(), false};
501
502 auto emitter = [](void* opaque, const ScannedEntry& entry, uint64_t, uint64_t) -> bool {
503 Context* context = reinterpret_cast<Context*>(opaque);
504 if (entry.name == context->name) {
505 context->entry = entry;
506 context->found = true;
507 return false;
508 }
509 return true;
510 };
511
512 uint64_t cookie = 0;
513 ReadStatus status;
514 {
516 LockGuard<Mutex> fileGuard(static_cast<FatFilesystem*>(m_pFilesystem)->m_FileMutationLock);
517 if (isDetached())
518 return LookupStatus::NotFound;
519 status = scanDirectory(cookie, emitter, &context);
520 if (context.found)
521 child = materialize(context.entry);
522 }
523
524 if (context.found)
525 return child ? LookupStatus::Found : LookupStatus::IoError;
526 return status == ReadStatus::IoError ? LookupStatus::IoError : LookupStatus::NotFound;
527}
528
529Directory::LookupStatus FatDirectory::resolveChildAt(uint64_t cookie, const StringView& name,
530 File*& child) {
531 child = nullptr;
532 struct Context {
533 StringView name;
534 ScannedEntry entry;
535 bool found;
536 } context = {name, ScannedEntry(), false};
537
538 auto emitter = [](void* opaque, const ScannedEntry& entry, uint64_t, uint64_t) -> bool {
539 Context* context = reinterpret_cast<Context*>(opaque);
540 if (entry.name == context->name) {
541 context->entry = entry;
542 context->found = true;
543 }
544 return false;
545 };
546
547 {
549 LockGuard<Mutex> fileGuard(static_cast<FatFilesystem*>(m_pFilesystem)->m_FileMutationLock);
550 if (isDetached())
551 return LookupStatus::NotFound;
552 scanDirectory(cookie, emitter, &context);
553 if (context.found)
554 child = materialize(context.entry);
555 }
556
557 if (context.found)
558 return child ? LookupStatus::Found : LookupStatus::IoError;
559 // A cookie is only a fast path; retain ordinary lookup semantics if a
560 // caller resumes with a stale location.
561 return resolveChild(name, child);
562}
563
564Directory::ReadStatus FatDirectory::readDirectory(uint64_t& cookie, DirectoryEntryEmitter emitter,
565 void* context) {
566 if (!emitter)
567 return ReadStatus::IoError;
568
569 struct Context {
570 DirectoryEntryEmitter emitter;
571 void* context;
572 FatFilesystem* filesystem;
573 } adapter = {emitter, context, static_cast<FatFilesystem*>(m_pFilesystem)};
574
575 auto scanEmitter = [](void* opaque, const ScannedEntry& entry, uint64_t currentCookie,
576 uint64_t nextCookie) -> bool {
577 Context* context = reinterpret_cast<Context*>(opaque);
578 const uintptr_t inode =
579 entry.type == EntryType::Regular
580 ? context->filesystem->fileIdentifier(entry.directoryCluster, entry.directoryOffset)
581 : LITTLE_TO_HOST16(entry.entry.DIR_FstClusLO) |
582 (static_cast<uint32_t>(LITTLE_TO_HOST16(entry.entry.DIR_FstClusHI)) << 16);
583 DirectoryEntryView view = {entry.name.view(), inode, entry.type, currentCookie, nextCookie};
584 return context->emitter(context->context, view);
585 };
586
588 if (isDetached())
589 return ReadStatus::Complete;
590 return scanDirectory(cookie, scanEmitter, &adapter);
591}
bool isDetached() const
Definition Directory.h:179
LookupStatus resolveChildAt(uint64_t cookie, const StringView &name, File *&child) override
FatDirectory(const FatDirectory &file)
ReadStatus readDirectory(uint64_t &cookie, DirectoryEntryEmitter emitter, void *context) override
void setInode(uintptr_t inode) override
uint32_t m_DirBlockSize
void cacheDirectoryContents() override
LookupStatus resolveChild(const StringView &name, File *&child) override
~FatDirectory() override
String convertFilenameTo(String filename) const
uint32_t m_ClusterCount
Mutex m_FileMutationLock
uint32_t m_BlockSize
void * readDirectoryPortion(uint32_t clus) const
String convertFilenameFrom(String filename) const
bool isEof(uint32_t cluster) const
uint32_t getClusterEntry(uint32_t cluster, bool bLock=true)
Superblock m_Superblock
bool readCluster(uint32_t block, uintptr_t buffer) const
Definition File.h:74
void setGidOnly(size_t gid)
Definition File.cc:1312
void setPermissionsOnly(uint32_t perms)
Definition File.cc:1304
void setUidOnly(size_t uid)
Definition File.cc:1308
bool compare(const char *s, size_t len) const
Definition String.cc:208
void rtrim(size_t n)
Definition String.cc:438
bool endswith(const char c) const
Definition String.cc:675
static size_t Utf32ToUtf8(uint32_t utf32, char *utf8)
Definition String.cc:550
A vector / dynamic array.
Definition Vector.h:33
void pushBack(const T &value)
Definition Vector.h:275
Definition ext2.h:201