The Pedigree Project 0.1
VFS.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "VFS.h"
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"
32
33#include "Directory.h"
34#include "File.h"
35#include "MemoryMappedFile.h"
36#include "MountView.h"
37
38#ifndef VFS_STANDALONE
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"
44
45#include "modules/Module.h"
46#endif
47
48class Disk;
49
51 explicit TrackedFile(File* file) : file(file), owners(1) {}
52
53 bool retain() {
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,
57 __ATOMIC_RELAXED)) {
58 return true;
59 }
60 }
61 return false;
62 }
63
64 File* const file;
65 size_t owners;
67};
68
70 public:
71 VfsMountState(Filesystem* filesystem, uint32_t id) : filesystem(filesystem), id(id) {}
73 storagePins.closeAndWait();
74 operations.closeAndWait();
75 }
76
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,
82 __ATOMIC_RELAXED))
83 return candidate;
84 }
85 return 0;
86 }
87
88 void retire() {
89 storagePins.wait();
90 operations.wait();
91 filesystem = nullptr;
92 retirement.beginRetirement();
93 retirement.finishRetirement();
94 }
95
96 void finishOwnedRetirement() {
97 // Both gates are already closed. Once observed drained, neither can gain
98 // another owner; one releasing caller performs retirement without waiting.
99 if (!ownedRetirement || !storagePins.isClosedAndDrained() || !operations.isClosedAndDrained() ||
100 !retirementClaimed.compareAndSwap(false, true))
101 return;
102#if THREADS && !defined(VFS_STANDALONE)
103 TerminationDeferral lifetime;
104#endif
105 Filesystem* retired = filesystem;
106 filesystem = nullptr;
107 retirement.beginRetirement();
108 retirement.finishRetirement();
109 delete retired;
110 }
111
112 Atomic<bool> ownedRetirement{false};
113 Atomic<bool> retirementClaimed{false};
114 Filesystem* filesystem;
115 const uint32_t id;
116 OperationBarrier operations;
117 OperationBarrier storagePins;
118 InodeEventSource retirement;
119};
120
122 public:
124#if THREADS && !defined(VFS_STANDALONE)
125 TerminationDeferral lifetime;
126#endif
127 auto retained = pedigree_std::move(state);
128 admission = OperationBarrier::Lease();
129 if (retained)
130 retained->finishOwnedRetirement();
131 }
132 // Release admission before the state that owns its barrier. Copies of the
133 // outer pin may survive registry shutdown and do not need fresh admission.
135 OperationBarrier::Lease admission;
136};
137
138VFS::FilesystemPin::FilesystemPin() = default;
139VFS::FilesystemPin::FilesystemPin(const FilesystemPin& other) = default;
140VFS::FilesystemPin::FilesystemPin(FilesystemPin&& other) noexcept = default;
141VFS::FilesystemPin::~FilesystemPin() {
142 reset();
143}
144VFS::FilesystemPin& VFS::FilesystemPin::operator=(const FilesystemPin& other) {
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);
149 }
150 return *this;
151}
152VFS::FilesystemPin& VFS::FilesystemPin::operator=(FilesystemPin&& other) noexcept {
153 if (this != &other) {
154 auto retired = pedigree_std::move(m_Pin);
155 m_Pin = pedigree_std::move(other.m_Pin);
156 }
157 return *this;
158}
159
160Filesystem* VFS::FilesystemPin::filesystem() const {
161 return m_Pin ? m_Pin->state->filesystem : nullptr;
162}
163
164VFS::MountIdentity VFS::FilesystemPin::identity() const {
165 MountIdentity identity;
166 if (m_Pin)
167 identity.m_State = m_Pin->state;
168 return identity;
169}
170
171VFS::FilesystemPin::operator bool() const {
172 return static_cast<bool>(m_Pin);
173}
174
175void VFS::FilesystemPin::reset() {
176 auto retired = pedigree_std::move(m_Pin);
177}
178
179bool VFS::MountIdentity::pin(FilesystemPin& pin) const {
180 pin.reset();
182 if (!retained || !m_State || !m_State->storagePins.tryAcquire(retained->admission))
183 return false;
184 retained->state = m_State;
185 pin.m_Pin = pedigree_std::move(retained);
186 return true;
187}
188
189bool VFS::pinFilesystem(Filesystem* key, FilesystemPin& pin) const {
190 pin.reset();
192 if (!retained)
193 return false;
194 {
195 LockGuard<Mutex> guard(m_MountTableLock);
196 MountInfo* info = m_Mounts.lookup(key);
197 if (!info || !info->state->storagePins.tryAcquire(retained->admission))
198 return false;
199 retained->state = info->state;
200 }
201 pin.m_Pin = pedigree_std::move(retained);
202 return true;
203}
204
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;
211
212VFS::MountInfo::MountInfo(const String& stableName, const String& path,
213 const SharedPointer<VfsMountState>& state)
214 : stableName(stableName), path(path), state(state) {}
215VFS::MountInfo::~MountInfo() = default;
216
217uint32_t VFS::MountIdentity::id() const {
218 return m_State ? m_State->id : 0;
219}
220
221VFS::MountIdentity::operator bool() const {
222 return static_cast<bool>(m_State);
223}
224
225bool VFS::MountIdentity::acquire(MountOperation& operation) const {
226 operation.reset();
227 if (!m_State || !m_State->operations.tryAcquire(operation.m_Admission))
228 return false;
229 operation.m_State = m_State;
230 return true;
231}
232
233bool VFS::MountIdentity::subscribeRetirement(const SharedPointer<FileEventObserver>& observer,
234 FileEventSubscription& subscription) const {
235 if (!m_State) {
236 subscription.reset();
237 return false;
238 }
239 return m_State->retirement.subscribeFileEvents(FileEvents::SourceRetired, observer, subscription);
240}
241
242VFS::MountOperation::MountOperation() = default;
243
244VFS::MountOperation::MountOperation(MountOperation&& other) noexcept
245 : m_State(pedigree_std::move(other.m_State)),
246 m_Admission(pedigree_std::move(other.m_Admission)) {}
247
248VFS::MountOperation::~MountOperation() {
249 reset();
250}
251
252VFS::MountOperation& VFS::MountOperation::operator=(MountOperation&& other) noexcept {
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);
258 admission = OperationBarrier::Lease();
259 if (retired)
260 retired->finishOwnedRetirement();
261 }
262 return *this;
263}
264
265Filesystem* VFS::MountOperation::filesystem() const {
266 return m_State ? m_State->filesystem : nullptr;
267}
268
269uint32_t VFS::MountOperation::id() const {
270 return m_State ? m_State->id : 0;
271}
272
273VFS::MountIdentity VFS::MountOperation::identity() const {
274 MountIdentity identity;
275 identity.m_State = m_State;
276 return identity;
277}
278
279VFS::MountOperation::operator bool() const {
280 return static_cast<bool>(m_Admission);
281}
282
283void VFS::MountOperation::reset() {
284 auto state = pedigree_std::move(m_State);
285 m_Admission = OperationBarrier::Lease();
286 if (state)
287 state->finishOwnedRetirement();
288}
289
290bool VFS::acquireMount(Filesystem* key, MountOperation& operation) const {
291 operation.reset();
292 LockGuard<Mutex> guard(m_MountTableLock);
293 MountInfo* info = m_Mounts.lookup(key);
294 if (!info || !info->state->operations.tryAcquire(operation.m_Admission))
295 return false;
296 operation.m_State = info->state;
297 return true;
298}
299
304
306
307#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
308VFS::RetainTrackedFileHook VFS::m_RetainTrackedFileHook = nullptr;
309#endif
310
312 return m_Instance;
313}
314
316 : m_PathMutationLock(),
317 m_PathGeneration(0),
318 m_MountView(nullptr),
319 m_MountMutationLock(),
320 m_MountTableLock(),
321 m_pRootFilesystem(nullptr),
322 m_Mounts(),
323 m_ProbeCallbacks(),
324 m_MountCallbacks()
325#if THREADS
326 ,
327 m_CallbackLock(),
328 m_NextCallbackSequence(1),
329 m_pActiveCallbacks(nullptr),
330 m_CallbacksClosing(false)
331#endif
332{
333}
334
336#if THREADS
337 TerminationDeferral teardownDeferral;
338#endif
339#if THREADS
340 m_CallbackLock.acquire();
341 m_CallbacksClosing = true;
342 if (m_pActiveCallbacks) {
343 m_CallbackLock.release();
344 FATAL("VFS destroyed with an active callback invocation");
345 }
346 for (auto item : m_ProbeCallbacks) {
347 if (item->state.inFlight || item->state.removers) {
348 m_CallbackLock.release();
349 FATAL("VFS destroyed before probe callback retirement completed");
350 }
351 }
352 for (auto item : m_MountCallbacks) {
353 if (item->state.inFlight || item->state.removers) {
354 m_CallbackLock.release();
355 FATAL("VFS destroyed before mount callback retirement completed");
356 }
357 }
358 m_CallbackLock.release();
359#endif
360
361 // Wipe out probe callbacks we know about.
362 for (auto it = m_ProbeCallbacks.begin(); it != m_ProbeCallbacks.end(); ++it) {
363 delete *it;
364 }
365 for (auto it = m_MountCallbacks.begin(); it != m_MountCallbacks.end(); ++it) {
366 delete *it;
367 }
368
369 // The view's inert paths must drain before backend storage-pin retirement.
370 delete m_MountView;
371 m_MountView = nullptr;
372
373 Vector<MountInfo*> mountInfo;
374 Vector<Filesystem*> filesystems;
375 {
376 LockGuard<Mutex> mutationGuard(m_MountMutationLock);
377 LockGuard<Mutex> tableGuard(m_MountTableLock);
378 for (auto it = m_Mounts.begin(); it != m_Mounts.end(); ++it) {
379 it.value()->state->storagePins.close();
380 it.value()->state->operations.close();
381 mountInfo.pushBack(it.value());
382 filesystems.pushBack(it.key());
383 }
384 m_Mounts.clear();
385 m_pRootFilesystem = nullptr;
386 }
387
388 // Filesystem destructors can re-enter VFS, so publication locks must no
389 // longer be held when ownership is released.
390 for (auto info : mountInfo) {
391 info->state->retire();
392 delete info;
393 }
394 for (auto filesystem : filesystems) {
395 delete filesystem;
396 }
397
398 TrackedFile* remaining[TrackedFileBuckets] = {};
399 bool reclaim = false;
400 {
401 LockGuard<Mutex> guard(m_TrackedFilesLock);
402 for (size_t bucket = 0; bucket < TrackedFileBuckets; ++bucket) {
403 remaining[bucket] = m_TrackedFiles[bucket].exchange(nullptr);
404 reclaim |= remaining[bucket] != nullptr;
405 }
406 }
407 if (reclaim) {
408 Rcu::synchronize();
409 for (auto* entry : remaining) {
410 while (entry) {
411 TrackedFile* next = entry->next.loadForUpdate();
412 delete entry;
413 entry = next;
414 }
415 }
416 }
417}
418
419bool VFS::mount(Disk* pDisk, String& stableName, Filesystem** pMountedFs) {
420#if THREADS
421 TerminationDeferral dispatchDeferral;
422#endif
423 DiskUse diskUse;
424 if (pDisk && !pDisk->acquireUse(diskUse))
425 return false;
426#if THREADS
427 Thread* current = Processor::information().getCurrentThread();
428 void* owner =
429 current ? static_cast<void*>(current) : static_cast<void*>(&Processor::information());
430 size_t boundary = 0;
431 m_CallbackLock.acquire();
432 boundary = m_NextCallbackSequence;
433 m_CallbackLock.release();
434
435 size_t afterSequence = 0;
436 while (true) {
437 ActiveInvocation invocation = {nullptr, owner, nullptr};
438 ProbeCallbackItem* item = acquireProbeCallback(afterSequence, boundary, invocation);
439 if (!item) {
440 break;
441 }
442
443 Filesystem* pFs = item->callback(pDisk);
444 if (pFs) {
445 m_CallbackLock.acquire();
446 // This is the publication commit point. Retirement which closes the
447 // entry first rejects the result while the provider remains pinned;
448 // commit which wins keeps the pin through every use below.
449 const bool committed = item->state.enabled && !m_CallbacksClosing;
450 m_CallbackLock.release();
451 if (!committed) {
452 // The provider must still be pinned while its rejected result is
453 // destroyed; removal can only return after finishCallback below.
454 delete pFs;
455 finishCallback(&item->state, invocation);
456 continue;
457 }
458
459 pFs->m_DiskUse = pedigree_std::move(diskUse);
460
461 if (stableName.length() == 0) {
462 stableName = pFs->getVolumeLabel();
463 }
464 stableName = registerFilesystem(pFs, stableName);
465 if (!stableName.length()) {
466 delete pFs;
467 finishCallback(&item->state, invocation);
468 return false;
469 }
470 if (bootMountView()) {
471 String path;
472 if (!getMountPath(pFs, path) || !attachFilesystem(getRootFilesystem(), pFs, path)) {
474 finishCallback(&item->state, invocation);
475 return false;
476 }
477 }
478 dispatchMountCallbacks(owner);
479
480 if (pMountedFs) {
481 *pMountedFs = pFs;
482 }
483
484 NOTICE("mounted filesystem '" << stableName << "'");
485
486 finishCallback(&item->state, invocation);
487 return true;
488 }
489
490 finishCallback(&item->state, invocation);
491 }
492#else
493 for (List<ProbeCallbackItem*>::Iterator it = m_ProbeCallbacks.begin();
494 it != m_ProbeCallbacks.end(); it++) {
495 Filesystem* pFs = (*it)->callback(pDisk);
496 if (pFs) {
497 pFs->m_DiskUse = pedigree_std::move(diskUse);
498
499 if (stableName.length() == 0) {
500 stableName = pFs->getVolumeLabel();
501 }
502 stableName = registerFilesystem(pFs, stableName);
503
504 if (!stableName.length()) {
505 delete pFs;
506 return false;
507 }
508 if (bootMountView()) {
509 String path;
510 if (!getMountPath(pFs, path) || !attachFilesystem(getRootFilesystem(), pFs, path)) {
512 return false;
513 }
514 }
515
516 for (List<MountCallbackItem*>::Iterator it2 = m_MountCallbacks.begin();
517 it2 != m_MountCallbacks.end(); it2++) {
518 (*it2)->callback();
519 }
520
521 if (pMountedFs) {
522 *pMountedFs = pFs;
523 }
524
525 NOTICE("mounted filesystem '" << stableName << "'");
526
527 return true;
528 }
529 }
530#endif
531 return false;
532}
533
534String VFS::registerFilesystem(Filesystem* pFs, const String& preferredStableName) {
535 LockGuard<Mutex> mutationGuard(m_MountMutationLock);
536 return registerFilesystemLocked(pFs, preferredStableName);
537}
538
539String VFS::registerFilesystemLocked(Filesystem* pFs, const String& preferredStableName) {
540 if (!pFs) {
541 return String();
542 }
543 if (pFs->m_pDisk && !pFs->m_DiskUse && !pFs->m_pDisk->acquireUse(pFs->m_DiskUse))
544 return String();
545
546 MountInfo* info = nullptr;
547 Filesystem* root = nullptr;
548 {
549 LockGuard<Mutex> tableGuard(m_MountTableLock);
550 MountInfo* existing = m_Mounts.lookup(pFs);
551 if (existing) {
552 return existing->stableName;
553 }
554
555 String stableName = getUniqueStableNameLocked(preferredStableName);
557 path += "/media/";
558 path += stableName;
559 const uint32_t id = VfsMountState::reserveId();
560 if (!id)
561 return String();
563 if (!state)
564 return String();
565 info = new MountInfo(stableName, String(path), state);
566 if (!info || !m_Mounts.tryInsert(pFs, info)) {
567 delete info;
568 return String();
569 }
570 root = m_pRootFilesystem;
571 }
572
573 if (root && !bootMountView()) {
574 attachFilesystem(root, pFs, info->path);
575 }
576
577 return info->stableName;
578}
579
580bool VFS::unregisterFilesystem(Filesystem* pFs, bool canDelete, bool terminal) {
581#if THREADS
582 TerminationDeferral teardownDeferral;
583#endif
584 if (!pFs) {
585 return false;
586 }
587
588 MountInfo* info = nullptr;
589 bool detach = false;
590 {
591 LockGuard<Mutex> mutationGuard(m_MountMutationLock);
592 {
593 LockGuard<Mutex> tableGuard(m_MountTableLock);
594 info = m_Mounts.lookup(pFs);
595 if (!info || !info->state->storagePins.tryCloseIfIdle()) {
596 return false;
597 }
598 m_Mounts.take(pFs, info);
599 info->state->operations.close();
600
601 if (pFs == m_pRootFilesystem) {
602 m_pRootFilesystem = nullptr;
603 } else {
604 detach = m_pRootFilesystem != nullptr;
605 }
606 }
607
608 if (detach && !bootMountView()) {
609 Directory::ChildLease pointLease;
610 File* point = findRetained(info->path, pointLease);
611 if (point && point->isDirectory()) {
612 Directory::fromFile(point)->setReparsePoint(nullptr);
613 }
614 }
615 }
616
617 info->state->retire();
618 delete info;
619 if (terminal && pFs->shutdown() != Filesystem::SyncStatus::Success) {
620 ERROR("Filesystem shutdown failed: " << pFs->getVolumeLabel());
621 return false;
622 }
623 if (canDelete) {
624 delete pFs;
625 }
626 return true;
627}
628
630#if THREADS && !defined(VFS_STANDALONE)
631 TerminationDeferral lifetime;
632#endif
633 if (!filesystem)
634 return false;
635 MountInfo* removed = nullptr;
636 {
637 LockGuard<Mutex> mutation(m_MountMutationLock);
638 LockGuard<Mutex> table(m_MountTableLock);
639 removed = m_Mounts.lookup(filesystem);
640 if (!removed || filesystem == m_pRootFilesystem)
641 return false;
642 m_Mounts.take(filesystem, removed);
643 removed->state->storagePins.close();
644 removed->state->operations.close();
645 }
646 // Existing sync/handle operations retain the state until their admission
647 // drains. Inert identities survive retirement without retaining the backend.
648 auto state = removed->state;
649 delete removed;
650 VfsMountState* retained = state.get();
651 if (!retained) {
652 FATAL("Owned filesystem retirement lost its mount state.");
653 return false;
654 }
655 retained->ownedRetirement = true;
656 retained->finishOwnedRetirement();
657 return true;
658}
659
660bool VFS::removeDiskFilesystem(Filesystem* filesystem, bool deviceAvailable) {
661#if THREADS && !defined(VFS_STANDALONE)
662 TerminationDeferral lifetime;
663#endif
664 FilesystemPin pin;
665 if (!filesystem || !pinFilesystem(filesystem, pin)) {
666 return false;
667 }
668 if (filesystem == getRootFilesystem()) {
669 WARNING("VFS: root filesystem device removed");
670 return filesystem->deviceRemoved(deviceAvailable);
671 }
672 MountInfo* removed;
673 {
674 LockGuard<Mutex> mutation(m_MountMutationLock);
675 LockGuard<Mutex> table(m_MountTableLock);
676 removed = m_Mounts.lookup(filesystem);
677 if (!removed) {
678 return false;
679 }
680 m_Mounts.take(filesystem, removed);
681 removed->state->storagePins.close();
682 removed->state->operations.close();
683 }
684 auto state = removed->state;
685 VfsMountState* retained = state.get();
686 if (!retained) {
687 FATAL("Removed filesystem lost its mount state");
688 return false;
689 }
690 // Stop handle/sync users before detaching, including an attachment that
691 // acquired its mount admission just before the disk was withdrawn.
692 retained->operations.wait();
693 if (auto* view = bootMountView()) {
694 if (!view->detachBackingForRemoval(filesystem)) {
695 return false;
696 }
697 } else {
698 Directory::ChildLease pointLease;
699 File* point = findRetained(removed->path, pointLease);
700 if (point && point->isDirectory()) {
701 Directory::fromFile(point)->setReparsePoint(nullptr);
702 }
703 }
704 delete removed;
705 if (!filesystem->deviceRemoved(deviceAvailable)) {
706 return false;
707 }
708 retained->retirement.beginRetirement();
709 retained->retirement.finishRetirement();
710 retained->ownedRetirement = true;
711 pin.reset();
712 retained->finishOwnedRetirement();
713 return true;
714}
715
717 if (bootMountView()) {
718 SYSCALL_ERROR(DeviceBusy);
719 return false;
720 }
721 LockGuard<Mutex> mutationGuard(m_MountMutationLock);
722 if (pFs) {
723 bool registered = false;
724 {
725 LockGuard<Mutex> tableGuard(m_MountTableLock);
726 registered = m_Mounts.lookup(pFs) != nullptr;
727 }
728 if (!registered) {
729 if (!registerFilesystemLocked(pFs, pFs->getVolumeLabel()).length())
730 return false;
731 }
732 }
733
734 {
735 LockGuard<Mutex> tableGuard(m_MountTableLock);
736 m_pRootFilesystem = pFs;
737 }
738 attachRegisteredFilesystemsLocked();
739 return true;
740}
741
743 LockGuard<Mutex> tableGuard(m_MountTableLock);
744 return m_pRootFilesystem;
745}
746
747#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
748VFS::HostedRootViewScope::HostedRootViewScope() : m_Vfs(VFS::instance()) {}
749VFS::HostedRootViewScope::~HostedRootViewScope() {
750 if (!close())
751 FATAL("Hosted root view scope still has retained owners");
752}
753bool VFS::HostedRootViewScope::open(Filesystem* filesystem) {
754 if (!filesystem || m_Filesystem)
755 return false;
756 auto* thread = Processor::information().getCurrentThread();
757 m_PreviousContext = thread && thread->getParent()
758 ? thread->getParent()->acquireFilesystemContext()
760 bool prepared = false;
761 {
762 LockGuard<Mutex> mutation(m_Vfs.m_MountMutationLock);
763 {
764 LockGuard<Mutex> table(m_Vfs.m_MountTableLock);
765 if (m_Vfs.m_Mounts.lookup(filesystem))
766 return false;
767 m_PreviousRoot = m_Vfs.m_pRootFilesystem;
768 m_PreviousView = m_Vfs.bootMountView();
769 }
770 if (m_PreviousRoot && !m_Vfs.pinFilesystem(m_PreviousRoot, m_PreviousRootPin))
771 return false;
772 if (!m_Vfs.registerFilesystemLocked(filesystem, String("hosted-root-fixture")).length())
773 return false;
774 m_Filesystem = filesystem;
775 m_View = new VfsMountView(m_Vfs);
776 if (m_View && m_View->initialise(filesystem)) {
777 NamespaceMutation writer(m_Vfs);
778 LockGuard<Mutex> table(m_Vfs.m_MountTableLock);
779 m_Vfs.m_pRootFilesystem = filesystem;
780 __atomic_store_n(&m_Vfs.m_MountView, m_View, __ATOMIC_RELEASE);
781 m_Installed = prepared = true;
782 }
783 }
784 if (!prepared)
785 close();
786 return prepared;
787}
788bool VFS::HostedRootViewScope::installContext(Process& process) {
790 return m_Installed && m_View->createBootContext(context) &&
791 process.installFilesystemContext(pedigree_std::move(context));
792}
793bool VFS::HostedRootViewScope::close() {
794 {
795 LockGuard<Mutex> mutation(m_Vfs.m_MountMutationLock);
796 NamespaceMutation writer(m_Vfs);
797 // Enrollment cannot race the final quiescence check and view restoration.
798 if (m_View && !m_View->quiescentForHostedTest())
799 return false;
800 if (m_Installed) {
801 LockGuard<Mutex> table(m_Vfs.m_MountTableLock);
802 if (m_Vfs.bootMountView() != m_View || m_Vfs.m_pRootFilesystem != m_Filesystem)
803 return false;
804 m_Vfs.m_pRootFilesystem = m_PreviousRoot;
805 __atomic_store_n(&m_Vfs.m_MountView, m_PreviousView, __ATOMIC_RELEASE);
806 m_Installed = false;
807 }
808 }
809 delete m_View;
810 m_View = nullptr;
811 if (m_Filesystem && !m_Vfs.unregisterFilesystem(m_Filesystem, false))
812 return false;
813 m_Filesystem = nullptr;
814 m_PreviousContext.reset();
815 m_PreviousRootPin.reset();
816 m_PreviousRoot = nullptr;
817 m_PreviousView = nullptr;
818 return true;
819}
820#endif
821
822#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
823Filesystem* VFS::swapRootFilesystemForHostedTest(Filesystem* pFs) {
824 LockGuard<Mutex> mutationGuard(m_MountMutationLock);
825 LockGuard<Mutex> tableGuard(m_MountTableLock);
826 Filesystem* previous = m_pRootFilesystem;
827 m_pRootFilesystem = pFs;
828 return previous;
829}
830#endif
831
832bool VFS::getMountPath(Filesystem* pFs, String& path) const {
833 LockGuard<Mutex> tableGuard(m_MountTableLock);
834 MountInfo* info = m_Mounts.lookup(pFs);
835 if (!info) {
836 return false;
837 }
838
839 path = info->path;
840 return true;
841}
842
844 LockGuard<Mutex> tableGuard(m_MountTableLock);
845 for (MountTable::Iterator it = m_Mounts.begin(); it != m_Mounts.end(); ++it) {
846 if (it.value()->path == path) {
847 return it.key();
848 }
849 }
850
851 return nullptr;
852}
853
855 LockGuard<Mutex> mutationGuard(m_MountMutationLock);
856 getMountsLocked(mounts);
857}
858
859void VFS::getMountsLocked(Vector<MountSnapshot>& mounts) const {
860 struct MountSnapshotSource {
861 MountSnapshotSource() : filesystem(nullptr) {}
862 MountSnapshotSource(Filesystem* filesystem, const String& stableName, const String& path)
863 : filesystem(filesystem), stableName(stableName), path(path) {}
864
865 Filesystem* filesystem;
866 String stableName;
867 String path;
868 };
869
870 mounts.clear();
872 {
873 LockGuard<Mutex> tableGuard(m_MountTableLock);
874 for (MountTable::Iterator it = m_Mounts.begin(); it != m_Mounts.end(); ++it) {
875 sources.createBack(it.key(), it.value()->stableName, it.value()->path);
876 }
877 }
878
879 for (const auto& source : sources) {
880 bool hasDisk = false;
881 String diskParentName;
882 String diskName;
883 Disk* disk = source.filesystem->getDisk();
884 if (disk) {
885 hasDisk = true;
886 disk->getName(diskName);
887 if (disk->getParent()) {
888 disk->getParent()->getName(diskParentName);
889 }
890 }
891 mounts.createBack(source.stableName, source.path, hasDisk, diskParentName, diskName);
892 }
893}
894
895namespace {
896Filesystem::SyncStatus syncPinnedFilesystem(Filesystem* filesystem) {
897 Metrics::increment(Metrics::VfsSyncCalls);
898#if !defined(VFS_STANDALONE) && THREADS
899 Thread* thread = Processor::information().getCurrentThread();
900 const int previousError = thread ? thread->getErrno() : 0;
901 if (thread)
902 thread->setErrno(0);
903#endif
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);
909 }
910#if !defined(VFS_STANDALONE) && THREADS
911 if (thread)
912 thread->setErrno(previousError);
913#endif
914 return result;
915}
916} // namespace
917
918Filesystem::SyncStatus VFS::syncFilesystem(Filesystem* key) {
919#if !defined(VFS_STANDALONE) && THREADS
920 TerminationDeferral lifetime;
921#endif
922 if (!key)
923 return Filesystem::SyncStatus::Unsupported;
925 if (!pin)
926 return Filesystem::SyncStatus::NoMemory;
927 {
928 LockGuard<Mutex> guard(m_MountTableLock);
929 MountInfo* info = m_Mounts.lookup(key);
930 if (!info)
931 return Filesystem::SyncStatus::Unsupported;
932 if (!info->state->storagePins.tryAcquire(pin->admission))
933 return Filesystem::SyncStatus::IoError;
934 pin->state = info->state;
935 }
936 return syncPinnedFilesystem(pin->state->filesystem);
937}
938
939Filesystem::SyncStatus VFS::syncAll() {
940#if !defined(VFS_STANDALONE) && THREADS
941 TerminationDeferral lifetime;
942#endif
943 struct Snapshot {
944 ~Snapshot() {
945 delete[] pins;
946 }
947 VfsFilesystemPin* pins = nullptr;
948 size_t count = 0;
949 } snapshot;
950 for (;;) {
951 size_t capacity;
952 {
953 LockGuard<Mutex> guard(m_MountTableLock);
954 capacity = m_Mounts.count();
955 }
956 if (capacity > ~size_t(0) / sizeof(VfsFilesystemPin)) {
957 ERROR("VFS::syncAll: filesystem snapshot is too large");
958 return Filesystem::SyncStatus::NoMemory;
959 }
960 delete[] snapshot.pins;
961 snapshot.pins = capacity ? new VfsFilesystemPin[capacity] : nullptr;
962 if (capacity && !snapshot.pins) {
963 ERROR("VFS::syncAll: cannot allocate filesystem snapshot");
964 return Filesystem::SyncStatus::NoMemory;
965 }
966 {
967 LockGuard<Mutex> guard(m_MountTableLock);
968 // Registration may have grown the table while allocation was unlocked.
969 if (m_Mounts.count() > capacity)
970 continue;
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);
975 (void)admitted;
976 }
977 }
978 break;
979 }
980
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"
989 : "I/O error";
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))
994 firstError = status;
995 }
996 }
997 return firstError;
998}
999
1001 Vector<MountIdentity> mounts;
1002 {
1003 LockGuard<Mutex> guard(m_MountTableLock);
1004 for (auto it = m_Mounts.begin(); it != m_Mounts.end(); ++it) {
1006 identity.m_State = it.value()->state;
1007 mounts.pushBack(identity);
1008 }
1009 }
1010 for (const auto& mount : mounts) {
1011 FilesystemPin pin;
1012 if (!mount.pin(pin)) {
1013 return false;
1014 }
1015 File* root = pin.filesystem()->getRoot();
1016 if (root && root->isDirectory()) {
1018 }
1019 }
1020 return true;
1021}
1022
1023File* VFS::find(const String& path, File* pStartNode) {
1024 Metrics::increment(Metrics::VfsFindCalls);
1025 // NOTICE("find: " << path);
1026
1027 File* pResult = 0;
1028
1029 pStartNode = resolveStartNode(path, pStartNode);
1030 if (pStartNode) {
1031 pResult = pStartNode->getFilesystem()->find(path.view(), pStartNode);
1032 }
1033
1034 if (!pResult) {
1035 Metrics::increment(Metrics::VfsFindMisses);
1036 }
1037
1038 // NOTICE("find: " << path << " -> " << pResult);
1039 return pResult;
1040}
1041
1042File* VFS::findRetained(const String& path, Directory::ChildLease& result, File* pStartNode) {
1043 pStartNode = resolveStartNode(path, pStartNode);
1044 if (!pStartNode) {
1045 return nullptr;
1046 }
1047 return pStartNode->getFilesystem()->findRetained(path.view(), result, pStartNode);
1048}
1049
1051 if (!callback) {
1052 FATAL("VFS cannot register a null probe callback");
1053 }
1054
1055 ProbeCallbackItem* item = new ProbeCallbackItem(callback);
1056#if THREADS
1057 m_CallbackLock.acquire();
1058 if (m_CallbacksClosing) {
1059 m_CallbackLock.release();
1060 delete item;
1061 FATAL("VFS probe callback registered during teardown");
1062 }
1063 for (auto registered : m_ProbeCallbacks) {
1064 if (registered->callback == callback) {
1065 if (!registered->state.draining) {
1066 registered->state.enabled = true;
1067 }
1068 m_CallbackLock.release();
1069 delete item;
1070 return;
1071 }
1072 }
1073 if (m_NextCallbackSequence == static_cast<size_t>(-1)) {
1074 m_CallbackLock.release();
1075 delete item;
1076 FATAL("VFS callback sequence exhausted");
1077 }
1078 item->state.sequence = m_NextCallbackSequence++;
1079 item->state.debugAddress = reinterpret_cast<uintptr_t>(callback);
1080 m_ProbeCallbacks.pushBack(item);
1081 m_CallbackLock.release();
1082#else
1083 for (auto registered : m_ProbeCallbacks) {
1084 if (registered->callback == callback) {
1085 delete item;
1086 return;
1087 }
1088 }
1089 m_ProbeCallbacks.pushBack(item);
1090#endif
1091}
1092
1094#if THREADS
1095 if (!callback) {
1096 return false;
1097 }
1098
1099 TerminationDeferral removalDeferral;
1100 Thread* current = Processor::information().getCurrentThread();
1101 const bool canYield =
1102 current && Processor::executionContext() == ExecutionContext::WaitableThread;
1103 void* owner =
1104 current ? static_cast<void*>(current) : static_cast<void*>(&Processor::information());
1105 ProbeCallbackItem* item = nullptr;
1106 bool callbackContext = false;
1107
1108 m_CallbackLock.acquire();
1109 if (m_CallbacksClosing) {
1110 m_CallbackLock.release();
1111 return false;
1112 }
1113 for (auto it = m_ProbeCallbacks.begin(); it != m_ProbeCallbacks.end(); ++it) {
1114 if ((*it)->callback != callback) {
1115 continue;
1116 }
1117
1118 item = *it;
1119 item->state.enabled = false;
1120 callbackContext = isCallbackInvocation(owner);
1121 if (!callbackContext && canYield) {
1122 item->state.draining = true;
1123 ++item->state.removers;
1124 }
1125 break;
1126 }
1127 m_CallbackLock.release();
1128
1129 if (!item) {
1130 return false;
1131 }
1132 if (callbackContext || !canYield) {
1133 return false;
1134 }
1135
1136 drainProbeCallback(item);
1137 return true;
1138#else
1139 for (auto it = m_ProbeCallbacks.begin(); it != m_ProbeCallbacks.end(); ++it) {
1140 ProbeCallbackItem* item = *it;
1141 if (item->callback == callback) {
1142 m_ProbeCallbacks.erase(it);
1143 delete item;
1144 return true;
1145 }
1146 }
1147 return false;
1148#endif
1149}
1150
1151void VFS::addMountCallback(MountCallback callback) {
1152 if (!callback) {
1153 FATAL("VFS cannot register a null mount callback");
1154 }
1155
1156 MountCallbackItem* item = new MountCallbackItem(callback);
1157#if THREADS
1158 m_CallbackLock.acquire();
1159 if (m_CallbacksClosing) {
1160 m_CallbackLock.release();
1161 delete item;
1162 FATAL("VFS mount callback registered during teardown");
1163 }
1164 for (auto registered : m_MountCallbacks) {
1165 if (registered->callback == callback) {
1166 if (!registered->state.draining) {
1167 registered->state.enabled = true;
1168 }
1169 m_CallbackLock.release();
1170 delete item;
1171 return;
1172 }
1173 }
1174 if (m_NextCallbackSequence == static_cast<size_t>(-1)) {
1175 m_CallbackLock.release();
1176 delete item;
1177 FATAL("VFS callback sequence exhausted");
1178 }
1179 item->state.sequence = m_NextCallbackSequence++;
1180 item->state.debugAddress = reinterpret_cast<uintptr_t>(callback);
1181 m_MountCallbacks.pushBack(item);
1182 m_CallbackLock.release();
1183#else
1184 for (auto registered : m_MountCallbacks) {
1185 if (registered->callback == callback) {
1186 delete item;
1187 return;
1188 }
1189 }
1190 m_MountCallbacks.pushBack(item);
1191#endif
1192}
1193
1194bool VFS::removeMountCallback(MountCallback callback) {
1195#if THREADS
1196 if (!callback) {
1197 return false;
1198 }
1199
1200 TerminationDeferral removalDeferral;
1201 Thread* current = Processor::information().getCurrentThread();
1202 const bool canYield =
1203 current && Processor::executionContext() == ExecutionContext::WaitableThread;
1204 void* owner =
1205 current ? static_cast<void*>(current) : static_cast<void*>(&Processor::information());
1206 MountCallbackItem* item = nullptr;
1207 bool callbackContext = false;
1208
1209 m_CallbackLock.acquire();
1210 if (m_CallbacksClosing) {
1211 m_CallbackLock.release();
1212 return false;
1213 }
1214 for (auto it = m_MountCallbacks.begin(); it != m_MountCallbacks.end(); ++it) {
1215 if ((*it)->callback != callback) {
1216 continue;
1217 }
1218
1219 item = *it;
1220 item->state.enabled = false;
1221 callbackContext = isCallbackInvocation(owner);
1222 if (!callbackContext && canYield) {
1223 item->state.draining = true;
1224 ++item->state.removers;
1225 }
1226 break;
1227 }
1228 m_CallbackLock.release();
1229
1230 if (!item) {
1231 return false;
1232 }
1233 if (callbackContext || !canYield) {
1234 return false;
1235 }
1236
1237 drainMountCallback(item);
1238 return true;
1239#else
1240 for (auto it = m_MountCallbacks.begin(); it != m_MountCallbacks.end(); ++it) {
1241 MountCallbackItem* item = *it;
1242 if (item->callback == callback) {
1243 m_MountCallbacks.erase(it);
1244 delete item;
1245 return true;
1246 }
1247 }
1248 return false;
1249#endif
1250}
1251
1252#if THREADS
1253VFS::ProbeCallbackItem* VFS::acquireProbeCallback(size_t& afterSequence, size_t boundary,
1254 ActiveInvocation& invocation) {
1255 ProbeCallbackItem* item = nullptr;
1256 m_CallbackLock.acquire();
1257 if (!m_CallbacksClosing) {
1258 for (auto candidate : m_ProbeCallbacks) {
1259 if (candidate->state.sequence <= afterSequence) {
1260 continue;
1261 }
1262 if (candidate->state.sequence >= boundary) {
1263 break;
1264 }
1265
1266 afterSequence = candidate->state.sequence;
1267 if (!candidate->state.enabled) {
1268 continue;
1269 }
1270
1271 item = candidate;
1272 invocation.state = &item->state;
1273 ++item->state.inFlight;
1274 invocation.next = m_pActiveCallbacks;
1275 m_pActiveCallbacks = &invocation;
1276 break;
1277 }
1278 }
1279 m_CallbackLock.release();
1280 return item;
1281}
1282
1283VFS::MountCallbackItem* VFS::acquireMountCallback(size_t& afterSequence, size_t boundary,
1284 ActiveInvocation& invocation) {
1285 MountCallbackItem* item = nullptr;
1286 m_CallbackLock.acquire();
1287 if (!m_CallbacksClosing) {
1288 for (auto candidate : m_MountCallbacks) {
1289 if (candidate->state.sequence <= afterSequence) {
1290 continue;
1291 }
1292 if (candidate->state.sequence >= boundary) {
1293 break;
1294 }
1295
1296 afterSequence = candidate->state.sequence;
1297 if (!candidate->state.enabled) {
1298 continue;
1299 }
1300
1301 item = candidate;
1302 invocation.state = &item->state;
1303 ++item->state.inFlight;
1304 invocation.next = m_pActiveCallbacks;
1305 m_pActiveCallbacks = &invocation;
1306 break;
1307 }
1308 }
1309 m_CallbackLock.release();
1310 return item;
1311}
1312
1313void VFS::finishCallback(CallbackState* state, ActiveInvocation& invocation) {
1314 auto completionGuard = state->drainWaiters.acquire();
1315 bool wakeDrainers = false;
1316
1317 m_CallbackLock.acquire();
1318 ActiveInvocation** link = &m_pActiveCallbacks;
1319 while (*link && *link != &invocation) {
1320 link = &((*link)->next);
1321 }
1322 if (!*link) {
1323 m_CallbackLock.release();
1324 FATAL("VFS lost an active callback invocation");
1325 }
1326 *link = invocation.next;
1327
1328 if (!state->inFlight) {
1329 m_CallbackLock.release();
1330 FATAL("VFS callback pin underflow");
1331 }
1332 --state->inFlight;
1333 wakeDrainers = !state->inFlight && state->draining;
1334 m_CallbackLock.release();
1335
1336 if (wakeDrainers) {
1337 completionGuard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(state));
1338 }
1339}
1340
1341void VFS::drainProbeCallback(ProbeCallbackItem* item) {
1342 while (true) {
1343 bool complete = false;
1344 {
1345 auto waitGuard = item->state.drainWaiters.acquire();
1346 m_CallbackLock.acquire();
1347 if (!item->state.inFlight) {
1348 complete = true;
1349 m_CallbackLock.release();
1350 } else {
1351 m_CallbackLock.release();
1352 const WaitQueue::WakeReason reason = waitGuard.waitForCompletion(
1353 WaitQueue::Channel(&item->state), Thread::CallbackDrain, item->state.debugAddress);
1354 (void)reason;
1355 }
1356 }
1357 if (complete) {
1358 break;
1359 }
1360 }
1361
1362 bool deleteItem = false;
1363 m_CallbackLock.acquire();
1364 if (!item->state.removers) {
1365 m_CallbackLock.release();
1366 FATAL("VFS probe callback remover underflow");
1367 }
1368 --item->state.removers;
1369 if (!item->state.removers) {
1370 for (auto it = m_ProbeCallbacks.begin(); it != m_ProbeCallbacks.end(); ++it) {
1371 if (*it == item) {
1372 m_ProbeCallbacks.erase(it);
1373 deleteItem = true;
1374 break;
1375 }
1376 }
1377 }
1378 m_CallbackLock.release();
1379
1380 if (deleteItem) {
1381 delete item;
1382 }
1383}
1384
1385void VFS::drainMountCallback(MountCallbackItem* item) {
1386 while (true) {
1387 bool complete = false;
1388 {
1389 auto waitGuard = item->state.drainWaiters.acquire();
1390 m_CallbackLock.acquire();
1391 if (!item->state.inFlight) {
1392 complete = true;
1393 m_CallbackLock.release();
1394 } else {
1395 m_CallbackLock.release();
1396 const WaitQueue::WakeReason reason = waitGuard.waitForCompletion(
1397 WaitQueue::Channel(&item->state), Thread::CallbackDrain, item->state.debugAddress);
1398 (void)reason;
1399 }
1400 }
1401 if (complete) {
1402 break;
1403 }
1404 }
1405
1406 bool deleteItem = false;
1407 m_CallbackLock.acquire();
1408 if (!item->state.removers) {
1409 m_CallbackLock.release();
1410 FATAL("VFS mount callback remover underflow");
1411 }
1412 --item->state.removers;
1413 if (!item->state.removers) {
1414 for (auto it = m_MountCallbacks.begin(); it != m_MountCallbacks.end(); ++it) {
1415 if (*it == item) {
1416 m_MountCallbacks.erase(it);
1417 deleteItem = true;
1418 break;
1419 }
1420 }
1421 }
1422 m_CallbackLock.release();
1423
1424 if (deleteItem) {
1425 delete item;
1426 }
1427}
1428
1429void VFS::dispatchMountCallbacks(void* owner) {
1430 size_t boundary = 0;
1431 m_CallbackLock.acquire();
1432 boundary = m_NextCallbackSequence;
1433 m_CallbackLock.release();
1434
1435 size_t afterSequence = 0;
1436 while (true) {
1437 ActiveInvocation invocation = {nullptr, owner, nullptr};
1438 MountCallbackItem* item = acquireMountCallback(afterSequence, boundary, invocation);
1439 if (!item) {
1440 break;
1441 }
1442
1443 item->callback();
1444 finishCallback(&item->state, invocation);
1445 }
1446}
1447
1448bool VFS::isCallbackInvocation(void* owner) const {
1449 for (ActiveInvocation* invocation = m_pActiveCallbacks; invocation;
1450 invocation = invocation->next) {
1451 if (invocation->owner == owner) {
1452 return true;
1453 }
1454 }
1455 return false;
1456}
1457#endif
1458
1459bool VFS::createFile(const String& path, uint32_t mask, File* pStartNode) {
1460 pStartNode = resolveStartNode(path, pStartNode);
1461 return pStartNode && pStartNode->getFilesystem()->createFile(path, mask, pStartNode);
1462}
1463
1464bool VFS::createDirectory(const String& path, uint32_t mask, File* pStartNode) {
1465 pStartNode = resolveStartNode(path, pStartNode);
1466 if (!pStartNode) {
1467 NOTICE("no start node found");
1468 return false;
1469 }
1470
1471 return pStartNode->getFilesystem()->createDirectory(path, mask, pStartNode);
1472}
1473
1474bool VFS::createSymlink(const String& path, const String& value, File* pStartNode) {
1475 pStartNode = resolveStartNode(path, pStartNode);
1476 return pStartNode && pStartNode->getFilesystem()->createSymlink(path, value, pStartNode);
1477}
1478
1479bool VFS::createLink(const String& path, File* target, File* pStartNode) {
1480 pStartNode = resolveStartNode(path, pStartNode);
1481 return pStartNode && pStartNode->getFilesystem()->createLink(path, target, pStartNode);
1482}
1483
1484bool VFS::remove(const String& path, File* pStartNode) {
1485 return remove(path, pStartNode, nullptr);
1486}
1487
1488bool VFS::remove(const String& path, File* pStartNode, File* expected) {
1489 pStartNode = resolveStartNode(path, pStartNode);
1490 return pStartNode && pStartNode->getFilesystem()->remove(path, pStartNode, expected);
1491}
1492
1493bool VFS::rename(const String& oldPath, File* oldStart, const String& newPath, File* newStart,
1494 bool noReplace) {
1495 oldStart = resolveStartNode(oldPath, oldStart);
1496 newStart = resolveStartNode(newPath, newStart);
1497 return oldStart && newStart &&
1498 oldStart->getFilesystem()->rename(oldPath.view(), oldStart, newPath.view(), newStart,
1499 noReplace);
1500}
1501
1502bool VFS::checkAccess(File* pFile, bool bRead, bool bWrite, bool bExecute) {
1503#ifdef VFS_STANDALONE
1504 // We don't check permissions on standalone builds of the VFS.
1505 return true;
1506#else
1507 if (!pFile) {
1508 // The error for a null file is not EPERM or EACCESS.
1509 return true;
1510 }
1511
1512 FilesystemCredentials credentials;
1513 if (!Process::currentFilesystemCredentials(credentials)) {
1514 SYSCALL_ERROR(PermissionDenied);
1515 return false;
1516 }
1517 return checkAccess(pFile, bRead, bWrite, bExecute, credentials);
1518#endif
1519}
1520
1521bool VFS::checkAccess(File* file, bool read, bool write, bool execute, int64_t uid, int64_t gid,
1522 const Vector<int64_t>& groups) {
1523 FilesystemCredentials 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];
1534 }
1535 }
1536 return checkAccess(file, read, write, execute, credentials);
1537}
1538
1539bool VFS::checkAccess(File* pFile, bool bRead, bool bWrite, bool bExecute,
1540 const FilesystemCredentials& credentials) {
1541#ifdef VFS_STANDALONE
1542 return true;
1543#else
1544 if (!pFile)
1545 return true;
1546 if (!credentials.valid) {
1547 SYSCALL_ERROR(PermissionDenied);
1548 return false;
1549 }
1550 const uint32_t processUid = credentials.uid;
1551 const uint32_t processGid = credentials.gid;
1552 uint32_t check = 0;
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);
1558
1559 const bool dacOverride = credentials.enforceCapabilities
1560 ? (credentials.capabilities & (uint64_t(1) << 1))
1561 : processUid == 0;
1562 const bool dacReadSearch =
1563 credentials.enforceCapabilities && (credentials.capabilities & (uint64_t(1) << 2));
1564 if (dacOverride) {
1565 if (!bExecute || pFile->isDirectory() || (permissions & (FILE_UX | FILE_GX | FILE_OX))) {
1566 return true;
1567 }
1568 }
1569 if (dacReadSearch && !bWrite && (!bExecute || pFile->isDirectory())) {
1570 return true;
1571 }
1572 if (fuid == processUid) {
1573 check = (permissions >> FILE_UBITS) & 0x7;
1574 } else {
1575 bool inFileGroup = fgid == processGid;
1576
1577 if (!inFileGroup) {
1578 for (size_t i = 0; i < credentials.groupCount; ++i) {
1579 if (credentials.groups[i] == fgid) {
1580 inFileGroup = true;
1581 break;
1582 }
1583 }
1584 }
1585
1586 check = (permissions >> (inFileGroup ? FILE_GBITS : FILE_OBITS)) & 0x7;
1587 }
1588
1589 if ((check & needed) != needed) {
1590 SYSCALL_ERROR(PermissionDenied);
1591 return false;
1592 }
1593
1594 return true;
1595#endif
1596}
1597
1598RcuPointer<VFS::TrackedFile>& VFS::trackedFileBucket(File* file) {
1599 uintptr_t key = reinterpret_cast<uintptr_t>(file) >> 4;
1600 key ^= key >> 8;
1601 key ^= key >> 16;
1602 return m_TrackedFiles[key % TrackedFileBuckets];
1603}
1604
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();
1611 }
1612 }
1613
1614 auto* entry = new TrackedFile(file);
1615 if (!entry) {
1616 return false;
1617 }
1618 if (first) {
1619 entry->next.exchange(first);
1620 }
1621 bucket.exchange(entry);
1622 return true;
1623}
1624
1625void VFS::trackFile(File* pFile) {
1626 LockGuard<Mutex> guard(m_TrackedFilesLock);
1627 if (!trackFileLocked(pFile)) {
1628 FATAL("VFS could not track a File");
1629 }
1630}
1631
1633 if (!file) {
1634 return false;
1635 }
1636 LockGuard<Mutex> guard(m_TrackedFilesLock);
1637 return trackFileLocked(file);
1638}
1639
1641 if (!pFile) {
1642 return false;
1643 }
1644
1645 bool retained = false;
1646 {
1647 RcuReadGuard guard;
1648 for (auto* entry = trackedFileBucket(pFile).load(guard); entry;
1649 entry = entry->next.load(guard)) {
1650 if (entry->file == pFile) {
1651 retained = entry->retain();
1652 break;
1653 }
1654 }
1655 }
1656 if (!retained) {
1657 return false;
1658 }
1659
1660#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1661 RetainTrackedFileHook hook = __atomic_load_n(&m_RetainTrackedFileHook, __ATOMIC_ACQUIRE);
1662 if (hook) {
1663 hook(pFile);
1664 }
1665#endif
1666
1667 return true;
1668}
1669
1670bool VFS::untrackFile(File* pFile, bool destroy) {
1671 {
1672 RcuReadGuard guard;
1673 TrackedFile* entry = trackedFileBucket(pFile).load(guard);
1674 while (entry && entry->file != pFile) {
1675 entry = entry->next.load(guard);
1676 }
1677 if (!entry) {
1678 return false;
1679 }
1680 size_t count = __atomic_load_n(&entry->owners, __ATOMIC_RELAXED);
1681 while (count > 1) {
1682 if (__atomic_compare_exchange_n(&entry->owners, &count, count - 1, false, __ATOMIC_ACQ_REL,
1683 __ATOMIC_RELAXED)) {
1684 return false;
1685 }
1686 }
1687 if (!count) {
1688 return false;
1689 }
1690 }
1691
1692 TrackedFile* retiring = nullptr;
1693 {
1694 LockGuard<Mutex> guard(m_TrackedFilesLock);
1695 auto* link = &trackedFileBucket(pFile);
1696 TrackedFile* entry = link->loadForUpdate();
1697 while (entry && entry->file != pFile) {
1698 link = &entry->next;
1699 entry = link->loadForUpdate();
1700 }
1701 if (!entry) {
1702 return false;
1703 }
1704
1705 size_t count = __atomic_load_n(&entry->owners, __ATOMIC_RELAXED);
1706 while (count) {
1707 if (__atomic_compare_exchange_n(&entry->owners, &count, count - 1, false, __ATOMIC_ACQ_REL,
1708 __ATOMIC_RELAXED)) {
1709 if (count > 1) {
1710 return false;
1711 }
1712 link->exchange(entry->next.loadForUpdate());
1713 retiring = entry;
1714 break;
1715 }
1716 }
1717 }
1718
1719 if (!retiring) {
1720 return false;
1721 }
1722 // Zero closes retain admission. Reclaim the record before destroying the
1723 // File, whose destructor may recursively release a tracked parent.
1724 Rcu::synchronize();
1725 delete retiring;
1726 if (destroy) {
1727 delete pFile;
1728 }
1729 return true;
1730}
1731
1732#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1733void VFS::setRetainTrackedFileHookForHostedTest(RetainTrackedFileHook hook) {
1734 __atomic_store_n(&m_RetainTrackedFileHook, hook, __ATOMIC_RELEASE);
1735}
1736#endif
1737
1738String VFS::getUniqueStableNameLocked(const String& preferredName) const {
1739 NormalStaticString safeName;
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 : '-');
1745 }
1746
1747 String base(safeName, safeName.length());
1748 if (!base.length() || base == "." || base == "..") {
1749 base.assign("filesystem");
1750 }
1751
1752 size_t suffix = 1;
1753 while (true) {
1754 NormalStaticString candidateBuffer;
1755 candidateBuffer += base;
1756 if (suffix > 1) {
1757 candidateBuffer += "-";
1758 candidateBuffer.append(suffix);
1759 }
1760 String candidate(candidateBuffer, candidateBuffer.length());
1761
1762 bool exists = false;
1763 for (MountTable::Iterator it = m_Mounts.begin(); it != m_Mounts.end(); ++it) {
1764 if (it.value()->stableName == candidate) {
1765 exists = true;
1766 break;
1767 }
1768 }
1769
1770 if (!exists) {
1771 return candidate;
1772 }
1773
1774 ++suffix;
1775 }
1776}
1777
1778File* VFS::resolveStartNode(const String& path, File* pStartNode) {
1779 if (!path.length() || path[0] != '/') {
1780 return pStartNode;
1781 }
1782
1783 Filesystem* root = nullptr;
1784 {
1785 LockGuard<Mutex> tableGuard(m_MountTableLock);
1786 root = m_pRootFilesystem;
1787 }
1788 return root ? root->getRoot() : nullptr;
1789}
1790
1791bool VFS::attachFilesystem(Filesystem* pRootFs, Filesystem* pFs, const String& path) {
1792 if (!pRootFs || !pFs || pFs == pRootFs) {
1793 return pFs == pRootFs;
1794 }
1795
1796 File* filesystemRoot = pFs->getRoot();
1797 if (!filesystemRoot || !filesystemRoot->isDirectory()) {
1798 ERROR("VFS: filesystem at " << path << " has no valid root directory");
1799 return false;
1800 }
1801
1802 Directory::ChildLease mediaLease;
1803 if (auto* view = bootMountView()) {
1804 FilesystemContextOwner bootstrap;
1805 if (!view->createBootContext(bootstrap)) {
1806 return false;
1807 }
1808 auto context = bootstrap.reference();
1810 options.requireDirectory = true;
1811 FilesystemPathRef attached;
1812 // Startup may import this registration before mount() resumes attaching it.
1813 if (view->resolve(context, FilesystemPathRef(), path, options, attached) &&
1814 attached->node() == filesystemRoot) {
1815 return true;
1816 }
1817 attached.reset();
1818 options.crossFinalMount = false;
1819 FilesystemPathRef media, point;
1820 if (!view->resolve(context, FilesystemPathRef(), String("/media"), options, media)) {
1821 FilesystemPathRef root;
1822 if (!view->bootRootPath(root) || !view->createDirectory(root, String("media"), 0755) ||
1823 !view->resolve(context, FilesystemPathRef(), String("/media"), options, media)) {
1824 return false;
1825 }
1826 }
1827 if (!view->resolve(context, FilesystemPathRef(), path, options, point)) {
1828 String name = path.view().substring(7, path.length()).toString();
1829 if (!view->createDirectory(media, name, 0755) ||
1830 !view->resolve(context, FilesystemPathRef(), path, options, point)) {
1831 return false;
1832 }
1833 }
1834 return view->attach(context, point, pFs);
1835 }
1836 if (!findRetained(String("/media"), mediaLease)) {
1837 createDirectory(String("/media"), 0755);
1838 }
1839
1840 Directory::ChildLease pointLease;
1841 File* point = findRetained(path, pointLease);
1842 if (!point) {
1843 createDirectory(path, 0755);
1844 point = findRetained(path, pointLease);
1845 }
1846
1847 if (!point || !point->isDirectory()) {
1848 ERROR("VFS: cannot attach filesystem at " << path);
1849 return false;
1850 }
1851
1853 NOTICE("VFS: attached filesystem at " << path);
1854 return true;
1855}
1856
1857void VFS::attachRegisteredFilesystemsLocked() {
1858 struct AttachWork {
1859 AttachWork() : filesystem(nullptr) {}
1860 AttachWork(Filesystem* filesystem, const String& path) : filesystem(filesystem), path(path) {}
1861
1862 Filesystem* filesystem;
1863 String path;
1864 };
1865
1866 Filesystem* root = nullptr;
1867 Vector<AttachWork> work;
1868 {
1869 LockGuard<Mutex> tableGuard(m_MountTableLock);
1870 root = m_pRootFilesystem;
1871 if (!root) {
1872 return;
1873 }
1874
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("/");
1879 continue;
1880 }
1881
1882 NormalStaticString path;
1883 path += "/media/";
1884 path += info->stableName;
1885 info->path.assign(path, path.length());
1886 work.createBack(it.key(), info->path);
1887 }
1888 }
1889
1890 for (const auto& item : work) {
1891 attachFilesystem(root, item.filesystem, item.path);
1892 }
1893}
1894
1895Module::UnloadAdmission VFS::unloadAdmission(bool terminal) {
1896 // Mapping/pressure callbacks remain registered until singleton destruction.
1897 // destroyVFS does not close that global service or its surviving clients.
1898 return terminal ? Module::UnloadAdmission::KeepMapped : Module::UnloadAdmission::Busy;
1899}
1900
1901#ifndef VFS_STANDALONE
1902static bool initVFS() {
1903 SwapStore::instance().prepareAtBoot();
1904 AnonymousMemoryMap prepareZeroPage(0, 0, MemoryMappedObject::None);
1905 return KernelElf::instance().registerUnloadAdmission(&initVFS, &VFS::unloadAdmission);
1906}
1907
1908static void destroyVFS() {}
1909
1910MODULE_INFO("vfs", &initVFS, &destroyVFS);
1911#endif
1912
1913bool VFS::diskMount(uint32_t id, MountIdentity& identity) const {
1914 identity = MountIdentity();
1915 if (!id || id > 0xfffff)
1916 return false;
1917 LockGuard<Mutex> guard(m_MountTableLock);
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()) {
1921 identity.m_State = state;
1922 return true;
1923 }
1924 }
1925 return false;
1926}
1927bool VFS::snapshotDiskMounts(Vector<MountIdentity>& mounts) const {
1928 mounts.clear();
1929 LockGuard<Mutex> guard(m_MountTableLock);
1930 if (!mounts.tryReserve(m_Mounts.count()))
1931 return false;
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())
1935 continue;
1936 MountIdentity identity;
1937 identity.m_State = state;
1938 mounts.pushBack(identity);
1939 }
1940 return true;
1941}
Memory-mapped file interface.
virtual void getName(String &str)
Definition Device.cc:130
Device * getParent() const
Definition Device.h:166
static Directory * fromFile(File *pF)
Definition Directory.h:151
void removeEphemeralFiles()
Definition Directory.cc:961
void setReparsePoint(Directory *pTarget)
Definition Directory.cc:920
Definition Disk.h:35
virtual void getName(String &str)
Definition Disk.cc:155
Definition File.h:75
virtual bool isDirectory()
Definition File.cc:804
File * findRetained(const StringView &path, Directory::ChildLease &result, File *pStartNode=nullptr)
Disk * m_pDisk
Definition Filesystem.h:188
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 *)
Definition Filesystem.h:70
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)
Definition Filesystem.cc:92
void finishRetirement()
Definition FileEvent.cc:338
bool registerUnloadAdmission(ModuleEntry ownerEntry, Module::UnloadAdmissionHook hook)
static KernelElf & instance()
Definition KernelElf.h:135
::Iterator< T, node_t > Iterator
Definition List.h:67
MUST_USE_RESULT bool installFilesystemContext(FilesystemContextOwner &&context)
Definition Process.cc:734
static ProcessorInformation & information()
static ExecutionContext executionContext()
Definition Processor.cc:109
static SharedPointer< T > tryAllocate(Args...)
void release()
Definition Spinlock.cc:168
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
StringView substring(size_t start, size_t end, bool hashed=HASH_STRINGVIEWS_BY_DEFAULT) const
StringView view() const
Definition String.cc:783
void setErrno(size_t err)
Definition Thread.h:482
size_t getErrno()
Definition Thread.h:477
An iterator applicable for many data structures.
Definition Iterator.h:147
bool tryInsert(const K &key, const E &value)
Definition Tree.h:169
Iterator begin()
Definition Tree.h:402
E lookup(const K &key) const
Definition Tree.h:193
void clear()
Definition Tree.h:383
Iterator end()
Definition Tree.h:427
bool take(const K &key, E &element)
Definition Tree.h:363
size_t count() const
Definition Tree.h:142
Definition VFS.h:58
bool createSymlink(const String &path, const String &value, File *pStartNode=0)
Definition VFS.cc:1474
bool removeEphemeralFiles()
Definition VFS.cc:1000
VFS()
Definition VFS.cc:315
bool removeMountCallback(MountCallback callback)
Definition VFS.cc:1194
bool removeProbeCallback(Filesystem::ProbeCallback callback)
Definition VFS.cc:1093
bool createFile(const String &path, uint32_t mask, File *pStartNode=0)
Definition VFS.cc:1459
void addProbeCallback(Filesystem::ProbeCallback callback)
Definition VFS.cc:1050
bool tryTrackFile(File *pFile)
Definition VFS.cc:1632
bool acquireMount(Filesystem *key, MountOperation &operation) const
Definition VFS.cc:290
static VFS m_Instance
Definition VFS.h:423
Filesystem * getFilesystemAt(const String &path) const
Definition VFS.cc:843
bool mount(Disk *pDisk, String &stableName, Filesystem **pMountedFs=nullptr)
Definition VFS.cc:419
bool unregisterFilesystem(Filesystem *pFs, bool canDelete=true, bool terminal=false)
Definition VFS.cc:580
File * findRetained(const String &path, Directory::ChildLease &result, File *pStartNode=nullptr)
Definition VFS.cc:1042
void getMounts(Vector< MountSnapshot > &mounts) const
Definition VFS.cc:854
bool setRootFilesystem(Filesystem *pFs)
Definition VFS.cc:716
File * find(const String &path, File *pStartNode=0)
Definition VFS.cc:1023
bool retireOwnedFilesystem(Filesystem *filesystem)
Definition VFS.cc:629
MUST_USE_RESULT bool retainTrackedFile(File *pFile)
Definition VFS.cc:1640
bool getMountPath(Filesystem *pFs, String &path) const
Definition VFS.cc:832
bool untrackFile(File *pFile, bool destroy=true)
Definition VFS.cc:1670
Filesystem::SyncStatus syncFilesystem(Filesystem *key)
Definition VFS.cc:918
bool removeDiskFilesystem(Filesystem *filesystem, bool deviceAvailable=false)
Definition VFS.cc:660
Filesystem::SyncStatus syncAll()
Definition VFS.cc:939
String registerFilesystem(Filesystem *pFs, const String &preferredStableName)
Definition VFS.cc:534
static bool checkAccess(File *pFile, bool bRead, bool bWrite, bool bExecute)
Definition VFS.cc:1502
Filesystem * getRootFilesystem() const
Definition VFS.cc:742
bool createLink(const String &path, File *target, File *pStartNode=0)
Definition VFS.cc:1479
static VFS & instance()
Definition VFS.cc:311
bool createDirectory(const String &path, uint32_t mask, File *pStartNode=0)
Definition VFS.cc:1464
bool remove(const String &path, File *pStartNode=0)
Definition VFS.cc:1484
void addMountCallback(MountCallback callback)
Definition VFS.cc:1151
void trackFile(File *pFile)
Track a File object that exists. It is necessary to keep track of File objects, or at least those tha...
Definition VFS.cc:1625
~VFS()
Definition VFS.cc:335
A vector / dynamic array.
Definition Vector.h:33
void createBack(Args &&... args)
Definition Vector.h:110
@ Dec
Definition Log.h:126
void pushBack(const T &value)
Definition Vector.h:275
void clear(bool freeMem=false)
Definition Vector.h:378
size_t count() const
Definition Vector.h:270