21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/compiler.h"
24#include "pedigree/kernel/machine/Disk.h"
25#include "pedigree/kernel/syscallError.h"
26#include "pedigree/kernel/utilities/assert.h"
27#include "pedigree/kernel/utilities/utility.h"
29#include "Ext2Filesystem.h"
31#include "modules/system/vfs/File.h"
36 allocatedDataBlocks(0),
37 size(LITTLE_TO_HOST32(pInode->i_size)),
47 filesystem(filesystem) {
48 reloadMappings(pInode, filesystem);
51Ext2InodeState::~Ext2InodeState() {
56 assert(blocks.
count() == 0);
57 size = LITTLE_TO_HOST32(pInode->i_size);
59 uint32_t totalBlocks = 0;
60 bool inlineSymlink =
false;
61 allocationValid = Ext2Node::decodeAllocation(*pInode, blockSize, totalBlocks, inlineSymlink);
62 if (!allocationValid) {
63 metadataBlocks = allocatedDataBlocks = 0;
66 size_t dataBlockCount = 0;
68 dataBlockCount = size / blockSize;
69 if (size % blockSize) {
74 blocks.
reserve(dataBlockCount,
false);
77 for (
size_t i = 0; i < 12 && i < dataBlockCount; i++)
78 blocks.
pushBack(LITTLE_TO_HOST32(pInode->i_block[i]));
81 for (
size_t i = 12; i < dataBlockCount; ++i) {
85 for (
size_t i = dataBlockCount; i < 12; ++i) {
86 const uint32_t block = LITTLE_TO_HOST32(pInode->i_block[i]);
88 while (blocks.
count() <= i) {
94 const size_t entries = blockSize /
sizeof(uint32_t);
95 loadMappings(filesystem, LITTLE_TO_HOST32(pInode->i_block[12]), 1, 12, entries);
96 loadMappings(filesystem, LITTLE_TO_HOST32(pInode->i_block[13]), 2, 12 + entries,
98 loadMappings(filesystem, LITTLE_TO_HOST32(pInode->i_block[14]), 3,
99 12 + entries + entries * entries, entries * entries * entries);
101 allocationValid = totalBlocks >= metadataBlocks;
102 allocatedDataBlocks = allocationValid ? totalBlocks - metadataBlocks : 0;
105void Ext2InodeState::loadMappings(
Ext2Filesystem* filesystem, uint32_t block,
unsigned depth,
106 size_t first,
size_t span) {
108 const size_t end = first + span < blocks.
count() ? first + span : blocks.
count();
109 for (
size_t i = first; i < end; ++i) {
115 const DiskReadView buffer = filesystem->readBlockView(block);
119 const size_t entries = filesystem->
m_BlockSize /
sizeof(uint32_t);
120 for (
size_t i = 0; i < entries; ++i) {
122 if (!buffer.readAt(child, i *
sizeof(child)))
124 child = LITTLE_TO_HOST32(child);
126 loadMappings(filesystem, child, depth - 1, first + i * (span / entries), span / entries);
127 }
else if (child || first + i < blocks.
count()) {
128 while (blocks.
count() <= first + i) {
131 blocks[first + i] = child;
137 : m_State(pFs->acquireInodeState(inode_num, pInode)),
138 m_pInode(m_State->metadata),
139 m_InodeNumber(inode_num),
141 m_Blocks(m_State->blocks),
142 m_nMetadataBlocks(m_State->metadataBlocks),
143 m_nSize(m_State->size) {}
147 : m_State(&admitted),
148 m_pInode(admitted.metadata),
149 m_InodeNumber(inode),
150 m_pExt2Fs(filesystem),
151 m_Blocks(admitted.blocks),
152 m_nMetadataBlocks(admitted.metadataBlocks),
153 m_nSize(admitted.size) {}
156 m_pExt2Fs->releaseInodeState(m_InodeNumber, m_State,
this);
159uintptr_t Ext2Node::readBlock(uint64_t location) {
160 if (!m_State->allocationValid) {
161 SYSCALL_ERROR(IoError);
165 uint32_t nBlock = location / m_pExt2Fs->
m_BlockSize;
166 if (nBlock >= m_Blocks.
count()) {
167 ERROR(
"Ext2Node::readBlock beyond blocks [" << nBlock <<
", " << m_Blocks.
count() <<
"]");
170 if (location >= m_nSize) {
171 ERROR(
"Ext2Node::readBlock beyond size [" << location <<
", " << m_nSize <<
"]");
175 if (!ensureBlockLoaded(nBlock)) {
178 uintptr_t result = m_pExt2Fs->
readBlock(m_Blocks[nBlock]);
188void Ext2Node::writeBlock(uint64_t location) {
190 uint32_t nBlock = location / m_pExt2Fs->
m_BlockSize;
191 if (nBlock >= m_Blocks.
count())
193 if (location >= m_nSize)
197 if (!ensureBlockLoaded(nBlock)) {
203void Ext2Node::trackBlock(uint32_t block,
bool writeInode) {
206 ++m_State->allocatedDataBlocks;
209 updateAllocatedSectorCount();
213 m_pExt2Fs->writeInode(getInodeNumber());
219 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
222 if (!m_State->allocationValid) {
223 SYSCALL_ERROR(IoError);
226 if (isInlineSymlink()) {
227 ByteSet(m_pInode->i_block, 0,
sizeof(m_pInode->i_block));
229 m_pInode->i_size = 0;
230 m_pExt2Fs->writeInode(getInodeNumber());
233 if (!trimToBlocks(0, allocationLockHeld)) {
237 m_pInode->i_size = 0;
238 m_pExt2Fs->writeInode(getInodeNumber());
242void Ext2Node::extend(
size_t newSize) {
246void Ext2Node::extend(
size_t newSize, uint64_t location, uint64_t size) {
250uint64_t Ext2Node::maximumFileSize()
const {
252 const uint64_t entries = blockSize /
sizeof(uint32_t);
253 const uint64_t maximumBlocks = 12 + entries + entries * entries;
254 constexpr uint64_t InodeSizeLimit = 0xffffffffULL;
255 return maximumBlocks > InodeSizeLimit / blockSize ? InodeSizeLimit : maximumBlocks * blockSize;
260 if (!m_State->allocationValid) {
261 SYSCALL_ERROR(IoError);
265 if (size > maximumFileSize()) {
266 SYSCALL_ERROR(FileTooLarge);
269 const size_t oldBlocks = m_Blocks.
count();
270 const size_t currentMaxSize = oldBlocks * blockSize;
271 if (size <= currentMaxSize) {
272 if (size > m_nSize && !onlyBlocks) {
273 if (!zeroRange(m_nSize, size)) {
277 m_pInode->i_size = HOST_TO_LITTLE32(size);
278 m_pExt2Fs->writeInode(getInodeNumber());
283 const size_t delta = size - currentMaxSize;
284 const size_t deltaBlocks = delta / blockSize + (delta % blockSize != 0);
286 if (!m_pExt2Fs->findFreeBlocks(m_InodeNumber, deltaBlocks, newBlocks)) {
292 for (uint32_t block : newBlocks) {
293 if (!addBlock(block, &pendingWrites)) {
301 if (!m_pExt2Fs->
m_pDisk->
zero(
static_cast<uint64_t
>(block) * blockSize, blockSize)) {
302 SYSCALL_ERROR(IoError);
307 for (uint32_t block : pendingWrites) {
309 m_pExt2Fs->unpinBlock(block);
311 if (success && !onlyBlocks && m_nSize < currentMaxSize) {
312 success = zeroRange(m_nSize, currentMaxSize);
315 for (
size_t i = attached; i < newBlocks.count(); ++i) {
316 m_pExt2Fs->releaseBlock(newBlocks[i], m_InodeNumber);
318 if (!trimToBlocks(oldBlocks)) {
319 ERROR(
"Ext2: unable to retire partially allocated extension blocks");
325 m_pInode->i_size = HOST_TO_LITTLE32(size);
327 m_pExt2Fs->writeInode(getInodeNumber());
331bool Ext2Node::ensureBlockLoaded(
size_t nBlock) {
332 if (nBlock >= m_Blocks.
count()) {
333 FATAL(
"EXT2: ensureBlockLoaded: Algorithmic error [block " << nBlock <<
" > "
334 << m_Blocks.
count() <<
"].");
336 if (m_Blocks[nBlock] == ~0U) {
337 return getBlockNumber(nBlock);
343bool Ext2Node::getBlockNumber(
size_t nBlock) {
346 assert(nBlock >= 12);
348 if (nBlock < nPerBlock + 12) {
349 return getBlockNumberIndirect(LITTLE_TO_HOST32(m_pInode->i_block[12]), 12, nBlock);
352 if (nBlock < (nPerBlock * nPerBlock) + nPerBlock + 12) {
353 return getBlockNumberBiindirect(LITTLE_TO_HOST32(m_pInode->i_block[13]), nPerBlock + 12,
357 return getBlockNumberTriindirect(LITTLE_TO_HOST32(m_pInode->i_block[14]),
358 (nPerBlock * nPerBlock) + nPerBlock + 12, nBlock);
361bool Ext2Node::getBlockNumberIndirect(uint32_t inode_block,
size_t nBlocks,
size_t nBlock) {
363 m_Blocks[nBlock] = 0;
366 const DiskReadView buffer = m_pExt2Fs->readBlockView(inode_block);
371 for (
size_t i = 0; i < m_pExt2Fs->
m_BlockSize / 4 && nBlocks < m_Blocks.
count(); i++) {
373 if (!buffer.readAt(block, i *
sizeof(block)))
375 m_Blocks[nBlocks++] = LITTLE_TO_HOST32(block);
381bool Ext2Node::getBlockNumberBiindirect(uint32_t inode_block,
size_t nBlocks,
size_t nBlock) {
385 m_Blocks[nBlock] = 0;
388 DiskReadView buffer = m_pExt2Fs->readBlockView(inode_block);
394 size_t nIndirectBlock = (nBlock - nBlocks) / nPerBlock;
396 uint32_t indirectBlock;
397 if (!buffer.readAt(indirectBlock, nIndirectBlock *
sizeof(indirectBlock)))
400 return getBlockNumberIndirect(LITTLE_TO_HOST32(indirectBlock),
401 nBlocks + nIndirectBlock * nPerBlock, nBlock);
404bool Ext2Node::getBlockNumberTriindirect(uint32_t inode_block,
size_t nBlocks,
size_t nBlock) {
408 m_Blocks[nBlock] = 0;
411 DiskReadView buffer = m_pExt2Fs->readBlockView(inode_block);
417 size_t nBiBlock = (nBlock - nBlocks) / (nPerBlock * nPerBlock);
420 if (!buffer.readAt(biBlock, nBiBlock *
sizeof(biBlock)))
423 return getBlockNumberBiindirect(LITTLE_TO_HOST32(biBlock),
424 nBlocks + nBiBlock * nPerBlock * nPerBlock, nBlock);
427void Ext2Node::writeBlockOrQueue(uint32_t block,
Vector<uint32_t>* pendingWrites) {
428 if (!pendingWrites) {
433 for (
auto pending : *pendingWrites) {
434 if (pending == block) {
441 if (!m_pExt2Fs->pinBlock(block)) {
450bool Ext2Node::setBlockNumber(
size_t blockNum, uint32_t blockValue,
453 m_pInode->i_block[blockNum] = HOST_TO_LITTLE32(blockValue);
457 const size_t entries = m_pExt2Fs->
m_BlockSize /
sizeof(uint32_t);
458 size_t index = blockNum - 12;
459 const unsigned depth = index < entries ? 1 : 2;
462 if (index >= entries * entries) {
463 SYSCALL_ERROR(FileTooLarge);
467 const size_t indices[2] = {depth == 1 ? index : index / entries, index % entries};
468 const size_t inodeIndex = depth == 1 ? 12 : 13;
469 uint32_t numbers[2] = {};
470 uintptr_t buffers[2] = {};
471 bool allocated[2] = {};
472 uint32_t next = LITTLE_TO_HOST32(m_pInode->i_block[inodeIndex]);
474 for (
unsigned level = 0; level < depth; ++level) {
475 numbers[level] = next;
477 numbers[level] = m_pExt2Fs->findFreeBlock(m_InodeNumber);
478 allocated[level] = numbers[level] != 0;
479 if (!allocated[level]) {
484 buffers[level] = m_pExt2Fs->
readBlock(numbers[level]);
485 if (!buffers[level]) {
486 SYSCALL_ERROR(IoError);
490 if (allocated[level]) {
491 ByteSet(
reinterpret_cast<void*
>(buffers[level]), 0, m_pExt2Fs->
m_BlockSize);
493 next = LITTLE_TO_HOST32(
reinterpret_cast<uint32_t*
>(buffers[level])[indices[level]]);
497 for (
unsigned level = depth; level-- > 0;) {
498 uint32_t* table =
reinterpret_cast<uint32_t*
>(buffers[level]);
499 table[indices[level]] =
500 HOST_TO_LITTLE32(level + 1 == depth ? blockValue : numbers[level + 1]);
501 writeBlockOrQueue(numbers[level], pendingWrites);
502 if (allocated[level]) {
506 m_pInode->i_block[inodeIndex] = HOST_TO_LITTLE32(numbers[0]);
508 for (
unsigned level = 0; level < depth; ++level) {
509 if (buffers[level]) {
510 m_pExt2Fs->unpinBlock(numbers[level]);
512 if (!ready && allocated[level]) {
513 m_pExt2Fs->releaseBlock(numbers[level], m_InodeNumber);
519bool Ext2Node::addBlock(uint32_t blockValue,
Vector<uint32_t>* pendingWrites) {
520 if (!setBlockNumber(m_Blocks.
count(), blockValue, pendingWrites)) {
523 trackBlock(blockValue, !pendingWrites);
527bool Ext2Node::ensureWritableRange(
size_t location,
size_t length) {
528 if (!m_State->allocationValid) {
529 SYSCALL_ERROR(IoError);
536 const size_t first = location / blockSize;
537 const size_t last = (location + length - 1) / blockSize;
538 for (
size_t index = first; index <= last; ++index) {
539 if (index >= m_Blocks.
count() || !ensureBlockLoaded(index)) {
540 SYSCALL_ERROR(IoError);
543 if (m_Blocks[index]) {
546 const uint32_t block = m_pExt2Fs->findFreeBlock(m_InodeNumber);
550 if (!m_pExt2Fs->
m_pDisk->
zero(
static_cast<uint64_t
>(block) * blockSize, blockSize)) {
551 m_pExt2Fs->releaseBlock(block, m_InodeNumber);
552 SYSCALL_ERROR(IoError);
555 if (!setBlockNumber(index, block)) {
556 m_pExt2Fs->releaseBlock(block, m_InodeNumber);
559 m_Blocks[index] = block;
560 ++m_State->allocatedDataBlocks;
561 updateAllocatedSectorCount();
562 m_pExt2Fs->writeInode(getInodeNumber());
569 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
572 m_pInode->i_size = HOST_TO_LITTLE32(size);
573 m_pInode->i_atime = HOST_TO_LITTLE32(atime);
574 m_pInode->i_mtime = HOST_TO_LITTLE32(mtime);
575 m_pInode->i_ctime = HOST_TO_LITTLE32(ctime);
581 m_pExt2Fs->writeInode(getInodeNumber());
587 attributes.accessed = LITTLE_TO_HOST32(m_pInode->i_atime);
588 attributes.modified = LITTLE_TO_HOST32(m_pInode->i_mtime);
589 attributes.changed = LITTLE_TO_HOST32(m_pInode->i_ctime);
590 attributes.uid = Ext2Owner::uid(*m_pInode);
591 attributes.gid = Ext2Owner::gid(*m_pInode);
592 attributes.permissions = modeToPermissions(LITTLE_TO_HOST16(m_pInode->i_mode));
593 attributes.size = LITTLE_TO_HOST32(m_pInode->i_size);
594 attributes.links = LITTLE_TO_HOST16(m_pInode->i_links_count);
595 attributes.blocks = LITTLE_TO_HOST32(m_pInode->i_blocks);
599void Ext2Node::updateInodeAttributes(
const File::Attributes& attributes, uint32_t mask) {
601 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
604 if (mask & (File::Owner | File::Group)) {
605 if (!changeInodeOwnership(attributes.uid, attributes.gid, mask & File::Owner,
608 mask &= ~(File::Owner | File::Group);
613 if (mask & File::AccessTime) {
614 m_pInode->i_atime = HOST_TO_LITTLE32(attributes.accessed);
616 if (mask & File::ModifyTime) {
617 m_pInode->i_mtime = HOST_TO_LITTLE32(attributes.modified);
620 HOST_TO_LITTLE32((mask & File::ChangeTime) ? attributes.changed : Time::getTime());
621 if (mask & File::Permissions) {
622 const uint16_t mode = LITTLE_TO_HOST16(m_pInode->i_mode);
624 HOST_TO_LITTLE16((mode & ~01777U) | permissionsToMode(attributes.permissions));
626 m_pExt2Fs->writeInode(getInodeNumber());
629bool Ext2Node::sync(
size_t offset,
bool async) {
631 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
634 const size_t nBlock = offset / m_pExt2Fs->
m_BlockSize;
635 if (offset >= m_nSize) {
638 if (nBlock >= m_Blocks.
count() || !ensureBlockLoaded(nBlock)) {
641 return m_pExt2Fs->syncBlock(m_Blocks[nBlock], async);
644bool Ext2Node::pinBlock(uint64_t location) {
645 uint32_t nBlock = location / m_pExt2Fs->
m_BlockSize;
646 if (nBlock >= m_Blocks.
count())
648 if (location >= m_nSize)
651 if (!ensureBlockLoaded(nBlock))
653 if (!m_Blocks[nBlock])
655 return m_pExt2Fs->pinBlock(m_Blocks[nBlock]);
658void Ext2Node::unpinBlock(uint64_t location) {
659 uint32_t nBlock = location / m_pExt2Fs->
m_BlockSize;
660 if (nBlock >= m_Blocks.
count())
662 if (location >= m_nSize)
665 if (!ensureBlockLoaded(nBlock))
667 if (!m_Blocks[nBlock])
669 m_pExt2Fs->unpinBlock(m_Blocks[nBlock]);
672uint32_t Ext2Node::modeToPermissions(uint32_t mode)
const {
673 uint32_t permissions = 0;
675 permissions |= FILE_STICKY;
676 if (mode & EXT2_S_IRUSR)
677 permissions |= FILE_UR;
678 if (mode & EXT2_S_IWUSR)
679 permissions |= FILE_UW;
680 if (mode & EXT2_S_IXUSR)
681 permissions |= FILE_UX;
682 if (mode & EXT2_S_IRGRP)
683 permissions |= FILE_GR;
684 if (mode & EXT2_S_IWGRP)
685 permissions |= FILE_GW;
686 if (mode & EXT2_S_IXGRP)
687 permissions |= FILE_GX;
688 if (mode & EXT2_S_IROTH)
689 permissions |= FILE_OR;
690 if (mode & EXT2_S_IWOTH)
691 permissions |= FILE_OW;
692 if (mode & EXT2_S_IXOTH)
693 permissions |= FILE_OX;
697uint32_t Ext2Node::permissionsToMode(uint32_t permissions)
const {
699 if (permissions & FILE_STICKY)
701 if (permissions & FILE_UR)
702 mode |= EXT2_S_IRUSR;
703 if (permissions & FILE_UW)
704 mode |= EXT2_S_IWUSR;
705 if (permissions & FILE_UX)
706 mode |= EXT2_S_IXUSR;
707 if (permissions & FILE_GR)
708 mode |= EXT2_S_IRGRP;
709 if (permissions & FILE_GW)
710 mode |= EXT2_S_IWGRP;
711 if (permissions & FILE_GX)
712 mode |= EXT2_S_IXGRP;
713 if (permissions & FILE_OR)
714 mode |= EXT2_S_IROTH;
715 if (permissions & FILE_OW)
716 mode |= EXT2_S_IWOTH;
717 if (permissions & FILE_OX)
718 mode |= EXT2_S_IXOTH;
virtual MUST_USE_RESULT bool zero(uint64_t location, size_t length)
void writeBlock(uint32_t block)
uintptr_t readBlock(uint32_t block)
bool wipe(bool allocationLockHeld=false)
Ext2Node(uintptr_t inode, Inode *metadata, Ext2Filesystem *filesystem, Ext2InodeState &admitted)
bool ensureLargeEnough(size_t size, uint64_t location, uint64_t opsize, bool onlyBlocks=false, bool nozeroblocks=false)
void fileAttributeChanged(size_t size, size_t atime, size_t mtime, size_t ctime)
A vector / dynamic array.
void reserve(size_t size, bool copy)
void pushBack(const T &value)