20#include "Filesystem.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/process/Process.h"
24#include "pedigree/kernel/process/TerminationDeferral.h"
25#include "pedigree/kernel/process/Thread.h"
26#include "pedigree/kernel/processor/Processor.h"
27#include "pedigree/kernel/processor/ProcessorInformation.h"
28#include "pedigree/kernel/syscallError.h"
29#include "pedigree/kernel/utilities/LazyEvaluate.h"
30#include "pedigree/kernel/utilities/StringView.h"
31#include "pedigree/kernel/utilities/utility.h"
52 Thread* owner = m_RemovalOwner;
55 return m_RemovalWrites;
59bool Filesystem::isDeviceRemoved()
const {
60 if (m_Operations.isOpen()) {
64 Thread* owner = m_RemovalOwner;
67 return !m_RemovalWrites;
71bool Filesystem::beginDeviceRemoval(
bool deviceAvailable) {
74 if (!deviceAvailable) {
82 m_RemovalWrites =
true;
84 const SyncStatus status =
shutdown();
85 m_RemovalOwner =
nullptr;
87 m_RemovalWrites =
false;
89 return status == SyncStatus::Success;
93 if (!beginDeviceRemoval(deviceAvailable)) {
102 return m_bReadOnly ? SyncStatus::Success : SyncStatus::Unsupported;
112 return FileHandleStatus::Unsupported;
117 return FileHandleStatus::Unsupported;
120FileHandleStatus Filesystem::fileHandleFsid(
FileSystemId&
id) {
122 return FileHandleStatus::Unsupported;
126class InodeRetirementDrain {
128 ~InodeRetirementDrain() {
130 m_File.get()->finishInodeRetirement();
132 bool retain(
File* file) {
136 SYSCALL_ERROR(IoError);
149 explicit TrueRootLease(
Filesystem* filesystem) : m_pRoot(filesystem->getRoot()) {}
158bool targetAbsentForCreate(
File* parent,
const String& filename) {
159 if (!filename.length() || filename ==
"." || filename ==
"..") {
160 SYSCALL_ERROR(InvalidArgument);
164 SYSCALL_ERROR(NotADirectory);
169 const Directory::LookupStatus status =
171 if (status == Directory::LookupStatus::NotFound) {
174 if (status == Directory::LookupStatus::IoError) {
175 SYSCALL_ERROR(IoError);
177 SYSCALL_ERROR(FileExists);
192 assert(pStartNode !=
nullptr);
198 return find(path.
view(), pStartNode);
203 TrueRootLease rootLease(
this);
204 File* trueRoot = rootLease.get();
206 pStartNode = trueRoot;
209 File* retained =
nullptr;
210 File* found =
findNode(pStartNode, path, pStartNode, trueRoot, &retained);
217 replacement.adopt(retained);
219 result.swap(replacement);
225 if (!tryAcquireOperation(operation)) {
228 TrueRootLease startLease(
this);
230 pStartNode = startLease.get();
235 File* retainedParent =
nullptr;
236 File* pParent =
findParent(path, pStartNode, filename, &retainedParent);
238 parentLease.adopt(retainedParent);
242 SYSCALL_ERROR(DoesNotExist);
246 if (!targetAbsentForCreate(pParent, filename))
259 return pFs->
createFile(pParent, filename, mask);
264 if (!tryAcquireOperation(operation)) {
267 TrueRootLease startLease(
this);
269 pStartNode = startLease.get();
274 File* retainedParent =
nullptr;
275 File* pParent =
findParent(path, pStartNode, filename, &retainedParent);
277 parentLease.adopt(retainedParent);
281 SYSCALL_ERROR(DoesNotExist);
285 if (!targetAbsentForCreate(pParent, filename))
303 if (!tryAcquireOperation(operation)) {
306 TrueRootLease startLease(
this);
308 pStartNode = startLease.get();
313 File* retainedParent =
nullptr;
314 File* pParent =
findParent(path, pStartNode, filename, &retainedParent);
316 parentLease.adopt(retainedParent);
320 SYSCALL_ERROR(DoesNotExist);
324 if (!targetAbsentForCreate(pParent, filename))
342 if (!tryAcquireOperation(operation)) {
345 TrueRootLease startLease(
this);
347 pStartNode = startLease.get();
352 File* retainedParent =
nullptr;
353 File* pParent =
findParent(path, pStartNode, filename, &retainedParent);
355 parentLease.adopt(retainedParent);
359 SYSCALL_ERROR(DoesNotExist);
363 if (!targetAbsentForCreate(pParent, filename))
372 if (
this != target->getFilesystem()) {
373 SYSCALL_ERROR(CrossDeviceLink);
382 return pFs->
createLink(pParent, filename, target);
387 if (!tryAcquireOperation(operation)) {
390 return remove(path, pStartNode,
nullptr);
395 if (!tryAcquireOperation(operation)) {
398 TrueRootLease startLease(
this);
400 pStartNode = startLease.get();
405 File* retainedParent =
nullptr;
406 File* pParent =
findParent(path, pStartNode, filename, &retainedParent);
408 parentLease.adopt(retainedParent);
412 SYSCALL_ERROR(DoesNotExist);
419 if (!filename.length() || filename ==
"." || filename ==
"..") {
420 SYSCALL_ERROR(InvalidArgument);
432 return pFs->
removeChild(pParent, filename, expected);
437 if (!tryAcquireOperation(operation)) {
441 SYSCALL_ERROR(DoesNotExist);
448 File* newStart,
bool noReplace) {
450 if (!tryAcquireOperation(operation)) {
453 if (!oldPath.length() || !newPath.length()) {
454 SYSCALL_ERROR(DoesNotExist);
457 TrueRootLease rootLease(
this);
459 oldStart = rootLease.get();
462 newStart = rootLease.get();
464 if (!oldStart || !newStart) {
465 SYSCALL_ERROR(DoesNotExist);
473 File* retained =
nullptr;
474 File* oldParentFile =
475 oldStart->getFilesystem()->
findParent(oldPath, oldStart, oldName, &retained);
477 oldParentLease.adopt(retained);
480 File* newParentFile =
481 newStart->getFilesystem()->
findParent(newPath, newStart, newName, &retained);
483 newParentLease.adopt(retained);
485 return renameChildren(
486 oldParentFile, oldName, newParentFile, newName, noReplace,
487 oldPath[oldPath.length() - 1] ==
'/' || newPath[newPath.length() - 1] ==
'/');
490bool Filesystem::renameChildren(
File* oldParentFile,
const String& oldName,
File* newParentFile,
491 const String& newName,
bool noReplace,
bool sourceMustBeDirectory,
494 InodeRetirementDrain retirement;
496 if (!oldParentFile || !newParentFile) {
497 SYSCALL_ERROR(DoesNotExist);
501 SYSCALL_ERROR(NotADirectory);
504 if (!oldName.length() || !newName.length() || oldName ==
"." || oldName ==
".." ||
505 newName ==
"." || newName ==
"..") {
506 SYSCALL_ERROR(InvalidArgument);
509 Filesystem* filesystem = oldParentFile->getFilesystem();
510 if (filesystem != newParentFile->getFilesystem()) {
511 SYSCALL_ERROR(CrossDeviceLink);
516 SYSCALL_ERROR(ReadOnlyFilesystem);
526 const bool oldFirst =
527 reinterpret_cast<uintptr_t
>(oldParent) <
reinterpret_cast<uintptr_t
>(newParent);
528 Directory* first = oldFirst ? oldParent : newParent;
529 Directory* second = oldFirst ? newParent : oldParent;
533 SYSCALL_ERROR(DoesNotExist);
540 if (sourceStatus != Directory::LookupStatus::Found) {
541 syscallError(sourceStatus == Directory::LookupStatus::IoError ? Error::IoError
542 : Error::DoesNotExist);
545 File* source = sourceLease.get();
546 if (oldParent == newParent && oldName == newName) {
548 SYSCALL_ERROR(FileExists);
554 if (replacedStatus == Directory::LookupStatus::IoError) {
555 SYSCALL_ERROR(IoError);
558 File* replaced = replacedLease.get();
559 if (oldPath && newPath &&
560 !VfsMountView::fromPath(*oldPath)->authorizeRename(*oldPath, source, *newPath, replaced,
566 if (noReplace && replaced) {
567 SYSCALL_ERROR(FileExists);
570 if (source->getFilesystem() != filesystem ||
571 (replaced && replaced->getFilesystem() != filesystem)) {
572 SYSCALL_ERROR(CrossDeviceLink);
576 (source == replaced || (source->getInode() && source->getInode() == replaced->getInode()))) {
579 if (sourceMustBeDirectory && !source->
isDirectory()) {
580 SYSCALL_ERROR(NotADirectory);
584 syscallError(replaced->
isDirectory() ? Error::IsADirectory : Error::NotADirectory);
590 auto* view = oldPath ? VfsMountView::fromPath(*oldPath) :
VFS::instance().mountView();
591 if ((view && (view->isMountpoint(source) || (replaced && view->isMountpoint(replaced)))) ||
594 SYSCALL_ERROR(DeviceBusy);
597 if (sourceDirectory) {
598 File* ancestor = newParent;
601 if (ancestor == source) {
602 SYSCALL_ERROR(InvalidArgument);
608 ancestorLease.swap(next);
609 ancestor = ancestorLease.get();
612 if (replacedDirectory == oldParent || replacedDirectory == newParent) {
613 SYSCALL_ERROR(NotEmpty);
618 : oldParent->namespaceMutationLock(),
619 sourceDirectory != nullptr);
621 : oldParent->namespaceMutationLock(),
622 replacedDirectory != nullptr);
623 if (replacedDirectory) {
625 if (replacedDirectory->
isEmpty(empty) != Directory::ReadStatus::Complete) {
626 SYSCALL_ERROR(IoError);
630 SYSCALL_ERROR(NotEmpty);
639 SYSCALL_ERROR(DoesNotExist);
642 bool sourceEphemeral =
false;
643 bool replacedEphemeral =
false;
652 if (sourceEphemeral) {
653 if (newName.length() > 255) {
654 SYSCALL_ERROR(NameTooLong);
657 if (sourceDirectory) {
658 SYSCALL_ERROR(OperationNotSupported);
663 if (replaced && !replacedEphemeral &&
664 (!retirement.retain(replaced) || !filesystem->
removeNode(newParent, newName, replaced))) {
667 }
else if (!retirement.retain(replacedEphemeral ?
nullptr : replaced) ||
668 !filesystem->
renameNode(oldParent, oldName, source, newParent, newName,
669 replacedEphemeral ? nullptr : replaced)) {
674 if (replacedDirectory) {
675 replacedDirectory->markDetached();
678 source->moveNamespace(newName, newParent);
679 if (sourceDirectory) {
680 __atomic_store_n(&sourceDirectory->
m_ParentInode, newParent->getInode(), __ATOMIC_RELEASE);
682 oldParent->moveReservedEntry(oldReservation, newParent, newReservation, source);
683 oldReservation.complete(Directory::LookupStatus::NotFound);
684 newReservation.complete(Directory::LookupStatus::Found);
692 SYSCALL_ERROR(OperationNotSupported);
698 InodeRetirementDrain retirement;
702 SYSCALL_ERROR(NotADirectory);
705 if (!filename.length() || filename ==
"." || filename ==
"..") {
706 SYSCALL_ERROR(InvalidArgument);
713 auto publishRemoval = [&](
File* target) {
716 target->retainDetachedParent();
717 directory->
publishEvent(FileEvents::Removed, filename.
view(), target->isDirectory());
721 target->publishEvent(FileEvents::DeletedSelf);
726 if (lookup != Directory::LookupStatus::Found) {
727 if (lookup == Directory::LookupStatus::IoError) {
728 SYSCALL_ERROR(IoError);
730 SYSCALL_ERROR(DoesNotExist);
734 if (expected && target.get() != expected) {
735 SYSCALL_ERROR(DoesNotExist);
739 if (parentPath && !VfsMountView::fromPath(*parentPath)
740 ->authorizeRemove(*parentPath, target.get(), namespaceWriter)) {
744 auto* view = parentPath ? VfsMountView::fromPath(*parentPath) :
VFS::instance().mountView();
745 if (view && view->isMountpoint(target.get())) {
746 SYSCALL_ERROR(DeviceBusy);
754 const Directory::ReadStatus status = childDirectory->
isEmpty(empty);
755 if (status != Directory::ReadStatus::Complete) {
756 SYSCALL_ERROR(IoError);
760 SYSCALL_ERROR(NotEmpty);
767 if (directory->removeEphemeralFileLocked(
HashedStringView(filename), target.get())) {
768 childDirectory->markDetached();
769 publishRemoval(target.get());
777 if (directory->removeEphemeralFileLocked(
HashedStringView(filename), target.get())) {
778 publishRemoval(target.get());
782 if (!retirement.retain(target.get()) || !
removeNode(parent, filename, target.get())) {
786 publishRemoval(target.get());
791 TrueRootLease rootLease(
this);
792 File* trueRoot = rootLease.get();
796 return findNode(pNode, path, pNode, trueRoot,
nullptr);
800 File** retainedResult) {
802 if (retainedResult && !*retainedResult) {
804 *retainedResult = pNode;
805 }
else if (pNode != stableStart && pNode != trueRoot) {
813 else if (path[0] ==
'/') {
821 while ((i < path.length()) && path[i] !=
'/') {
822 i = path.nextCharacter(i);
824 while (i < path.length()) {
825 size_t n = path.nextCharacter(i);
826 if (n >= path.length()) {
828 }
else if (path[n] ==
'/') {
843 if (currentComponent.length() == 0) {
844 return findNode(pNode, restOfPath, stableStart, trueRoot, retainedResult);
856 followedLease.swap(nextLease);
861 SYSCALL_ERROR(NotADirectory);
865 bool dot = currentComponent ==
".";
866 bool dotdot = currentComponent ==
"..";
872 File* parent = parentLease.get();
875 if (dot || (dotdot && !parent) || (dotdot && pNode == trueRoot)) {
876 return findNode(pNode, restOfPath, stableStart, trueRoot, retainedResult);
878 return findNode(parent, restOfPath, stableStart, trueRoot, retainedResult);
883 SYSCALL_ERROR(NotADirectory);
891 String fullPath, reparseFullPath;
894 WARNING(
"VFS: found reparse point at '"
895 << fullPath <<
"', following it (new target: " << reparseFullPath <<
")");
906 SYSCALL_ERROR(OperationNotSupported);
912 if (lookup == Directory::LookupStatus::Found) {
913 return findNode(child.get(), restOfPath, stableStart, trueRoot, retainedResult);
915 if (lookup == Directory::LookupStatus::IoError) {
916 SYSCALL_ERROR(IoError);
922 File** retainedParent) {
923 if (retainedParent) {
924 *retainedParent =
nullptr;
926 TrueRootLease rootLease(
this);
927 File* trueRoot = rootLease.get();
932 if (path.length() > 1 && path[path.length() - 1] ==
'/') {
933 path = path.
substring(0, path.length() - 1);
938 ssize_t lastSlash = -1;
939 for (ssize_t i = path.length() - 1; i >= 0; i = path.prevCharacter(i)) {
940 if (path[i] ==
'/') {
947 File* parentNode =
nullptr;
948 if (lastSlash == -1) {
949 filename = path.toString();
950 parentNode = pStartNode;
952 *retainedParent = parentNode;
956 filename = path.
substring(path.nextCharacter(lastSlash), path.length()).toString();
958 if (lastSlash == 0) {
959 parentNode = trueRoot;
961 *retainedParent = parentNode;
964 parentNode =
findNode(pStartNode, path, pStartNode, trueRoot, retainedParent);
973 if (retainedParent && *retainedParent) {
975 *retainedParent =
nullptr;
977 if (retainedParent &&
VFS::instance().retainTrackedFile(reparseNode)) {
978 *retainedParent = reparseNode;
980 parentNode = reparseNode;
990 if (!tryAcquireOperation(operation)) {
static Directory * fromFile(File *pF)
EphemeralEntryCache m_EphemeralEntries
virtual bool cacheResolvedChildren() const
Mutex & namespaceMutationLock()
ReadStatus isEmpty(bool &empty)
bool reserveRenameEntry(const String &name, NameReservation &reservation)
MUST_USE_RESULT LookupStatus lookupChild(const HashedStringView &s, ChildLease &child) const
Directory * getReparsePoint() const
Get the reparse point attached to this directory. Reparse points allow locations on the filesystem to...
virtual void getFullPath(String &result, bool bWithMount=true)
virtual bool retainVfsReference()
void getNamespace(ParentLease &parent, String &name) const
void retainDetachedParent()
virtual bool isDirectory()
void publishEvent(FileEventMask mask, const StringView &name=StringView(), bool targetIsDirectory=false)
virtual bool removeNode(File *parent, const String &filename, File *file)=0
File * findRetained(const StringView &path, Directory::ChildLease &result, File *pStartNode=nullptr)
virtual bool renameNode(Directory *oldParent, const String &oldName, File *source, Directory *newParent, const String &newName, File *replaced)
bool removeChild(File *parent, const String &filename, File *expected, const FilesystemPathRef *parentPath=nullptr)
File * findParent(StringView path, File *pStartNode, String &filename, File **retainedParent=nullptr)
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)
static Mutex m_StructureLock
bool createLink(const StringView &path, File *target, File *pStartNode=0)
virtual SyncStatus shutdown()
virtual File * find(const StringView &path)
File * findNode(File *pNode, StringView path)
virtual SyncStatus sync()
bool createFile(const StringView &path, uint32_t mask, File *pStartNode=0)
virtual FileHandleStatus encodeFileHandle(File &file, FileHandle &handle)
bool remove(const StringView &path, File *pStartNode=0)
virtual bool deviceRemoved(bool deviceAvailable=false)
LookupResult lookup(const K &k) const
MUST_USE_RESULT bool tryAcquire(Lease &lease)
static ProcessorInformation & information()
StringView substring(size_t start, size_t end, bool hashed=HASH_STRINGVIEWS_BY_DEFAULT) const
static Symlink * fromFile(File *pF)
virtual File * followLinkRetained(Directory::ChildLease &result)
MUST_USE_RESULT bool retainTrackedFile(File *pFile)
bool untrackFile(File *pFile, bool destroy=true)
static bool checkAccess(File *pFile, bool bRead, bool bWrite, bool bExecute)