21#include "pedigree/kernel/Atomic.h"
22#include "pedigree/kernel/LockGuard.h"
23#include "pedigree/kernel/Log.h"
24#include "pedigree/kernel/Metrics.h"
25#include "pedigree/kernel/machine/Disk.h"
26#include "pedigree/kernel/syscallError.h"
27#include "pedigree/kernel/utilities/Iterator.h"
28#include "pedigree/kernel/utilities/StaticString.h"
29#include "pedigree/kernel/utilities/StringView.h"
30#include "pedigree/kernel/utilities/Vector.h"
31#include "pedigree/kernel/utilities/utility.h"
39#include "pedigree/kernel/process/Process.h"
40#include "pedigree/kernel/process/TerminationDeferral.h"
41#include "pedigree/kernel/process/Thread.h"
42#include "pedigree/kernel/processor/Processor.h"
43#include "pedigree/kernel/processor/ProcessorInformation.h"
45#include "modules/Module.h"
54 size_t count = __atomic_load_n(&owners, __ATOMIC_RELAXED);
55 while (count && count != ~
size_t(0)) {
56 if (__atomic_compare_exchange_n(&owners, &count, count + 1,
false, __ATOMIC_ACQUIRE,
77 static uint32_t reserveId() {
78 static uint32_t next = 1;
79 uint32_t candidate = __atomic_load_n(&next, __ATOMIC_RELAXED);
80 while (candidate <= 0x7fffffffU) {
81 if (__atomic_compare_exchange_n(&next, &candidate, candidate + 1,
false, __ATOMIC_RELAXED,
92 retirement.beginRetirement();
96 void finishOwnedRetirement() {
99 if (!ownedRetirement || !storagePins.isClosedAndDrained() || !operations.isClosedAndDrained() ||
100 !retirementClaimed.compareAndSwap(
false,
true))
102#if THREADS && !defined(VFS_STANDALONE)
106 filesystem =
nullptr;
107 retirement.beginRetirement();
124#if THREADS && !defined(VFS_STANDALONE)
127 auto retained = pedigree_std::move(state);
130 retained->finishOwnedRetirement();
138VFS::FilesystemPin::FilesystemPin() =
default;
139VFS::FilesystemPin::FilesystemPin(
const FilesystemPin& other) =
default;
140VFS::FilesystemPin::FilesystemPin(FilesystemPin&& other)
noexcept =
default;
141VFS::FilesystemPin::~FilesystemPin() {
145 if (
this != &other) {
146 auto replacement = other.m_Pin;
147 auto retired = pedigree_std::move(m_Pin);
148 m_Pin = pedigree_std::move(replacement);
153 if (
this != &other) {
154 auto retired = pedigree_std::move(m_Pin);
155 m_Pin = pedigree_std::move(other.m_Pin);
160Filesystem* VFS::FilesystemPin::filesystem()
const {
161 return m_Pin ? m_Pin->state->filesystem :
nullptr;
171VFS::FilesystemPin::operator bool()
const {
172 return static_cast<bool>(m_Pin);
175void VFS::FilesystemPin::reset() {
176 auto retired = pedigree_std::move(m_Pin);
179bool VFS::MountIdentity::pin(FilesystemPin& pin)
const {
182 if (!retained || !m_State || !m_State->storagePins.tryAcquire(retained->admission))
184 retained->state = m_State;
185 pin.m_Pin = pedigree_std::move(retained);
189bool VFS::pinFilesystem(
Filesystem* key, FilesystemPin& pin)
const {
196 MountInfo* info = m_Mounts.
lookup(key);
197 if (!info || !info->state->storagePins.tryAcquire(retained->admission))
199 retained->state = info->state;
201 pin.m_Pin = pedigree_std::move(retained);
205VFS::MountIdentity::MountIdentity() =
default;
206VFS::MountIdentity::MountIdentity(
const MountIdentity& other) =
default;
207VFS::MountIdentity::MountIdentity(MountIdentity&& other)
noexcept =
default;
208VFS::MountIdentity::~MountIdentity() =
default;
209VFS::MountIdentity& VFS::MountIdentity::operator=(
const MountIdentity& other) =
default;
210VFS::MountIdentity& VFS::MountIdentity::operator=(MountIdentity&& other)
noexcept =
default;
212VFS::MountInfo::MountInfo(
const String& stableName,
const String& path,
214 : stableName(stableName), path(path), state(state) {}
215VFS::MountInfo::~MountInfo() =
default;
217uint32_t VFS::MountIdentity::id()
const {
218 return m_State ? m_State->id : 0;
221VFS::MountIdentity::operator bool()
const {
222 return static_cast<bool>(m_State);
225bool VFS::MountIdentity::acquire(MountOperation& operation)
const {
227 if (!m_State || !m_State->operations.tryAcquire(operation.m_Admission))
229 operation.m_State = m_State;
236 subscription.reset();
239 return m_State->retirement.subscribeFileEvents(FileEvents::SourceRetired, observer, subscription);
242VFS::MountOperation::MountOperation() =
default;
244VFS::MountOperation::MountOperation(MountOperation&& other) noexcept
245 : m_State(pedigree_std::move(other.m_State)),
246 m_Admission(pedigree_std::move(other.m_Admission)) {}
248VFS::MountOperation::~MountOperation() {
253 if (
this != &other) {
254 auto retired = pedigree_std::move(m_State);
255 auto admission = pedigree_std::move(m_Admission);
256 m_State = pedigree_std::move(other.m_State);
257 m_Admission = pedigree_std::move(other.m_Admission);
260 retired->finishOwnedRetirement();
265Filesystem* VFS::MountOperation::filesystem()
const {
266 return m_State ? m_State->filesystem :
nullptr;
269uint32_t VFS::MountOperation::id()
const {
270 return m_State ? m_State->id : 0;
279VFS::MountOperation::operator bool()
const {
280 return static_cast<bool>(m_Admission);
283void VFS::MountOperation::reset() {
284 auto state = pedigree_std::move(m_State);
287 state->finishOwnedRetirement();
294 if (!info || !info->state->operations.tryAcquire(operation.m_Admission))
296 operation.m_State = info->state;
307#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
308VFS::RetainTrackedFileHook VFS::m_RetainTrackedFileHook =
nullptr;
316 : m_PathMutationLock(),
318 m_MountView(nullptr),
319 m_MountMutationLock(),
321 m_pRootFilesystem(nullptr),
328 m_NextCallbackSequence(1),
329 m_pActiveCallbacks(nullptr),
330 m_CallbacksClosing(false)
341 m_CallbacksClosing =
true;
342 if (m_pActiveCallbacks) {
344 FATAL(
"VFS destroyed with an active callback invocation");
346 for (
auto item : m_ProbeCallbacks) {
347 if (item->state.inFlight || item->state.removers) {
349 FATAL(
"VFS destroyed before probe callback retirement completed");
352 for (
auto item : m_MountCallbacks) {
353 if (item->state.inFlight || item->state.removers) {
355 FATAL(
"VFS destroyed before mount callback retirement completed");
362 for (
auto it = m_ProbeCallbacks.begin(); it != m_ProbeCallbacks.end(); ++it) {
365 for (
auto it = m_MountCallbacks.begin(); it != m_MountCallbacks.end(); ++it) {
371 m_MountView =
nullptr;
378 for (
auto it = m_Mounts.
begin(); it != m_Mounts.
end(); ++it) {
379 it.value()->state->storagePins.close();
380 it.value()->state->operations.close();
385 m_pRootFilesystem =
nullptr;
390 for (
auto info : mountInfo) {
391 info->state->retire();
394 for (
auto filesystem : filesystems) {
399 bool reclaim =
false;
402 for (
size_t bucket = 0; bucket < TrackedFileBuckets; ++bucket) {
403 remaining[bucket] = m_TrackedFiles[bucket].exchange(
nullptr);
404 reclaim |= remaining[bucket] !=
nullptr;
409 for (
auto* entry : remaining) {
424 if (pDisk && !pDisk->acquireUse(diskUse))
432 boundary = m_NextCallbackSequence;
435 size_t afterSequence = 0;
438 ProbeCallbackItem* item = acquireProbeCallback(afterSequence, boundary, invocation);
449 const bool committed = item->state.enabled && !m_CallbacksClosing;
455 finishCallback(&item->state, invocation);
459 pFs->m_DiskUse = pedigree_std::move(diskUse);
461 if (stableName.length() == 0) {
465 if (!stableName.length()) {
467 finishCallback(&item->state, invocation);
470 if (bootMountView()) {
474 finishCallback(&item->state, invocation);
478 dispatchMountCallbacks(owner);
484 NOTICE(
"mounted filesystem '" << stableName <<
"'");
486 finishCallback(&item->state, invocation);
490 finishCallback(&item->state, invocation);
494 it != m_ProbeCallbacks.end(); it++) {
497 pFs->m_DiskUse = pedigree_std::move(diskUse);
499 if (stableName.length() == 0) {
504 if (!stableName.length()) {
508 if (bootMountView()) {
517 it2 != m_MountCallbacks.end(); it2++) {
525 NOTICE(
"mounted filesystem '" << stableName <<
"'");
536 return registerFilesystemLocked(pFs, preferredStableName);
543 if (pFs->
m_pDisk && !pFs->m_DiskUse && !pFs->
m_pDisk->acquireUse(pFs->m_DiskUse))
546 MountInfo* info =
nullptr;
550 MountInfo* existing = m_Mounts.
lookup(pFs);
552 return existing->stableName;
555 String stableName = getUniqueStableNameLocked(preferredStableName);
559 const uint32_t
id = VfsMountState::reserveId();
565 info =
new MountInfo(stableName,
String(path), state);
566 if (!info || !m_Mounts.
tryInsert(pFs, info)) {
570 root = m_pRootFilesystem;
573 if (root && !bootMountView()) {
574 attachFilesystem(root, pFs, info->path);
577 return info->stableName;
594 info = m_Mounts.
lookup(pFs);
595 if (!info || !info->state->storagePins.tryCloseIfIdle()) {
598 m_Mounts.
take(pFs, info);
599 info->state->operations.close();
601 if (pFs == m_pRootFilesystem) {
602 m_pRootFilesystem =
nullptr;
604 detach = m_pRootFilesystem !=
nullptr;
608 if (detach && !bootMountView()) {
617 info->state->retire();
619 if (terminal && pFs->
shutdown() != Filesystem::SyncStatus::Success) {
630#if THREADS && !defined(VFS_STANDALONE)
639 removed = m_Mounts.
lookup(filesystem);
640 if (!removed || filesystem == m_pRootFilesystem)
642 m_Mounts.
take(filesystem, removed);
643 removed->state->storagePins.
close();
644 removed->state->operations.
close();
648 auto state = removed->state;
652 FATAL(
"Owned filesystem retirement lost its mount state.");
655 retained->ownedRetirement =
true;
656 retained->finishOwnedRetirement();
661#if THREADS && !defined(VFS_STANDALONE)
665 if (!filesystem || !pinFilesystem(filesystem, pin)) {
669 WARNING(
"VFS: root filesystem device removed");
676 removed = m_Mounts.
lookup(filesystem);
680 m_Mounts.
take(filesystem, removed);
681 removed->state->storagePins.
close();
682 removed->state->operations.
close();
684 auto state = removed->state;
687 FATAL(
"Removed filesystem lost its mount state");
692 retained->operations.
wait();
693 if (
auto* view = bootMountView()) {
694 if (!view->detachBackingForRemoval(filesystem)) {
708 retained->retirement.beginRetirement();
710 retained->ownedRetirement =
true;
712 retained->finishOwnedRetirement();
717 if (bootMountView()) {
718 SYSCALL_ERROR(DeviceBusy);
723 bool registered =
false;
726 registered = m_Mounts.
lookup(pFs) !=
nullptr;
729 if (!registerFilesystemLocked(pFs, pFs->
getVolumeLabel()).length())
736 m_pRootFilesystem = pFs;
738 attachRegisteredFilesystemsLocked();
744 return m_pRootFilesystem;
747#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
748VFS::HostedRootViewScope::HostedRootViewScope() : m_Vfs(
VFS::instance()) {}
749VFS::HostedRootViewScope::~HostedRootViewScope() {
751 FATAL(
"Hosted root view scope still has retained owners");
753bool VFS::HostedRootViewScope::open(
Filesystem* filesystem) {
754 if (!filesystem || m_Filesystem)
757 m_PreviousContext = thread && thread->getParent()
758 ? thread->getParent()->acquireFilesystemContext()
760 bool prepared =
false;
765 if (m_Vfs.m_Mounts.lookup(filesystem))
767 m_PreviousRoot = m_Vfs.m_pRootFilesystem;
768 m_PreviousView = m_Vfs.bootMountView();
770 if (m_PreviousRoot && !m_Vfs.pinFilesystem(m_PreviousRoot, m_PreviousRootPin))
772 if (!m_Vfs.registerFilesystemLocked(filesystem,
String(
"hosted-root-fixture")).length())
774 m_Filesystem = filesystem;
776 if (m_View && m_View->initialise(filesystem)) {
777 NamespaceMutation writer(m_Vfs);
779 m_Vfs.m_pRootFilesystem = filesystem;
780 __atomic_store_n(&m_Vfs.m_MountView, m_View, __ATOMIC_RELEASE);
781 m_Installed = prepared =
true;
788bool VFS::HostedRootViewScope::installContext(
Process& process) {
790 return m_Installed && m_View->createBootContext(context) &&
793bool VFS::HostedRootViewScope::close() {
796 NamespaceMutation writer(m_Vfs);
798 if (m_View && !m_View->quiescentForHostedTest())
802 if (m_Vfs.bootMountView() != m_View || m_Vfs.m_pRootFilesystem != m_Filesystem)
804 m_Vfs.m_pRootFilesystem = m_PreviousRoot;
805 __atomic_store_n(&m_Vfs.m_MountView, m_PreviousView, __ATOMIC_RELEASE);
811 if (m_Filesystem && !m_Vfs.unregisterFilesystem(m_Filesystem,
false))
813 m_Filesystem =
nullptr;
814 m_PreviousContext.reset();
815 m_PreviousRootPin.reset();
816 m_PreviousRoot =
nullptr;
817 m_PreviousView =
nullptr;
822#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
827 m_pRootFilesystem = pFs;
846 if (it.value()->path == path) {
856 getMountsLocked(mounts);
860 struct MountSnapshotSource {
861 MountSnapshotSource() : filesystem(nullptr) {}
863 : filesystem(filesystem), stableName(stableName), path(path) {}
874 for (MountTable::Iterator it = m_Mounts.
begin(); it != m_Mounts.
end(); ++it) {
875 sources.
createBack(it.key(), it.value()->stableName, it.value()->path);
879 for (
const auto& source : sources) {
880 bool hasDisk =
false;
883 Disk* disk = source.filesystem->getDisk();
891 mounts.
createBack(source.stableName, source.path, hasDisk, diskParentName, diskName);
896Filesystem::SyncStatus syncPinnedFilesystem(
Filesystem* filesystem) {
897 Metrics::increment(Metrics::VfsSyncCalls);
898#if !defined(VFS_STANDALONE) && THREADS
900 const int previousError = thread ? thread->
getErrno() : 0;
904 const auto result = filesystem->
sync();
905 if (result == Filesystem::SyncStatus::Unsupported) {
906 Metrics::increment(Metrics::VfsSyncUnsupported);
907 }
else if (result != Filesystem::SyncStatus::Success) {
908 Metrics::increment(Metrics::VfsSyncFailed);
910#if !defined(VFS_STANDALONE) && THREADS
919#if !defined(VFS_STANDALONE) && THREADS
923 return Filesystem::SyncStatus::Unsupported;
926 return Filesystem::SyncStatus::NoMemory;
931 return Filesystem::SyncStatus::Unsupported;
932 if (!info->state->storagePins.tryAcquire(pin->admission))
933 return Filesystem::SyncStatus::IoError;
934 pin->state = info->state;
936 return syncPinnedFilesystem(pin->state->filesystem);
940#if !defined(VFS_STANDALONE) && THREADS
954 capacity = m_Mounts.
count();
957 ERROR(
"VFS::syncAll: filesystem snapshot is too large");
958 return Filesystem::SyncStatus::NoMemory;
960 delete[] snapshot.pins;
962 if (capacity && !snapshot.pins) {
963 ERROR(
"VFS::syncAll: cannot allocate filesystem snapshot");
964 return Filesystem::SyncStatus::NoMemory;
969 if (m_Mounts.
count() > capacity)
971 for (
auto it = m_Mounts.
begin(); it != m_Mounts.
end(); ++it) {
972 auto& pin = snapshot.pins[snapshot.count++];
973 pin.state = it.value()->state;
974 const bool admitted = pin.state->storagePins.tryAcquire(pin.admission);
981 auto firstError = Filesystem::SyncStatus::Success;
982 for (
size_t n = 0; n < snapshot.count; ++n) {
983 auto& pin = snapshot.pins[n];
984 const auto status = pin.admission ? syncPinnedFilesystem(pin.state->filesystem)
985 : Filesystem::SyncStatus::IoError;
986 if (status != Filesystem::SyncStatus::Success) {
987 const char* reason = status == Filesystem::SyncStatus::Unsupported ?
"unsupported"
988 : status == Filesystem::SyncStatus::NoMemory ?
"out of memory"
990 ERROR(
"VFS::syncAll: filesystem " <<
Dec << pin.state->id <<
" sync failed: " << reason);
991 if (firstError == Filesystem::SyncStatus::Success ||
992 (firstError == Filesystem::SyncStatus::Unsupported &&
993 status != Filesystem::SyncStatus::Unsupported))
1004 for (
auto it = m_Mounts.
begin(); it != m_Mounts.
end(); ++it) {
1006 identity.m_State = it.value()->state;
1010 for (
const auto&
mount : mounts) {
1012 if (!
mount.pin(pin)) {
1024 Metrics::increment(Metrics::VfsFindCalls);
1029 pStartNode = resolveStartNode(path, pStartNode);
1031 pResult = pStartNode->getFilesystem()->
find(path.
view(), pStartNode);
1035 Metrics::increment(Metrics::VfsFindMisses);
1043 pStartNode = resolveStartNode(path, pStartNode);
1047 return pStartNode->getFilesystem()->
findRetained(path.
view(), result, pStartNode);
1052 FATAL(
"VFS cannot register a null probe callback");
1058 if (m_CallbacksClosing) {
1061 FATAL(
"VFS probe callback registered during teardown");
1063 for (
auto registered : m_ProbeCallbacks) {
1064 if (registered->callback == callback) {
1065 if (!registered->state.draining) {
1066 registered->state.enabled =
true;
1073 if (m_NextCallbackSequence ==
static_cast<size_t>(-1)) {
1076 FATAL(
"VFS callback sequence exhausted");
1078 item->state.sequence = m_NextCallbackSequence++;
1079 item->state.debugAddress =
reinterpret_cast<uintptr_t
>(callback);
1080 m_ProbeCallbacks.pushBack(item);
1083 for (
auto registered : m_ProbeCallbacks) {
1084 if (registered->callback == callback) {
1089 m_ProbeCallbacks.pushBack(item);
1101 const bool canYield =
1106 bool callbackContext =
false;
1109 if (m_CallbacksClosing) {
1113 for (
auto it = m_ProbeCallbacks.begin(); it != m_ProbeCallbacks.end(); ++it) {
1114 if ((*it)->callback != callback) {
1119 item->state.enabled =
false;
1120 callbackContext = isCallbackInvocation(owner);
1121 if (!callbackContext && canYield) {
1122 item->state.draining =
true;
1123 ++item->state.removers;
1132 if (callbackContext || !canYield) {
1136 drainProbeCallback(item);
1139 for (
auto it = m_ProbeCallbacks.begin(); it != m_ProbeCallbacks.end(); ++it) {
1141 if (item->callback == callback) {
1142 m_ProbeCallbacks.erase(it);
1153 FATAL(
"VFS cannot register a null mount callback");
1159 if (m_CallbacksClosing) {
1162 FATAL(
"VFS mount callback registered during teardown");
1164 for (
auto registered : m_MountCallbacks) {
1165 if (registered->callback == callback) {
1166 if (!registered->state.draining) {
1167 registered->state.enabled =
true;
1174 if (m_NextCallbackSequence ==
static_cast<size_t>(-1)) {
1177 FATAL(
"VFS callback sequence exhausted");
1179 item->state.sequence = m_NextCallbackSequence++;
1180 item->state.debugAddress =
reinterpret_cast<uintptr_t
>(callback);
1181 m_MountCallbacks.pushBack(item);
1184 for (
auto registered : m_MountCallbacks) {
1185 if (registered->callback == callback) {
1190 m_MountCallbacks.pushBack(item);
1202 const bool canYield =
1207 bool callbackContext =
false;
1210 if (m_CallbacksClosing) {
1214 for (
auto it = m_MountCallbacks.begin(); it != m_MountCallbacks.end(); ++it) {
1215 if ((*it)->callback != callback) {
1220 item->state.enabled =
false;
1221 callbackContext = isCallbackInvocation(owner);
1222 if (!callbackContext && canYield) {
1223 item->state.draining =
true;
1224 ++item->state.removers;
1233 if (callbackContext || !canYield) {
1237 drainMountCallback(item);
1240 for (
auto it = m_MountCallbacks.begin(); it != m_MountCallbacks.end(); ++it) {
1242 if (item->callback == callback) {
1243 m_MountCallbacks.erase(it);
1254 ActiveInvocation& invocation) {
1255 ProbeCallbackItem* item =
nullptr;
1257 if (!m_CallbacksClosing) {
1258 for (
auto candidate : m_ProbeCallbacks) {
1259 if (candidate->state.sequence <= afterSequence) {
1262 if (candidate->state.sequence >= boundary) {
1266 afterSequence = candidate->state.sequence;
1267 if (!candidate->state.enabled) {
1272 invocation.state = &item->state;
1273 ++item->state.inFlight;
1274 invocation.next = m_pActiveCallbacks;
1275 m_pActiveCallbacks = &invocation;
1284 ActiveInvocation& invocation) {
1285 MountCallbackItem* item =
nullptr;
1287 if (!m_CallbacksClosing) {
1288 for (
auto candidate : m_MountCallbacks) {
1289 if (candidate->state.sequence <= afterSequence) {
1292 if (candidate->state.sequence >= boundary) {
1296 afterSequence = candidate->state.sequence;
1297 if (!candidate->state.enabled) {
1302 invocation.state = &item->state;
1303 ++item->state.inFlight;
1304 invocation.next = m_pActiveCallbacks;
1305 m_pActiveCallbacks = &invocation;
1313void VFS::finishCallback(CallbackState* state, ActiveInvocation& invocation) {
1314 auto completionGuard = state->drainWaiters.acquire();
1315 bool wakeDrainers =
false;
1318 ActiveInvocation** link = &m_pActiveCallbacks;
1319 while (*link && *link != &invocation) {
1320 link = &((*link)->next);
1324 FATAL(
"VFS lost an active callback invocation");
1326 *link = invocation.next;
1328 if (!state->inFlight) {
1330 FATAL(
"VFS callback pin underflow");
1333 wakeDrainers = !state->inFlight && state->draining;
1337 completionGuard.wakeAll(WaitQueue::WakeReason::Signalled,
WaitQueue::Channel(state));
1341void VFS::drainProbeCallback(ProbeCallbackItem* item) {
1343 bool complete =
false;
1345 auto waitGuard = item->state.drainWaiters.acquire();
1347 if (!item->state.inFlight) {
1352 const WaitQueue::WakeReason reason = waitGuard.waitForCompletion(
1362 bool deleteItem =
false;
1364 if (!item->state.removers) {
1366 FATAL(
"VFS probe callback remover underflow");
1368 --item->state.removers;
1369 if (!item->state.removers) {
1370 for (
auto it = m_ProbeCallbacks.begin(); it != m_ProbeCallbacks.end(); ++it) {
1372 m_ProbeCallbacks.erase(it);
1385void VFS::drainMountCallback(MountCallbackItem* item) {
1387 bool complete =
false;
1389 auto waitGuard = item->state.drainWaiters.acquire();
1391 if (!item->state.inFlight) {
1396 const WaitQueue::WakeReason reason = waitGuard.waitForCompletion(
1406 bool deleteItem =
false;
1408 if (!item->state.removers) {
1410 FATAL(
"VFS mount callback remover underflow");
1412 --item->state.removers;
1413 if (!item->state.removers) {
1414 for (
auto it = m_MountCallbacks.begin(); it != m_MountCallbacks.end(); ++it) {
1416 m_MountCallbacks.erase(it);
1429void VFS::dispatchMountCallbacks(
void* owner) {
1430 size_t boundary = 0;
1432 boundary = m_NextCallbackSequence;
1435 size_t afterSequence = 0;
1437 ActiveInvocation invocation = {
nullptr, owner,
nullptr};
1438 MountCallbackItem* item = acquireMountCallback(afterSequence, boundary, invocation);
1444 finishCallback(&item->state, invocation);
1448bool VFS::isCallbackInvocation(
void* owner)
const {
1449 for (ActiveInvocation* invocation = m_pActiveCallbacks; invocation;
1450 invocation = invocation->next) {
1451 if (invocation->owner == owner) {
1460 pStartNode = resolveStartNode(path, pStartNode);
1461 return pStartNode && pStartNode->getFilesystem()->
createFile(path, mask, pStartNode);
1465 pStartNode = resolveStartNode(path, pStartNode);
1467 NOTICE(
"no start node found");
1471 return pStartNode->getFilesystem()->
createDirectory(path, mask, pStartNode);
1475 pStartNode = resolveStartNode(path, pStartNode);
1476 return pStartNode && pStartNode->getFilesystem()->
createSymlink(path, value, pStartNode);
1480 pStartNode = resolveStartNode(path, pStartNode);
1481 return pStartNode && pStartNode->getFilesystem()->
createLink(path, target, pStartNode);
1485 return remove(path, pStartNode,
nullptr);
1489 pStartNode = resolveStartNode(path, pStartNode);
1490 return pStartNode && pStartNode->getFilesystem()->
remove(path, pStartNode, expected);
1495 oldStart = resolveStartNode(oldPath, oldStart);
1496 newStart = resolveStartNode(newPath, newStart);
1497 return oldStart && newStart &&
1498 oldStart->getFilesystem()->
rename(oldPath.
view(), oldStart, newPath.
view(), newStart,
1503#ifdef VFS_STANDALONE
1513 if (!Process::currentFilesystemCredentials(credentials)) {
1514 SYSCALL_ERROR(PermissionDenied);
1517 return checkAccess(pFile, bRead, bWrite, bExecute, credentials);
1524 if (uid >= 0 &&
static_cast<uint64_t
>(uid) < UINT32_MAX && gid >= 0 &&
1525 static_cast<uint64_t
>(gid) < UINT32_MAX && groups.
count() <= credentials.MaximumGroups) {
1526 credentials.uid = uid;
1527 credentials.gid = gid;
1528 credentials.groupCount = groups.
count();
1529 credentials.valid =
true;
1530 for (
size_t i = 0; i < groups.
count(); ++i) {
1531 if (groups[i] < 0 ||
static_cast<uint64_t
>(groups[i]) >= UINT32_MAX)
1532 credentials.valid =
false;
1533 credentials.groups[i] = groups[i];
1536 return checkAccess(file, read, write, execute, credentials);
1541#ifdef VFS_STANDALONE
1546 if (!credentials.valid) {
1547 SYSCALL_ERROR(PermissionDenied);
1550 const uint32_t processUid = credentials.uid;
1551 const uint32_t processGid = credentials.gid;
1553 const auto attributes = pFile->getAttributes();
1554 const int64_t fuid = attributes.uid;
1555 const int64_t fgid = attributes.gid;
1556 uint32_t permissions = attributes.permissions;
1557 uint32_t needed = (bRead ? FILE_UR : 0) | (bWrite ? FILE_UW : 0) | (bExecute ? FILE_UX : 0);
1559 const bool dacOverride = credentials.enforceCapabilities
1560 ? (credentials.capabilities & (uint64_t(1) << 1))
1562 const bool dacReadSearch =
1563 credentials.enforceCapabilities && (credentials.capabilities & (uint64_t(1) << 2));
1565 if (!bExecute || pFile->
isDirectory() || (permissions & (FILE_UX | FILE_GX | FILE_OX))) {
1569 if (dacReadSearch && !bWrite && (!bExecute || pFile->
isDirectory())) {
1572 if (fuid == processUid) {
1573 check = (permissions >> FILE_UBITS) & 0x7;
1575 bool inFileGroup = fgid == processGid;
1578 for (
size_t i = 0; i < credentials.groupCount; ++i) {
1579 if (credentials.groups[i] == fgid) {
1586 check = (permissions >> (inFileGroup ? FILE_GBITS : FILE_OBITS)) & 0x7;
1589 if ((check & needed) != needed) {
1590 SYSCALL_ERROR(PermissionDenied);
1599 uintptr_t key =
reinterpret_cast<uintptr_t
>(file) >> 4;
1602 return m_TrackedFiles[key % TrackedFileBuckets];
1605bool VFS::trackFileLocked(
File* file) {
1606 auto& bucket = trackedFileBucket(file);
1607 TrackedFile* first = bucket.loadForUpdate();
1608 for (
auto* entry = first; entry; entry = entry->next.loadForUpdate()) {
1609 if (entry->file == file) {
1610 return entry->retain();
1614 auto* entry =
new TrackedFile(file);
1619 entry->next.exchange(first);
1621 bucket.exchange(entry);
1627 if (!trackFileLocked(pFile)) {
1628 FATAL(
"VFS could not track a File");
1637 return trackFileLocked(file);
1645 bool retained =
false;
1648 for (
auto* entry = trackedFileBucket(pFile).load(guard); entry;
1649 entry = entry->next.load(guard)) {
1650 if (entry->file == pFile) {
1651 retained = entry->retain();
1660#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1661 RetainTrackedFileHook hook = __atomic_load_n(&m_RetainTrackedFileHook, __ATOMIC_ACQUIRE);
1673 TrackedFile* entry = trackedFileBucket(pFile).load(guard);
1674 while (entry && entry->file != pFile) {
1675 entry = entry->next.load(guard);
1680 size_t count = __atomic_load_n(&entry->owners, __ATOMIC_RELAXED);
1682 if (__atomic_compare_exchange_n(&entry->owners, &count, count - 1,
false, __ATOMIC_ACQ_REL,
1683 __ATOMIC_RELAXED)) {
1695 auto* link = &trackedFileBucket(pFile);
1697 while (entry && entry->file != pFile) {
1698 link = &entry->next;
1699 entry = link->loadForUpdate();
1705 size_t count = __atomic_load_n(&entry->owners, __ATOMIC_RELAXED);
1707 if (__atomic_compare_exchange_n(&entry->owners, &count, count - 1,
false, __ATOMIC_ACQ_REL,
1708 __ATOMIC_RELAXED)) {
1712 link->exchange(entry->next.loadForUpdate());
1732#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1733void VFS::setRetainTrackedFileHookForHostedTest(RetainTrackedFileHook hook) {
1734 __atomic_store_n(&m_RetainTrackedFileHook, hook, __ATOMIC_RELEASE);
1738String VFS::getUniqueStableNameLocked(
const String& preferredName)
const {
1740 for (
size_t i = 0; i < preferredName.length(); ++i) {
1741 char c = preferredName[i];
1742 bool allowed = (c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9') ||
1743 c ==
'.' || c ==
'_' || c ==
'-';
1744 safeName.append(allowed ? c :
'-');
1747 String base(safeName, safeName.length());
1748 if (!base.length() || base ==
"." || base ==
"..") {
1749 base.assign(
"filesystem");
1755 candidateBuffer += base;
1757 candidateBuffer +=
"-";
1758 candidateBuffer.append(suffix);
1760 String candidate(candidateBuffer, candidateBuffer.length());
1762 bool exists =
false;
1763 for (MountTable::Iterator it = m_Mounts.
begin(); it != m_Mounts.
end(); ++it) {
1764 if (it.value()->stableName == candidate) {
1778File* VFS::resolveStartNode(
const String& path,
File* pStartNode) {
1779 if (!path.length() || path[0] !=
'/') {
1786 root = m_pRootFilesystem;
1788 return root ? root->
getRoot() :
nullptr;
1792 if (!pRootFs || !pFs || pFs == pRootFs) {
1793 return pFs == pRootFs;
1797 if (!filesystemRoot || !filesystemRoot->
isDirectory()) {
1798 ERROR(
"VFS: filesystem at " << path <<
" has no valid root directory");
1803 if (
auto* view = bootMountView()) {
1805 if (!view->createBootContext(bootstrap)) {
1808 auto context = bootstrap.reference();
1810 options.requireDirectory =
true;
1814 attached->node() == filesystemRoot) {
1818 options.crossFinalMount =
false;
1822 if (!view->bootRootPath(root) || !view->createDirectory(root,
String(
"media"), 0755) ||
1829 if (!view->createDirectory(media, name, 0755) ||
1834 return view->attach(context, point, pFs);
1848 ERROR(
"VFS: cannot attach filesystem at " << path);
1853 NOTICE(
"VFS: attached filesystem at " << path);
1857void VFS::attachRegisteredFilesystemsLocked() {
1859 AttachWork() : filesystem(nullptr) {}
1860 AttachWork(
Filesystem* filesystem,
const String& path) : filesystem(filesystem), path(path) {}
1870 root = m_pRootFilesystem;
1875 for (MountTable::Iterator it = m_Mounts.
begin(); it != m_Mounts.
end(); ++it) {
1876 MountInfo* info = it.value();
1877 if (it.key() == root) {
1878 info->path.assign(
"/");
1884 path += info->stableName;
1885 info->path.assign(path, path.length());
1890 for (
const auto& item : work) {
1891 attachFilesystem(root, item.filesystem, item.path);
1895Module::UnloadAdmission VFS::unloadAdmission(
bool terminal) {
1898 return terminal ? Module::UnloadAdmission::KeepMapped : Module::UnloadAdmission::Busy;
1901#ifndef VFS_STANDALONE
1902static bool initVFS() {
1903 SwapStore::instance().prepareAtBoot();
1908static void destroyVFS() {}
1910MODULE_INFO(
"vfs", &initVFS, &destroyVFS);
1913bool VFS::diskMount(uint32_t
id, MountIdentity&
identity)
const {
1915 if (!
id ||
id > 0xfffff)
1918 for (
auto it = m_Mounts.
begin(); it != m_Mounts.
end(); ++it) {
1919 const auto& state = it.value()->state;
1920 if (state->id ==
id && state->filesystem->getDisk() && state->operations.isOpen()) {
1930 if (!mounts.tryReserve(m_Mounts.
count()))
1932 for (
auto it = m_Mounts.
begin(); it != m_Mounts.
end(); ++it) {
1933 const auto& state = it.value()->state;
1934 if (state->id > 0xfffff || !state->filesystem->getDisk() || !state->operations.isOpen())
Memory-mapped file interface.
virtual void getName(String &str)
Device * getParent() const
static Directory * fromFile(File *pF)
void removeEphemeralFiles()
void setReparsePoint(Directory *pTarget)
virtual void getName(String &str)
virtual bool isDirectory()
File * findRetained(const StringView &path, Directory::ChildLease &result, File *pStartNode=nullptr)
virtual const String & getVolumeLabel() const =0
bool createSymlink(const StringView &path, const String &value, File *pStartNode=0)
bool createDirectory(const StringView &path, uint32_t mask, File *pStartNode=0)
bool rename(const StringView &oldPath, File *oldStart, const StringView &newPath, File *newStart, bool noReplace=false)
bool createLink(const StringView &path, File *target, File *pStartNode=0)
virtual SyncStatus shutdown()
virtual File * find(const StringView &path)
Filesystem *(* ProbeCallback)(Disk *)
virtual SyncStatus sync()
virtual File * getRoot() const =0
bool createFile(const StringView &path, uint32_t mask, File *pStartNode=0)
bool remove(const StringView &path, File *pStartNode=0)
virtual bool deviceRemoved(bool deviceAvailable=false)
bool registerUnloadAdmission(ModuleEntry ownerEntry, Module::UnloadAdmissionHook hook)
static KernelElf & instance()
::Iterator< T, node_t > Iterator
MUST_USE_RESULT bool installFilesystemContext(FilesystemContextOwner &&context)
static ProcessorInformation & information()
static ExecutionContext executionContext()
static SharedPointer< T > tryAllocate(Args...)
bool acquire(bool recurse=false, bool safe=true)
StringView substring(size_t start, size_t end, bool hashed=HASH_STRINGVIEWS_BY_DEFAULT) const
void setErrno(size_t err)
An iterator applicable for many data structures.
bool tryInsert(const K &key, const E &value)
E lookup(const K &key) const
bool take(const K &key, E &element)
bool createSymlink(const String &path, const String &value, File *pStartNode=0)
bool removeEphemeralFiles()
bool removeMountCallback(MountCallback callback)
bool removeProbeCallback(Filesystem::ProbeCallback callback)
bool createFile(const String &path, uint32_t mask, File *pStartNode=0)
void addProbeCallback(Filesystem::ProbeCallback callback)
bool tryTrackFile(File *pFile)
bool acquireMount(Filesystem *key, MountOperation &operation) const
Filesystem * getFilesystemAt(const String &path) const
bool mount(Disk *pDisk, String &stableName, Filesystem **pMountedFs=nullptr)
bool unregisterFilesystem(Filesystem *pFs, bool canDelete=true, bool terminal=false)
File * findRetained(const String &path, Directory::ChildLease &result, File *pStartNode=nullptr)
void getMounts(Vector< MountSnapshot > &mounts) const
bool setRootFilesystem(Filesystem *pFs)
File * find(const String &path, File *pStartNode=0)
bool retireOwnedFilesystem(Filesystem *filesystem)
MUST_USE_RESULT bool retainTrackedFile(File *pFile)
bool getMountPath(Filesystem *pFs, String &path) const
bool untrackFile(File *pFile, bool destroy=true)
Filesystem::SyncStatus syncFilesystem(Filesystem *key)
bool removeDiskFilesystem(Filesystem *filesystem, bool deviceAvailable=false)
Filesystem::SyncStatus syncAll()
String registerFilesystem(Filesystem *pFs, const String &preferredStableName)
static bool checkAccess(File *pFile, bool bRead, bool bWrite, bool bExecute)
Filesystem * getRootFilesystem() const
bool createLink(const String &path, File *target, File *pStartNode=0)
bool createDirectory(const String &path, uint32_t mask, File *pStartNode=0)
bool remove(const String &path, File *pStartNode=0)
void addMountCallback(MountCallback callback)
void trackFile(File *pFile)
Track a File object that exists. It is necessary to keep track of File objects, or at least those tha...
A vector / dynamic array.
void createBack(Args &&... args)
void pushBack(const T &value)
void clear(bool freeMem=false)