The Pedigree Project 0.1
FatFilesystem.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 "FatFilesystem.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/machine/Disk.h"
23#include "pedigree/kernel/process/Scheduler.h"
24#include "pedigree/kernel/processor/types.h"
25#include "pedigree/kernel/syscallError.h"
26#include "pedigree/kernel/utilities/StaticString.h"
27#include "pedigree/kernel/utilities/String.h"
28#include "pedigree/kernel/utilities/Tree.h"
30#include "pedigree/kernel/utilities/utility.h"
31
32#include "FatDirectory.h"
33#include "FatFile.h"
34#include "FatSymlink.h"
35#include "fat.h"
36#include "modules/Module.h"
37#include "modules/system/vfs/Directory.h"
38#include "modules/system/vfs/File.h"
39#include "modules/system/vfs/VFS.h"
40
41class Filesystem;
42
43// helper functions
44
45static bool isPowerOf2(uint32_t n) {
46 uint8_t log;
47
48 for (log = 0; log < 16; log++) {
49 if (n & 1) {
50 n >>= 1;
51 return (n != 0) ? false : true;
52 }
53 n >>= 1;
54 }
55 return false;
56}
57
58FatFilesystem::FatFilesystem()
59 : m_FileMutationLock(),
60 m_Superblock(),
61 m_Superblock16(),
62 m_Superblock32(),
63 m_FsInfo(),
64 m_Type(FAT12),
65 m_DataAreaStart(0),
66 m_RootDirCount(0),
67 m_FatSector(0),
68 m_RootDir(),
69 m_BlockSize(0),
70 m_ClusterCount(0),
71 m_pFatCache(0),
72 m_FatLock(),
73#if THREADS || defined(STANDALONE_MUTEXES)
74 m_AllocationLock(),
75#endif
76 m_pRoot(0),
77 m_FatCache(),
78 m_DirtyFatSectors(),
79 m_FreeClusterHint() {
80}
81
82FatFilesystem::~FatFilesystem() {
83 if (m_pRoot)
84 delete m_pRoot;
85 drainFileStates();
86 {
88 if (!syncPendingAttributes())
89 ERROR("FAT: pending attributes remain during unmount");
90 clearPendingAttributes();
91 }
92 if (m_pFatCache)
93 delete[] m_pFatCache;
94}
95
97 m_pDisk = pDisk;
98
99 // Attempt to read the superblock.
100 const BufferView superblock = m_pDisk->read(0);
101 if (!superblock || superblock.size() < (36 + sizeof(Superblock32))) {
102 if (superblock) {
103 m_pDisk->unpin(0);
104 }
105 return false;
106 }
107 uint8_t* buffer = superblock.as<uint8_t>();
108
109 MemoryCopy(reinterpret_cast<void*>(&m_Superblock), reinterpret_cast<void*>(buffer),
110 sizeof(Superblock));
111 MemoryCopy(reinterpret_cast<void*>(&m_Superblock16), reinterpret_cast<void*>(buffer + 36),
112 sizeof(Superblock16));
113 MemoryCopy(reinterpret_cast<void*>(&m_Superblock32), reinterpret_cast<void*>(buffer + 36),
114 sizeof(Superblock32));
115 m_pDisk->unpin(0);
116
118 String devName;
119 pDisk->getName(devName);
120
121 /* check for EITHER a near jmp, or a jmp and a nop */
122 if (m_Superblock.BS_jmpBoot[0] != 0xE9) {
123 if (!(m_Superblock.BS_jmpBoot[0] == 0xEB && m_Superblock.BS_jmpBoot[2] == 0x90)) {
124 ERROR("FAT: Superblock not found on device " << devName << " [" << m_Superblock.BS_jmpBoot[0]
125 << ", " << m_Superblock.BS_jmpBoot[2] << "]");
126 return false;
127 }
128 }
129
132 // SecPerClus must be a power of 2
133 if (!isPowerOf2(m_Superblock.BPB_SecPerClus)) {
134 ERROR("FAT: SecPerClus not a power of 2 (" << m_Superblock.BPB_SecPerClus << ")");
135 return false;
136 }
137
138 // and there must be at least 1 FAT, and at most 2
139 if (m_Superblock.BPB_NumFATs < 1 || m_Superblock.BPB_NumFATs > 2) {
140 ERROR("FAT: Too many (or too few) FATs (" << m_Superblock.BPB_NumFATs << ")");
141 return false;
142 }
143
146 // number of root directory sectors
147 if (!m_Superblock.BPB_BytsPerSec) // we sanity check the value, because we
148 // divide by this later
149 {
150 return false;
151 }
152 uint32_t rootDirSectors =
153 ((m_Superblock.BPB_RootEntCnt * 32) + (m_Superblock.BPB_BytsPerSec - 1)) /
154 m_Superblock.BPB_BytsPerSec;
155
156 // determine the size of the FAT
157 uint32_t fatSz =
158 (m_Superblock.BPB_FATSz16) ? m_Superblock.BPB_FATSz16 : m_Superblock32.BPB_FATSz32;
159
160 // find the first data sector
161 uint32_t firstDataSector =
162 m_Superblock.BPB_RsvdSecCnt + (m_Superblock.BPB_NumFATs * fatSz) + rootDirSectors;
163
164 // determine the number of data sectors, so we can determine FAT type
165 uint32_t totDataSec = 0, totSec = (m_Superblock.BPB_TotSec16) ? m_Superblock.BPB_TotSec16
166 : m_Superblock.BPB_TotSec32;
167 totDataSec = totSec - firstDataSector;
168
169 if (!m_Superblock.BPB_SecPerClus) // again, sanity checking due to division by this
170 {
171 ERROR("FAT: SecPerClus is zero!");
172 return false;
173 }
174 uint32_t clusterCount = totDataSec / m_Superblock.BPB_SecPerClus;
175 m_ClusterCount = clusterCount;
176
177 // TODO: magic numbers here, perhaps #define MAXCLUS_{12|16|32} would work
178 // better for readability
179 if (clusterCount < 4085) {
180 m_Type = FAT12;
181 NOTICE("FAT: Device " << devName << " is type FAT12");
182 } else if (clusterCount < 65525) {
183 m_Type = FAT16;
184 NOTICE("FAT: Device " << devName << " is type FAT16");
185 } else {
186 m_Type = FAT32;
187 NOTICE("FAT: Device " << devName << " is type FAT32");
188 }
189
190 switch (m_Type) {
191 case FAT12:
192 case FAT16:
193
194 m_RootDir.sector = m_Superblock.BPB_RsvdSecCnt + (m_Superblock.BPB_NumFATs * fatSz);
195
196 break;
197
198 case FAT32:
199
200 m_RootDir.cluster = m_Superblock32.BPB_RootClus;
201
202 break;
203 }
204
205 // fill the filesystem data
206 m_DataAreaStart = firstDataSector;
207 m_RootDirCount = rootDirSectors;
208 m_BlockSize = m_Superblock.BPB_SecPerClus * m_Superblock.BPB_BytsPerSec;
209
210 // read in the FAT32 FSInfo structure
211 if (m_Type == FAT32) {
212 uint32_t sec = m_Superblock32.BPB_FsInfo;
213 readSectorBlock(sec, 512, reinterpret_cast<uintptr_t>(&m_FsInfo));
214 }
215
216 // Save the start sector of the FAT now
217 m_FatSector = m_Superblock.BPB_RsvdSecCnt;
218
219 // Setup the free cluster hint for non-FAT32 volumes
221
222 // VFS needs a complete root and stable name before publishing this filesystem.
223 loadRootDir();
224 if (!m_pRoot) {
225 return false;
226 }
227 cacheVolumeLabel();
228
229 // FAT16/32 entry 1 records clean shutdown and absence of previous I/O errors.
230 const uint32_t cleanMask = m_Type == FAT16 ? 0xc000 : 0x0c000000;
231 if (m_Type != FAT12 && (getClusterEntry(1) & cleanMask) != cleanMask) {
232 WARNING("FAT: volume was not clean at mount; filesystem check required");
233 }
234
235 return true;
236}
237
238Filesystem* FatFilesystem::probe(Disk* pDisk) {
239 FatFilesystem* pFs = new FatFilesystem();
240 if (!pFs->initialise(pDisk)) {
241 delete pFs;
242 return 0;
243 } else {
244 return pFs;
245 }
246}
247
248void FatFilesystem::loadRootDir() {
249 if (m_pRoot) {
250 return;
251 }
252
253 // needs to return a file referring to the root directory
254 uint32_t cluster = 0;
255 if (m_Type == FAT32)
256 cluster = m_RootDir.cluster;
257
258 FatFileInfo info;
259 info.creationTime = info.modifiedTime = info.accessedTime = 0;
260
261 m_pRoot = new FatDirectory(String(""), cluster, this, 0, info);
262}
263
265 return m_pRoot;
266}
267
268void FatFilesystem::cacheVolumeLabel() {
269 // The root directory (typically) contains the volume label, with a specific
270 // flag In my experience, it's always the first entry, and it's always
271 // there. Even so, we want to cater to unusual formats.
272 //
273 // In order to do so we check the entire root directory.
274
275 uint32_t sz = m_Type == FAT32 ? m_BlockSize : m_RootDirCount * m_Superblock.BPB_BytsPerSec;
276
277 uint32_t clus = 0;
278 if (m_Type == FAT32)
279 clus = m_RootDir.cluster;
280
281 String volid;
282
283 uint8_t* buffer = reinterpret_cast<uint8_t*>(readDirectoryPortion(clus));
284 if (!buffer) {
285 ERROR("FAT: unable to read the root directory while finding its volume label");
286 return;
287 }
288
289 size_t i;
290 bool endOfDir = false;
291 while (true) {
292 for (i = 0; i < sz; i += sizeof(Dir)) {
293 Dir* ent = reinterpret_cast<Dir*>(&buffer[i]);
294
295 if (ent->DIR_Name[0] == 0) {
296 endOfDir = true;
297 break;
298 }
299
300 if (ent->DIR_Attr & ATTR_VOLUME_ID) {
301 const String shortName(reinterpret_cast<const char*>(ent->DIR_Name), 11, true);
302 volid = convertFilenameFrom(shortName);
303 delete[] buffer;
304 m_VolumeLabel = volid;
305 return;
306 }
307 }
308
309 if (endOfDir)
310 break;
311
312 if (clus == 0 && m_Type != FAT32)
313 break; // not found
314
315 // find the next cluster in the chain, if this is the end, break, if
316 // not, continue
317 clus = getClusterEntry(clus);
318 if (clus == 0)
319 break; // something broke!
320
321 if (isEof(clus))
322 break;
323
324 // continue by reading in this cluster
325 if (!readCluster(clus, reinterpret_cast<uintptr_t>(buffer)))
326 break;
327 }
328
329 delete[] buffer;
330
331 // none found, do a default
333 str += "no-volume-label@";
334 str.append(reinterpret_cast<uintptr_t>(this), 16);
335 m_VolumeLabel.assign(str, str.length(), true);
336}
337
339 return m_VolumeLabel;
340}
341
343
344uint64_t FatFilesystem::read(File* pFile, uint64_t location, uint64_t size, uintptr_t buffer,
345 bool bCanBlock) {
346 OperationBarrier::Lease operation;
347 if (!tryAcquireOperation(operation)) {
348 return 0;
349 }
351 if (!size || !m_BlockSize)
352 return 0;
353 // Sanity check.
354 if (pFile->isDirectory())
355 return 0;
356
357 // the inode of the file is the first cluster
358 uint32_t clus = pFile->getInode();
359 if (clus == 0)
360 return 0; // can't do it
361
362 // validity checking
363 if (location >= pFile->getSize()) {
364 WARNING("FAT: Attempting to read past the EOF [loc=" << location << ", sz=" << size
365 << ", fsz=" << pFile->getSize() << "]");
366 return 0;
367 }
368
369 uint64_t endOffset = location + size;
370 uint64_t finalSize = size;
371 if (endOffset > pFile->getSize()) {
372 finalSize = pFile->getSize() - location;
373
374 // overflow (location > size) or zero bytes required (location == size)
375 if ((finalSize == 0) || (finalSize > pFile->getSize())) {
376 WARNING("FAT: location + size > EOF");
377 return 0;
378 }
379 }
380
381 // finalSize holds the total amount of data to read, now find the cluster
382 // and sector offsets
383 uint32_t clusOffset = location / (m_Superblock.BPB_SecPerClus * m_Superblock.BPB_BytsPerSec);
384 uint32_t firstOffset = location % (m_Superblock.BPB_SecPerClus *
385 m_Superblock.BPB_BytsPerSec); // the offset within the
386 // cluster specified above to
387 // start reading from
388
389 // tracking info
390
391 uint64_t bytesRead = 0;
392 uint64_t currOffset = firstOffset;
393 clus = fileClusterAt(pFile, clusOffset);
394 if (!clus)
395 return 0;
396
397 // buffers
398 uint8_t* tmpBuffer = new uint8_t[m_BlockSize];
399 uint8_t* destBuffer = reinterpret_cast<uint8_t*>(buffer);
400
401 // main read loop
402 while (true) {
403 // read in the entire cluster
404 if (!readCluster(clus, reinterpret_cast<uintptr_t>(tmpBuffer))) {
405 delete[] tmpBuffer;
406 return bytesRead;
407 }
408
409 // How many bytes should we copy?
410 size_t bytesToCopy = finalSize - bytesRead;
411 if (bytesToCopy > m_BlockSize - currOffset) {
412 bytesToCopy = m_BlockSize - currOffset;
413 }
414
415 // Perform the copy.
416 MemoryCopy(&destBuffer[bytesRead], &tmpBuffer[currOffset], bytesToCopy);
417 bytesRead += bytesToCopy;
418
419 // Done?
420 if (bytesRead == finalSize) {
421 delete[] tmpBuffer;
422 return bytesRead;
423 }
424
425 // end of cluster, set the offset back to zero
426 currOffset = 0;
427
428 // grab the next cluster, check for EOF
429 clus = getClusterEntry(clus);
430 if (clus == 0)
431 break; // something broke!
432
433 if (isEof(clus))
434 break;
435 }
436
437 delete[] tmpBuffer;
438
439 // if we reach here, something's gone wrong
440 WARNING("FAT: read returning zero... Something's not right.");
441 return 0;
442}
443
445
446uint32_t FatFilesystem::findFreeCluster(bool* persisted) {
447#if THREADS || defined(STANDALONE_MUTEXES)
449#endif
450 if (persisted)
451 *persisted = false;
452 if (!syncFat(false))
453 return 0;
454 const uint32_t first =
456 for (uint32_t scanned = 0; scanned < m_ClusterCount; ++scanned) {
457 const uint32_t cluster = 2 + ((first - 2 + scanned) % m_ClusterCount);
458 if (getClusterEntry(cluster, false))
459 continue;
460 const bool succeeded = setClusterEntry(cluster, eofValue(), false);
461 if (!succeeded && getClusterEntry(cluster, false) != eofValue())
462 return 0;
463 if (persisted) {
464 // A failed flush retains this reservation in the FAT cache. The caller
465 // attaches it to the in-memory chain before reporting failure for retry.
466 *persisted = succeeded;
467 m_FreeClusterHint = cluster + 1;
468 return cluster;
469 }
470 if (!succeeded) {
471 // No caller owns this reservation. Retain its rollback as dirty until
472 // the next allocator can confirm it before publishing any allocation.
473 setClusterEntry(cluster, 0, false);
474 return 0;
475 }
476 m_FreeClusterHint = cluster + 1;
477 return cluster;
478 }
479 SYSCALL_ERROR(NoSpaceLeftOnDevice);
480 return 0;
481}
482
484
485uint64_t FatFilesystem::write(File* file, uint64_t location, uint64_t size, uintptr_t buffer,
486 bool bCanBlock) {
487 OperationBarrier::Lease operation;
488 if (!tryAcquireOperation(operation)) {
489 return 0;
490 }
492 if (m_bReadOnly) {
493 SYSCALL_ERROR(ReadOnlyFilesystem);
494 return 0;
495 }
496 if (!size)
497 return 0;
498 if (!buffer || file->isDirectory() || location > UINT32_MAX || size > UINT32_MAX - location) {
499 SYSCALL_ERROR(InvalidArgument);
500 return 0;
501 }
502 if (!syncFat() || !ensureCapacity(file, location + size)) {
503 SYSCALL_ERROR(IoError);
504 return 0;
505 }
506
507 const size_t oldSize = file->getSize();
508 if (location > oldSize && !zeroRange(file, oldSize, location)) {
509 SYSCALL_ERROR(IoError);
510 return 0;
511 }
512 uint32_t cluster = fileClusterAt(file, location / m_BlockSize);
513 if (!cluster) {
514 SYSCALL_ERROR(IoError);
515 return 0;
516 }
517
518 uint8_t* temporary = new uint8_t[m_BlockSize];
519 uint64_t written = 0;
520 size_t offset = location % m_BlockSize;
521 while (written < size) {
522 size_t length = m_BlockSize - offset;
523 if (length > size - written)
524 length = size - written;
525 if (!readCluster(cluster, reinterpret_cast<uintptr_t>(temporary)))
526 break;
527 MemoryCopy(temporary + offset, reinterpret_cast<void*>(buffer + written), length);
528 if (!writeCluster(cluster, reinterpret_cast<uintptr_t>(temporary)))
529 break;
530 written += length;
531 offset = 0;
532 if (written == size)
533 break;
534 cluster = getClusterEntry(cluster);
535 if (cluster < 2 || cluster >= m_ClusterCount + 2 || isEof(cluster))
536 break;
537 }
538 delete[] temporary;
539
540 const size_t newSize = written && location + written > oldSize ? location + written : oldSize;
541 const bool metadataDirty = !file->isSymlink() && static_cast<FatFile*>(file)->m_MetadataDirty;
542 if ((newSize != oldSize || metadataDirty) && !updateFileMetadata(file, newSize)) {
543 SYSCALL_ERROR(IoError);
544 return 0;
545 }
546 if (newSize != oldSize)
547 publishSize(file, newSize);
548 if (written != size)
549 SYSCALL_ERROR(IoError);
550 return written;
551}
552
553uint64_t FatFilesystem::allocatedBlocks(File* file) {
554 OperationBarrier::Lease operation;
555 if (!tryAcquireOperation(operation)) {
556 return 0;
557 }
558 if (!file->getInode() && file->isDirectory() && m_Type != FAT32)
559 return (uint64_t(m_RootDirCount) * m_Superblock.BPB_BytsPerSec + 511) / 512;
560 uint32_t count = 0, last = 0;
561 if (!chainExtent(file, count, last)) {
562 WARNING("FAT: unable to count an invalid cluster chain");
563 return 0;
564 }
565 return uint64_t(count) * m_BlockSize / 512;
566}
567
568bool FatFilesystem::ensureCapacity(File* file, size_t size) {
569 if (!m_BlockSize || size > UINT32_MAX)
570 return false;
571 uint32_t count = 0, last = 0;
572 if (!chainExtent(file, count, last))
573 return false;
574 const uint32_t required = size / m_BlockSize + (size % m_BlockSize != 0);
575 if (count >= required)
576 return true;
577 FatFile::State* state =
578 !file->isDirectory() && !file->isSymlink() ? static_cast<FatFile*>(file)->m_State : nullptr;
579 if (state && !state->clusters.tryReserve(required)) {
580 SYSCALL_ERROR(OutOfMemory);
581 return false;
582 }
583#if THREADS || defined(STANDALONE_MUTEXES)
585#endif
586 if (!syncFat(false))
587 return false;
588 bool complete = true;
589 while (count < required) {
590 uint32_t found = 0;
591 const uint32_t first =
593 for (uint32_t scanned = 0; scanned < m_ClusterCount; ++scanned) {
594 const uint32_t cluster = 2 + (first - 2 + scanned) % m_ClusterCount;
595 if (!getClusterEntry(cluster, false)) {
596 found = cluster;
597 break;
598 }
599 }
600 if (!found || !setClusterEntry(found, eofValue(), false, false)) {
601 complete = false;
602 break;
603 }
604 if (last && !setClusterEntry(last, found, false, false)) {
605 setClusterEntry(found, 0, false, false);
606 complete = false;
607 break;
608 }
609 if (!last)
610 file->setInode(found);
611 if (state) {
612 state->clusters.pushBack(found);
613 state->chainRevision = __atomic_load_n(&m_ChainRevision, __ATOMIC_ACQUIRE);
614 }
615 last = found;
616 m_FreeClusterHint = found + 1;
617 ++count;
618 if (!file->isDirectory() && !file->isSymlink())
619 static_cast<FatFile*>(file)->m_MetadataDirty = true;
620 }
621 // Keep every reservation reachable in memory after a failed device flush.
622 // The logical size is published only after zeroing and directory persistence.
623 return syncFat(false) && complete;
624}
625
626bool FatFilesystem::updateFileMetadata(File* file, size_t size) {
627 if (isNodeUnlinked(file))
628 return true;
629 uint32_t directoryCluster = 0, directoryOffset = 0;
630 FatFile* regular = nullptr;
631 if (file->isDirectory()) {
632 FatDirectory* directory = static_cast<FatDirectory*>(file);
633 directoryCluster = directory->getDirCluster();
634 directoryOffset = directory->getDirOffset();
635 } else if (file->isSymlink()) {
636 FatSymlink* symlink = static_cast<FatSymlink*>(file);
637 directoryCluster = symlink->getDirCluster();
638 directoryOffset = symlink->getDirOffset();
639 } else {
640 regular = static_cast<FatFile*>(file);
641 regular->m_MetadataDirty = true;
642 directoryCluster = regular->getDirCluster();
643 directoryOffset = regular->getDirOffset();
644 }
645 Dir* entry = getDirectoryEntry(directoryCluster, directoryOffset);
646 if (!entry)
647 return false;
648 writeEntryAttributes(file, entry);
649 // Absolute values make retry safe even if a failed flush updated the disk cache.
650 entry->DIR_FileSize = HOST_TO_LITTLE32(size);
651 const uintptr_t cluster = regular && !size ? 0 : file->getInode();
652 entry->DIR_FstClusLO = HOST_TO_LITTLE16(cluster & 0xFFFF);
653 entry->DIR_FstClusHI = HOST_TO_LITTLE16((cluster >> 16) & 0xFFFF);
654 const bool succeeded = writeDirectoryEntry(entry, directoryCluster, directoryOffset);
655 delete entry;
656 if (regular && succeeded)
657 regular->m_MetadataDirty = false;
658 return succeeded;
659}
660
661bool FatFilesystem::zeroRange(File* file, size_t begin, size_t end) {
662 if (begin >= end)
663 return true;
664 uint32_t cluster = fileClusterAt(file, begin / m_BlockSize);
665 uint8_t* zeroes = new uint8_t[m_BlockSize];
666 ByteSet(zeroes, 0, m_BlockSize);
667 Disk::WriteBuffer requests[Disk::MaxWriteBuffers];
668 size_t pending = 0;
669 bool succeeded = true;
670 auto drain = [&] {
671 if (pending)
672 succeeded = m_pDisk->writeFromBatch(requests, pending) && succeeded;
673 pending = 0;
674 };
675 while (begin < end) {
676 if (cluster < 2 || cluster >= m_ClusterCount + 2 || isEof(cluster)) {
677 succeeded = false;
678 break;
679 }
680 const size_t offset = begin % m_BlockSize;
681 const size_t length = pedigree_std::min(size_t(m_BlockSize - offset), end - begin);
682 requests[pending++] = {
683 uint64_t(getSectorNumber(cluster)) * m_Superblock.BPB_BytsPerSec + offset, zeroes, length,
684 false};
685 if (pending == Disk::MaxWriteBuffers)
686 drain();
687 begin += length;
688 if (begin < end)
689 cluster = getClusterEntry(cluster);
690 }
691 drain();
692 const bool durable = m_pDisk->syncData();
693 delete[] zeroes;
694 return succeeded && durable;
695}
696
697bool FatFilesystem::syncFileMetadata(File* file) {
699 if (m_bReadOnly)
700 return !m_IoFailed;
701 if (!syncFat())
702 return false;
703 FatFile* regular = static_cast<FatFile*>(file);
704 regular->copyStateAttributes();
705 if (isNodeUnlinked(file))
706 return trimFileAllocation(regular) && m_pDisk->syncData();
707 if (regular->m_MetadataDirty && !updateFileMetadata(file, file->getSize()))
708 return false;
709 return trimFileAllocation(regular) && m_pDisk->syncData();
710}
711
712void* FatFilesystem::readDirectoryPortion(uint32_t clus) const {
713 if (clus == 0 && m_Type == FAT32)
714 return nullptr;
715
716 const size_t size = clus == 0 ? m_RootDirCount * m_Superblock.BPB_BytsPerSec : m_BlockSize;
717 uint8_t* dirBuffer = new uint8_t[size];
718 if (!readDirectoryPortion(clus, reinterpret_cast<uintptr_t>(dirBuffer))) {
719 delete[] dirBuffer;
720 return nullptr;
721 }
722
723 return dirBuffer;
724}
725
726bool FatFilesystem::readDirectoryPortion(uint32_t clus, uintptr_t buffer) const {
727 if (!buffer)
728 return false;
729
730 if (clus == 0) {
731 if (m_Type == FAT32)
732 return false;
733 const uint32_t size = m_RootDirCount * m_Superblock.BPB_BytsPerSec;
734 return readSectorBlock(m_RootDir.sector, size, buffer);
735 }
736
737 return readCluster(clus, buffer);
738}
739
740bool FatFilesystem::writeDirectoryPortion(uint32_t clus, void* p) {
741 if (!p)
742 return false;
743 bool secMethod = false;
744 uint32_t sz = m_BlockSize;
745 uint32_t sec = m_RootDir.sector;
746 if (clus == 0) {
747 if (m_Type != FAT32) {
748 sz = m_RootDirCount * m_Superblock.BPB_BytsPerSec;
749 secMethod = true;
750 } else
751 return false;
752 }
753
754 if (secMethod)
755 return writeSectorBlock(sec, sz, reinterpret_cast<uintptr_t>(p));
756 return writeCluster(clus, reinterpret_cast<uintptr_t>(p));
757}
758
759Dir* FatFilesystem::getDirectoryEntry(uint32_t clus, uint32_t offset) const {
760 const size_t size = clus == 0 ? m_RootDirCount * m_Superblock.BPB_BytsPerSec : m_BlockSize;
761 if (offset > size || (size - offset) < sizeof(Dir))
762 return nullptr;
763
764 uint8_t* dirBuffer = reinterpret_cast<uint8_t*>(readDirectoryPortion(clus));
765 if (!dirBuffer)
766 return 0;
767
768 Dir* ent = reinterpret_cast<Dir*>(&dirBuffer[offset]);
769 Dir* ret = new Dir;
770 MemoryCopy(ret, ent, sizeof(Dir));
771
772 delete[] dirBuffer;
773
774 return ret;
775}
776
777bool FatFilesystem::writeDirectoryEntry(Dir* dir, uint32_t clus, uint32_t offset) {
778 // don't bother reading and writing if the cluster is zero or if there's no
779 // entry to write
780 if (dir == 0)
781 return false;
782
783 const size_t size = clus == 0 ? m_RootDirCount * m_Superblock.BPB_BytsPerSec : m_BlockSize;
784 if (offset > size || (size - offset) < sizeof(Dir))
785 return false;
786
787 uint8_t* dirBuffer = reinterpret_cast<uint8_t*>(readDirectoryPortion(clus));
788 if (!dirBuffer)
789 return false;
790
791 Dir* ent = reinterpret_cast<Dir*>(&dirBuffer[offset]);
792 MemoryCopy(ent, dir, sizeof(Dir));
793
794 const bool success = writeDirectoryPortion(clus, dirBuffer);
795
796 delete[] dirBuffer;
797 return success;
798}
799
800void FatFilesystem::cacheDirectoryContents(File* pFile) {}
801
802bool FatFilesystem::readCluster(uint32_t block, uintptr_t buffer) const {
803 block = getSectorNumber(block);
804 return readSectorBlock(block, m_BlockSize, buffer);
805}
806
807bool FatFilesystem::readSectorBlock(uint32_t sec, size_t size, uintptr_t buffer) const {
808 OperationBarrier::Lease operation;
809 if (!tryAcquireOperation(operation)) {
810 return false;
811 }
812 if (!buffer) {
813 return false;
814 }
815
816 size_t off = 0;
817 while (size) {
818 size_t sz = (size > 512) ? 512 : size;
819 const uint64_t diskLocation =
820 (static_cast<uint64_t>(m_Superblock.BPB_BytsPerSec) * static_cast<uint64_t>(sec)) + off;
821 const BufferView diskBuffer = m_pDisk->read(diskLocation);
822 if (!diskBuffer || diskBuffer.size() < sz) {
823 if (diskBuffer) {
824 m_pDisk->unpin(diskLocation);
825 }
826 return false;
827 }
828 MemoryCopy(reinterpret_cast<void*>(buffer), diskBuffer.data(), sz);
829 m_pDisk->unpin(diskLocation);
830 buffer += sz;
831 size -= sz;
832 off += sz;
833 }
834 return true;
835}
836
837bool FatFilesystem::writeCluster(uint32_t block, uintptr_t buffer) {
838 block = getSectorNumber(block);
839 return writeSectorBlock(block, m_BlockSize, buffer);
840}
841
842bool FatFilesystem::writeSectorBlock(uint32_t sec, size_t size, uintptr_t buffer) {
843 OperationBarrier::Lease operation;
844 if (!tryAcquireOperation(operation)) {
845 return false;
846 }
847 if (!buffer || !m_pDisk)
848 return false;
849 const bool written = m_pDisk->writeFrom(uint64_t(sec) * m_Superblock.BPB_BytsPerSec,
850 reinterpret_cast<void*>(buffer), size);
851 const bool durable = m_pDisk->syncData();
852 return written && durable;
853}
854
855uint32_t FatFilesystem::getSectorNumber(uint32_t cluster) const {
856 return ((cluster - 2) * m_Superblock.BPB_SecPerClus) + m_DataAreaStart;
857}
858
859uint8_t* FatFilesystem::getFatSector(uint32_t sector) {
860 uint8_t* bytes = reinterpret_cast<uint8_t*>(m_FatCache.lookup(sector));
861 if (bytes)
862 return bytes;
863 bytes = new uint8_t[m_Superblock.BPB_BytsPerSec];
864 if (!readSectorBlock(m_FatSector + sector, m_Superblock.BPB_BytsPerSec,
865 reinterpret_cast<uintptr_t>(bytes))) {
866 delete[] bytes;
867 return nullptr;
868 }
869 m_FatCache.insert(sector, reinterpret_cast<uintptr_t>(bytes));
870 return bytes;
871}
872
873uint32_t FatFilesystem::getClusterEntry(uint32_t cluster, bool bLock) {
874 if (!m_Superblock.BPB_BytsPerSec)
875 return 0;
876 const uint32_t offset =
877 m_Type == FAT12 ? cluster + cluster / 2 : cluster * (m_Type == FAT16 ? 2 : 4);
878 const size_t length = m_Type == FAT32 ? 4 : 2;
879 uint32_t entry = 0;
881 for (size_t byte = 0; byte < length; ++byte) {
882 const uint32_t sector = (offset + byte) / m_Superblock.BPB_BytsPerSec;
883 const uint8_t* bytes = getFatSector(sector);
884 if (!bytes) {
886 return 0;
887 }
888 entry |= uint32_t(bytes[(offset + byte) % m_Superblock.BPB_BytsPerSec]) << (8 * byte);
889 }
891 if (m_Type == FAT12)
892 return (entry >> ((cluster & 1) ? 4 : 0)) & 0x0FFF;
893 return entry & (m_Type == FAT16 ? 0xFFFF : 0x0FFFFFFF);
894}
895
896bool FatFilesystem::setClusterEntry(uint32_t cluster, uint32_t value, bool bLock, bool persist) {
897 if (cluster < 2 || cluster >= m_ClusterCount + 2 || !m_Superblock.BPB_BytsPerSec)
898 return false;
899#if THREADS || defined(STANDALONE_MUTEXES)
901#endif
902 const uint32_t offset =
903 m_Type == FAT12 ? cluster + cluster / 2 : cluster * (m_Type == FAT16 ? 2 : 4);
904 const size_t length = m_Type == FAT32 ? 4 : 2;
905 uint8_t* bytes[4] = {};
906 uint32_t original = 0;
908 for (size_t byte = 0; byte < length; ++byte) {
909 const uint32_t sector = (offset + byte) / m_Superblock.BPB_BytsPerSec;
910 uint8_t* page = getFatSector(sector);
911 if (!page) {
913 return false;
914 }
915 bytes[byte] = page + (offset + byte) % m_Superblock.BPB_BytsPerSec;
916 original |= uint32_t(*bytes[byte]) << (8 * byte);
917 }
918 if (m_Type == FAT12)
919 value = cluster & 1 ? (original & 0x000F) | ((value & 0x0FFF) << 4)
920 : (original & 0xF000) | (value & 0x0FFF);
921 else if (m_Type == FAT32)
922 value = (original & 0xF0000000) | (value & 0x0FFFFFFF);
923 if (original != value)
924 __atomic_add_fetch(&m_ChainRevision, uint64_t(1), __ATOMIC_RELEASE);
925 for (size_t byte = 0; byte < length; ++byte) {
926 *bytes[byte] = static_cast<uint8_t>(value >> (8 * byte));
927 m_DirtyFatSectors.insert((offset + byte) / m_Superblock.BPB_BytsPerSec, true);
928 }
930 return !persist || syncFat(false);
931}
932
933bool FatFilesystem::syncFat(bool bLock) {
934 OperationBarrier::Lease operation;
935 if (!tryAcquireOperation(operation)) {
936 return false;
937 }
938#if THREADS || defined(STANDALONE_MUTEXES)
940#endif
942 if (!m_DirtyFatSectors.count())
943 return true;
944 if (!invalidateFsInfoHints())
945 return false;
946 const uint32_t sectorsPerFat =
947 m_Type == FAT32 ? m_Superblock32.BPB_FATSz32 : m_Superblock.BPB_FATSz16;
948 const size_t copies = m_Superblock.BPB_NumFATs ? m_Superblock.BPB_NumFATs : 1;
949 Disk::WriteBuffer requests[Disk::MaxWriteBuffers];
950 size_t pending = 0;
951 bool succeeded = true;
952 auto drain = [&] {
953 if (pending)
954 succeeded = m_pDisk->writeFromBatch(requests, pending) && succeeded;
955 pending = 0;
956 };
957 for (auto it = m_DirtyFatSectors.begin(); it != m_DirtyFatSectors.end(); ++it) {
958 const uint32_t sector = it.key();
959 const uintptr_t bytes = m_FatCache.lookup(sector);
960 if (!bytes) {
961 succeeded = false;
962 continue;
963 }
964 for (size_t copy = 0; copy < copies; ++copy) {
965 requests[pending++] = {
966 uint64_t(m_FatSector + sector + copy * sectorsPerFat) * m_Superblock.BPB_BytsPerSec,
967 reinterpret_cast<void*>(bytes), m_Superblock.BPB_BytsPerSec, false};
968 if (pending == Disk::MaxWriteBuffers)
969 drain();
970 }
971 }
972 drain();
973 const bool durable = m_pDisk->syncData();
974 if (!succeeded || !durable)
975 return false;
976 m_DirtyFatSectors.clear();
977 return true;
978}
979
981 // Special dot & dotdot handling. Because periods are eaten by the
982 // algorithm, we need to ensure that the dot and dotdot entries are returned
983 // with only padding.
984 if (!StringCompare(static_cast<const char*>(fn), ".") ||
985 !StringCompare(static_cast<const char*>(fn), "..")) {
987 ret = fn;
988 ret.pad(11);
989 return String(static_cast<const char*>(ret));
990 }
991
992 // Strip the filename of any whitespace that might be dangling off the end.
993 fn.rstrip();
994
995 NormalStaticString filename, ext;
996
997 // Initial generation loop.
998 size_t lastPeriod = ~0UL;
999 for (size_t i = 0; i < fn.length(); ++i) {
1000 // Valid character?
1001 if (fn[i] == ' ' || fn[i] == '"' || fn[i] == '/' || fn[i] == '\\' || fn[i] == '[' ||
1002 fn[i] == ']' || fn[i] == ':' || fn[i] == ';' || fn[i] == '=' || fn[i] == ',')
1003 continue; // Illegal for SFN.
1004 else if (fn[i] == '.') {
1005 if ((i + 1) >= fn.length()) {
1006 // Stripped input but whitespace follows. Ignore and terminate
1007 // loop.
1008 break;
1009 }
1010 lastPeriod = i;
1011 } else {
1012 filename += toUpper(fn[i]);
1013 }
1014 }
1015
1016 // Truncate filename if the filename portion is > 8 characters long
1017 if (lastPeriod > 8) {
1018 filename.truncate(6);
1019 filename += "~1";
1021 }
1022 // Or is the filename now longer than the distance to the last period?
1023 else if (lastPeriod != ~0UL) {
1024 filename.truncate(lastPeriod);
1025 }
1026
1027 // Is the filename now empty?
1028 if (!filename.length()) {
1029 // Yes, dotfile (eg, .vimrc)
1030 ++lastPeriod;
1031
1032 // .vimrc -> VIMRC~1
1033 for (size_t i = 0; i < 6; ++i) {
1034 if ((lastPeriod + i) >= fn.length())
1035 break;
1036 filename += toUpper(fn[lastPeriod + i]);
1037 }
1038
1039 // Add tail, pad, and return.
1040 filename += "~1";
1041 filename.pad(11);
1042 return String(static_cast<const char*>(filename));
1043 }
1044
1045 // Pull the extension out, truncated to 3 characters, and skipping the full
1046 // stoop.
1047 for (size_t i = 1; i < 4; ++i) {
1048 if ((lastPeriod + i) >= fn.length())
1049 break;
1050 ext += toUpper(fn[lastPeriod + i]);
1051 }
1052
1053 // Pad as necessary.
1054 filename.pad(8);
1055 ext.pad(3);
1056
1057 // Merge the two strings and return!
1058 filename.append(ext);
1059 filename += '\0';
1060 return String(static_cast<const char*>(filename));
1061}
1062
1065
1066 size_t i;
1067 for (i = 0; i < 8; i++) {
1068 if (i >= filename.length())
1069 break;
1070 if (filename[i] != ' ')
1071 ret += toLower(filename[i]);
1072 else
1073 break;
1074 }
1075
1076 for (i = 0; i < 3; i++) {
1077 if ((8 + i) >= filename.length())
1078 break;
1079 if (filename[8 + i] != ' ') {
1080 if (i == 0)
1081 ret += '.';
1082 ret += toLower(filename[8 + i]);
1083 } else
1084 break;
1085 }
1086
1087 ret += '\0';
1088
1089 return String(static_cast<const char*>(ret));
1090}
1091
1092File* FatFilesystem::createFile(File* parentDir, const String& filename, uint32_t mask,
1093 bool bDirectory, uint32_t dirClus, bool publish) {
1094 if (m_bReadOnly) {
1095 SYSCALL_ERROR(ReadOnlyFilesystem);
1096 return 0;
1097 }
1098 // Validate input
1099 if (!parentDir->isDirectory()) {
1100 return 0;
1101 }
1102
1103 FatFileInfo info;
1104 info.creationTime = Time::getTime();
1105 info.modifiedTime = info.creationTime;
1106 info.accessedTime = info.creationTime;
1107
1108 // Directory or File?
1109 // Note that new files in FAT always have a zero cluster, but new
1110 // directories require a cluster (to keep the "." and ".." entries from
1111 // jumping in)
1112 File* pFile;
1113 if (bDirectory) {
1114 pFile = new FatDirectory(filename, dirClus, this, parentDir, info);
1115
1116 char* buffer = new char[m_BlockSize];
1117 ByteSet(buffer, 0, m_BlockSize);
1118
1119 // Clean out the clusters for the directory before creating ./..
1120 // entries.
1121 uint32_t clus = dirClus;
1122 do {
1123 // Write zero cluster.
1124 if (!writeCluster(clus, reinterpret_cast<uintptr_t>(buffer))) {
1125 delete[] buffer;
1126 delete pFile;
1127 return nullptr;
1128 }
1129 clus = getClusterEntry(clus);
1130 } while (!isEof(clus));
1131 delete[] buffer;
1132 } else {
1133 pFile =
1134 new FatFile(filename, info.accessedTime, info.modifiedTime, info.creationTime, 0, this, 0,
1135 0xdeadbeef, // Sentinel values that'll throw an error if they're
1136 // used
1137 0xbeefdead, // before being set to correct values.
1138 parentDir);
1139 }
1140
1141 if (publish) {
1142 FatDirectory* parent = static_cast<FatDirectory*>(Directory::fromFile(parentDir));
1143 if (!parent->addEntry(filename, pFile, (bDirectory ? 1 : 0))) {
1144 if (!bDirectory && !m_bReadOnly)
1145 releaseClusterChain(pFile->getInode());
1146 delete pFile;
1147 return 0;
1148 }
1149 }
1150
1151 return pFile;
1152}
1153
1154bool FatFilesystem::createFile(File* parent, const String& filename, uint32_t mask) {
1155 File* f = createFile(parent, filename, mask, false);
1156 return (f != 0);
1157}
1158
1159bool FatFilesystem::createDirectory(File* parent, const String& filename, uint32_t mask) {
1160 if (m_bReadOnly) {
1161 SYSCALL_ERROR(ReadOnlyFilesystem);
1162 return false;
1163 }
1164 // Allocate a cluster for the directory itself
1165 uint32_t clus = findFreeCluster();
1166 if (!clus)
1167 return false;
1168
1169 File* f = createFile(parent, filename, mask, true, clus, false);
1170 if (!f) {
1171 setClusterEntry(clus, 0);
1172 return false;
1173 }
1174
1175 FatDirectory* fatDir = static_cast<FatDirectory*>(f);
1176 FatFileInfo info = {};
1177 FatDirectory dot(String("."), clus, this, f, info);
1178 FatDirectory dotdot(String(".."), parent->getInode(), this, f, info);
1179 if (!fatDir->addEntry(String("."), &dot, 1) || !fatDir->addEntry(String(".."), &dotdot, 1)) {
1180 if (!m_bReadOnly)
1181 releaseClusterChain(clus);
1182 delete f;
1183 return false;
1184 }
1185
1186 FatDirectory* fatParent = static_cast<FatDirectory*>(Directory::fromFile(parent));
1187 if (!fatParent->addEntry(filename, f, 1)) {
1188 if (!m_bReadOnly)
1189 releaseClusterChain(clus);
1190 delete f;
1191 return false;
1192 }
1193
1194 return true;
1195}
1196
1197bool FatFilesystem::createSymlink(File* parent, const String& filename, const String& value) {
1198 if (m_bReadOnly) {
1199 SYSCALL_ERROR(ReadOnlyFilesystem);
1200 return 0;
1201 }
1202 // Validate input
1203 if (!parent->isDirectory()) {
1204 return false;
1205 }
1206
1207 FatFileInfo info;
1208 info.creationTime = Time::getTime();
1209 info.modifiedTime = info.creationTime;
1210 info.accessedTime = info.creationTime;
1211
1212 // Deviation from the spec here: Because the 'inode' is used for fstat,
1213 // we can't leave it at zero or else all newly created files without
1214 // data will look the same!
1215 uint32_t clus = findFreeCluster();
1216 if (!clus)
1217 return false;
1218 File* pFile = new FatSymlink(
1219 filename, info.accessedTime, info.modifiedTime, info.creationTime, clus, this, value.length(),
1220 0xdeadbeef, // Sentinel values that'll throw an error if they're used
1221 0xbeefdead, // before being set to correct values.
1222 parent);
1223
1224 // The unpublished node has no directory entry to update. Publish its final
1225 // size with the name only after the target data has reached the disk.
1226 if (value.length() && write(pFile, 0, value.length(),
1227 reinterpret_cast<uintptr_t>(value.cstr())) != value.length()) {
1228 if (!m_bReadOnly)
1229 releaseClusterChain(clus);
1230 delete pFile;
1231 return false;
1232 }
1233
1234 String symlinkFilename = filename;
1235 symlinkFilename += FatDirectory::symlinkSuffix();
1236
1237 FatDirectory* fatParent = static_cast<FatDirectory*>(Directory::fromFile(parent));
1238 if (!fatParent->addEntry(symlinkFilename, pFile, 0)) {
1239 if (!m_bReadOnly)
1240 releaseClusterChain(clus);
1241 delete pFile;
1242 return false;
1243 }
1244
1245 return true;
1246}
1247
1248bool FatFilesystem::releaseClusterChain(uint32_t clus, bool lockFile) {
1249 if (!clus)
1250 return true;
1251
1252 LockGuard<Mutex> fileGuard(m_FileMutationLock, lockFile);
1253#if THREADS || defined(STANDALONE_MUTEXES)
1255#endif
1256 const uint32_t first = clus;
1257 size_t visited = 0;
1258 while (true) {
1259 if (clus < 2 || clus >= (m_ClusterCount + 2) || visited++ >= m_ClusterCount) {
1260 ERROR("Invalid or cyclic FAT cluster chain during release");
1261 return false;
1262 }
1263
1264 clus = getClusterEntry(clus, false);
1265 if (!clus) {
1266 ERROR("Found a free cluster in an allocated FAT chain");
1267 return false;
1268 }
1269 if (isEof(clus))
1270 break;
1271 }
1272
1273 // Preflight populated every FAT sector needed below. Stage the whole
1274 // reclamation even if a flush fails, retaining all frees for later retry.
1275 bool succeeded = true;
1276 clus = first;
1277 while (visited--) {
1278 const uint32_t next = getClusterEntry(clus, false);
1279 succeeded = setClusterEntry(clus, 0, false, false) && succeeded;
1280 clus = next;
1281 }
1282 return syncFat(false) && succeeded;
1283}
1284
1285bool FatFilesystem::removeNode(File* parent, const String& filename, File* file) {
1286 if (m_bReadOnly) {
1287 SYSCALL_ERROR(ReadOnlyFilesystem);
1288 return false;
1289 }
1290 FatDirectory* parentDir = static_cast<FatDirectory*>(Directory::fromFile(parent));
1291
1292 if (file->isDirectory()) {
1293 FatDirectory* child = static_cast<FatDirectory*>(file);
1294 if (child == parentDir) {
1295 SYSCALL_ERROR(InvalidArgument);
1296 return false;
1297 }
1298 LockGuard<Mutex> namespaceGuard(child->namespaceMutationLock());
1299 bool empty = false;
1300 const Directory::ReadStatus status = child->isEmpty(empty);
1301 if (status != Directory::ReadStatus::Complete) {
1302 SYSCALL_ERROR(IoError);
1303 return false;
1304 }
1305 if (!empty) {
1306 SYSCALL_ERROR(NotEmpty);
1307 return false;
1308 }
1309
1310 LockGuard<Mutex> childGuard(child->m_Lock);
1311 if (child->isDetached() || !parentDir->removeEntry(filename, file))
1312 return false;
1313 child->markDetached();
1314 return true;
1315 }
1316
1317 if (!parentDir->removeEntry(filename, file))
1318 return false;
1319 return true;
1320}
1321
1322#ifndef FAT_STANDALONE
1323static bool initFat() {
1324 VFS::instance().addProbeCallback(&FatFilesystem::probe);
1325 return true;
1326}
1327
1328static void destroyFat() {
1329 if (!VFS::instance().removeProbeCallback(&FatFilesystem::probe)) {
1330 FATAL("FAT probe callback was not registered during unload");
1331 }
1332}
1333
1334MODULE_INFO("fat", &initFat, &destroyFat, "vfs");
1335#endif
static Directory * fromFile(File *pF)
Definition Directory.h:151
Mutex & namespaceMutationLock()
Definition Directory.h:416
bool isDetached() const
Definition Directory.h:182
ReadStatus isEmpty(bool &empty)
Definition Directory.cc:318
Definition Disk.h:35
virtual BufferView read(uint64_t location)
Definition Disk.cc:163
virtual void getName(String &str)
Definition Disk.cc:155
virtual void unpin(uint64_t location)=0
virtual MUST_USE_RESULT bool writeFromBatch(WriteBuffer *buffers, size_t count)
Definition Disk.cc:285
virtual MUST_USE_RESULT bool syncData()
Definition Disk.cc:299
virtual bool removeEntry(const String &filename, File *pFile)
virtual bool addEntry(String filename, File *pFile, size_t type, bool publish=true)
File * getRoot() const override
uint32_t eofValue() const
bool writeSectorBlock(uint32_t sec, size_t size, uintptr_t buffer)
String convertFilenameTo(String filename) const
uint32_t findFreeCluster(bool *persisted=nullptr)
uint32_t m_ClusterCount
bool writeCluster(uint32_t block, uintptr_t buffer)
Mutex m_FileMutationLock
uint32_t m_BlockSize
uint8_t * m_pFatCache
void * readDirectoryPortion(uint32_t clus) const
uint32_t m_FreeClusterHint
bool writeDirectoryPortion(uint32_t clus, void *p)
UnlikelyLock m_FatLock
bool createFile(File *parent, const String &filename, uint32_t mask) override
String convertFilenameFrom(String filename) const
Dir * getDirectoryEntry(uint32_t clus, uint32_t offset) const
uint16_t m_FatSector
bool createDirectory(File *parent, const String &filename, uint32_t mask) override
bool removeNode(File *parent, const String &filename, File *file) override
bool isEof(uint32_t cluster) const
uint32_t getClusterEntry(uint32_t cluster, bool bLock=true)
const String & getVolumeLabel() const override
uint32_t getSectorNumber(uint32_t cluster) const
uint64_t m_DataAreaStart
bool setClusterEntry(uint32_t cluster, uint32_t value, bool bLock=true, bool persist=true)
bool createSymlink(File *parent, const String &filename, const String &value) override
bool readSectorBlock(uint32_t sec, size_t size, uintptr_t buffer) const
Superblock m_Superblock
bool readCluster(uint32_t block, uintptr_t buffer) const
bool initialise(Disk *pDisk) override
bool releaseClusterChain(uint32_t clus, bool lockFile=true)
bool writeDirectoryEntry(Dir *dir, uint32_t clus, uint32_t offset)
Definition File.h:75
virtual bool isSymlink()
Definition File.cc:800
virtual bool isDirectory()
Definition File.cc:804
Disk * m_pDisk
Definition Filesystem.h:188
bool m_bReadOnly
Definition Filesystem.h:186
Iterator begin()
Definition Tree.h:402
E lookup(const K &key) const
Definition Tree.h:193
void clear()
Definition Tree.h:383
void insert(const K &key, const E &value)
Definition Tree.h:149
Iterator end()
Definition Tree.h:427
size_t count() const
Definition Tree.h:142
void addProbeCallback(Filesystem::ProbeCallback callback)
Definition VFS.cc:1050
static VFS & instance()
Definition VFS.cc:311
void pushBack(const T &value)
Definition Vector.h:275
Definition ext2.h:201