21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/Metrics.h"
24#include "pedigree/kernel/machine/Disk.h"
25#include "pedigree/kernel/process/Scheduler.h"
26#include "pedigree/kernel/process/Thread.h"
27#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
28#include "pedigree/kernel/processor/Processor.h"
29#include "pedigree/kernel/processor/ProcessorInformation.h"
30#include "pedigree/kernel/processor/VirtualAddressSpace.h"
31#include "pedigree/kernel/processor/types.h"
32#include "pedigree/kernel/syscallError.h"
33#include "pedigree/kernel/utilities/Iterator.h"
34#include "pedigree/kernel/utilities/Pair.h"
35#include "pedigree/kernel/utilities/Result.h"
36#include "pedigree/kernel/utilities/Vector.h"
37#include "pedigree/kernel/utilities/assert.h"
38#include "pedigree/kernel/utilities/utility.h"
40#include "Filesystem.h"
44RetainedFile::RetainedFile() =
default;
46RetainedFile::RetainedFile(
RetainedFile&& other) noexcept : m_File(other.m_File) {
47 other.m_File =
nullptr;
50RetainedFile::~RetainedFile() {
57 m_File = other.m_File;
58 other.m_File =
nullptr;
63void RetainedFile::adopt(
File* file) {
68void RetainedFile::reset() {
76 uintptr_t
identity = __atomic_load_n(&m_FutexIdentity, __ATOMIC_ACQUIRE);
78 static uintptr_t nextIdentity = 0;
79 const uintptr_t allocated = __atomic_add_fetch(&nextIdentity, uintptr_t(1), __ATOMIC_RELAXED);
81 assert(allocated && allocated <= (~uintptr_t(0) >> 1));
82 __atomic_compare_exchange_n(&m_FutexIdentity, &
identity, allocated,
false, __ATOMIC_ACQ_REL,
91File::ParentLease::ParentLease()
94#if THREADS && !defined(STANDALONE_MUTEXES)
96 m_TerminationDeferral(true)
101File::ParentLease::~ParentLease() {
107void File::ParentLease::swap(ParentLease& other) {
108 File* parent = m_Parent;
109 bool retained = m_Retained;
110 m_Parent = other.m_Parent;
111 m_Retained = other.m_Retained;
112 other.m_Parent = parent;
113 other.m_Retained = retained;
118 File* pFile =
reinterpret_cast<File*
>(meta);
121 case CacheConstants::WriteBack: {
123 if (!pFile->tryAcquireFilesystemOperation(operation)) {
128 case CacheConstants::Eviction:
135 "File: unknown cache callback -- could indicate potential "
136 "future I/O issues.");
142bool File::fillCacheCallback(CacheConstants::CallbackCause cause, uintptr_t loc, uintptr_t page,
144 File* pFile =
reinterpret_cast<File*
>(meta);
145 if (cause == CacheConstants::WriteBack) {
147 if (!pFile->tryAcquireFilesystemOperation(operation)) {
151 }
else if (cause == CacheConstants::Eviction) {
153 }
else if (cause != CacheConstants::Eviction) {
154 WARNING(
"File: unknown fill-cache callback cause.");
159File::CacheState::CacheState() : data(FILE_BAD_BLOCK), indexLock(), fill(), fillLock() {}
175 m_pDetachedParent(0),
176 m_bDetachedParentHandled(false),
186 m_MonitorTargets() {}
189 Time::Timestamp creationTime, uintptr_t inode,
Filesystem* pFs,
size_t size,
192 m_AccessedTime(accessedTime),
193 m_ModifiedTime(modifiedTime),
194 m_CreationTime(creationTime),
200 m_pDetachedParent(0),
201 m_bDetachedParentHandled(false),
227 for (
auto target : m_MonitorTargets) {
230 m_MonitorTargets.clear();
239uint64_t
File::read(uint64_t location, uint64_t size, uintptr_t buffer,
bool bCanBlock) {
241 if (!tryAcquireFilesystemOperation(operation)) {
244 Metrics::increment(Metrics::FileReadCalls);
249 Metrics::add(Metrics::FileReadBytes,
read);
255 const size_t fileSize = getSize();
256 if (!size || location >= fileSize) {
260 const uint64_t remaining = fileSize - location;
261 if (size > remaining) {
270 if (filled && !m_bDirect) {
271 const size_t cached = cacheState().fill.
read(location, size, buffer);
282 if (location >= fileSize) {
283 Metrics::add(Metrics::FileReadBytes, n);
287 uintptr_t block = location / blockSize;
288 uintptr_t offs = location % blockSize;
289 uintptr_t sz = (size + offs > blockSize) ? blockSize - offs : size;
292 if (sz > (fileSize - location))
293 sz = fileSize - location;
295 const size_t readAheadLimit = MaxReadPages * blockSize;
296 const size_t readAheadBytes =
297 size > blockSize ? (size < readAheadLimit - offs ? size + offs : readAheadLimit) : 0;
298 uintptr_t buff =
readIntoCache(block,
false, readAheadBytes);
299 if (buff == FILE_BAD_BLOCK) {
300 ERROR(
"File::read - failed to get page from cache, returning early");
301 Metrics::add(Metrics::FileReadBytes, n);
306 ForwardMemoryCopy(
reinterpret_cast<void*
>(buffer),
reinterpret_cast<void*
>(buff + offs), sz);
315 Metrics::add(Metrics::FileReadBytes, n);
320 bool (*prepare)(uintptr_t,
size_t)) {
322 if (!tryAcquireFilesystemOperation(operation)) {
325 Metrics::increment(Metrics::FileCachedReadCalls);
330 const size_t fileSize = getSize();
331 if (location >= fileSize) {
334 if (size > fileSize - location) {
335 size = fileSize - location;
337 const size_t read = cacheState().fill.
read(location, size, buffer, prepare);
338 Metrics::add(Metrics::FileReadBytes,
read);
342uint64_t
File::write(uint64_t location, uint64_t size, uintptr_t buffer,
bool bCanBlock) {
344 return guard.write(location, size, buffer, bCanBlock);
348 if (!size || location > (~
static_cast<uint64_t
>(0) - size)) {
352 const uint64_t endLocation = location + size;
353 if (endLocation >
static_cast<uint64_t
>(~
static_cast<size_t>(0))) {
363 if (cacheState().executableMappings) {
364 SYSCALL_ERROR(TextFileBusy);
369 const size_t blockSize =
372 if (!prepareWrite(location, size)) {
378 uintptr_t block = location / blockSize;
379 uintptr_t offs = location % blockSize;
380 uintptr_t sz = (size + offs > blockSize) ? blockSize - offs : size;
383 if (buff == FILE_BAD_BLOCK) {
384 ERROR(
"File::write - failed to get page from cache, returning early");
388 ForwardMemoryCopy(
reinterpret_cast<void*
>(buff + offs),
reinterpret_cast<void*
>(buffer), sz);
391 const uint64_t pageOffset = block * blockSize;
392 const size_t firstBlock = offs / filesystemBlockSize;
393 const size_t endBlock = (offs + sz + filesystemBlockSize - 1) / filesystemBlockSize;
394 const size_t fileBlockOffset = firstBlock * filesystemBlockSize;
395 writeBlocks(pageOffset + fileBlockOffset, buff + fileBlockOffset,
396 (endBlock - firstBlock) * filesystemBlockSize);
408 if (location >= getSize()) {
410 fileAttributeChanged();
415uint64_t
File::append(uint64_t size, uintptr_t buffer, uint64_t& location,
bool bCanBlock) {
417 return guard.append(size, buffer, location, bCanBlock);
424File::WriteGuard::WriteGuard(
File& file) : m_File(file), m_Guard(file.writeSerializationLock()) {}
426File::WriteGuard::~WriteGuard() {
427 if (m_MetadataPending) {
428 m_File.publishWriteMetadata();
432void File::publishWriteMetadata() {
434 if (!tryAcquireFilesystemOperation(operation)) {
438 Attributes attributes;
439 attributes.modified = attributes.changed = Time::getTime();
440 updateAttributes(attributes, ModifyTime | ChangeTime);
445uint64_t File::WriteGuard::write(uint64_t location, uint64_t size, uintptr_t buffer,
bool bCanBlock,
446 bool publishMetadata) {
448 if (!m_File.tryAcquireFilesystemOperation(operation)) {
451 Metrics::increment(Metrics::FileWriteCalls);
453 const uint64_t written = m_File.writeUnlocked(location, size, buffer, bCanBlock);
454 Metrics::add(Metrics::FileWriteBytes, written);
456 if (publishMetadata) {
457 m_File.publishWriteMetadata();
459 m_MetadataPending =
true;
465uint64_t File::WriteGuard::append(uint64_t size, uintptr_t buffer, uint64_t& location,
466 bool bCanBlock,
bool publishMetadata) {
468 if (!m_File.tryAcquireFilesystemOperation(operation)) {
471 Metrics::increment(Metrics::FileWriteCalls);
473 location = m_File.getSize();
474 const uint64_t written = m_File.writeUnlocked(location, size, buffer, bCanBlock);
475 Metrics::add(Metrics::FileWriteBytes, written);
477 if (publishMetadata) {
478 m_File.publishWriteMetadata();
480 m_MetadataPending =
true;
488 if (!tryAcquireFilesystemOperation(operation)) {
492 if (!allowPhysicalPage())
495 WARNING(
"File in direct mode, cannot get backing page.");
507 blockSize = nativeBlockSize;
509 offset &= ~(blockSize - 1);
512 if (offset >= getSize()) {
519 uintptr_t vaddr = FILE_BAD_BLOCK;
531 vaddr = cacheState().fill.
lookup(offset);
540 if (va.
isMapped(
reinterpret_cast<void*
>(vaddr))) {
541 physical_uintptr_t phys = 0;
543 va.
getMapping(
reinterpret_cast<void*
>(vaddr), phys, flags);
544 __atomic_add_fetch(&physicalPageLoans(), 1, __ATOMIC_RELEASE);
550 cacheState().fill.
release(offset);
566 blockSize = nativeBlockSize;
568 offset &= ~(blockSize - 1);
573 cacheState().fill.
release(offset);
577 assert(__atomic_load_n(&physicalPageLoans(), __ATOMIC_ACQUIRE));
578 __atomic_sub_fetch(&physicalPageLoans(), 1, __ATOMIC_RELEASE);
582 const bool succeeded =
sync(offset, async);
591void File::endMappingRelease() {
601 if (!tryAcquireFilesystemOperation(operation)) {
604 if (length && length - 1 > ~
size_t(0) - offset) {
605 SYSCALL_ERROR(InvalidArgument);
611 const size_t firstBlock = offset / blockSize;
612 const size_t lastBlock = length ? (offset + length - 1) / blockSize : ~size_t(0);
618 size_t snapshotSize = 0;
621 snapshotSize = cacheState().data.count();
629 for (
auto it = cacheState().data.begin();
630 it != cacheState().data.end() && pages.
count() < snapshotSize; ++it) {
631 const uintptr_t buffer = it.__getNode()->value;
632 const size_t block = it.__getNode()->key.hash();
633 if (buffer != FILE_BAD_BLOCK && block >= firstBlock && block <= lastBlock) {
639 bool succeeded =
true;
640 uint64_t batch[Disk::MaxSyncPages];
641 size_t batchCount = 0;
642 auto flushBatch = [&] {
645 succeeded =
syncPages(batch, batchCount) && succeeded;
646 for (
size_t i = 0; i < batchCount; ++i) {
648 cacheState().fill.
release(batch[i]);
654 for (
const SyncPage& page : pages) {
655 const uint64_t location = page.block * blockSize;
659 const uintptr_t buffer = cacheState().fill.
lookup(location);
662 if (buffer != page.buffer) {
663 cacheState().fill.
release(location);
666 batch[batchCount++] = location;
667 if (batchCount == Disk::MaxSyncPages)
677 batch[batchCount++] = location;
678 if (batchCount == Disk::MaxSyncPages)
690 if (!tryAcquireFilesystemOperation(operation)) {
693 if (count > Disk::MaxSyncPages || (count && !offsets))
695 for (
size_t i = 0; i < count; ++i) {
696 if (offsets[i] >= getSize())
699 bool succeeded =
true;
700 for (
size_t i = 0; i < count; ++i)
701 succeeded =
sync(offsets[i],
false) && succeeded;
707 if (!tryAcquireFilesystemOperation(operation)) {
714 if (m_pFilesystem && !m_pFilesystem->tryAcquireOperation(operation)) {
715 SYSCALL_ERROR(DeviceDoesNotExist);
722 return getAttributes().changed;
727 if (!tryAcquireFilesystemOperation(operation)) {
731 attributes.changed = t;
732 updateAttributes(attributes, ChangeTime);
737 return getAttributes().accessed;
742 if (!tryAcquireFilesystemOperation(operation)) {
746 attributes.accessed = t;
747 updateAttributes(attributes, AccessTime);
751void File::setTimes(Time::Timestamp accessed, Time::Timestamp modified,
bool changeAccessed,
752 bool changeModified) {
754 if (!tryAcquireFilesystemOperation(operation)) {
758 Attributes attributes;
759 attributes.accessed = accessed;
760 attributes.modified = modified;
761 attributes.changed = Time::getTime();
762 updateAttributes(attributes, ChangeTime | (changeAccessed ? AccessTime : 0U) |
763 (changeModified ? ModifyTime : 0U));
768 return getAttributes().modified;
773 if (!tryAcquireFilesystemOperation(operation)) {
777 attributes.modified = t;
778 updateAttributes(attributes, ModifyTime);
792size_t File::getSize() {
796void File::setSize(
size_t sz) {
830 return ~uint64_t(0) >> 1;
833uintptr_t File::getInode()
const {
837void File::setInode(uintptr_t inode) {
842 return m_pFilesystem;
849void File::fileAttributeChanged() {}
853 subscription.reset();
854 return FileHandleStatus::Unsupported;
857void File::publishInodeEvent(
const FileEvent&) {}
859void File::finishInodeRetirement() {}
866 uint32_t producer = 0;
867#if THREADS && !defined(STANDALONE_MUTEXES)
872 const FileEvent event(mask, name, targetIsDirectory, producer);
873 constexpr FileEventMask InodeEvents = FileEvents::Modify | FileEvents::Attributes |
874 FileEvents::Open | FileEvents::CloseWrite |
875 FileEvents::CloseNoWrite;
876 if (!name.length() && (mask & InodeEvents))
878 if (mask & FileEvents::DeletedSelf) {
884 constexpr FileEventMask ChildEvents = FileEvents::Access | FileEvents::Modify |
885 FileEvents::Attributes | FileEvents::CloseWrite |
886 FileEvents::CloseNoWrite | FileEvents::Open;
887 if (!name.length() && (mask & ChildEvents)) {
890 if (snapshotNamespace(parent, childName, mask)) {
896void File::increaseRefCount(
bool bIsWriter) {
903void File::decreaseRefCount(
bool bIsWriter) {
919 return m_pFilesystem && m_pFilesystem->
getRoot() ==
this;
922void File::setPermissions(uint32_t perms) {
924 if (!tryAcquireFilesystemOperation(operation)) {
927 Attributes attributes;
928 attributes.permissions = perms;
929 updateAttributes(attributes, Permissions);
933uint32_t File::getPermissions()
const {
934 return getAttributes().permissions;
937bool File::setOwnership(
size_t uid,
size_t gid,
bool changeUid,
bool changeGid) {
939 if (!tryAcquireFilesystemOperation(operation)) {
942 if (!changeUid && !changeGid)
944 if (!changeOwnership(uid, gid, changeUid, changeGid))
950bool File::changeOwnership(
size_t uid,
size_t gid,
bool changeUid,
bool changeGid) {
951 Attributes attributes = getAttributes();
952 attributes.uid = uid;
953 attributes.gid = gid;
954 updateAttributes(attributes, (changeUid ? Owner : 0U) | (changeGid ?
Group : 0U));
958void File::setUid(
size_t uid) {
960 if (!tryAcquireFilesystemOperation(operation)) {
963 Attributes attributes;
964 attributes.uid = uid;
965 updateAttributes(attributes, Owner);
969size_t File::getUid()
const {
970 return getAttributes().uid;
973void File::setGid(
size_t gid) {
975 if (!tryAcquireFilesystemOperation(operation)) {
978 Attributes attributes;
979 attributes.gid = gid;
980 updateAttributes(attributes,
Group);
984size_t File::getGid()
const {
985 return getAttributes().gid;
988File* File::getParent()
const {
989 return __atomic_load_n(&m_pParent, __ATOMIC_ACQUIRE);
993 snapshotNamespace(parent, name, FileEvents::None);
996bool File::snapshotNamespace(ParentLease& parent,
String& name, FileEventMask interest)
const {
997 ParentLease replacement;
998 bool captured =
false;
1001 File* current = getParent();
1004 if (replacement.m_Retained || (m_pFilesystem && current == m_pFilesystem->
getRoot())) {
1005 replacement.m_Parent = current;
1010 (replacement.m_Parent && replacement.m_Parent->hasFileEventObservers(interest))) {
1015 parent.swap(replacement);
1019void File::moveNamespace(
const String& name,
File* parent) {
1022 __atomic_store_n(&m_pParent, parent, __ATOMIC_RELEASE);
1027 if (__atomic_exchange_n(&m_bDetachedParentHandled,
true, __ATOMIC_ACQ_REL)) {
1031 File* parent = getParent();
1036 File* root = m_pFilesystem ? m_pFilesystem->
getRoot() :
nullptr;
1037 if (parent == root) {
1047 __atomic_store_n(&m_pParent,
nullptr, __ATOMIC_RELEASE);
1055 ReadyMask ready = ReadyNone;
1056 if (reading &&
select(
false, 0)) {
1059 if (writing &&
select(
true, 0)) {
1060 ready |= ReadyWrite;
1092 Attributes attributes;
1095 attributes.accessed = m_AccessedTime;
1096 attributes.modified = m_ModifiedTime;
1097 attributes.changed = m_CreationTime;
1098 attributes.uid = m_Uid;
1099 attributes.gid = m_Gid;
1100 attributes.permissions = m_Permissions;
1102 attributes.size =
const_cast<File*
>(
this)->getSize();
1103 attributes.blocks = attributes.size / 512 + (attributes.size % 512 != 0);
1109 return XattrStatus::Unsupported;
1112XattrStatus File::listExtendedAttributes(
void*,
size_t,
size_t& required) {
1114 return XattrStatus::Unsupported;
1117XattrStatus File::setExtendedAttribute(
const StringView&,
const void*,
size_t,
unsigned) {
1118 return XattrStatus::Unsupported;
1121XattrStatus File::removeExtendedAttribute(
const StringView&) {
1122 return XattrStatus::Unsupported;
1125void File::updateAttributes(
const Attributes& attributes, uint32_t mask) {
1128 if (mask & AccessTime)
1129 m_AccessedTime = attributes.accessed;
1130 if (mask & ModifyTime)
1131 m_ModifiedTime = attributes.modified;
1132 m_CreationTime = (mask & ChangeTime) ? attributes.changed : Time::getTime();
1134 m_Uid = attributes.uid;
1136 m_Gid = attributes.gid;
1137 if (mask & Permissions)
1138 m_Permissions = attributes.permissions;
1140 fileAttributeChanged();
1147bool File::prepareWrite(uint64_t location, uint64_t size) {
1148 const uint64_t end = location + size;
1149 extend(
static_cast<size_t>(end), location, size);
1150 return getSize() >= end;
1155Mutex& File::writeSerializationLock() {
1159Mutex& File::dataMutationLock() {
1160 return m_DataMutationLock;
1163size_t& File::physicalPageLoans() {
1164 return m_PhysicalPageLoans;
1168 if (!cacheState().fill.empty()) {
1169 SYSCALL_ERROR(IoError);
1173 cacheState().data.clear();
1184 if ((executable && (sharedWrite || state.sharedWriteMappings)) ||
1185 (sharedWrite && state.executableMappings)) {
1186 SYSCALL_ERROR(TextFileBusy);
1190 ++state.executableMappings;
1192 ++state.sharedWriteMappings;
1196void File::releaseMappingUse(
bool executable,
bool sharedWrite) {
1198 CacheState& state = cacheState();
1200 assert(state.executableMappings);
1201 --state.executableMappings;
1204 assert(state.sharedWriteMappings);
1205 --state.sharedWriteMappings;
1209bool File::allowResize(
size_t,
size_t) {
1215 if (!tryAcquireFilesystemOperation(operation)) {
1219 syscallError(
isDirectory() ? Error::IsADirectory : Error::InvalidArgument);
1228 if (m_pFilesystem && m_pFilesystem->
isReadOnly()) {
1229 SYSCALL_ERROR(ReadOnlyFilesystem);
1232 if (cacheState().executableMappings) {
1233 SYSCALL_ERROR(TextFileBusy);
1236 const size_t oldSize = getSize();
1238 if (!allowResize(oldSize, size)) {
1241 if (size == oldSize) {
1242 return resizeFile(size);
1244 if (size < oldSize) {
1246 size_t mappingLoans = 0;
1249 const auto mappingStatus = mappings.
prepareFileResize(
this, oldSize, size, mappingPlan);
1250 if (mappingStatus != MemoryMapManager::ResizeStatus::Ready) {
1251 syscallError(mappingStatus == MemoryMapManager::ResizeStatus::NoMemory ? Error::OutOfMemory
1252 : mappingStatus == MemoryMapManager::ResizeStatus::Invalid
1253 ? Error::InvalidArgument
1254 : Error::OperationNotSupported);
1257 context.mappingLoans = mappingPlan.get()->loans();
1258 context.mappingLoanCount = mappingPlan.get()->loanCount();
1259 mappingLoans = mappingPlan.get()->totalLoans();
1263 if (__atomic_load_n(&physicalPageLoans(), __ATOMIC_ACQUIRE) != mappingLoans) {
1264 SYSCALL_ERROR(DeviceBusy);
1268 const size_t boundaryOffset = size - size % pageSize;
1269 const size_t cutoff = boundaryOffset + (size % pageSize ? pageSize : 0);
1270 if (cutoff < size) {
1271 SYSCALL_ERROR(InvalidArgument);
1275 struct BoundaryPage {
1281 cache.release(offset);
1283 } boundary{cacheState().fill, boundaryOffset, 0};
1285 const auto status = cacheState().fill.
prepareDiscardFrom(cutoff, context.mappingLoans,
1286 context.mappingLoanCount, fillPlan);
1287 if (status != Cache::DiscardStatus::Ready) {
1288 syscallError(status == Cache::DiscardStatus::NoMemory ? Error::OutOfMemory
1289 : status == Cache::DiscardStatus::Busy ? Error::DeviceBusy
1290 : status == Cache::DiscardStatus::Invalid ? Error::InvalidArgument
1294 if (size % pageSize)
1295 boundary.address = cacheState().fill.
lookup(boundaryOffset);
1298 if (!prepareShrink(context, backend))
1301 mappingPlan.get()->commit();
1303 backend.get()->commit();
1305 fillPlan.get()->commit();
1306 if (boundary.address) {
1307 ByteSet(
reinterpret_cast<void*
>(boundary.address + size % pageSize), 0,
1308 pageSize - size % pageSize);
1309 cacheState().fill.
markDirty(boundaryOffset);
1314 const size_t firstDiscard = cutoff / cacheBlockSize;
1316 for (
auto it = cacheState().data.begin(); it != cacheState().data.end();) {
1317 if (it.__getNode()->key.hash() >= firstDiscard)
1318 it = cacheState().data.erase(it);
1322 }
else if (!resizeFile(size)) {
1327 const size_t offset = oldSize % pageSize;
1329 const size_t pageOffset = oldSize - offset;
1330 const uintptr_t buffer = cacheState().fill.
lookup(pageOffset);
1332 const size_t amount =
1333 (size - oldSize < pageSize - offset) ? size - oldSize : pageSize - offset;
1334 ByteSet(
reinterpret_cast<void*
>(buffer + offset), 0, amount);
1335 cacheState().fill.
markDirty(pageOffset);
1336 cacheState().fill.
release(pageOffset);
1341 attributes.modified = attributes.changed = Time::getTime();
1342 updateAttributes(attributes, ModifyTime | ChangeTime);
1348 SYSCALL_ERROR(OperationNotSupported);
1352bool File::resizeFile(
size_t) {
1353 SYSCALL_ERROR(OperationNotSupported);
1371 FATAL(
"A bytewise File subclass didn't implement readBytewise");
1378 FATAL(
"A bytewise File subclass didn't implement writeBytewise");
1386 ERROR(
"File: base class readBlock() called for " << fullPath);
1401 for (
size_t offset = 0; offset < length; offset += blockSize) {
1402 writeBlock(location + offset, addr + offset);
1407 if (m_Size < newSize)
1423 if (!address || address == FILE_BAD_BLOCK || !
pinBlock(location))
1430 }
while (retryChanged);
1441 m_Permissions = perms;
1459 it != m_MonitorTargets.end(); it++) {
1468 m_MonitorTargets.clear();
1490 m_MonitorTargets.pushBack(
1491 new MonitorTarget(pThread, pEvent, pedigree_std::move(registration)));
1501 it != m_MonitorTargets.end();) {
1504 if (pMT->pThread == pThread) {
1506 it = m_MonitorTargets.erase(it);
1519 it != m_MonitorTargets.end();) {
1521 if (pMT->pEvent == pEvent) {
1523 it = m_MonitorTargets.erase(it);
1547 if (!parent.get()) {
1556 current.swap(parent);
1564 if (bWithMount && m_pFilesystem) {
1566 if (
VFS::instance().getMountPath(m_pFilesystem, mountPath) && mountPath !=
"/") {
1573 }
else if (bWithMount && !m_pFilesystem) {
1574 ERROR(
"File::getFullPath called without a filesystem!");
1577 result.assign(str, str.length());
1590 auto result = cacheState().data.lookup(key);
1591 if (result.hasValue()) {
1592 return result.value();
1594 return FILE_BAD_BLOCK;
1604 if (value == FILE_BAD_BLOCK) {
1605 cacheState().data.remove(key);
1606 }
else if (!cacheState().data.update(key, value)) {
1607 cacheState().data.insert(key, value);
1620 if (!tryAcquireFilesystemOperation(operation)) {
1627 const size_t limit = MaxReadPages * pageSize - offset % pageSize;
1628 return read(offset, length < limit ? length : limit, 0);
1631 return populateRangeLocked(offset, length);
1634size_t File::populateRangeLocked(
size_t offset,
size_t length) {
1635 const size_t fileSize = getSize();
1637 if (!length || offset >= fileSize || !pageSize)
1639 const size_t within = offset % pageSize;
1640 const size_t first = offset - within;
1641 const size_t limit = MaxReadPages * pageSize - within;
1642 if (length > fileSize - offset)
1643 length = fileSize - offset;
1647 Cache& cache = cacheState().fill;
1648 const uintptr_t demand = cache.lookup(first);
1651 cache.release(first);
1652 return length < pageSize - within ? length : pageSize - within;
1655 const size_t count = (within + length - 1) / pageSize + 1;
1656 ReadPage pages[MaxReadPages] = {};
1657 ReadPage pending[MaxReadPages] = {};
1658 size_t pendingSlots[MaxReadPages] = {};
1659 bool inserted[MaxReadPages] = {};
1660 size_t pendingCount = 0;
1661 for (
size_t i = 0; i < count; ++i) {
1662 const size_t location = first + i * pageSize;
1663 pages[i].offset = location;
1664 pages[i].buffer = cache.lookup(location);
1665 if (pages[i].buffer) {
1666 pages[i].complete =
true;
1669 bool existed =
false;
1670 const uintptr_t fresh = cache.insert(location, &existed);
1675 pages[i].buffer = cache.lookup(location);
1676 if (!pages[i].buffer)
1678 inserted[i] = !existed;
1680 pages[i].complete =
true;
1683 pendingSlots[pendingCount] = i;
1684 pending[pendingCount++] = pages[i];
1687 const bool allRead =
readPages(pending, pendingCount);
1689 for (
size_t i = 0; i < pendingCount; ++i)
1690 pages[pendingSlots[i]].complete = pending[i].complete;
1693 size_t readyPages = 0;
1694 while (readyPages < count && pages[readyPages].complete)
1696 for (
size_t i = 0; i < count; ++i) {
1697 if (!pages[i].buffer)
1699 if (pages[i].complete) {
1701 cache.markNoLongerEditing(pages[i].offset);
1704 cache.release(pages[i].offset);
1705 if (inserted[i] && !pages[i].complete) {
1706 const bool discarded = cache.discardEditing(pages[i].offset);
1712 const size_t available = readyPages * pageSize - within;
1713 return available < length ? available : length;
1717 if (count > MaxReadPages || (count && !pages))
1719 bool succeeded =
true;
1720 for (
size_t i = 0; i < count; ++i) {
1721 pages[i].complete =
readPage(pages[i].offset, pages[i].buffer);
1722 succeeded = pages[i].complete && succeeded;
1730 if (!blockSize || pageSize % blockSize)
1732 ByteSet(
reinterpret_cast<void*
>(destination), 0, pageSize);
1733 const size_t size = getSize();
1734 for (
size_t i = 0; i < pageSize && location < size && i < size - location; i += blockSize) {
1735 const uintptr_t block =
readBlock(location + i);
1736 if (!block || block == FILE_BAD_BLOCK)
1738 const size_t remaining = size - location - i;
1739 ForwardMemoryCopy(
reinterpret_cast<void*
>(destination + i),
reinterpret_cast<void*
>(block),
1740 remaining < blockSize ? remaining : blockSize);
1748#if defined(PEDIGREE_BUILDUTILS)
1749 if (m_bForceFillCache) {
1759 return blockSize < nativeBlockSize;
1764 cacheState().fill.
setCallback(fillCacheCallback,
this);
1767void File::shutdownFillCacheWriteback() {
1773 if (!tryAcquireFilesystemOperation(operation)) {
1778 const size_t pageOffset = offset - (offset % pageSize);
1780 present = cacheState().fill.
lookup(pageOffset) != 0;
1785 const bool succeeded = cacheState().fill.
sync(pageOffset, async);
1786 cacheState().fill.
release(pageOffset);
1795 const size_t offset = block * (fillCache ? nativeBlockSize : blockSize);
1802 const uintptr_t cached = cacheState().fill.
lookup(offset);
1807 if (readAheadBytes > nativeBlockSize) {
1810 cacheState().fillLock.
release();
1812 if (!populateRangeLocked(offset, readAheadBytes))
1813 return FILE_BAD_BLOCK;
1821 bool didExist =
false;
1822 uintptr_t vaddr = cacheState().fill.
insert(offset, nativeBlockSize, &didExist);
1824 return FILE_BAD_BLOCK;
1828 bool existingReference =
false;
1830 vaddr = cacheState().fill.
lookup(offset);
1832 return FILE_BAD_BLOCK;
1838 existingReference =
true;
1840 if (!cacheState().fill.sync(offset,
false)) {
1841 cacheState().fill.
release(offset);
1842 return FILE_BAD_BLOCK;
1848 if (overwriteWholePage) {
1849 ByteSet(
reinterpret_cast<void*
>(vaddr), 0, nativeBlockSize);
1850 }
else if (!
readPage(offset, vaddr)) {
1851 if (existingReference) {
1852 cacheState().fill.
release(offset);
1854 if (!didExist && !cacheState().fill.discardEditing(offset)) {
1855 WARNING(
"File::readIntoCache could not discard a failed fill for offset " << offset);
1857 return FILE_BAD_BLOCK;
1862 if (existingReference) {
1866 vaddr = cacheState().fill.
lookup(offset);
1870 return vaddr ? vaddr : FILE_BAD_BLOCK;
1878 const uintptr_t buff =
readBlock(offset);
1880 ERROR(
"File::readIntoCache - bad read (" << (block * blockSize) <<
" - block size is "
1881 << blockSize <<
")");
1882 return FILE_BAD_BLOCK;
Memory-mapped file interface.
size_t read(uintptr_t offset, size_t length, uintptr_t buffer, bool(*prepare)(uintptr_t, size_t)=nullptr)
bool shutdown(ShutdownMode mode=ShutdownMode::WriteBack)
void setCallback(writeback_t newCallback, void *meta)
void release(uintptr_t key)
uintptr_t insert(uintptr_t key, bool *alreadyExisted=nullptr)
DiscardStatus prepareDiscardFrom(uintptr_t cutoff, const DiscardReference *references, size_t count, UniquePointer< PreparedDiscard > &result)
void markExternallyWritable(uintptr_t key)
void markDirty(uintptr_t key)
bool sync(uintptr_t key, bool async)
void markNoLongerEditing(uintptr_t key, size_t length=0)
uintptr_t lookup(uintptr_t key)
bool tryAcquireRegistration(SendLease ®istration)
void notifyFileEvent(const FileEvent &event)
void notifyFinalFileEvent(const FileEvent &event)
static bool anyFileEventObservers()
virtual XattrStatus getExtendedAttribute(const StringView &name, void *buffer, size_t capacity, size_t &required)
virtual void getFullPath(String &result, bool bWithMount=true)
virtual uintptr_t readBlock(uint64_t location)
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
size_t readCached(uint64_t location, size_t size, uintptr_t buffer, bool(*prepare)(uintptr_t, size_t)=nullptr)
void monitor(Thread *pThread, Event *pEvent)
virtual bool retainVfsReference()
virtual bool isSeekable() const
virtual uint64_t read(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
virtual void unpinBlock(uint64_t location)
virtual int command(const size_t command, void *buffer)
Time::Timestamp getCreationTime()
void markPageExternallyWritable(size_t offset)
virtual bool syncPages(const uint64_t *offsets, size_t count)
uint64_t writeUnlocked(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock)
virtual void extend(size_t newSize)
bool supportsRegularFileOperations()
size_t populateRange(size_t offset, size_t length)
static bool writeCallback(CacheConstants::CallbackCause cause, uintptr_t loc, uintptr_t page, void *meta)
void cullMonitorTargets(Thread *pThread)
uint64_t append(uint64_t size, uintptr_t buffer, uint64_t &location, bool bCanBlock=true)
virtual void releaseVfsReference()
virtual bool tryBeginMappingRelease()
void getNamespace(ParentLease &parent, String &name) const
uintptr_t getCachedPage(size_t block, bool locked=true)
virtual uint64_t write(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
void setCreationTime(Time::Timestamp t)
virtual void writeBlocks(uint64_t location, uintptr_t addr, size_t length)
bool syncRange(size_t offset, size_t length)
virtual bool isSocket() const
virtual void preallocate(size_t expectedSize, bool zero=true)
void retainDetachedParent()
virtual bool isStableVfsRoot() const
virtual bool isBytewise() const
Time::Timestamp getAccessedTime()
void evict(uint64_t location)
virtual bool isDirectory()
virtual bool readPage(uint64_t location, uintptr_t destination)
void setModifiedTime(Time::Timestamp t)
void setGidOnly(size_t gid)
Time::Timestamp getModifiedTime()
virtual uintptr_t futexIdentity()
virtual physical_uintptr_t getPhysicalPage(size_t offset)
void publishEvent(FileEventMask mask, const StringView &name=StringView(), bool targetIsDirectory=false)
void setPermissionsOnly(uint32_t perms)
void releaseReadReference(uintptr_t block)
virtual bool isFifo() const
virtual bool isPipe() const
virtual bool readPages(ReadPage *pages, size_t count)
virtual bool useFillCache() const
virtual MUST_USE_RESULT uintptr_t acquireCachedBlock(uint64_t location, bool retryChanged)
bool syncAndReturnPhysicalPage(size_t offset, bool async)
void setAccessedTime(Time::Timestamp t)
void setUidOnly(size_t uid)
virtual uint64_t maximumFileSize() const
bool syncFillCache(size_t offset, bool async, bool &present)
virtual ReadyMask queryReady(bool reading, bool writing)
void enableFillCacheWriteback()
virtual bool supports(const size_t command) const
bool acquireMappingUse(bool executable, bool sharedWrite)
void setCachedPage(size_t block, uintptr_t value, bool locked=true)
virtual bool allowMapping(bool shared, bool writeRequested, bool &mayWrite)
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual bool prepareSharedMapping(size_t offset, size_t length)
virtual size_t getBlockSize() const
virtual void returnPhysicalPage(size_t offset)
void getFilesystemLabel(HugeStaticString &s)
virtual int select(bool bWriting=false, int timeout=0)
virtual File * openForDescriptor(RetainedFile &owner)
virtual bool supportsReadinessNotifications() const
uintptr_t readIntoCache(uintptr_t block, bool overwriteWholePage=false, size_t readAheadBytes=0)
virtual MUST_USE_RESULT bool pinBlock(uint64_t location)
virtual void writeBlock(uint64_t location, uintptr_t addr)
virtual const String & getVolumeLabel() const =0
virtual File * getRoot() const =0
::Iterator< T, node_t > Iterator
ResizeStatus prepareFileResize(File *file, size_t oldSize, size_t newSize, UniquePointer< PreparedFileResize > &result)
static MemoryMapManager & instance()
static constexpr size_t getPageSize() PURE
static ProcessorInformation & information()
void notifyReadiness(ReadyMask mask)
void closeReadiness(ReadyMask mask=ReadyInvalid|ReadyHangup)
static Scheduler & instance()
bool tryAcquire(size_t n=1)
Process * getParent() const
bool sendEvent(Event *pEvent)
MUST_USE_RESULT bool retainTrackedFile(File *pFile)
bool untrackFile(File *pFile, bool destroy=true)
A vector / dynamic array.
virtual bool isMapped(void *virtualAddress)=0
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
void pushBack(const T &value)