21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/machine/Disk.h"
23#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
24#include "pedigree/kernel/syscallError.h"
25#include "pedigree/kernel/utilities/utility.h"
27#include "Ext2Filesystem.h"
34 :
File(name, LITTLE_TO_HOST32(inode->i_atime), LITTLE_TO_HOST32(inode->i_mtime),
35 LITTLE_TO_HOST32(inode->i_ctime), inode_num, static_cast<
Filesystem*>(pFs),
36 LITTLE_TO_HOST32(inode->i_size),
40 if (!m_State->cache) {
42 if (!m_State->cache) {
46 m_State->cache->fill.
setCallback(sharedFillCallback, m_State);
51 if (!m_State->files.tryReserve(m_State->files.
count() + 1)) {
56 if (!m_State->futexIdentity) {
59 uint32_t mode = LITTLE_TO_HOST32(inode->i_mode);
70 for (
size_t i = 0; i < m_State->files.
count(); ++i) {
71 if (m_State->files[i] ==
this) {
72 m_State->files.
erase(i);
79 if (!m_State->files.
count() && m_State->cache && m_State->cache->fill.
empty()) {
80 delete m_State->cache;
81 m_State->cache =
nullptr;
86 return *m_State->cache;
99 return inodeAttributes();
102void Ext2File::updateAttributes(
const Attributes& attributes, uint32_t mask) {
103 updateInodeAttributes(attributes, mask);
108 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
113 if (m_State->quotaFile) {
114 SYSCALL_ERROR(NotEnoughPermissions);
118 SYSCALL_ERROR(ReadOnlyFilesystem);
121 if (offset >= m_nSize) {
122 SYSCALL_ERROR(InvalidArgument);
125 if (length > m_nSize - offset) {
126 length = m_nSize - offset;
128 const bool success = ensureWritableRange(offset, length);
131 cacheState().data.clear();
136bool Ext2File::prepareWrite(uint64_t location, uint64_t size) {
138 if (m_State->quotaFile && !m_State->quotaInternalWrite) {
139 SYSCALL_ERROR(NotEnoughPermissions);
143 SYSCALL_ERROR(ReadOnlyFilesystem);
146 const size_t oldSize = m_nSize;
147 const size_t end =
static_cast<size_t>(location + size);
153 const size_t within = oldSize % pageSize;
155 const size_t pageOffset = oldSize - within;
156 const uintptr_t page = cacheState().fill.
lookup(pageOffset);
158 const size_t amount = end - oldSize < pageSize - within ? end - oldSize : pageSize - within;
159 ByteSet(
reinterpret_cast<void*
>(page + within), 0, amount);
161 cacheState().fill.
release(pageOffset);
165 const bool success = ensureWritableRange(location, size);
168 cacheState().data.clear();
173size_t Ext2File::getSize() {
174 return __atomic_load_n(&m_nSize, __ATOMIC_ACQUIRE);
178 return Ext2Node::maximumFileSize();
182 return m_State->futexIdentity;
185Mutex& Ext2File::dataMutationLock() {
186 return m_State->dataLock;
189Mutex& Ext2File::writeSerializationLock() {
190 return m_State->writeLock;
193size_t& Ext2File::physicalPageLoans() {
194 return m_State->pageLoans;
205 m_State->writebackLock.
release();
211 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
217 if (m_State->quotaFile) {
218 SYSCALL_ERROR(NotEnoughPermissions);
227 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
231 if (m_State->quotaFile) {
232 SYSCALL_ERROR(NotEnoughPermissions);
235 Ext2Node::extend(newSize, 0, 0);
241 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
245 if (m_State->quotaFile) {
246 SYSCALL_ERROR(NotEnoughPermissions);
249 Ext2Node::extend(newSize, location, size);
255 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
261bool Ext2File::prepareShrink(
const ShrinkContext& context,
266 return prepareDataShrink(context.newSize, prepared);
269bool Ext2File::resizeFile(
size_t size) {
271 if (!resizeData(size)) {
274 for (
Ext2File* alias : m_State->files) {
275 alias->setSize(m_nSize);
283 LITTLE_TO_HOST32(m_pInode->i_mtime),
284 LITTLE_TO_HOST32(m_pInode->i_ctime));
289 return Ext2Node::readBlock(location);
296 if (!destination || !blockSize || pageSize % blockSize || location % pageSize ||
297 !m_State->allocationValid || location >= m_nSize) {
298 SYSCALL_ERROR(IoError);
301 ByteSet(
reinterpret_cast<void*
>(destination), 0, pageSize);
302 const size_t remaining = m_nSize - location;
303 const size_t length = remaining < pageSize ? remaining : pageSize;
304 for (
size_t offset = 0; offset < length; offset += blockSize) {
305 const size_t block = (location + offset) / blockSize;
306 if (block >= m_Blocks.
count() || !ensureBlockLoaded(block)) {
307 SYSCALL_ERROR(IoError);
311 return transferBlocksLocked(m_State, location, destination, length,
false);
315 if (count > MaxReadPages || (count && !pages)) {
316 SYSCALL_ERROR(IoError);
319 for (
size_t i = 0; i < count; ++i)
320 pages[i].complete =
false;
327 if (!disk || !blockSize || pageSize % blockSize || !m_State->allocationValid) {
328 SYSCALL_ERROR(IoError);
334 for (
size_t i = 0; i < count; ++i) {
335 if (!pages[i].buffer || pages[i].offset % pageSize || pages[i].offset >= m_nSize)
337 ByteSet(
reinterpret_cast<void*
>(pages[i].buffer), 0, pageSize);
338 const size_t remaining = m_nSize - pages[i].offset;
339 const size_t length = remaining < pageSize ? remaining : pageSize;
341 for (
size_t offset = 0; offset < length; offset += blockSize) {
342 const size_t block = (pages[i].offset + offset) / blockSize;
343 if (block >= m_Blocks.
count() || !ensureBlockLoaded(block)) {
348 pages[i].complete = mapped;
352 size_t owners[Disk::MaxReadBuffers] = {};
353 size_t requestCount = 0;
354 auto transfer = [&] {
357 const bool allRead = disk->
readIntoBatch(requests, requestCount);
359 for (
size_t i = 0; i < requestCount; ++i)
360 pages[owners[i]].complete = requests[i].complete && pages[owners[i]].complete;
363 for (
size_t i = 0; i < count; ++i) {
364 if (!pages[i].complete)
366 const size_t remaining = m_nSize - pages[i].offset;
367 const size_t length = remaining < pageSize ? remaining : pageSize;
368 for (
size_t offset = 0; offset < length;) {
369 const size_t block = (pages[i].offset + offset) / blockSize;
370 const uint32_t physical = m_Blocks[block];
371 size_t amount = length - offset < blockSize ? length - offset : blockSize;
376 while (amount % blockSize == 0 && amount < length - offset) {
377 const size_t next = block + amount / blockSize;
378 if (next >= m_Blocks.
count() || m_Blocks[next] == ~uint32_t{0} ||
379 static_cast<uint64_t
>(m_Blocks[next]) !=
380 static_cast<uint64_t
>(physical) + amount / blockSize)
382 const size_t available = length - offset - amount;
383 amount += available < blockSize ? available : blockSize;
385 requests[requestCount] = {
static_cast<uint64_t
>(physical) * blockSize,
386 reinterpret_cast<void*
>(pages[i].buffer + offset), amount,
false};
387 owners[requestCount++] = i;
388 if (requestCount == Disk::MaxReadBuffers)
394 bool succeeded =
true;
395 for (
size_t i = 0; i < count; ++i)
396 succeeded = pages[i].complete && succeeded;
398 SYSCALL_ERROR(IoError);
407 Ext2Node::writeBlock(location);
418 if (!length || location > ~uint64_t{0} - (length - 1))
421 const uint64_t last = location + length - 1;
422 uint64_t page = location - location % pageSize;
425 if (last - page < pageSize)
431bool Ext2File::sharedFillCallback(CacheConstants::CallbackCause cause, uintptr_t location,
432 uintptr_t page,
void* metadata) {
434 if (cause == CacheConstants::Eviction) {
439 if (cause != CacheConstants::WriteBack) {
443 if (!state->filesystem->tryAcquireOperation(operation)) {
447 if (state->orphan && !state->files.
count() &&
448 !__atomic_load_n(&state->syncReferences, __ATOMIC_ACQUIRE)) {
456 const bool durable = disk->
syncData();
457 return written && durable;
464 if (!state->filesystem->tryAcquireOperation(operation)) {
468 if (!count || (state->orphan && !state->files.
count() &&
469 !__atomic_load_n(&state->syncReferences, __ATOMIC_ACQUIRE)))
473 if (!disk || !state->allocationValid)
475 bool succeeded =
true;
483 for (
size_t i = 0; i < count; ++i) {
484 const auto& page = pages[i];
485 if (state->filesystem->
m_BlockSize != pageSize || page.key % pageSize ||
486 page.key >= state->size || state->size - page.key < pageSize) {
488 succeeded = transferBlocksLocked(state, page.key, page.location, pageSize,
true) && succeeded;
491 const size_t block = page.key / pageSize;
492 if (block >= state->blocks.
count() || state->blocks[block] == ~uint32_t{0}) {
496 if (!state->blocks[block])
498 buffers[pending++] = {
static_cast<uint64_t
>(state->blocks[block]) * pageSize,
499 reinterpret_cast<const void*
>(page.location), pageSize,
false};
500 if (pending == Disk::MaxWriteBuffers)
504 const bool durable = disk->
syncData();
505 return succeeded && durable;
508bool Ext2File::transferBlocksLocked(
Ext2InodeState* state, uint64_t location, uintptr_t addr,
509 size_t length,
bool write) {
513 if (!disk || !blockSize || !state->allocationValid || location % blockSize ||
514 location > ~uint64_t{0} - length)
516 if (location >= state->size)
518 const size_t remaining = state->size - location;
519 if (length > remaining)
522 for (
size_t offset = 0; offset < length;) {
523 const uint64_t blockLocation = location + offset;
524 const size_t block = blockLocation / blockSize;
525 if (block >= state->blocks.
count() || state->blocks[block] == ~uint32_t(0))
527 const uint32_t physicalBlock = state->blocks[block];
528 size_t amount = length - offset < blockSize ? length - offset : blockSize;
529 if (!physicalBlock) {
534 while (amount % blockSize == 0 && amount < length - offset) {
535 const size_t next = block + amount / blockSize;
536 if (next >= state->blocks.
count() || state->blocks[next] == ~uint32_t(0) ||
537 static_cast<uint64_t
>(state->blocks[next]) !=
538 static_cast<uint64_t
>(physicalBlock) + amount / blockSize)
540 const size_t available = length - offset - amount;
541 amount += available < blockSize ? available : blockSize;
544 const uint64_t diskOffset =
static_cast<uint64_t
>(physicalBlock) * blockSize;
545 const bool transferred =
546 write ? disk->writeFrom(diskOffset,
reinterpret_cast<const void*
>(addr + offset), amount)
547 : disk->readInto(diskOffset, reinterpret_cast<void*>(addr + offset), amount);
557 return Ext2Node::pinBlock(location);
562 Ext2Node::unpinBlock(location);
567 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
573 succeeded = cacheState().fill.
syncAll(sharedFillBatchCallback, m_State);
578 return m_pExt2Fs->syncInode(getInodeNumber(), *
this) && succeeded;
583 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
586 bool present =
false;
588 const bool succeeded =
syncFillCache(offset, async, present);
589 if (present || m_pExt2Fs->isDeviceRemoved()) {
594 return Ext2Node::sync(offset, async);
599 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
602 if (count > Disk::MaxSyncPages || (count && !offsets))
608 uintptr_t keys[Disk::MaxSyncPages];
609 for (
size_t i = 0; i < count; ++i) {
610 if (offsets[i] >= getSize() || offsets[i] % pageSize || offsets[i] > ~uintptr_t{0})
612 keys[i] = offsets[i];
616 return cacheState().fill.
syncBatch(keys, count, sharedFillBatchCallback, m_State);
622 if (!disk || !blockSize)
624 for (
size_t i = 0; i < count; ++i) {
625 if (offsets[i] >= m_nSize || (offsets[i] % blockSize) ||
626 offsets[i] / blockSize >= m_Blocks.
count())
629 uint64_t locations[Disk::MaxSyncPages];
630 size_t locationCount = 0;
631 for (
size_t i = 0; i < count; ++i) {
632 const size_t block = offsets[i] / blockSize;
633 if (!ensureBlockLoaded(block))
635 if (!m_Blocks[block])
637 const uint64_t location =
static_cast<uint64_t
>(m_Blocks[block]) * blockSize;
638 if (location >= disk->
getSize())
640 locations[locationCount++] = location;
642 return disk->
syncPages(locations, locationCount);
void setBackgroundWriteback(writeback_batch_t callback)
MUST_USE_RESULT bool syncAll()
void setCallback(writeback_t newCallback, void *meta)
void release(uintptr_t key)
MUST_USE_RESULT bool syncBatch(const uintptr_t *keys, size_t count, writeback_batch_t callback, void *metadata)
void markDirty(uintptr_t key)
uintptr_t lookup(uintptr_t key)
void setDirtyTracking(DirtyTracking tracking)
virtual size_t getSize() const
Gets the size of the disk.
virtual MUST_USE_RESULT bool readIntoBatch(ReadBuffer *buffers, size_t count)
virtual MUST_USE_RESULT bool writeFromBatch(WriteBuffer *buffers, size_t count)
virtual MUST_USE_RESULT bool syncPages(const uint64_t *locations, size_t count)
virtual MUST_USE_RESULT bool syncData()
virtual size_t getBlockSize() const override
virtual void writeBlock(uint64_t location, uintptr_t addr) override
bool syncPages(const uint64_t *offsets, size_t count) override
virtual uintptr_t readBlock(uint64_t location) override
virtual void extend(size_t newSize) override
bool readPage(uint64_t location, uintptr_t destination) override
virtual uintptr_t futexIdentity() override
virtual bool useFillCache() const override
virtual void writeBlocks(uint64_t location, uintptr_t addr, size_t length) override
virtual void unpinBlock(uint64_t location) override
virtual uint64_t maximumFileSize() const override
virtual bool pinBlock(uint64_t location) override
Ext2File(const Ext2File &file)
virtual void preallocate(size_t expectedSize, bool zero=true) override
bool readPages(ReadPage *pages, size_t count) override
virtual bool sync() override
virtual bool tryBeginMappingRelease() override
void fileAttributeChanged() override
virtual void truncate() override
virtual ~Ext2File() override
virtual bool prepareSharedMapping(size_t offset, size_t length) override
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)
virtual uint64_t write(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
virtual void writeBlocks(uint64_t location, uintptr_t addr, size_t length)
void setGidOnly(size_t gid)
virtual uintptr_t futexIdentity()
void setPermissionsOnly(uint32_t perms)
virtual bool useFillCache() const
void setUidOnly(size_t uid)
bool syncFillCache(size_t offset, bool async, bool &present)
static constexpr size_t getPageSize() PURE
bool tryAcquire(size_t n=1)
void pushBack(const T &value)