/* This file is part of Fennix Kernel. Fennix Kernel is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fennix Kernel is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Fennix Kernel. If not, see . */ #include #include #include #include #include #include "../kernel.h" // show debug messages // #define DEBUG_FILESYSTEM 1 #ifdef DEBUG_FILESYSTEM #define vfsdbg(m, ...) debug(m, ##__VA_ARGS__) #else #define vfsdbg(m, ...) #endif namespace vfs { Node *Virtual::GetNodeFromPath_Unsafe(const char *Path, Node *Parent) { vfsdbg("GetNodeFromPath( Path: \"%s\" Parent: \"%s\" )", Path, Parent ? Parent->Name : "(null)"); if (strcmp(Path, "/") == 0) return FileSystemRoot->Children[0]; // 0 - filesystem root if (strcmp(Path, ".") == 0) return Parent; if (strcmp(Path, "..") == 0) { if (Parent) { if (Parent->Parent) return Parent->Parent; else return Parent; } else return nullptr; } Node *ReturnNode = Parent; bool IsAbsolutePath = cwk_path_is_absolute(Path); if (!ReturnNode) ReturnNode = FileSystemRoot->Children[0]; // 0 - filesystem root if (IsAbsolutePath) ReturnNode = FileSystemRoot->Children[0]; // 0 - filesystem root cwk_segment segment; if (unlikely(!cwk_path_get_first_segment(Path, &segment))) { error("Path doesn't have any segments."); return nullptr; } do { char *SegmentName = new char[segment.end - segment.begin + 1]; memcpy(SegmentName, segment.begin, segment.end - segment.begin); vfsdbg("GetNodeFromPath()->SegmentName: \"%s\"", SegmentName); GetNodeFromPathNextParent: foreach (auto Child in ReturnNode->Children) { vfsdbg("comparing \"%s\" with \"%s\"", Child->Name, SegmentName); if (strcmp(Child->Name, SegmentName) == 0) { ReturnNode = Child; goto GetNodeFromPathNextParent; } } delete[] SegmentName; } while (cwk_path_get_next_segment(&segment)); const char *basename; cwk_path_get_basename(Path, &basename, nullptr); vfsdbg("BaseName: \"%s\" NodeName: \"%s\"", basename, ReturnNode->Name); if (strcmp(basename, ReturnNode->Name) == 0) { vfsdbg("GetNodeFromPath()->\"%s\"", ReturnNode->Name); return ReturnNode; } vfsdbg("GetNodeFromPath()->\"(null)\""); return nullptr; } Node *Virtual::GetNodeFromPath(const char *Path, Node *Parent) { SmartLock(VirtualLock); return GetNodeFromPath_Unsafe(Path, Parent); } bool Virtual::PathIsRelative(const char *Path) { vfsdbg("PathIsRelative( Path: \"%s\" )", Path); bool IsRelative = cwk_path_is_relative(Path); vfsdbg("PathIsRelative()->\"%s\"", IsRelative ? "true" : "false"); return IsRelative; } Node *Virtual::GetParent(const char *Path, Node *Parent) { vfsdbg("GetParent( Path: \"%s\" Parent: \"%s\" )", Path, Parent ? Parent->Name : "(nil)"); if (Parent) { vfsdbg("GetParent()->\"%s\"", Parent->Name); return Parent; } Parent = FileSystemRoot->Children[0]; size_t length; cwk_path_get_root(Path, &length); if (length > 0) { foreach (auto Child in FileSystemRoot->Children) { if (strcmp(Child->Name, Path) == 0) { Parent = Child; break; } } } vfsdbg("GetParent()->\"%s\"", ParentNode->Name); return Parent; } const char *Virtual::NormalizePath(const char *Path, Node *Parent) { assert(Parent != nullptr); vfsdbg("NormalizePath( Path: \"%s\" Parent: \"%s\" )", Path, Parent->Name); size_t PathSize = strlen((char *)Path) + 1; char *NormalizedPath = new char[PathSize]; { Memory::SmartHeap sh(PathSize); memcpy(sh, (char *)Path, PathSize); cwk_path_normalize(sh, NormalizedPath, PathSize); } const char *FinalPath; if (cwk_path_is_relative(NormalizedPath)) { size_t PathSize = cwk_path_join(Parent->FullPath, NormalizedPath, nullptr, 0); FinalPath = new char[PathSize + 1]; cwk_path_join(Parent->FullPath, NormalizedPath, (char *)FinalPath, PathSize + 1); delete[] NormalizedPath; } else FinalPath = NormalizedPath; vfsdbg("NormalizePath()->\"%s\"", FinalPath); return FinalPath; } bool Virtual::PathExists(const char *Path, Node *Parent) { if (isempty((char *)Path)) { vfsdbg("PathExists()->PathIsEmpty"); return false; } if (Parent == nullptr) Parent = FileSystemRoot; vfsdbg("PathExists( Path: \"%s\" Parent: \"%s\" )", Path, Parent->Name); const char *CleanPath = NormalizePath(Path, Parent); bool ret = GetNodeFromPath(CleanPath, Parent) != nullptr; delete[] CleanPath; vfsdbg("PathExists()->\"%s\"", ret ? "true" : "false"); return ret; } Node *Virtual::Create(const char *Path, NodeType Type, Node *Parent) { if (isempty((char *)Path)) return nullptr; SmartLock(VirtualLock); Node *RootNode = FileSystemRoot->Children[0]; Node *CurrentParent = this->GetParent(Path, Parent); vfsdbg("Virtual::Create( Path: \"%s\" Parent: \"%s\" )", Path, Parent ? Parent->Name : CurrentParent->Name); const char *CleanPath = this->NormalizePath(Path, CurrentParent); vfsdbg("CleanPath: \"%s\"", CleanPath); VirtualLock.Unlock(); if (PathExists(CleanPath, CurrentParent)) { error("Path \"%s\" already exists.", CleanPath); goto CreatePathError; } VirtualLock.Lock(__FUNCTION__); cwk_segment segment; if (!cwk_path_get_first_segment(CleanPath, &segment)) { error("Path doesn't have any segments."); goto CreatePathError; } do { char *SegmentName = new char[segment.end - segment.begin + 1]; memcpy(SegmentName, segment.begin, segment.end - segment.begin); vfsdbg("SegmentName: \"%s\"", SegmentName); auto GetChild = [](const char *Name, Node *Parent) { vfsdbg("GetChild( Name: \"%s\" Parent: \"%s\" )", Name, Parent->Name); if (!Parent) { vfsdbg("GetChild()->nullptr"); return (Node *)nullptr; } foreach (auto Child in Parent->Children) { if (strcmp(Child->Name, Name) == 0) { vfsdbg("GetChild()->\"%s\"", Child->Name); return Child; } } vfsdbg("GetChild()->nullptr (not found)"); return (Node *)nullptr; }; if (Parent) { if (GetChild(SegmentName, RootNode) != nullptr) { RootNode = GetChild(SegmentName, RootNode); delete[] SegmentName; continue; } } if (GetChild(SegmentName, CurrentParent) == nullptr) { Node *NewNode = new Node(CurrentParent, SegmentName, NodeType::DIRECTORY); CurrentParent = NewNode; CurrentParent->Type = Type; CurrentParent->FullPath = CleanPath; } else { CurrentParent = GetChild(SegmentName, CurrentParent); } delete[] SegmentName; } while (cwk_path_get_next_segment(&segment)); vfsdbg("Virtual::Create()->\"%s\"", CurrentParent->Name); #ifdef DEBUG VirtualLock.Unlock(); debug("Path created: \"%s\"", CurrentParent->FullPath); VirtualLock.Lock(__FUNCTION__); #endif return CurrentParent; CreatePathError: delete[] CleanPath; vfsdbg("Virtual::Create()->nullptr"); return nullptr; } int Virtual::Delete(const char *Path, bool Recursive, Node *Parent) { vfsdbg("Virtual::Delete( Path: \"%s\" Parent: \"%s\" )", Path, Parent ? Parent->Name : "(null)"); if (isempty((char *)Path)) return -EINVAL; if (Parent == nullptr) Parent = FileSystemRoot; const char *CleanPath = this->NormalizePath(Path, Parent); vfsdbg("CleanPath: \"%s\"", CleanPath); if (!PathExists(CleanPath, Parent)) { vfsdbg("Path \"%s\" doesn't exist.", CleanPath); delete[] CleanPath; return -ENOENT; } Node *NodeToDelete = GetNodeFromPath(CleanPath, Parent); delete[] CleanPath; return NodeToDelete->Delete(Recursive); } int Virtual::Delete(Node *Path, bool Recursive, Node *Parent) { return Delete(Path->FullPath, Recursive, Parent); } RefNode *Virtual::Open(const char *Path, Node *Parent) { vfsdbg("Opening \"%s\" with parent \"%s\"", Path, Parent ? Parent->Name : "(null)"); if (strcmp(Path, "/") == 0) return FileSystemRoot->CreateReference(); if (!Parent) Parent = FileSystemRoot->Children[0]; if (strcmp(Path, ".") == 0) return Parent->CreateReference(); if (strcmp(Path, "..") == 0) { if (Parent->Parent) return Parent->Parent->CreateReference(); else return Parent->CreateReference(); } Node *CurrentParent = this->GetParent(Path, Parent); const char *CleanPath = NormalizePath(Path, CurrentParent); if (PathExists(CleanPath, CurrentParent)) { Node *node = GetNodeFromPath(CleanPath, CurrentParent); if (node) { delete[] CleanPath; /* TODO: Check if dir or file? */ return node->CreateReference(); } } return nullptr; } Virtual::Virtual() { SmartLock(VirtualLock); trace("Initializing virtual file system..."); FileSystemRoot = new Node(nullptr, "", NodeType::MOUNTPOINT); FileSystemRoot->vFS = this; } Virtual::~Virtual() { SmartLock(VirtualLock); stub; /* TODO: sync, cache */ } }