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/Thread.h"
25#include "pedigree/kernel/processor/Processor.h"
26#include "pedigree/kernel/processor/ProcessorInformation.h"
27#include "pedigree/kernel/syscallError.h"
28#include "pedigree/kernel/utilities/LazyEvaluate.h"
29#include "pedigree/kernel/utilities/StringView.h"
30#include "pedigree/kernel/utilities/utility.h"
45 return m_bReadOnly ? SyncStatus::Success : SyncStatus::Unsupported;
55 return FileHandleStatus::Unsupported;
60 return FileHandleStatus::Unsupported;
63FileHandleStatus Filesystem::fileHandleFsid(
FileSystemId&
id) {
65 return FileHandleStatus::Unsupported;
69class InodeRetirementDrain {
71 ~InodeRetirementDrain() {
73 m_File.get()->finishInodeRetirement();
75 bool retain(
File* file) {
79 SYSCALL_ERROR(IoError);
92 explicit TrueRootLease(
Filesystem* filesystem) : m_pRoot(filesystem->getRoot()) {}
101bool targetAbsentForCreate(
File* parent,
const String& filename) {
102 if (!filename.length() || filename ==
"." || filename ==
"..") {
103 SYSCALL_ERROR(InvalidArgument);
107 SYSCALL_ERROR(NotADirectory);
112 const Directory::LookupStatus status =
114 if (status == Directory::LookupStatus::NotFound) {
117 if (status == Directory::LookupStatus::IoError) {
118 SYSCALL_ERROR(IoError);
120 SYSCALL_ERROR(FileExists);
135 assert(pStartNode !=
nullptr);
141 return find(path.
view(), pStartNode);
146 TrueRootLease rootLease(
this);
147 File* trueRoot = rootLease.get();
149 pStartNode = trueRoot;
152 File* retained =
nullptr;
153 File* found =
findNode(pStartNode, path, pStartNode, trueRoot, &retained);
160 replacement.adopt(retained);
162 result.swap(replacement);
167 TrueRootLease startLease(
this);
169 pStartNode = startLease.get();
174 File* retainedParent =
nullptr;
175 File* pParent =
findParent(path, pStartNode, filename, &retainedParent);
177 parentLease.adopt(retainedParent);
181 SYSCALL_ERROR(DoesNotExist);
185 if (!targetAbsentForCreate(pParent, filename))
198 return pFs->
createFile(pParent, filename, mask);
202 TrueRootLease startLease(
this);
204 pStartNode = startLease.get();
209 File* retainedParent =
nullptr;
210 File* pParent =
findParent(path, pStartNode, filename, &retainedParent);
212 parentLease.adopt(retainedParent);
216 SYSCALL_ERROR(DoesNotExist);
220 if (!targetAbsentForCreate(pParent, filename))
237 TrueRootLease startLease(
this);
239 pStartNode = startLease.get();
244 File* retainedParent =
nullptr;
245 File* pParent =
findParent(path, pStartNode, filename, &retainedParent);
247 parentLease.adopt(retainedParent);
251 SYSCALL_ERROR(DoesNotExist);
255 if (!targetAbsentForCreate(pParent, filename))
272 TrueRootLease startLease(
this);
274 pStartNode = startLease.get();
279 File* retainedParent =
nullptr;
280 File* pParent =
findParent(path, pStartNode, filename, &retainedParent);
282 parentLease.adopt(retainedParent);
286 SYSCALL_ERROR(DoesNotExist);
290 if (!targetAbsentForCreate(pParent, filename))
299 if (
this != target->getFilesystem()) {
300 SYSCALL_ERROR(CrossDeviceLink);
309 return pFs->
createLink(pParent, filename, target);
313 return remove(path, pStartNode,
nullptr);
317 TrueRootLease startLease(
this);
319 pStartNode = startLease.get();
324 File* retainedParent =
nullptr;
325 File* pParent =
findParent(path, pStartNode, filename, &retainedParent);
327 parentLease.adopt(retainedParent);
331 SYSCALL_ERROR(DoesNotExist);
338 if (!filename.length() || filename ==
"." || filename ==
"..") {
339 SYSCALL_ERROR(InvalidArgument);
351 return pFs->
removeChild(pParent, filename, expected);
356 SYSCALL_ERROR(DoesNotExist);
363 File* newStart,
bool noReplace) {
364 if (!oldPath.length() || !newPath.length()) {
365 SYSCALL_ERROR(DoesNotExist);
368 TrueRootLease rootLease(
this);
370 oldStart = rootLease.get();
373 newStart = rootLease.get();
375 if (!oldStart || !newStart) {
376 SYSCALL_ERROR(DoesNotExist);
384 File* retained =
nullptr;
385 File* oldParentFile =
386 oldStart->getFilesystem()->
findParent(oldPath, oldStart, oldName, &retained);
388 oldParentLease.adopt(retained);
391 File* newParentFile =
392 newStart->getFilesystem()->
findParent(newPath, newStart, newName, &retained);
394 newParentLease.adopt(retained);
396 return renameChildren(
397 oldParentFile, oldName, newParentFile, newName, noReplace,
398 oldPath[oldPath.length() - 1] ==
'/' || newPath[newPath.length() - 1] ==
'/');
401bool Filesystem::renameChildren(
File* oldParentFile,
const String& oldName,
File* newParentFile,
402 const String& newName,
bool noReplace,
bool sourceMustBeDirectory) {
403 InodeRetirementDrain retirement;
405 if (!oldParentFile || !newParentFile) {
406 SYSCALL_ERROR(DoesNotExist);
410 SYSCALL_ERROR(NotADirectory);
413 if (!oldName.length() || !newName.length() || oldName ==
"." || oldName ==
".." ||
414 newName ==
"." || newName ==
"..") {
415 SYSCALL_ERROR(InvalidArgument);
418 Filesystem* filesystem = oldParentFile->getFilesystem();
419 if (filesystem != newParentFile->getFilesystem()) {
420 SYSCALL_ERROR(CrossDeviceLink);
425 SYSCALL_ERROR(ReadOnlyFilesystem);
435 const bool oldFirst =
436 reinterpret_cast<uintptr_t
>(oldParent) <
reinterpret_cast<uintptr_t
>(newParent);
437 Directory* first = oldFirst ? oldParent : newParent;
438 Directory* second = oldFirst ? newParent : oldParent;
442 SYSCALL_ERROR(DoesNotExist);
449 if (sourceStatus != Directory::LookupStatus::Found) {
450 syscallError(sourceStatus == Directory::LookupStatus::IoError ? Error::IoError
451 : Error::DoesNotExist);
454 File* source = sourceLease.get();
455 if (oldParent == newParent && oldName == newName) {
457 SYSCALL_ERROR(FileExists);
463 if (replacedStatus == Directory::LookupStatus::IoError) {
464 SYSCALL_ERROR(IoError);
467 File* replaced = replacedLease.get();
470 if (noReplace && replaced) {
471 SYSCALL_ERROR(FileExists);
474 if (source->getFilesystem() != filesystem ||
475 (replaced && replaced->getFilesystem() != filesystem)) {
476 SYSCALL_ERROR(CrossDeviceLink);
480 (source == replaced || (source->getInode() && source->getInode() == replaced->getInode()))) {
483 if (sourceMustBeDirectory && !source->
isDirectory()) {
484 SYSCALL_ERROR(NotADirectory);
488 syscallError(replaced->
isDirectory() ? Error::IsADirectory : Error::NotADirectory);
495 if ((sourceDirectory && (view ? view->isMountpoint(sourceDirectory)
496 : sourceDirectory->getReparsePoint() != nullptr)) ||
497 (replacedDirectory && (view ? view->isMountpoint(replacedDirectory)
498 : replacedDirectory->getReparsePoint() != nullptr))) {
499 SYSCALL_ERROR(DeviceBusy);
502 if (sourceDirectory) {
503 File* ancestor = newParent;
506 if (ancestor == source) {
507 SYSCALL_ERROR(InvalidArgument);
513 ancestorLease.swap(next);
514 ancestor = ancestorLease.get();
517 if (replacedDirectory == oldParent || replacedDirectory == newParent) {
518 SYSCALL_ERROR(NotEmpty);
523 : oldParent->namespaceMutationLock(),
524 sourceDirectory != nullptr);
526 : oldParent->namespaceMutationLock(),
527 replacedDirectory != nullptr);
528 if (replacedDirectory) {
530 if (replacedDirectory->
isEmpty(empty) != Directory::ReadStatus::Complete) {
531 SYSCALL_ERROR(IoError);
535 SYSCALL_ERROR(NotEmpty);
544 SYSCALL_ERROR(DoesNotExist);
547 bool sourceEphemeral =
false;
548 bool replacedEphemeral =
false;
557 if (sourceEphemeral) {
558 if (newName.length() > 255) {
559 SYSCALL_ERROR(NameTooLong);
562 if (sourceDirectory) {
563 SYSCALL_ERROR(OperationNotSupported);
568 if (replaced && !replacedEphemeral &&
569 (!retirement.retain(replaced) || !filesystem->
removeNode(newParent, newName, replaced))) {
572 }
else if (!retirement.retain(replacedEphemeral ?
nullptr : replaced) ||
573 !filesystem->
renameNode(oldParent, oldName, source, newParent, newName,
574 replacedEphemeral ? nullptr : replaced)) {
579 if (replacedDirectory) {
580 replacedDirectory->markDetached();
583 source->moveNamespace(newName, newParent);
584 if (sourceDirectory) {
585 __atomic_store_n(&sourceDirectory->
m_ParentInode, newParent->getInode(), __ATOMIC_RELEASE);
587 oldParent->moveReservedEntry(oldReservation, newParent, newReservation, source);
588 oldReservation.complete(Directory::LookupStatus::NotFound);
589 newReservation.complete(Directory::LookupStatus::Found);
597 SYSCALL_ERROR(OperationNotSupported);
602 InodeRetirementDrain retirement;
606 SYSCALL_ERROR(NotADirectory);
609 if (!filename.length() || filename ==
"." || filename ==
"..") {
610 SYSCALL_ERROR(InvalidArgument);
617 auto publishRemoval = [&](
File* target) {
620 target->retainDetachedParent();
621 directory->
publishEvent(FileEvents::Removed, filename.
view(), target->isDirectory());
625 target->publishEvent(FileEvents::DeletedSelf);
630 if (lookup != Directory::LookupStatus::Found) {
631 if (lookup == Directory::LookupStatus::IoError) {
632 SYSCALL_ERROR(IoError);
634 SYSCALL_ERROR(DoesNotExist);
638 if (expected && target.get() != expected) {
639 SYSCALL_ERROR(DoesNotExist);
644 if (view && view->isMountpoint(target.get())) {
645 SYSCALL_ERROR(DeviceBusy);
653 const Directory::ReadStatus status = childDirectory->
isEmpty(empty);
654 if (status != Directory::ReadStatus::Complete) {
655 SYSCALL_ERROR(IoError);
659 SYSCALL_ERROR(NotEmpty);
666 if (directory->removeEphemeralFileLocked(
HashedStringView(filename), target.get())) {
667 childDirectory->markDetached();
668 publishRemoval(target.get());
676 if (directory->removeEphemeralFileLocked(
HashedStringView(filename), target.get())) {
677 publishRemoval(target.get());
681 if (!retirement.retain(target.get()) || !
removeNode(parent, filename, target.get())) {
685 publishRemoval(target.get());
690 TrueRootLease rootLease(
this);
691 File* trueRoot = rootLease.get();
695 return findNode(pNode, path, pNode, trueRoot,
nullptr);
699 File** retainedResult) {
701 if (retainedResult && !*retainedResult) {
703 *retainedResult = pNode;
704 }
else if (pNode != stableStart && pNode != trueRoot) {
712 else if (path[0] ==
'/') {
720 while ((i < path.length()) && path[i] !=
'/') {
721 i = path.nextCharacter(i);
723 while (i < path.length()) {
724 size_t n = path.nextCharacter(i);
725 if (n >= path.length()) {
727 }
else if (path[n] ==
'/') {
742 if (currentComponent.length() == 0) {
743 return findNode(pNode, restOfPath, stableStart, trueRoot, retainedResult);
755 followedLease.swap(nextLease);
760 SYSCALL_ERROR(NotADirectory);
764 bool dot = currentComponent ==
".";
765 bool dotdot = currentComponent ==
"..";
771 File* parent = parentLease.get();
774 if (dot || (dotdot && !parent) || (dotdot && pNode == trueRoot)) {
775 return findNode(pNode, restOfPath, stableStart, trueRoot, retainedResult);
777 return findNode(parent, restOfPath, stableStart, trueRoot, retainedResult);
782 SYSCALL_ERROR(NotADirectory);
790 String fullPath, reparseFullPath;
793 WARNING(
"VFS: found reparse point at '"
794 << fullPath <<
"', following it (new target: " << reparseFullPath <<
")");
805 SYSCALL_ERROR(OperationNotSupported);
811 if (lookup == Directory::LookupStatus::Found) {
812 return findNode(child.get(), restOfPath, stableStart, trueRoot, retainedResult);
814 if (lookup == Directory::LookupStatus::IoError) {
815 SYSCALL_ERROR(IoError);
821 File** retainedParent) {
822 if (retainedParent) {
823 *retainedParent =
nullptr;
825 TrueRootLease rootLease(
this);
826 File* trueRoot = rootLease.get();
831 if (path.length() > 1 && path[path.length() - 1] ==
'/') {
832 path = path.
substring(0, path.length() - 1);
837 ssize_t lastSlash = -1;
838 for (ssize_t i = path.length() - 1; i >= 0; i = path.prevCharacter(i)) {
839 if (path[i] ==
'/') {
846 File* parentNode =
nullptr;
847 if (lastSlash == -1) {
848 filename = path.toString();
849 parentNode = pStartNode;
851 *retainedParent = parentNode;
855 filename = path.
substring(path.nextCharacter(lastSlash), path.length()).toString();
857 if (lastSlash == 0) {
858 parentNode = trueRoot;
860 *retainedParent = parentNode;
863 parentNode =
findNode(pStartNode, path, pStartNode, trueRoot, retainedParent);
872 if (retainedParent && *retainedParent) {
874 *retainedParent =
nullptr;
876 if (retainedParent &&
VFS::instance().retainTrackedFile(reparseNode)) {
877 *retainedParent = reparseNode;
879 parentNode = reparseNode;
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)
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)
LookupResult lookup(const K &k) const
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)