20 #include "Ext2Filesystem.h" 21 #include "Ext2Directory.h" 24 #include "Ext2Symlink.h" 26 #include "modules/system/users/Group.h" 27 #include "modules/system/users/User.h" 28 #include "modules/system/vfs/File.h" 29 #include "modules/system/vfs/VFS.h" 30 #include "pedigree/kernel/Log.h" 31 #include "pedigree/kernel/compiler.h" 32 #include "pedigree/kernel/machine/Disk.h" 33 #include "pedigree/kernel/machine/Machine.h" 34 #include "pedigree/kernel/machine/Timer.h" 35 #include "pedigree/kernel/process/Process.h" 36 #include "pedigree/kernel/process/Thread.h" 37 #include "pedigree/kernel/processor/Processor.h" 38 #include "pedigree/kernel/processor/ProcessorInformation.h" 39 #include "pedigree/kernel/syscallError.h" 40 #include "pedigree/kernel/utilities/StaticString.h" 41 #include "pedigree/kernel/utilities/Vector.h" 42 #include "pedigree/kernel/utilities/assert.h" 43 #include "pedigree/kernel/utilities/utility.h" 45 #ifndef EXT2_STANDALONE 46 #include "modules/Module.h" 53 static uint8_t g_pSparseBlock[4096]
ALIGN(4096)
SECTION(".bss");
55 #ifdef EXT2_STANDALONE 56 extern uint32_t getUnixTimestamp();
58 static uint32_t getUnixTimestamp()
65 Ext2Filesystem::Ext2Filesystem()
66 : m_pSuperblock(0), m_pGroupDescriptors(0), m_pInodeTables(0),
67 m_pInodeBitmaps(0), m_pBlockBitmaps(0), m_BlockSize(0), m_InodeSize(0),
68 m_nGroupDescriptors(0),
76 Ext2Filesystem::~Ext2Filesystem()
78 delete[] m_pBlockBitmaps;
79 delete[] m_pInodeBitmaps;
80 delete[] m_pInodeTables;
81 delete[] m_pGroupDescriptors;
93 uintptr_t block = m_pDisk->read(1024ULL);
94 if (!block || block == ~static_cast<uintptr_t>(0U))
98 m_pDisk->pin(1024ULL);
99 m_pSuperblock =
reinterpret_cast<Superblock *
>(block);
102 if (LITTLE_TO_HOST16(m_pSuperblock->s_magic) != 0xEF53)
104 ERROR(
"Ext2: Superblock not found on device " << devName);
105 m_pDisk->unpin(1024ULL);
110 if (LITTLE_TO_HOST16(m_pSuperblock->s_state) != EXT2_STATE_CLEAN)
112 WARNING(
"Ext2: filesystem on device " << devName <<
" is not clean.");
116 if (checkRequiredFeature(1))
119 "Ext2: filesystem on device " 121 <<
" requires compression, some files may fail to read.");
124 uint32_t algo_bitmap = LITTLE_TO_HOST32(m_pSuperblock->s_algo_bitmap);
129 "Ext2: filesystem on device '" 130 << devName <<
"' uses compression algorithm LZV1.");
132 case EXT2_LZRW3A_ALG:
134 "Ext2: filesystem on device '" 135 << devName <<
"' uses compression algorithm LZRW3A.");
139 "Ext2: filesystem on device '" 140 << devName <<
"' uses compression algorithm gzip.");
144 "Ext2: filesystem on device '" 145 << devName <<
"' uses compression algorithm bzip2.");
149 "Ext2: filesystem on device '" 150 << devName <<
"' uses compression algorithm LZO.");
154 "Ext2: unknown compression algorithm " 155 << algo_bitmap <<
" on device '" << devName
156 <<
"' -- cannot mount!");
165 if (LITTLE_TO_HOST32(m_pSuperblock->s_rev_level) >= 1)
168 m_InodeSize = LITTLE_TO_HOST16(m_pSuperblock->s_inode_size);
172 m_InodeSize =
sizeof(
Inode);
176 m_BlockSize = 1024 << LITTLE_TO_HOST32(m_pSuperblock->s_log_block_size);
179 assert(m_BlockSize <= 4096);
182 uint32_t gdBlock = LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
185 uint32_t inodeCount = LITTLE_TO_HOST32(m_pSuperblock->s_inodes_count);
186 uint32_t inodesPerGroup =
187 LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
188 m_nGroupDescriptors =
189 (inodeCount / inodesPerGroup) + (inodeCount % inodesPerGroup);
192 m_pGroupDescriptors =
new GroupDesc *[m_nGroupDescriptors];
193 for (
size_t i = 0; i < m_nGroupDescriptors; i++)
195 uintptr_t idx = (i *
sizeof(
GroupDesc)) / m_BlockSize;
196 uintptr_t off = (i *
sizeof(
GroupDesc)) % m_BlockSize;
198 uintptr_t groupBlock =
readBlock(gdBlock + idx);
199 m_pGroupDescriptors[i] =
200 reinterpret_cast<GroupDesc *
>(groupBlock + off);
211 Inode *inode = getInode(EXT2_ROOT_INO);
215 bool hasVolumeLabel = LITTLE_TO_HOST32(m_pSuperblock->s_rev_level) >= 1;
216 if ((!hasVolumeLabel) || (m_pSuperblock->s_volume_name[0] ==
'\0'))
219 str +=
"no-volume-label@";
220 str.append(reinterpret_cast<uintptr_t>(
this), 16);
221 m_VolumeLabel.assign(str, str.length(),
true);
226 StringCopyN(buffer, m_pSuperblock->s_volume_name, 16);
228 m_VolumeLabel.assign(buffer);
254 return m_VolumeLabel;
259 size_t type, uint32_t inodeOverride)
261 NOTICE(
"CREATE: " << filename);
266 SYSCALL_ERROR(NotADirectory);
271 if (filename.length() == 0 || !StringCompare(filename,
".") ||
272 !StringCompare(filename,
".."))
274 SYSCALL_ERROR(InvalidArgument);
279 uint32_t inode_num = inodeOverride;
282 inode_num = findFreeInode();
285 SYSCALL_ERROR(NoSpaceLeftOnDevice);
290 #ifdef EXT2_STANDALONE 306 uint32_t timestamp = getUnixTimestamp();
310 Inode *newInode = getInode(inode_num);
313 ByteSet(reinterpret_cast<uint8_t *>(newInode), 0, m_InodeSize);
314 newInode->i_mode = HOST_TO_LITTLE16(mask | type);
315 newInode->i_uid = HOST_TO_LITTLE16(uid);
316 newInode->i_atime = newInode->i_ctime = newInode->i_mtime =
317 HOST_TO_LITTLE32(timestamp);
318 newInode->i_gid = HOST_TO_LITTLE16(gid);
323 if (value.length() && value.length() < 4 * 15)
326 reinterpret_cast<void *>(newInode->i_block), value, value.length());
327 newInode->i_size = HOST_TO_LITTLE32(value.length());
341 new Ext2File(filename, inode_num, newInode,
this, parent);
349 new Ext2Directory(filename, inode_num, newInode,
this, parent);
357 Inode *parentInode = getInode(pE2Parent->getInodeNumber());
361 String(
"."), inode_num, newInode,
this, pE2Dir);
363 String(
".."), pE2Parent->getInodeNumber(), parentInode,
375 new Ext2Symlink(filename, inode_num, newInode,
this, parent);
377 pNewNode = pNewSymlink;
381 FATAL(
"EXT2: Unrecognised file type: " <<
Hex << type);
386 if (value.length() && value.length() >= 4 * 15)
388 const char *pStr = value;
389 pFile->
write(0ULL, value.length(),
reinterpret_cast<uintptr_t
>(pStr));
393 if (!pE2Parent->addEntry(filename, pFile, type))
395 ERROR(
"EXT2: Internal error adding directory entry.");
396 SYSCALL_ERROR(IoError);
405 writeInode(inode_num);
406 writeInode(pE2Parent->getInodeNumber());
409 if (type == EXT2_S_IFDIR)
411 uint32_t group = (inode_num - 1) /
412 LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
413 GroupDesc *pDesc = m_pGroupDescriptors[group];
415 pDesc->bg_used_dirs_count++;
420 LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
421 uint32_t groupBlock = (group *
sizeof(
GroupDesc)) / m_BlockSize;
427 if (m_pSuperblock->s_prealloc_blocks &&
431 m_pSuperblock->s_prealloc_blocks * m_BlockSize, 0, 0,
true);
433 else if (m_pSuperblock->s_prealloc_dir_blocks && pFile->
isDirectory())
436 m_pSuperblock->s_prealloc_dir_blocks * m_BlockSize, 0, 0,
true);
443 File *parent,
const String &filename, uint32_t mask)
445 return createNode(parent, filename, mask,
String(
""), EXT2_S_IFREG);
449 File *parent,
const String &filename, uint32_t mask)
451 if (!createNode(parent, filename, mask,
String(
""), EXT2_S_IFDIR))
461 return createNode(parent, filename, 0777, value, EXT2_S_IFLNK);
492 Inode *inode = pNode->getInode();
493 uint32_t mask = LITTLE_TO_HOST16(inode->i_mode) & 0x0FFF;
494 size_t type = LITTLE_TO_HOST16(inode->i_mode) & 0xF000;
497 parent, filename, mask,
String(
""), type, pNode->getInodeNumber());
505 SYSCALL_ERROR(IoError);
515 filename = pDirectory->
getName();
521 filename = pSymlink->
getName();
530 NOTICE(
"REMOVE: " << filename);
533 bool result = pE2Parent->
removeEntry(filename, pNode);
536 if (result && file->
isDirectory() && (filename !=
"." && filename !=
".."))
538 uint32_t inode_num = pNode->getInodeNumber();
540 uint32_t group = (inode_num - 1) /
541 LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
542 GroupDesc *pDesc = m_pGroupDescriptors[group];
544 pDesc->bg_used_dirs_count--;
549 LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
550 uint32_t groupBlock = (group *
sizeof(
GroupDesc)) / m_BlockSize;
560 return reinterpret_cast<uintptr_t
>(g_pSparseBlock);
562 return m_pDisk->read(
563 static_cast<uint64_t>(m_BlockSize) * static_cast<uint64_t>(block));
572 static_cast<uint64_t>(m_BlockSize) * static_cast<uint64_t>(block));
575 void Ext2Filesystem::pinBlock(uint64_t location)
577 m_pDisk->pin(static_cast<uint64_t>(m_BlockSize) * location);
580 void Ext2Filesystem::unpinBlock(uint64_t location)
582 m_pDisk->unpin(static_cast<uint64_t>(m_BlockSize) * location);
585 void Ext2Filesystem::sync(
size_t offset,
bool async)
588 m_pDisk->write(static_cast<uint64_t>(m_BlockSize) * offset);
590 m_pDisk->flush(static_cast<uint64_t>(m_BlockSize) * offset);
593 uint32_t Ext2Filesystem::findFreeBlock(uint32_t inode)
596 if (findFreeBlocks(inode, 1, blocks))
613 inode / LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
614 uint32_t startGroup = group;
616 for (; count && group < m_nGroupDescriptors; ++group)
618 count -= findFreeBlocksInGroup(group, count, blocks);
625 ERROR(
"FALLING BACK TO STARTING FROM ZERO");
626 for (group = 0; count && group < startGroup; ++group)
628 count -= findFreeBlocksInGroup(group, count, blocks);
643 const uint32_t blocksPerGroup =
644 LITTLE_TO_HOST32(m_pSuperblock->s_blocks_per_group);
645 const size_t bitmapBlockBytes = m_BlockSize;
646 size_t currentCount = 0;
649 GroupDesc *pDesc = m_pGroupDescriptors[group];
650 if (!pDesc->bg_free_blocks_count)
656 ensureFreeBlockBitmapLoaded(group);
660 const uint32_t bytesToSearch = blocksPerGroup >> 3;
664 typedef uint64_t searchType;
665 size_t base = list[idx];
666 searchType *ptr =
reinterpret_cast<searchType *
>(base);
667 searchType *ptr_end = adjust_pointer(ptr, bitmapBlockBytes);
670 bool changedBitmap =
false;
676 searchType tmp = *ptr;
679 if (tmp != static_cast<searchType>(-1))
682 for (
size_t j = 0; j < (
sizeof(searchType) * 8);
683 j++, tmp >>=
static_cast<searchType
>(1))
689 *ptr |= (
static_cast<searchType
>(1) << j);
690 pDesc->bg_free_blocks_count--;
693 changedBitmap =
true;
696 m_pSuperblock->s_free_blocks_count--;
701 LITTLE_TO_HOST32(m_pSuperblock->s_blocks_per_group);
704 LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block);
706 result += ((idx * bitmapBlockBytes) +
707 (reinterpret_cast<uintptr_t>(ptr) - base))
716 if ((++currentCount >= maxCount) ||
717 (!pDesc->bg_free_blocks_count))
731 uint32_t desc_block =
732 LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_block_bitmap) +
736 changedBitmap =
false;
740 if (currentCount >= maxCount)
746 if (++ptr >= ptr_end)
748 if ((++idx * bitmapBlockBytes) >= bytesToSearch)
752 ptr =
reinterpret_cast<searchType *
>(base);
753 ptr_end = adjust_pointer(ptr, bitmapBlockBytes);
757 if (currentCount >= maxCount)
760 m_pDisk->write(1024ULL);
765 LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
766 uint32_t groupBlock = (group *
sizeof(
GroupDesc)) / m_BlockSize;
775 for (uint32_t group = 0; group < m_nGroupDescriptors; group++)
778 GroupDesc *pDesc = m_pGroupDescriptors[group];
779 if (!pDesc->bg_free_inodes_count)
786 ensureFreeInodeBitmapLoaded(group);
791 i < LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group) / 8;
792 i +=
sizeof(uint32_t))
795 size_t idx = i / (m_BlockSize * 8);
796 size_t off = i % (m_BlockSize * 8);
801 uintptr_t block = list[idx];
802 uint32_t *ptr =
reinterpret_cast<uint32_t *
>(block + off);
810 for (
size_t j = 0; j < 32; j++, tmp >>= 1)
817 pDesc->bg_free_inodes_count--;
820 m_pSuperblock->s_free_inodes_count--;
821 m_pDisk->write(1024ULL);
824 uint32_t desc_block =
826 m_pGroupDescriptors[group]->bg_inode_bitmap) +
833 LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
834 uint32_t result = (group *
sizeof(
GroupDesc)) / m_BlockSize;
840 LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
864 block -= LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block);
866 uint32_t blocksPerGroup =
867 LITTLE_TO_HOST32(m_pSuperblock->s_blocks_per_group);
868 uint32_t group = block / blocksPerGroup;
869 uint32_t index = block % blocksPerGroup;
875 FATAL(
"Releasing block zero!");
878 ensureFreeBlockBitmapLoaded(group);
881 GroupDesc *pDesc = m_pGroupDescriptors[group];
884 size_t bitmapField = (index / 8) / m_BlockSize;
885 size_t bitmapOffset = (index / 8) % m_BlockSize;
888 uintptr_t diskBlock = list[bitmapField];
889 uint8_t *ptr =
reinterpret_cast<uint8_t *
>(diskBlock + bitmapOffset);
890 uint8_t bit = (index % 8);
891 if ((*ptr & (1 << bit)) == 0)
892 ERROR(
"bit already freed for block " <<
Dec << block <<
Hex);
896 pDesc->bg_free_blocks_count++;
897 m_pSuperblock->s_free_blocks_count++;
900 m_pDisk->write(1024ULL);
903 uint32_t desc_block =
904 LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_block_bitmap) +
910 uint32_t gdBlock = LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
911 uint32_t groupBlock = (group *
sizeof(
GroupDesc)) / m_BlockSize;
917 Inode *pInode = getInode(inode);
920 uint32_t inodesPerGroup =
921 LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
922 uint32_t group = inode / inodesPerGroup;
923 uint32_t index = inode % inodesPerGroup;
925 bool bRemove = decreaseInodeRefcount(inode + 1);
931 pInode->i_dtime = HOST_TO_LITTLE32(getUnixTimestamp());
933 ensureFreeInodeBitmapLoaded(group);
936 GroupDesc *pDesc = m_pGroupDescriptors[group];
937 pDesc->bg_free_inodes_count++;
938 m_pSuperblock->s_free_inodes_count++;
941 size_t bitmapField = (index / 8) / m_BlockSize;
942 size_t bitmapOffset = (index / 8) % m_BlockSize;
945 uintptr_t block = list[bitmapField];
946 uint8_t *ptr =
reinterpret_cast<uint8_t *
>(block + bitmapOffset);
947 *ptr &= ~(1 << (index % 8));
950 m_pDisk->write(1024ULL);
953 uint32_t desc_block =
954 LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_inode_bitmap) +
961 LITTLE_TO_HOST32(m_pSuperblock->s_first_data_block) + 1;
962 uint32_t groupBlock = (group *
sizeof(
GroupDesc)) / m_BlockSize;
970 Inode *Ext2Filesystem::getInode(uint32_t inode)
976 uint32_t inodesPerGroup =
977 LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
978 uint32_t group = inode / inodesPerGroup;
979 uint32_t index = inode % inodesPerGroup;
981 ensureInodeTableLoaded(group);
984 size_t blockNum = (index * m_InodeSize) / m_BlockSize;
985 size_t blockOff = (index * m_InodeSize) % m_BlockSize;
987 uintptr_t block = list[blockNum];
989 Inode *pInode =
reinterpret_cast<Inode *
>(block + blockOff);
990 if (pInode->i_flags & EXT2_COMPRBLK_FL)
993 "Ext2: inode " << inode
994 <<
" has compressed blocks - not yet supported!");
999 void Ext2Filesystem::writeInode(uint32_t inode)
1003 uint32_t inodesPerGroup =
1004 LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
1005 uint32_t group = inode / inodesPerGroup;
1006 uint32_t index = inode % inodesPerGroup;
1008 ensureInodeTableLoaded(group);
1010 size_t blockNum = (index * m_InodeSize) / m_BlockSize;
1011 uint64_t diskBlock =
1012 LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_inode_table) + blockNum;
1016 bool Ext2Filesystem::checkOptionalFeature(
size_t feature)
1018 if (LITTLE_TO_HOST32(m_pSuperblock->s_rev_level) < 1)
1020 return m_pSuperblock->s_feature_compat & feature;
1023 bool Ext2Filesystem::checkRequiredFeature(
size_t feature)
1025 if (LITTLE_TO_HOST32(m_pSuperblock->s_rev_level) < 1)
1027 return m_pSuperblock->s_feature_incompat & feature;
1030 bool Ext2Filesystem::checkReadOnlyFeature(
size_t feature)
1032 if (LITTLE_TO_HOST32(m_pSuperblock->s_rev_level) < 1)
1034 return m_pSuperblock->s_feature_ro_compat & feature;
1037 void Ext2Filesystem::ensureFreeBlockBitmapLoaded(
size_t group)
1039 assert(group < m_nGroupDescriptors);
1042 if (list.
size() > 0)
1048 uint32_t blocksPerGroup =
1049 LITTLE_TO_HOST32(m_pSuperblock->s_blocks_per_group);
1050 size_t nBlocks = blocksPerGroup / (m_BlockSize * 8);
1051 if (blocksPerGroup % (m_BlockSize * 8))
1054 for (
size_t i = 0; i < nBlocks; i++)
1056 uint32_t blockNumber =
1057 LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_block_bitmap) + i;
1062 void Ext2Filesystem::ensureFreeInodeBitmapLoaded(
size_t group)
1064 assert(group < m_nGroupDescriptors);
1067 if (list.
size() > 0)
1073 uint32_t inodesPerGroup =
1074 LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
1075 size_t nBlocks = inodesPerGroup / (m_BlockSize * 8);
1076 if (inodesPerGroup % (m_BlockSize * 8))
1079 for (
size_t i = 0; i < nBlocks; i++)
1081 uint32_t blockNumber =
1082 LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_inode_bitmap) + i;
1087 void Ext2Filesystem::ensureInodeTableLoaded(
size_t group)
1089 assert(group < m_nGroupDescriptors);
1092 if (list.
size() > 0)
1097 uint32_t inodesPerGroup =
1098 LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
1099 size_t nBlocks = (inodesPerGroup * m_InodeSize) / m_BlockSize;
1100 if ((inodesPerGroup * m_InodeSize) / m_BlockSize)
1104 for (
size_t i = 0; i < nBlocks; i++)
1106 uint32_t blockNumber =
1107 LITTLE_TO_HOST32(m_pGroupDescriptors[group]->bg_inode_table) + i;
1108 uintptr_t buffer =
readBlock(blockNumber);
1115 void Ext2Filesystem::increaseInodeRefcount(uint32_t inode)
1117 Inode *pInode = getInode(inode);
1121 uint32_t current_count = LITTLE_TO_HOST32(pInode->i_links_count);
1122 pInode->i_links_count = HOST_TO_LITTLE32(current_count + 1);
1127 bool Ext2Filesystem::decreaseInodeRefcount(uint32_t inode)
1129 Inode *pInode = getInode(inode);
1133 uint32_t current_count = LITTLE_TO_HOST32(pInode->i_links_count);
1134 bool bRemove = current_count <= 1;
1136 pInode->i_links_count = HOST_TO_LITTLE32(current_count - 1);
1142 #ifndef EXT2_STANDALONE 1143 static bool initExt2()
1149 static void destroyExt2()
1153 MODULE_INFO(
"ext2", &initExt2, &destroyExt2,
"vfs");
virtual bool createFile(File *parent, const String &filename, uint32_t mask)
void pushBack(const T &value)
virtual bool addEntry(String filename, File *pFile, size_t type)
virtual void pinBlock(uint64_t location)
virtual bool createNode(File *parent, const String &filename, uint32_t mask, const String &value, size_t type, uint32_t inodeOverride=0)
Ext2File(const Ext2File &file)
virtual Timer * getTimer()=0
virtual void writeBlock(uint64_t location, uintptr_t addr)
size_t findFreeBlocksInGroup(uint32_t group, size_t maxCount, Vector< uint32_t > &blocks)
virtual String getVolumeLabel() const
void addProbeCallback(Filesystem::ProbeCallback callback)
static ProcessorInformation & information()
virtual Time::Timestamp getUnixTimestamp()
virtual File * getRoot() const
virtual bool createDirectory(File *parent, const String &filename, uint32_t mask)
void releaseBlock(uint32_t block)
virtual bool initialise(Disk *pDisk)
bool ensureLargeEnough(size_t size, uint64_t location, uint64_t opsize, bool onlyBlocks=false, bool nozeroblocks=false)
virtual bool createLink(File *parent, const String &filename, File *target)
bool releaseInode(uint32_t inode)
void setModifiedTime(Time::Timestamp t)
bool findFreeBlocks(uint32_t inode, size_t count, Vector< uint32_t > &blocks)
virtual void getName(String &str)
virtual uintptr_t readBlock(uint64_t location)
uintptr_t readBlock(uint32_t block)
virtual bool isDirectory()
void writeBlock(uint32_t block)
virtual bool removeEntry(const String &filename, Ext2Node *pFile)
virtual bool remove(File *parent, File *file)
void setAccessedTime(Time::Timestamp t)
virtual bool createSymlink(File *parent, const String &filename, const String &value)
virtual uint64_t write(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final