20#include "Ext2Filesystem.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/TargetInfo.h"
24#include "pedigree/kernel/compiler.h"
25#include "pedigree/kernel/machine/Disk.h"
26#include "pedigree/kernel/machine/Machine.h"
27#include "pedigree/kernel/machine/Timer.h"
28#include "pedigree/kernel/process/Process.h"
29#include "pedigree/kernel/process/Thread.h"
30#include "pedigree/kernel/processor/Processor.h"
31#include "pedigree/kernel/processor/ProcessorInformation.h"
32#include "pedigree/kernel/syscallError.h"
33#include "pedigree/kernel/utilities/StaticString.h"
34#include "pedigree/kernel/utilities/Vector.h"
35#include "pedigree/kernel/utilities/assert.h"
36#include "pedigree/kernel/utilities/utility.h"
38#include "Ext2Directory.h"
41#include "Ext2Symlink.h"
43#include "modules/system/users/Group.h"
44#include "modules/system/users/User.h"
45#include "modules/system/vfs/File.h"
46#include "modules/system/vfs/VFS.h"
48#ifndef EXT2_STANDALONE
49#include "modules/Module.h"
56static uint8_t g_pSparseBlock[4096]
ALIGN(4096)
SECTION(".bss");
59extern uint32_t getUnixTimestamp();
61static uint32_t getUnixTimestamp() {
67Ext2Filesystem::Ext2Filesystem()
69 m_pGroupDescriptors(0),
75 m_nGroupDescriptors(0),
76#if THREADS || defined(STANDALONE_MUTEXES)
78 m_InodeTableLoadLock(),
83Ext2Filesystem::~Ext2Filesystem() {
86 drainAttributeWrites();
88 for (
auto it = m_InodeStates.
begin(); it != m_InodeStates.
end(); ++it) {
89 assert(!it.value()->references);
92 m_InodeStates.
clear();
97void Ext2Filesystem::releaseMetadata() {
107 const uint32_t start = LITTLE_TO_HOST32(descriptor->bg_block_bitmap);
109 unpinBlock(start + i);
113 const uint32_t start = LITTLE_TO_HOST32(descriptor->bg_inode_bitmap);
115 unpinBlock(start + i);
119 const uint32_t start = LITTLE_TO_HOST32(descriptor->bg_inode_table);
122 unpinBlock(start + i);
127 const uint32_t gdBlock = LITTLE_TO_HOST32(
m_pSuperblock->s_first_data_block) + 1;
155 if (!beginDeviceRemoval(deviceAvailable)) {
161 for (
auto it = m_InodeStates.
begin(); it != m_InodeStates.
end(); ++it) {
162 auto* state = it.value();
166 state->removedMetadata = *state->metadata;
167 state->metadata = &state->removedMetadata;
168 state->orphan =
false;
170 if (state->cache && !state->cache->fill.shutdown(Cache::ShutdownMode::DiscardDeferred)) {
176 for (
size_t n = 0; n < m_AttributeWriteCount; ++n) {
177 if (m_AttributeWrites[n].ownsPin) {
181 m_AttributeWriteCount = 0;
194 if (!block || block.size() <
sizeof(
Superblock)) {
198 ERROR(
"Ext2: Failed to read a superblock on " << devName);
205 ERROR(
"Ext2: Superblock was not found on device " << devName);
212 ERROR(
"Ext2: unsupported inode creator format on " << devName);
218 if (m_MountState != EXT2_STATE_CLEAN) {
219 WARNING(
"Ext2: filesystem on device " << devName <<
" is not clean.");
223 if (checkRequiredFeature(1)) {
224 WARNING(
"Ext2: filesystem on device " << devName
225 <<
" requires compression, some files may fail to read.");
228 uint32_t algo_bitmap = LITTLE_TO_HOST32(
m_pSuperblock->s_algo_bitmap);
229 switch (algo_bitmap) {
231 NOTICE(
"Ext2: filesystem on device '" << devName <<
"' uses compression algorithm LZV1.");
233 case EXT2_LZRW3A_ALG:
234 NOTICE(
"Ext2: filesystem on device '" << devName <<
"' uses compression algorithm LZRW3A.");
237 NOTICE(
"Ext2: filesystem on device '" << devName <<
"' uses compression algorithm gzip.");
240 NOTICE(
"Ext2: filesystem on device '" << devName <<
"' uses compression algorithm bzip2.");
243 NOTICE(
"Ext2: filesystem on device '" << devName <<
"' uses compression algorithm LZO.");
246 ERROR(
"Ext2: unknown compression algorithm " << algo_bitmap <<
" on device '" << devName
247 <<
"' -- cannot mount!");
267 ERROR(
"Ext2: filesystem's block size is too large (must be 4096 or less, but is " <<
m_BlockSize
272 ERROR(
"Ext2: filesystem block size " <<
m_BlockSize <<
" exceeds the target page size "
278 uint32_t gdBlock = LITTLE_TO_HOST32(
m_pSuperblock->s_first_data_block) + 1;
281 uint32_t inodeCount = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_count);
282 uint32_t inodesPerGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
283 if (!inodeCount || !inodesPerGroup) {
284 ERROR(
"Ext2: filesystem on device '" << devName <<
"' has invalid inode geometry.");
287 m_nGroupDescriptors = (inodeCount / inodesPerGroup) + ((inodeCount % inodesPerGroup) ? 1 : 0);
298 uintptr_t groupBlock =
readBlock(gdBlock + idx);
300 ERROR(
"Ext2: Failed to read block group descriptor " << i);
314 Inode* inode = getInode(EXT2_ROOT_INO);
316 ERROR(
"failed to retrieve root directory inode (corrupted inode table?");
319 if ((LITTLE_TO_HOST16(inode->i_mode) & 0xF000) != EXT2_S_IFDIR) {
320 ERROR(
"root directory is not a directory");
326 bool hasVolumeLabel = LITTLE_TO_HOST32(
m_pSuperblock->s_rev_level) >= 1;
327 if ((!hasVolumeLabel) || (
m_pSuperblock->s_volume_name[0] ==
'\0')) {
329 str +=
"no-volume-label@";
330 str.append(
reinterpret_cast<uintptr_t
>(
this), 16);
366 const uint8_t* value =
reinterpret_cast<const uint8_t*
>(
m_pSuperblock->s_uuid);
367 static const char digits[] =
"0123456789abcdef";
370 for (
size_t i = 0; i < 16; ++i) {
371 if (i == 4 || i == 6 || i == 8 || i == 10) {
374 text[pos++] = digits[value[i] >> 4];
375 text[pos++] = digits[value[i] & 15];
378 uuid.assign(text, pos);
383 const String& value,
size_t type, uint32_t inodeOverride) {
385 if (inodeOverride && isQuotaFile(inodeOverride)) {
386 SYSCALL_ERROR(NotEnoughPermissions);
391 SYSCALL_ERROR(NotADirectory);
396 if (filename.length() == 0 || !StringCompare(filename.cstr(),
".") ||
397 !StringCompare(filename.cstr(),
"..")) {
398 SYSCALL_ERROR(InvalidArgument);
402#ifdef EXT2_STANDALONE
403 uint32_t uid = 0, gid = 0;
406 if (!Process::currentFilesystemCredentials(credentials)) {
407 SYSCALL_ERROR(PermissionDenied);
410 const uint32_t uid = credentials.uid, gid = credentials.gid;
414 uint32_t inode_num = inodeOverride;
416 inode_num = findFreeInode(uid, gid);
417 if (inode_num == 0) {
422 uint32_t timestamp = getUnixTimestamp();
426 Inode* newInode = getInode(inode_num);
427 if (!inodeOverride) {
429 newInode->i_mode = HOST_TO_LITTLE16(mask | type);
430 newInode->i_atime = newInode->i_ctime = newInode->i_mtime = HOST_TO_LITTLE32(timestamp);
435 if (value.length() && value.length() < 4 * 15) {
436 MemoryCopy(
reinterpret_cast<void*
>(newInode->i_block), value.cstr(), value.length());
437 newInode->i_size = HOST_TO_LITTLE32(value.length());
444 bool dotEntryCreated =
false;
445 bool dotDotEntryCreated =
false;
451 Ext2File* pNewFile =
new Ext2File(filename, inode_num, newInode,
this, parent);
452 if (!pNewFile || !pNewFile->valid()) {
453 if (!inodeOverride) {
457 SYSCALL_ERROR(OutOfMemory);
468 pNewDirectory = pE2Dir;
472 if (!inodeOverride) {
476 dotEntryCreated = pE2Dir->
addEntry(
String(
"."), pE2Dir, EXT2_S_IFDIR);
477 if (dotEntryCreated) {
478 dotDotEntryCreated = pE2Dir->
addEntry(
String(
".."), pE2Parent, EXT2_S_IFDIR);
480 if (!dotEntryCreated || !dotDotEntryCreated) {
481 const int failure = currentIoError();
483 ERROR(
"EXT2: Failed to unwind a new directory's self link");
485 }
else if (!dotEntryCreated) {
489 syscallError(failure);
498 pNewNode = pNewSymlink;
502 FATAL(
"EXT2: Unrecognised file type: " <<
Hex << type);
507 if (value.length() && value.length() >= 4 * 15) {
509 if (pFile->
write(0ULL, value.length(),
reinterpret_cast<uintptr_t
>(value.cstr())) !=
511 const int failure = currentIoError();
515 syscallError(failure);
522 if (!pE2Parent->
addEntry(filename, pFile, type)) {
523 const int failure = currentIoError();
524 ERROR(
"EXT2: Internal error adding directory entry.");
525 if (!inodeOverride) {
526 if (pNewDirectory && dotDotEntryCreated &&
528 ERROR(
"EXT2: Failed to unwind a new directory's parent link");
531 if (pNewDirectory && dotEntryCreated) {
533 ERROR(
"EXT2: Failed to unwind a new directory's self link");
541 syscallError(failure);
550 writeInode(inode_num);
551 writeInode(pE2Parent->getInodeNumber());
554 if (type == EXT2_S_IFDIR) {
555#if THREADS || defined(STANDALONE_MUTEXES)
558 uint32_t group = (inode_num - 1) / LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
561 pDesc->bg_used_dirs_count++;
565 uint32_t gdBlock = LITTLE_TO_HOST32(
m_pSuperblock->s_first_data_block) + 1;
593 return createNode(parent, filename, 0777, value, EXT2_S_IFLNK);
616 Inode* inode = pNode->getInode();
617 uint32_t mask = LITTLE_TO_HOST16(inode->i_mode) & 0x0FFF;
618 size_t type = LITTLE_TO_HOST16(inode->i_mode) & 0xF000;
620 return createNode(parent, filename, mask,
String(
""), type, pNode->getInodeNumber());
625 if (isQuotaFile(file->getInode())) {
626 SYSCALL_ERROR(NotEnoughPermissions);
631 SYSCALL_ERROR(IoError);
648 const bool ordinaryDirectory =
650 bool result = ordinaryDirectory
651 ?
static_cast<Ext2Directory*
>(file)->removeFromParent(pE2Parent, filename)
655 if (result && ordinaryDirectory) {
656#if THREADS || defined(STANDALONE_MUTEXES)
659 uint32_t inode_num = pNode->getInodeNumber();
661 uint32_t group = (inode_num - 1) / LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
664 pDesc->bg_used_dirs_count--;
668 uint32_t gdBlock = LITTLE_TO_HOST32(
m_pSuperblock->s_first_data_block) + 1;
678 if (!tryAcquireOperation(operation)) {
682 return reinterpret_cast<uintptr_t
>(g_pSparseBlock);
684 const uint64_t location =
static_cast<uint64_t
>(
m_BlockSize) *
static_cast<uint64_t
>(block);
692 return view.address();
695DiskReadView Ext2Filesystem::readBlockView(uint32_t block) {
697 if (!tryAcquireOperation(operation)) {
709 if (!tryAcquireOperation(operation)) {
718bool Ext2Filesystem::pinBlock(uint64_t location) {
725void Ext2Filesystem::unpinBlock(uint64_t location) {
732bool Ext2Filesystem::syncBlock(uint32_t block,
bool async) {
736 const uint64_t location =
static_cast<uint64_t
>(
m_BlockSize) * block;
751bool Ext2Filesystem::syncInode(uint32_t inode,
Ext2Node& node,
bool includeNamespaceMetadata) {
753 if (!tryAcquireOperation(operation)) {
759#if THREADS || defined(STANDALONE_MUTEXES)
763 AttributeRetirement attributes;
764 if (readAttributeBlockLocked(node.m_pInode, attributes) != XattrStatus::Success ||
765 !flushAttributeWritesLocked())
767 const uint32_t inodesPerGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
768 const uint32_t blocksPerGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_blocks_per_group);
769 if (!inodesPerGroup || !blocksPerGroup) {
772 const uint32_t inodeGroup = (inode - 1) / inodesPerGroup;
773 const uint32_t index = (inode - 1) % inodesPerGroup;
775 if (inodeGroup >=
m_nGroupDescriptors || !loadInodeTableBlock(inodeGroup, inodeTableIndex)) {
781 groups.pushBack(i == inodeGroup ? 1 : 0);
783 bool succeeded =
true;
784 const uint32_t firstBlock = LITTLE_TO_HOST32(
m_pSuperblock->s_first_data_block);
785 auto includeBlockGroup = [&](uint32_t block) {
789 if (block < firstBlock || (block - firstBlock) / blocksPerGroup >= groups.count()) {
793 groups[(block - firstBlock) / blocksPerGroup] = 1;
795 const uint32_t attributeBlock = LITTLE_TO_HOST32(node.m_pInode->i_file_acl);
796 includeBlockGroup(attributeBlock);
797 if (attributeBlock && !syncBlock(attributeBlock,
false))
799 if (!node.isInlineSymlink()) {
800 for (
size_t i = 0; i < 12; ++i)
801 includeBlockGroup(LITTLE_TO_HOST32(node.m_pInode->i_block[i]));
804 const size_t entries =
m_BlockSize /
sizeof(uint32_t);
806 succeeded = node.collectMappingPages(
807 node.isInlineSymlink() ? 0 : LITTLE_TO_HOST32(node.m_pInode->i_block[12]), 1, 12,
808 entries, mappings) &&
810 succeeded = node.collectMappingPages(
811 node.isInlineSymlink() ? 0 : LITTLE_TO_HOST32(node.m_pInode->i_block[13]), 2,
812 12 + entries, entries * entries, mappings) &&
814 succeeded = node.collectMappingPages(
815 node.isInlineSymlink() ? 0 : LITTLE_TO_HOST32(node.m_pInode->i_block[14]), 3,
816 12 + entries + entries * entries, entries * entries * entries, mappings) &&
819 includeBlockGroup(mapping.block);
820 if (mapping.depth == 1) {
821 const uint32_t* children =
reinterpret_cast<const uint32_t*
>(mapping.buffer);
822 for (
size_t i = 0; i < entries; ++i) {
823 includeBlockGroup(LITTLE_TO_HOST32(children[i]));
826 succeeded = syncBlock(mapping.block,
false) && succeeded;
827 unpinBlock(mapping.block);
832 uint64_t locations[Disk::MaxSyncPages];
833 size_t locationCount = 0;
834 auto flushMetadata = [&] {
840 auto submitMetadata = [&](uint64_t location) {
841 for (
size_t i = 0; i < locationCount; ++i) {
842 if (locations[i] == location) {
846 locations[locationCount++] = location;
847 if (locationCount == Disk::MaxSyncPages) {
851 for (
size_t group = 0; group < groups.count(); ++group) {
852 if (!groups[group]) {
856 if (ensureFreeBlockBitmapLoaded(group)) {
857 const uint32_t start = LITTLE_TO_HOST32(descriptor->bg_block_bitmap);
859 submitMetadata(
static_cast<uint64_t
>(start + i) *
m_BlockSize);
865 submitMetadata(
static_cast<uint64_t
>(descriptorBlock) *
m_BlockSize);
867 if (ensureFreeInodeBitmapLoaded(inodeGroup)) {
870 submitMetadata(
static_cast<uint64_t
>(start + i) *
m_BlockSize);
875 submitMetadata(1024);
880 if (includeNamespaceMetadata) {
884#if THREADS || defined(STANDALONE_MUTEXES)
891 const uint32_t inodeTable = LITTLE_TO_HOST32(descriptor->bg_inode_table);
892 bool loadedInodeTable =
false;
895 submitMetadata(
static_cast<uint64_t
>(inodeTable + i) *
m_BlockSize);
896 loadedInodeTable =
true;
899 const uint32_t blockBitmap = LITTLE_TO_HOST32(descriptor->bg_block_bitmap);
901 submitMetadata(
static_cast<uint64_t
>(blockBitmap + i) *
m_BlockSize);
903 const uint32_t inodeBitmap = LITTLE_TO_HOST32(descriptor->bg_inode_bitmap);
905 submitMetadata(
static_cast<uint64_t
>(inodeBitmap + i) *
m_BlockSize);
909 submitMetadata(
static_cast<uint64_t
>(descriptorBlock) *
m_BlockSize);
914 const uint32_t inodeBlock = LITTLE_TO_HOST32(
m_pGroupDescriptors[inodeGroup]->bg_inode_table) +
916 return succeeded && syncBlock(inodeBlock,
false);
919uint32_t Ext2Filesystem::findFreeBlock(uint32_t inode) {
921 if (findFreeBlocks(inode, 1, blocks)) {
928bool Ext2Filesystem::findFreeBlocks(uint32_t inode,
size_t count,
Vector<uint32_t>& blocks) {
929#if THREADS || defined(STANDALONE_MUTEXES)
935 if (!blocks.tryReserve(count)) {
936 SYSCALL_ERROR(OutOfMemory);
940 SYSCALL_ERROR(ValueTooLarge);
943 const uint64_t reserved =
static_cast<uint64_t
>(count) *
m_BlockSize;
944 auto status = prepareQuotaInodeLocked(inode);
945 if (status == QuotaStatus::Success)
946 status = m_Quota.reserve(inode, reserved);
947 if (!quotaSucceeded(status))
949 const uint32_t inodeNumber = inode;
955 uint32_t group = inode / LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
956 uint32_t startGroup = group;
962 if (
m_pGroupDescriptors[group]->bg_free_blocks_count && !ensureFreeBlockBitmapLoaded(group)) {
963 error = currentIoError();
973 ERROR(
"FALLING BACK TO STARTING FROM ZERO");
974 for (group = 0; count && !error && group < startGroup; ++group) {
975 if (
m_pGroupDescriptors[group]->bg_free_blocks_count && !ensureFreeBlockBitmapLoaded(group)) {
976 error = currentIoError();
983 for (uint32_t block : blocks) {
987 m_Quota.refund(inodeNumber, reserved);
988 syscallError(error ? error : Error::NoSpaceLeftOnDevice);
999 const uint32_t blocksPerGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_blocks_per_group);
1001 size_t currentCount = 0;
1005 if (!pDesc->bg_free_blocks_count) {
1007 return currentCount;
1010 if (!ensureFreeBlockBitmapLoaded(group)) {
1016 const uint32_t bytesToSearch = blocksPerGroup >> 3;
1020 typedef uint64_t searchType;
1021 size_t base = list[idx];
1022 searchType* ptr =
reinterpret_cast<searchType*
>(base);
1023 searchType* ptr_end = adjust_pointer(ptr, bitmapBlockBytes);
1026 bool changedBitmap =
false;
1031 searchType tmp = *ptr;
1034 if (tmp !=
static_cast<searchType
>(-1)) {
1036 for (
size_t j = 0; j < (
sizeof(searchType) * 8); j++, tmp >>=
static_cast<searchType
>(1)) {
1038 if ((tmp & 1) == 0) {
1040 *ptr |= (
static_cast<searchType
>(1) << j);
1041 pDesc->bg_free_blocks_count--;
1044 changedBitmap =
true;
1050 uint32_t result = group * LITTLE_TO_HOST32(
m_pSuperblock->s_blocks_per_group);
1052 result += LITTLE_TO_HOST32(
m_pSuperblock->s_first_data_block);
1054 result += ((idx * bitmapBlockBytes) + (
reinterpret_cast<uintptr_t
>(ptr) - base)) << 3;
1062 if ((++currentCount >= maxCount) || (!pDesc->bg_free_blocks_count)) {
1072 if (changedBitmap) {
1074 uint32_t desc_block = LITTLE_TO_HOST32(
m_pGroupDescriptors[group]->bg_block_bitmap) + idx;
1077 changedBitmap =
false;
1081 if (currentCount >= maxCount) {
1086 if (++ptr >= ptr_end) {
1087 if ((++idx * bitmapBlockBytes) >= bytesToSearch)
1091 ptr =
reinterpret_cast<searchType*
>(base);
1092 ptr_end = adjust_pointer(ptr, bitmapBlockBytes);
1096 if (currentCount >= maxCount) {
1102 uint32_t gdBlock = LITTLE_TO_HOST32(
m_pSuperblock->s_first_data_block) + 1;
1107 return currentCount;
1110void Ext2Filesystem::releaseBlock(uint32_t block, uint32_t inode) {
1111#if THREADS || defined(STANDALONE_MUTEXES)
1118bool Ext2Filesystem::prepareBlockReleaseLocked(uint32_t block) {
1119 const uint32_t first = LITTLE_TO_HOST32(
m_pSuperblock->s_first_data_block);
1120 const uint32_t perGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_blocks_per_group);
1122 SYSCALL_ERROR(IoError);
1125 return ensureFreeBlockBitmapLoaded((block - first) / perGroup);
1128bool Ext2Filesystem::prepareInodeWrite(uint32_t inode) {
1129 const uint32_t perGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
1131 SYSCALL_ERROR(IoError);
1135 SYSCALL_ERROR(IoError);
1139 return loadInodeTableBlock((inode - 1) / perGroup, block) != 0;
1145 block -= LITTLE_TO_HOST32(
m_pSuperblock->s_first_data_block);
1147 uint32_t blocksPerGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_blocks_per_group);
1148 uint32_t group = block / blocksPerGroup;
1149 uint32_t index = block % blocksPerGroup;
1154 FATAL(
"Releasing block zero!");
1157 if (!ensureFreeBlockBitmapLoaded(group)) {
1169 uintptr_t diskBlock = list[bitmapField];
1170 uint8_t* ptr =
reinterpret_cast<uint8_t*
>(diskBlock + bitmapOffset);
1171 uint8_t bit = (index % 8);
1172 if ((*ptr & (1 << bit)) == 0) {
1173 ERROR(
"bit already freed for block " <<
Dec << block <<
Hex);
1176 *ptr &= ~(1 << bit);
1181 pDesc->bg_free_blocks_count++;
1188 uint32_t desc_block = LITTLE_TO_HOST32(
m_pGroupDescriptors[group]->bg_block_bitmap) + bitmapField;
1193 uint32_t gdBlock = LITTLE_TO_HOST32(
m_pSuperblock->s_first_data_block) + 1;
1202 if (!state->references && !state->cache) {
1203 state->reloadMappings(metadata,
this);
1205 ++state->references;
1209 m_InodeStates.
insert(inode, state);
1216 if (!state->references && !state->cache) {
1217 state->reloadMappings(metadata,
this);
1219 ++state->references;
1223 if (!state || !m_InodeStates.
tryInsert(inode, state)) {
1232 assert(state == m_InodeStates.
lookup(inode) && state->references);
1233 if (--state->references) {
1236 if (!state->orphan) {
1237 assert(!state->pageLoans && !state->files.
count());
1240 if (!state->cache) {
1241 state->blocks.
clear(
true);
1242 state->metadataBlocks = 0;
1244 state->files.
clear(
true);
1247 m_InodeStates.
remove(inode);
1248 if (state->orphan && !isDeviceRemoved()) {
1249#if THREADS || defined(STANDALONE_MUTEXES)
1260 LockGuard<Mutex> metadataGuard(state ? state->writebackLock : m_InodeStateLock, state !=
nullptr);
1261#if THREADS || defined(STANDALONE_MUTEXES)
1264 const bool remove = decreaseInodeRefcount(inodeNumber);
1267 state->orphan =
true;
1268 state->inodeEvents.beginRetirement();
1277 Inode* pInode = getInode(inodeNumber);
1279 m_TeardownFailed =
true;
1282 const uint32_t inodeIndex = inodeNumber - 1;
1284 uint32_t inodesPerGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
1285 uint32_t group = inodeIndex / inodesPerGroup;
1286 uint32_t index = inodeIndex % inodesPerGroup;
1288 uint32_t allocatedBlocks = 0;
1289 bool inlineSymlink =
false;
1291 if (!Ext2Node::decodeAllocation(*pInode,
m_BlockSize, allocatedBlocks, inlineSymlink) ||
1292 prepareAttributeRetirementLocked(pInode, attributes) != XattrStatus::Success ||
1293 !ensureFreeInodeBitmapLoaded(group) ||
1294 (attributes.block && reserveAttributeWritesLocked(12) != XattrStatus::Success)) {
1295 ERROR(
"Ext2: retaining an orphan inode whose attributes could not be retired");
1296 m_TeardownFailed =
true;
1303 if (retiringNode && !retiringNode->
wipe(
true)) {
1304 ERROR(
"Ext2: retaining an orphan inode whose blocks could not be retired");
1305 m_TeardownFailed =
true;
1309 if (attributes.block) {
1311 const uint32_t allocated = LITTLE_TO_HOST32(pInode->i_blocks);
1312 assert(allocated >= sectors);
1313 pInode->i_file_acl = 0;
1314 pInode->i_blocks = HOST_TO_LITTLE32(allocated - sectors);
1315 commitAttributeRetirementLocked(attributes);
1316 recordAttributeInodeLocked(inodeNumber);
1319 pInode->i_dtime = HOST_TO_LITTLE32(getUnixTimestamp());
1321 if (!ensureFreeInodeBitmapLoaded(group)) {
1322 m_TeardownFailed =
true;
1328 pDesc->bg_free_inodes_count++;
1336 uintptr_t block = list[bitmapField];
1337 uint8_t* ptr =
reinterpret_cast<uint8_t*
>(block + bitmapOffset);
1338 *ptr &= ~(1 << (index % 8));
1339 m_Quota.forget(inodeNumber);
1340 if (attributes.block) {
1341 const uint32_t bitmap = LITTLE_TO_HOST32(pDesc->bg_inode_bitmap) + bitmapField;
1342 recordAttributeWriteLocked(
static_cast<uint64_t
>(bitmap) *
m_BlockSize,
1343 AttributeWriteKind::Allocation);
1344 recordAttributeAllocationLocked(attributes.block);
1345 const uint32_t descriptor = LITTLE_TO_HOST32(
m_pSuperblock->s_first_data_block) + 1 +
1347 recordAttributeWriteLocked(
static_cast<uint64_t
>(descriptor) *
m_BlockSize,
1348 AttributeWriteKind::Allocation);
1355 uint32_t desc_block =
1361 uint32_t gdBlock = LITTLE_TO_HOST32(
m_pSuperblock->s_first_data_block) + 1;
1366 writeInode(inodeNumber);
1369Inode* Ext2Filesystem::getInode(uint32_t inode) {
1371 if (!tryAcquireOperation(operation)) {
1374 const uint32_t inodesPerGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
1376 SYSCALL_ERROR(IoError);
1380 const uint32_t group = inode / inodesPerGroup;
1381 const uint64_t byteOffset =
static_cast<uint64_t
>(inode % inodesPerGroup) *
m_InodeSize;
1385 SYSCALL_ERROR(IoError);
1388 const uintptr_t block = loadInodeTableBlock(group, blockNum);
1392 Inode* pInode =
reinterpret_cast<Inode*
>(block + blockOff);
1393 if (pInode->i_flags & EXT2_COMPRBLK_FL) {
1394 WARNING(
"Ext2: inode " << inode <<
" has compressed blocks - not yet supported!");
1399void Ext2Filesystem::writeInode(uint32_t inode) {
1400 if (!prepareInodeWrite(inode))
1403 const uint32_t inodesPerGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
1404 const uint32_t group = inode / inodesPerGroup;
1405 const size_t blockNum =
1407 const uint32_t diskBlock =
1412bool Ext2Filesystem::checkOptionalFeature(
size_t feature) {
1418bool Ext2Filesystem::checkRequiredFeature(
size_t feature) {
1424bool Ext2Filesystem::checkReadOnlyFeature(
size_t feature) {
1430bool Ext2Filesystem::ensureFreeBlockBitmapLoaded(
size_t group) {
1434 if (list.count() > 0)
1440 uint32_t blocksPerGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_blocks_per_group);
1441 size_t nBlocks = blocksPerGroup / (
m_BlockSize * 8);
1445 if (!list.tryReserve(nBlocks)) {
1446 SYSCALL_ERROR(OutOfMemory);
1451 for (
size_t i = 0; i < nBlocks; i++) {
1452 uint32_t blockNumber = start + i;
1454 while (list.count()) {
1455 unpinBlock(start + list.count() - 1);
1458 SYSCALL_ERROR(IoError);
1461 uintptr_t buffer =
readBlock(blockNumber);
1463 while (list.count()) {
1464 unpinBlock(start + list.count() - 1);
1467 SYSCALL_ERROR(IoError);
1470 list.pushBack(buffer);
1476bool Ext2Filesystem::ensureFreeInodeBitmapLoaded(
size_t group) {
1480 if (list.count() > 0)
1486 uint32_t inodesPerGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
1487 size_t nBlocks = inodesPerGroup / (
m_BlockSize * 8);
1491 if (!list.tryReserve(nBlocks)) {
1492 SYSCALL_ERROR(OutOfMemory);
1497 for (
size_t i = 0; i < nBlocks; i++) {
1498 uint32_t blockNumber = start + i;
1500 while (list.count()) {
1501 unpinBlock(start + list.count() - 1);
1504 SYSCALL_ERROR(IoError);
1507 uintptr_t buffer =
readBlock(blockNumber);
1509 while (list.count()) {
1510 unpinBlock(start + list.count() - 1);
1513 SYSCALL_ERROR(IoError);
1516 list.pushBack(buffer);
1522uintptr_t Ext2Filesystem::loadInodeTableBlock(
size_t group,
size_t block) {
1523#if THREADS || defined(STANDALONE_MUTEXES)
1527 SYSCALL_ERROR(IoError);
1530 const uint32_t inodesPerGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
1531 const uint64_t inodeTableBytes =
static_cast<uint64_t
>(inodesPerGroup) *
m_InodeSize;
1534 if (block >= blocks || !start || block > ~uint32_t{0} - start) {
1535 SYSCALL_ERROR(IoError);
1540 if (block < list.count() && list[block])
1542 if (!list.tryReserve(block + 1)) {
1543 SYSCALL_ERROR(OutOfMemory);
1546 while (list.count() <= block)
1549 const uintptr_t buffer =
readBlock(start + block);
1551 SYSCALL_ERROR(IoError);
1556 list[block] = buffer;
1560void Ext2Filesystem::increaseInodeRefcount(uint32_t inode) {
1563 LockGuard<Mutex> metadataGuard(state ? state->writebackLock : m_InodeStateLock, state != nullptr);
1564#if THREADS || defined(STANDALONE_MUTEXES)
1568 Inode* pInode = getInode(inode);
1572 uint32_t current_count = LITTLE_TO_HOST16(pInode->i_links_count);
1573 pInode->i_links_count = HOST_TO_LITTLE16(current_count + 1);
1575 state->orphan =
false;
1581bool Ext2Filesystem::decreaseInodeRefcount(uint32_t inode) {
1582 Inode* pInode = getInode(inode);
1586 uint32_t current_count = LITTLE_TO_HOST16(pInode->i_links_count);
1587 bool bRemove = current_count <= 1;
1589 pInode->i_links_count = HOST_TO_LITTLE16(current_count - 1);
1595#ifndef EXT2_STANDALONE
1596static bool initExt2() {
1601static void destroyExt2() {
1602 if (!
VFS::instance().removeProbeCallback(&Ext2Filesystem::probe)) {
1603 FATAL(
"Ext2 probe callback was not registered during unload");
1607MODULE_INFO(
"ext2", &initExt2, &destroyExt2,
"vfs");
static DiskReadView borrowed(const void *data, size_t size)
virtual bool sync(uint64_t location, bool async)
virtual BufferView read(uint64_t location)
virtual void getName(String &str)
virtual void unpin(uint64_t location)=0
virtual MUST_USE_RESULT bool syncPages(const uint64_t *locations, size_t count)
virtual void write(uint64_t location)
virtual DiskReadView readView(uint64_t location)
virtual MUST_USE_RESULT bool pin(uint64_t location)=0
Pins a cache page.
virtual bool removeEntry(const String &filename, Ext2Node *pFile)
virtual bool addEntry(const String &filename, File *pFile, size_t type)
virtual bool createNode(File *parent, const String &filename, uint32_t mask, const String &value, size_t type, uint32_t inodeOverride=0)
size_t m_nGroupDescriptors
GroupDesc ** m_pGroupDescriptors
virtual bool removeNode(File *parent, const String &filename, File *file)
void writeBlock(uint32_t block)
void retireInodeLocked(uint32_t inode, Ext2Node *lastNode)
virtual bool initialise(Disk *pDisk)
uintptr_t readBlock(uint32_t block)
Vector< size_t > * m_pInodeTables
virtual bool createDirectory(File *parent, const String &filename, uint32_t mask)
Vector< size_t > * m_pBlockBitmaps
virtual bool createSymlink(File *parent, const String &filename, const String &value)
void releaseBlockLocked(uint32_t block, uint32_t inode=0)
virtual const String & getVolumeLabel() const
size_t findFreeBlocksInGroup(uint32_t group, size_t maxCount, Vector< uint32_t > &blocks)
virtual bool deviceRemoved(bool deviceAvailable=false)
Mutex m_InodeTableLoadLock
virtual bool getUuid(String &uuid) const
Vector< size_t > * m_pInodeBitmaps
bool releaseInode(uint32_t inode, Ext2Node *retiringNode=nullptr)
virtual bool createFile(File *parent, const String &filename, uint32_t mask)
virtual bool createLink(File *parent, const String &filename, File *target)
Superblock * m_pSuperblock
virtual File * getRoot() const
bool wipe(bool allocationLockHeld=false)
bool ensureLargeEnough(size_t size, uint64_t location, uint64_t opsize, bool onlyBlocks=false, bool nozeroblocks=false)
virtual uint64_t write(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
virtual bool isDirectory()
void setModifiedTime(Time::Timestamp t)
void setAccessedTime(Time::Timestamp t)
bool remove(const StringView &path, File *pStartNode=0)
virtual bool deviceRemoved(bool deviceAvailable=false)
virtual Timer * getTimer()=0
bool compare(const char *s, size_t len) const
static constexpr size_t getPageSize() noexcept
virtual Time::Timestamp getUnixTimestamp()
bool tryInsert(const K &key, const E &value)
void remove(const K &key)
E lookup(const K &key) const
void insert(const K &key, const E &value)
void addProbeCallback(Filesystem::ProbeCallback callback)
A vector / dynamic array.
void pushBack(const T &value)
void clear(bool freeMem=false)