From f06c0b19fa27ae7efc111c0af19d074e613243d5 Mon Sep 17 00:00:00 2001 From: EnderIce2 Date: Sun, 18 May 2025 11:38:42 +0000 Subject: [PATCH] fix(kernel/vfs): :sparkles: fully implement ustar driver implementation + mounting system --- Drivers/include/fs.h | 28 +- Kernel/drivers/fs/ustar/ustar.cpp | 1074 ++++++++++++++++++++++++++--- Kernel/fs/vfs.cpp | 41 ++ Kernel/include/fs/vfs.hpp | 92 ++- Kernel/include/interface/fs.h | 28 +- Kernel/subsystem/linux/init.cpp | 21 +- Kernel/subsystem/windows/init.cpp | 45 ++ 7 files changed, 1209 insertions(+), 120 deletions(-) diff --git a/Drivers/include/fs.h b/Drivers/include/fs.h index c55969d5..4e4b3a58 100644 --- a/Drivers/include/fs.h +++ b/Drivers/include/fs.h @@ -323,6 +323,28 @@ struct InodeOperations } __attribute__((packed)); struct FileSystemInfo; + +struct FileSystemDevice +{ + struct + { + /** + * @brief Inode + * + * If the device is a block device, this will be NULL. + */ + struct Inode *node; + struct InodeOperations *ops; + } inode; + + /** + * @brief Block Device + * + * If the device is a block device, this will be non-NULL. + */ + struct BlockDevice *Block; +}; + struct SuperBlockOperations { int (*AllocateInode)(struct FileSystemInfo *Info, struct Inode **Result); @@ -360,7 +382,7 @@ struct SuperBlockOperations * * @return Zero on success, otherwise an error code. */ - int (*Probe)(void *Device); + int (*Probe)(struct FileSystemDevice *Device); /** * Mount the filesystem. @@ -369,11 +391,11 @@ struct SuperBlockOperations * * @param FS Filesystem to mount. * @param Root Pointer to the root inode. - * @param Device Device to mount. + * @param Device Device to mount. This pointer will be undefined after the function returns! * * @return Zero on success, otherwise an error code. */ - int (*Mount)(struct FileSystemInfo *FS, struct Inode **Root, void *Device); + int (*Mount)(struct FileSystemInfo *FS, struct Inode **Root, struct FileSystemDevice *Device); /** * Unmount the filesystem. diff --git a/Kernel/drivers/fs/ustar/ustar.cpp b/Kernel/drivers/fs/ustar/ustar.cpp index b71120a2..f20e34ac 100644 --- a/Kernel/drivers/fs/ustar/ustar.cpp +++ b/Kernel/drivers/fs/ustar/ustar.cpp @@ -16,136 +16,1014 @@ */ #include -#include #include #include +#include #include +#include "../../../kernel.h" + using namespace vfs; namespace Driver::UnixStandardTAR { dev_t DriverID; + enum TypeFlag + { + AREGTYPE = '\0', + REGTYPE = '0', + LNKTYPE = '1', + SYMTYPE = '2', + CHRTYPE = '3', + BLKTYPE = '4', + DIRTYPE = '5', + FIFOTYPE = '6', + CONTTYPE = '7' + }; + + enum ModeFlag + { + TSUID = 04000, + TSGID = 02000, + TSVTX = 01000, + TUREAD = 00400, + TUWRITE = 00200, + TUEXEC = 00100, + TGREAD = 00040, + TGWRITE = 00020, + TGEXEC = 00010, + TOREAD = 00004, + TOWRITE = 00002, + TOEXEC = 00001, + }; + + struct TarHeader + { + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char typeflag[1]; + char link[100]; + char signature[6]; + char version[2]; + char owner[32]; + char group[32]; + char dev_maj[8]; + char dev_min[8]; + char prefix[155]; + char pad[12]; + } __packed; + + constexpr static int INODE_CHECKSUM = 0x7757A4; + +#define TAR_BLOCK_SIZE 512 +#define TMAGIC "ustar" +#define TMAGLEN 6 +#define TVERSION "00" +#define TVERSLEN 2 + + inline uint32_t GetSize(const char *String) + { + uint32_t ret = 0; + while (*String) + { + ret *= 8; + ret += *String - '0'; + String++; + } + return ret; + } + + inline int StringToInt(const char *String) + { + int ret = 0; + for (int i = 0; String[i] != '\0'; ++i) + ret = ret * 10 + String[i] - '0'; + return ret; + } + + class USTARInstance + { + public: + struct USTARInode + { + Inode Node; + off_t HeaderOffset; // Offset of the header in the device + USTARInode *Parent; + std::string Name; + std::string Path; + std::vector Children; + bool Deleted; + int Checksum; + }; + std::unordered_map Files; + ino_t NextInode = 0; + FileSystemDevice Device; + + private: + ssize_t DeviceRead(void *Buffer, size_t Size, off_t Offset) + { + if (Device.inode.node) + return Device.inode.ops->Read(Device.inode.node, Buffer, Size, Offset); + else if (Device.Block) + return Device.Block->Ops->Read(nullptr, Buffer, Size, Offset); + else + return -EINVAL; + } + + ssize_t DeviceWrite(const void *Buffer, size_t Size, off_t Offset) + { + if (Device.inode.node) + return Device.inode.ops->Write(Device.inode.node, Buffer, Size, Offset); + else if (Device.Block) + return Device.Block->Ops->Write(nullptr, Buffer, Size, Offset); + else + return -EINVAL; + } + + public: + int Lookup(Inode *_Parent, const char *Name, Inode **Result) + { + auto Parent = (USTARInode *)_Parent; + debug("looking up for %s", Name); + const char *basename; + size_t length; + cwk_path_get_basename(Name, &basename, &length); + if (basename == NULL) + { + if (strcmp(Name, "/") == 0) + { + auto &it = Files.at(0); + *Result = &it->Node; + return 0; + } + + error("Invalid name %s", Name); + return -EINVAL; + } + + if (_Parent) + { + for (const auto &child : Parent->Children) + { + if (child->Deleted || strcmp(child->Name.c_str(), basename) != 0) + continue; + + *Result = &child->Node; + return 0; + } + + return -ENOENT; + } + + auto fileItr = Files.begin(); + while (fileItr != Files.end()) + { + USTARInode *node = fileItr->second; + + if (node->Deleted || strcmp(node->Name.c_str(), basename) != 0) + { + fileItr++; + continue; + } + + *Result = &fileItr->second->Node; + return 0; + } + return -ENOENT; + } + + int Create(Inode *_Parent, const char *Name, mode_t Mode, Inode **Result) + { + USTARInode *Parent = (USTARInode *)_Parent; + + Inode inode{}; + inode.Mode = Mode; + inode.Device = DriverID; + inode.RawDevice = 0; + inode.Index = NextInode; + inode.Offset = 0; + inode.PrivateData = this; + + const char *basename; + size_t length; + cwk_path_get_basename(Name, &basename, &length); + + auto SetMode = [&](mode_t &Mode, TarHeader *header) + { + if (Mode & S_IFREG) + header->typeflag[0] = REGTYPE; + else if (Mode & S_IFLNK) + header->typeflag[0] = SYMTYPE; + else if (Mode & S_IFCHR) + header->typeflag[0] = CHRTYPE; + else if (Mode & S_IFBLK) + header->typeflag[0] = BLKTYPE; + else if (Mode & S_IFDIR) + header->typeflag[0] = DIRTYPE; + else if (Mode & S_IFIFO) + header->typeflag[0] = FIFOTYPE; + + mode_t final = 0; + if (Mode & S_ISUID) + final |= TSUID; + else if (Mode & S_ISGID) + final |= TSGID; + else if (Mode & S_ISVTX) + final |= TSVTX; + else if (Mode & S_IRUSR) + final |= TUREAD; + else if (Mode & TUWRITE) + final |= TUWRITE; + else if (Mode & TUEXEC) + final |= TUEXEC; + else if (Mode & TGREAD) + final |= TGREAD; + else if (Mode & TGWRITE) + final |= TGWRITE; + else if (Mode & TGEXEC) + final |= TGEXEC; + else if (Mode & TOREAD) + final |= TOREAD; + else if (Mode & TOWRITE) + final |= TOWRITE; + else if (Mode & TOEXEC) + final |= TOEXEC; + + snprintf(header->mode, sizeof(header->mode), "%07o", final); + }; + + TarHeader *hdr = new TarHeader{}; + SetMode(inode.Mode, hdr); + strncpy(hdr->name, basename, sizeof(hdr->name)); + strncpy(hdr->signature, TMAGIC, TMAGLEN); + strncpy(hdr->version, TVERSION, TVERSLEN); + + USTARInode *node = new USTARInode{.Node = inode, + .HeaderOffset = 0, + .Parent = Parent, + .Name{}, + .Path{}, + .Children{}, + .Deleted = false, + .Checksum = INODE_CHECKSUM}; + + node->Name.assign(basename, length); + node->Path.assign(Name, strlen(Name)); + + auto file = Files.insert(std::make_pair(NextInode, node)); + assert(file.second == true); + *Result = &Files.at(NextInode)->Node; + if (Parent) + { + Parent->Children.push_back(Files.at(NextInode)); + Files.at(NextInode)->Parent = Parent; + } + NextInode++; + return 0; + } + + int Remove(Inode *Parent, const char *Name) + { + assert(!"not implemented"); + } + + int Rename(Inode *Parent, const char *OldName, const char *NewName) + { + assert(!"not implemented"); + } + + ssize_t Read(Inode *Node, void *Buffer, size_t Size, off_t Offset) + { + auto fileItr = Files.find(Node->Index); + assert(fileItr != Files.end()); + + if (fileItr->second->Deleted) + return -ENOENT; + + USTARInode *node = fileItr->second; + TarHeader header; + if (DeviceRead(&header, sizeof(TarHeader), node->HeaderOffset) != sizeof(TarHeader)) + return -EIO; + size_t fileSize = GetSize(header.size); + + if (Size <= 0) + { + debug("Size is less than or equal to 0"); + Size = fileSize; + } + + if ((size_t)Offset > fileSize) + { + debug("Offset %d is greater than file size %d", Offset, fileSize); + return 0; + } + + if ((fileSize - Offset) == 0) + { + debug("Offset %d is equal to file size %d", Offset, fileSize); + return 0; /* EOF */ + } + + if ((size_t)Offset + Size > fileSize) + { + debug("Offset %d + Size %d is greater than file size %d", + Offset, Size, fileSize); + Size = fileSize - Offset; + } + + off_t dataOffset = node->HeaderOffset + sizeof(TarHeader) + Offset; + if (DeviceRead(Buffer, Size, dataOffset) != (ssize_t)Size) + return -EIO; + return Size; + } + + ssize_t Write(Inode *Node, const void *Buffer, size_t Size, off_t Offset) + { + assert(!"not implemented"); + } + + int Truncate(Inode *Node, off_t Size) + { + assert(!"not implemented"); + } + + int Open(Inode *Node, int Flags, mode_t Mode) + { + return 0; + } + + int Close(Inode *Node) + { + return 0; + } + + int Ioctl(Inode *Node, unsigned long Request, void *Argp) + { + assert(!"not implemented"); + } + + __no_sanitize("alignment") ssize_t ReadDir(Inode *_Node, kdirent *Buffer, size_t Size, off_t Offset, off_t Entries) + { + /* FIXME: FIX ALIGNMENT FOR DIRENT! */ + auto Node = (USTARInode *)_Node; + debug("reading directory %s", Node->Path.c_str()); + + off_t realOffset = Offset; + + size_t totalSize = 0; + uint16_t reclen = 0; + struct kdirent *ent = nullptr; + + if (Offset == 0) + { + reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen(".") + 1); + if (totalSize + reclen >= Size) + return -EINVAL; + + ent = (struct kdirent *)((uintptr_t)Buffer + totalSize); + ent->d_ino = Node->Node.Index; + ent->d_off = Offset++; + ent->d_reclen = reclen; + ent->d_type = DT_DIR; + strcpy(ent->d_name, "."); + totalSize += reclen; + debug("."); + } + + if (Offset <= 1) + { + reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen("..") + 1); + if (totalSize + reclen >= Size) + { + if (realOffset == 1) + return -EINVAL; + return totalSize; + } + + ent = (struct kdirent *)((uintptr_t)Buffer + totalSize); + + if (Node->Parent) + ent->d_ino = Node->Parent->Node.Index; + else + { + warn("Parent is null for %s", Node->Name.c_str()); + ent->d_ino = Node->Node.Index; + } + ent->d_off = Offset++; + ent->d_reclen = reclen; + ent->d_type = DT_DIR; + strcpy(ent->d_name, ".."); + totalSize += reclen; + debug(".."); + } + + if (!S_ISDIR(Node->Node.Mode)) + return -ENOTDIR; + + if ((Offset >= 2 ? (Offset - 2) : Offset) > (off_t)Node->Children.size()) + return -EINVAL; + + off_t entries = 0; + for (const auto &var : Node->Children) + { + if (var->Node.Offset < Offset) + continue; + + if (entries >= Entries) + break; + + if (var->Deleted) + continue; + + TarHeader header; + ssize_t hdrRead = DeviceRead(&header, sizeof(TarHeader), var->HeaderOffset); + if (hdrRead != sizeof(TarHeader)) + { + warn("Failed to read header for %s at offset %ld", var->Name.c_str(), var->HeaderOffset); + continue; + } + size_t fileSize = GetSize(header.size); + debug("Entry: %s, typeflag: %c, size: %zu, offset: %ld", var->Name.c_str(), header.typeflag[0], fileSize, var->HeaderOffset); + + reclen = (uint16_t)(offsetof(struct kdirent, d_name) + var->Name.size() + 1); + + if (totalSize + reclen > Size) + { + debug("not enough space for %s (%zu + %zu = %zu > %zu)", var->Name.c_str(), totalSize, reclen, totalSize + reclen, Size); + break; + } + + ent = (struct kdirent *)((uintptr_t)Buffer + totalSize); + ent->d_ino = var->Node.Index; + ent->d_off = var->Node.Offset; + ent->d_reclen = reclen; + switch (header.typeflag[0]) + { + case AREGTYPE: + case REGTYPE: + ent->d_type = DT_REG; + break; + case LNKTYPE: + fixme("Hard link not implemented for %s", header.name); + ent->d_type = DT_LNK; + break; + case SYMTYPE: + ent->d_type = DT_LNK; + break; + case CHRTYPE: + ent->d_type = DT_CHR; + break; + case BLKTYPE: + ent->d_type = DT_BLK; + break; + case DIRTYPE: + ent->d_type = DT_DIR; + break; + case FIFOTYPE: + ent->d_type = DT_FIFO; + break; + case CONTTYPE: + default: + ent->d_type = 0; + break; + } + strncpy(ent->d_name, var->Name.c_str(), strlen(var->Name.c_str())); + debug("Added entry: %s, type: %d, size: %zu", var->Name.c_str(), ent->d_type, fileSize); + totalSize += reclen; + entries++; + } + + if (totalSize + sizeof(struct kdirent) >= Size) + return totalSize; + + ent = (struct kdirent *)((uintptr_t)Buffer + totalSize); + ent->d_ino = 0; + ent->d_off = 0; + ent->d_reclen = 0; + ent->d_type = DT_UNKNOWN; + ent->d_name[0] = '\0'; + return totalSize; + } + + int MkDir(Inode *Parent, const char *Name, mode_t Mode, Inode **Result) + { + assert(!"not implemented"); + } + + int RmDir(Inode *Parent, const char *Name) + { + assert(!"not implemented"); + } + + int SymLink(Inode *Parent, const char *Name, const char *Target, Inode **Result) + { + int ret = this->Create(Parent, Name, S_IFLNK, Result); + if (ret < 0) + return ret; + + USTARInode *node = (USTARInode *)*Result; + TarHeader header; + if (DeviceRead(&header, sizeof(TarHeader), node->HeaderOffset) != sizeof(TarHeader)) + return -EIO; + strncpy(header.link, Target, MIN(sizeof(header.link) - 1, strlen(Target))); + return 0; + } + + ssize_t ReadLink(Inode *Node, char *Buffer, size_t Size) + { + auto fileItr = Files.find(Node->Index); + assert(fileItr != Files.end()); + + if (fileItr->second->Deleted) + return -ENOENT; + + USTARInode *node = fileItr->second; + TarHeader header; + if (DeviceRead(&header, sizeof(TarHeader), node->HeaderOffset) != sizeof(TarHeader)) + return -EIO; + + size_t linkLen = 0; + while (linkLen < sizeof(header.link) && header.link[linkLen] != '\0') + ++linkLen; + if (linkLen > Size) + linkLen = Size; + memcpy(Buffer, header.link, linkLen); + debug("Read %zu bytes from %d: \"%.*s\"", linkLen, Node->Index, (int)linkLen, Buffer); + return linkLen; + } + + off_t Seek(Inode *Node, off_t Offset) + { + assert(!"not implemented"); + } + + int Stat(Inode *Node, kstat *Stat) + { + auto fileItr = Files.find(Node->Index); + assert(fileItr != Files.end()); + if (fileItr->second->Deleted) + return -ENOENT; + + USTARInode *node = fileItr->second; + TarHeader header; + if (DeviceRead(&header, sizeof(TarHeader), node->HeaderOffset) != sizeof(TarHeader)) + return -EIO; + size_t fileSize = GetSize(header.size); + + debug("Header: \"%.*s\"", (int)sizeof(struct TarHeader), &header); + Stat->Device = DriverID; + Stat->Index = Node->Index; + Stat->HardLinks = 1; + Stat->UserID = GetSize(header.uid); + Stat->GroupID = GetSize(header.gid); + Stat->RawDevice = Stat->MakeDevice(GetSize(header.dev_maj), GetSize(header.dev_min)); + Stat->Size = fileSize; + Stat->AccessTime = GetSize(header.mtime); + Stat->ModifyTime = GetSize(header.mtime); + Stat->ChangeTime = GetSize(header.mtime); + Stat->BlockSize = 512; + Stat->Blocks = (fileSize + 511) / 512; + Stat->Attribute = 0; + + mode_t hdrMode = StringToInt(header.mode); + + if (hdrMode & TSUID) + Stat->Mode |= S_ISUID; + else if (hdrMode & TSGID) + Stat->Mode |= S_ISGID; + else if (hdrMode & TSVTX) + Stat->Mode |= S_ISVTX; + else if (hdrMode & TUREAD) + Stat->Mode |= S_IRUSR; + else if (hdrMode & TUWRITE) + Stat->Mode |= S_IWUSR; + else if (hdrMode & TUEXEC) + Stat->Mode |= S_IXUSR; + else if (hdrMode & TGREAD) + Stat->Mode |= S_IRGRP; + else if (hdrMode & TGWRITE) + Stat->Mode |= S_IWGRP; + else if (hdrMode & TGEXEC) + Stat->Mode |= S_IXGRP; + else if (hdrMode & TOREAD) + Stat->Mode |= S_IROTH; + else if (hdrMode & TOWRITE) + Stat->Mode |= S_IWOTH; + else if (hdrMode & TOEXEC) + Stat->Mode |= S_IXOTH; + + switch (header.typeflag[0]) + { + case AREGTYPE: + case REGTYPE: + Stat->Mode |= S_IFREG; + break; + case LNKTYPE: + fixme("Hard link not implemented for %s", header.name); + Stat->Mode |= S_IFLNK; + break; + case SYMTYPE: + Stat->Mode |= S_IFLNK; + break; + case CHRTYPE: + Stat->Mode |= S_IFCHR; + break; + case BLKTYPE: + Stat->Mode |= S_IFBLK; + break; + case DIRTYPE: + Stat->Mode |= S_IFDIR; + break; + case FIFOTYPE: + Stat->Mode |= S_IFIFO; + break; + case CONTTYPE: + warn("Reserved type for %s", header.name); + __fallthrough; + default: + error("Unknown type: %d for %s", header.typeflag[0], header.name); + break; + } + return 0; + } + + int ScanArchiveFromDevice() + { + off_t offset = 0; + std::vector tmpNodes; + + auto SetMode = [&](Inode &uNode, TarHeader *header) + { + mode_t hdrMode = StringToInt(header->mode); + if (hdrMode & TSUID) + uNode.Mode |= S_ISUID; + else if (hdrMode & TSGID) + uNode.Mode |= S_ISGID; + else if (hdrMode & TSVTX) + uNode.Mode |= S_ISVTX; + else if (hdrMode & TUREAD) + uNode.Mode |= S_IRUSR; + else if (hdrMode & TUWRITE) + uNode.Mode |= S_IWUSR; + else if (hdrMode & TUEXEC) + uNode.Mode |= S_IXUSR; + else if (hdrMode & TGREAD) + uNode.Mode |= S_IRGRP; + else if (hdrMode & TGWRITE) + uNode.Mode |= S_IWGRP; + else if (hdrMode & TGEXEC) + uNode.Mode |= S_IXGRP; + else if (hdrMode & TOREAD) + uNode.Mode |= S_IROTH; + else if (hdrMode & TOWRITE) + uNode.Mode |= S_IWOTH; + else if (hdrMode & TOEXEC) + uNode.Mode |= S_IXOTH; + + switch (header->typeflag[0]) + { + case AREGTYPE: + case REGTYPE: + uNode.Mode |= S_IFREG; + break; + case LNKTYPE: + uNode.Mode |= S_IFLNK; + break; + case SYMTYPE: + uNode.Mode |= S_IFLNK; + break; + case CHRTYPE: + uNode.Mode |= S_IFCHR; + break; + case BLKTYPE: + uNode.Mode |= S_IFBLK; + break; + case DIRTYPE: + uNode.Mode |= S_IFDIR; + break; + case FIFOTYPE: + uNode.Mode |= S_IFIFO; + break; + case CONTTYPE: + warn("Reserved type for %s", header->name); + __fallthrough; + default: + error("Unknown type: %d for %s", header->typeflag[0], header->name); + break; + } + }; + + while (true) + { + TarHeader header; + if (DeviceRead(&header, sizeof(TarHeader), offset) != sizeof(TarHeader)) + break; + if (strncmp(header.signature, TMAGIC, TMAGLEN - 1) != 0) + break; + if (isempty(header.name)) + break; + + /* This removes the "." at the beginning of the file name "./foo/bar" > "/foo/bar" */ + if (header.name[0] == '.' && header.name[1] == '/') + memmove(header.name, header.name + 1, strlen(header.name)); + + if (isempty((char *)header.name)) + fixme("Ignoring empty file name \"%.*s\"", sizeof(struct TarHeader), header); + + struct Inode uNode; + uNode.Device = DriverID; + uNode.RawDevice = 0; + uNode.Index = NextInode; + SetMode(uNode, &header); + uNode.Offset = 0; + uNode.PrivateData = this; + + const char *basename; + size_t length; + cwk_path_get_basename(header.name, &basename, &length); + + USTARInode *node = new USTARInode{.Node = uNode, + .HeaderOffset = offset, + .Parent = nullptr, + .Name{}, + .Path{}, + .Children{}, + .Deleted = false, + .Checksum = INODE_CHECKSUM}; + + if (basename) + node->Name.assign(basename, length); + else + node->Name.assign((const char *)header.name, strlen(header.name)); + node->Path.assign((const char *)header.name, strlen(header.name)); + + Files.insert(std::make_pair(NextInode, node)); + tmpNodes.push_back(node); + + size_t size = GetSize(header.size); + offset += ((size / 512) + 1) * 512; + if (size % 512) + offset += 512; + NextInode++; + } + + /* TODO: This code can be significantly optimized but good luck understanding it */ + USTARInode *parent = nullptr; + std::vector parentStack; + std::vector pathStack; + for (auto &file : tmpNodes) + { + if (file->Path == "/") /* This is root / */ + { + parentStack.push_back(file); + pathStack.push_back(&file->Path); + // debug("root / generated"); + continue; + } + + /* pathStack is never empty */ + + if (file->Path.back() == '/') /* This is a directory */ + { + const char *path = file->Path.c_str(); + size_t length; + /* This converts /one/two/path.txt to /one/two/ */ + cwk_path_get_dirname(path, &length); + std::string dirName(path, length); + + /* Check if the directory is at the same level as the current directory */ + if (dirName == *pathStack.back()) + { + parent = parentStack.back(); + parentStack.push_back(file); + pathStack.push_back(&file->Path); + parent->Children.push_back(file); + file->Parent = parent; + // debug("adding \"%s\" to \"%s\"", file->Path.c_str(), parent->Path.c_str()); + continue; + } + else + { + /* Check if the directory is at a higher level */ + for (size_t i = 0; i < parentStack.size(); i++) + { + if (dirName != *pathStack[i]) + continue; + + /* Adjust vectors */ + while (!parentStack.empty()) + { + if (dirName == *pathStack.back()) + break; + + // debug("popping \"%s\"", pathStack.back()->c_str()); + parentStack.pop_back(); + pathStack.pop_back(); + } + + parent = parentStack.back(); + parentStack.push_back(file); + pathStack.push_back(&file->Path); + parent->Children.push_back(file); + file->Parent = parent; + // debug("adding \"%s\" to \"%s\"", file->Path.c_str(), parent->Path.c_str()); + goto foundEnd; + } + + // This is a new directory level + parentStack.pop_back(); + pathStack.pop_back(); + parent = parentStack.back(); + parentStack.push_back(file); + pathStack.push_back(&file->Path); + parent->Children.push_back(file); + file->Parent = parent; + // debug("adding \"%s\" to \"%s\"", file->Path.c_str(), parent->Path.c_str()); + foundEnd: + continue; + } + } + + /* From here, it's a file */ + + const char *path = file->Path.c_str(); + size_t length; + /* This converts /one/two/path.txt to /one/two/ */ + cwk_path_get_dirname(path, &length); + std::string dirName(path, length); + + /* Check if the file is at the same level as the current directory */ + if (dirName == *pathStack.back()) + { + parent = parentStack.back(); + parent->Children.push_back(file); + file->Parent = parent; + // debug("adding \"%s\" to \"%s\"", file->Path.c_str(), parent->Path.c_str()); + continue; + } + + /* Check if the file is at a higher level */ + for (size_t i = 0; i < parentStack.size(); i++) + { + if (dirName != *pathStack[i]) + continue; + + /* Adjust vectors */ + while (!parentStack.empty()) + { + if (dirName == *pathStack.back()) + break; + + // debug("popping \"%s\"", pathStack.back()->c_str()); + parentStack.pop_back(); + pathStack.pop_back(); + } + + parent = parentStack.back(); + parent->Children.push_back(file); + file->Parent = parent; + // debug("adding \"%s\" to \"%s\"", file->Path.c_str(), parent->Path.c_str()); + } + } + + std::function ustarTree = [&](USTARInode *node, int level, const std::string &prefix) + { + debug("%*s\"%s\"%ld", level * 4, prefix.c_str(), node->Name.c_str(), node->Node.Offset); + off_t offset = 2; /* 0 . | 1 .. */ + for (auto &child : node->Children) + { +#ifdef DEBUG + if (offset <= 2) + { + if (offset == 2) + offset = 0; + /* Pseudo directories . and .. */ + USTARInode pseudoDot{}; + pseudoDot.Node = node->Node; + pseudoDot.Name = "."; + pseudoDot.Node.Offset = offset++; + USTARInode pseudoDDot{}; + pseudoDDot.Node = node->Parent ? node->Parent->Node : node->Node; + pseudoDDot.Name = ".."; + pseudoDDot.Node.Offset = offset++; + ustarTree(&pseudoDot, level + 1, "|-- "); + ustarTree(&pseudoDDot, level + 1, "|-- "); + } +#endif + + child->Node.Offset = offset++; + ustarTree(child, level + 1, child == node->Children.back() ? "`-- " : "|-- "); + } + }; + + ustarTree(tmpNodes[0], 0, ""); + return 0; + } + }; + + std::vector Namespaces; + int USTAR_AllocateInode(FileSystemInfo *Info, Inode **Result) { - assert(!"NOT IMPLEMENTED"); + assert(!"not implemented"); } int USTAR_DeleteInode(FileSystemInfo *Info, Inode *Node) { - assert(!"NOT IMPLEMENTED"); + assert(!"not implemented"); } int USTAR_Synchronize(FileSystemInfo *Info, Inode *Node) { - assert(!"NOT IMPLEMENTED"); + assert(!"not implemented"); } int USTAR_Destroy(FileSystemInfo *Info) { - assert(!"NOT IMPLEMENTED"); + assert(!"not implemented"); } - int USTAR_Probe(void *Device) + int USTAR_Probe(FileSystemDevice *Device) { - assert(!"NOT IMPLEMENTED"); + func("%#lx", Device); + uint8_t buffer[TAR_BLOCK_SIZE]; + int bytesRead = 0; + + if (Device->Block) + bytesRead = Device->Block->Ops->Read(nullptr, buffer, TAR_BLOCK_SIZE, 0); + else if (Device->inode.node && Device->inode.ops && Device->inode.ops->Read) + bytesRead = Device->inode.ops->Read(Device->inode.node, buffer, TAR_BLOCK_SIZE, 0); + else + return -EINVAL; + + if (bytesRead != TAR_BLOCK_SIZE) + return -EIO; + + TarHeader *hdr = (TarHeader *)buffer; + + if (strncmp(hdr->signature, TMAGIC, TMAGLEN) != 0) + { + /* For some reason if GRUB inflates the archive, the magic is "ustar " */ + if (strncmp(hdr->signature, TMAGIC, TMAGLEN - 1) == 0) + return 0; + + debug("Invalid signature!"); + return -ENODEV; + } + + return 0; } - int USTAR_Mount(FileSystemInfo *FS, Inode **Root, void *Device) + int USTAR_Mount(FileSystemInfo *FS, Inode **Root, FileSystemDevice *Device) { - assert(!"NOT IMPLEMENTED"); + USTARInstance *instance = new USTARInstance(); + instance->Device = *Device; + if (instance->ScanArchiveFromDevice() < 0) + { + delete instance; + return -EIO; + } + // Find root inode (should be index 0) + auto it = instance->Files.find(0); + if (it == instance->Files.end()) + { + delete instance; + return -ENOENT; + } + *Root = &it->second->Node; + (*Root)->PrivateData = instance; + Namespaces.push_back(instance); + return 0; } int USTAR_Unmount(FileSystemInfo *FS) { - assert(!"NOT IMPLEMENTED"); + assert(!"not implemented"); } - int USTAR_Lookup(Inode *Parent, const char *Name, Inode **Result) - { - assert(!"NOT IMPLEMENTED"); - } - - int USTAR_Create(Inode *Parent, const char *Name, mode_t Mode, Inode **Result) - { - assert(!"NOT IMPLEMENTED"); - } - - int USTAR_Remove(Inode *Parent, const char *Name) - { - assert(!"NOT IMPLEMENTED"); - } - - int USTAR_Rename(Inode *Parent, const char *OldName, const char *NewName) - { - assert(!"NOT IMPLEMENTED"); - } - - ssize_t USTAR_Read(Inode *Node, void *Buffer, size_t Size, off_t Offset) - { - assert(!"NOT IMPLEMENTED"); - } - - ssize_t USTAR_Write(Inode *Node, const void *Buffer, size_t Size, off_t Offset) - { - assert(!"NOT IMPLEMENTED"); - } - - int USTAR_Truncate(Inode *Node, off_t Size) - { - assert(!"NOT IMPLEMENTED"); - } - - int USTAR_Open(Inode *Node, int Flags, mode_t Mode) - { - assert(!"NOT IMPLEMENTED"); - } - - int USTAR_Close(Inode *Node) - { - assert(!"NOT IMPLEMENTED"); - } - - int USTAR_Ioctl(Inode *Node, unsigned long Request, void *Argp) - { - assert(!"NOT IMPLEMENTED"); - } - - ssize_t USTAR_ReadDir(Inode *Node, kdirent *Buffer, size_t Size, off_t Offset, off_t Entries) - { - assert(!"NOT IMPLEMENTED"); - } - - int USTAR_MkDir(Inode *Parent, const char *Name, mode_t Mode, Inode **Result) - { - assert(!"NOT IMPLEMENTED"); - } - - int USTAR_RmDir(Inode *Parent, const char *Name) - { - assert(!"NOT IMPLEMENTED"); - } - - int USTAR_SymLink(Inode *Parent, const char *Name, const char *Target, Inode **Result) - { - assert(!"NOT IMPLEMENTED"); - } - - ssize_t USTAR_ReadLink(Inode *Node, char *Buffer, size_t Size) - { - assert(!"NOT IMPLEMENTED"); - } - - off_t USTAR_Seek(Inode *Node, off_t Offset) - { - assert(!"NOT IMPLEMENTED"); - } - - int USTAR_Stat(Inode *Node, kstat *Stat) - { - assert(!"NOT IMPLEMENTED"); - } + int USTAR_Lookup(Inode *p, const char *nm, Inode **r) { return ((USTARInstance *)p->PrivateData)->Lookup(p, nm, r); } + int USTAR_Create(Inode *p, const char *nm, mode_t m, Inode **r) { return ((USTARInstance *)p->PrivateData)->Create(p, nm, m, r); } + int USTAR_Remove(Inode *p, const char *nm) { return ((USTARInstance *)p->PrivateData)->Remove(p, nm); } + int USTAR_Rename(Inode *p, const char *on, const char *nn) { return ((USTARInstance *)p->PrivateData)->Rename(p, on, nn); } + ssize_t USTAR_Read(Inode *n, void *b, size_t s, off_t o) { return ((USTARInstance *)n->PrivateData)->Read(n, b, s, o); } + ssize_t USTAR_Write(Inode *n, const void *b, size_t s, off_t o) { return ((USTARInstance *)n->PrivateData)->Write(n, b, s, o); } + int USTAR_Truncate(Inode *n, off_t s) { return ((USTARInstance *)n->PrivateData)->Truncate(n, s); } + int USTAR_Open(Inode *n, int f, mode_t m) { return ((USTARInstance *)n->PrivateData)->Open(n, f, m); } + int USTAR_Close(Inode *n) { return ((USTARInstance *)n->PrivateData)->Close(n); } + int USTAR_Ioctl(Inode *n, unsigned long rq, void *ap) { return ((USTARInstance *)n->PrivateData)->Ioctl(n, rq, ap); } + ssize_t USTAR_ReadDir(Inode *n, kdirent *b, size_t s, off_t o, off_t Entries) { return ((USTARInstance *)n->PrivateData)->ReadDir(n, b, s, o, Entries); } + int USTAR_MkDir(Inode *p, const char *nm, mode_t m, Inode **r) { return ((USTARInstance *)p->PrivateData)->MkDir(p, nm, m, r); } + int USTAR_RmDir(Inode *p, const char *nm) { return ((USTARInstance *)p->PrivateData)->RmDir(p, nm); } + int USTAR_SymLink(Inode *p, const char *nm, const char *t, Inode **r) { return ((USTARInstance *)p->PrivateData)->SymLink(p, nm, t, r); } + ssize_t USTAR_ReadLink(Inode *n, char *b, size_t s) { return ((USTARInstance *)n->PrivateData)->ReadLink(n, b, s); } + off_t USTAR_Seek(Inode *n, off_t o) { return ((USTARInstance *)n->PrivateData)->Seek(n, o); } + int USTAR_Stat(Inode *n, kstat *st) { return ((USTARInstance *)n->PrivateData)->Stat(n, st); } static SuperBlockOperations ustarSuperOps = { .AllocateInode = USTAR_AllocateInode, @@ -185,7 +1063,13 @@ namespace Driver::UnixStandardTAR return 0; } - int Final() { return 0; } + int Final() + { + for (auto &&i : Namespaces) + delete i; + return 0; + } + int Panic() { return 0; } int Probe() { return 0; } diff --git a/Kernel/fs/vfs.cpp b/Kernel/fs/vfs.cpp index 28f7db06..0dceeb82 100644 --- a/Kernel/fs/vfs.cpp +++ b/Kernel/fs/vfs.cpp @@ -524,6 +524,26 @@ namespace vfs return Target->__Close(); } + FileSystemInfo *Virtual::Probe(FileSystemDevice *Device) + { + for (auto &&i : FileSystems) + { + if (i.second->SuperOps.Probe == nullptr) + { + debug("%s does not support probing", i.second->Name); + continue; + } + + int ret = i.second->SuperOps.Probe(Device); + if (ret == 0) + return i.second; + debug("%s returned %d", i.second->Name, ret); + } + + debug("No filesystems matched"); + return nullptr; + } + eNode Virtual::Mount(Node &Parent, Inode *inode, std::string Name, FileSystemInfo *fsi) { assert(Parent); @@ -541,6 +561,27 @@ namespace vfs return {ret, 0}; } + eNode Virtual::Mount(Node &Parent, std::string Name, FileSystemInfo *fsi, FileSystemDevice *Device) + { + Inode *inode; + int ret = fsi->SuperOps.Mount(fsi, &inode, Device); + if (ret != 0) + return {nullptr, ret}; + + return this->Mount(Parent, inode, Name, fsi); + + // Node node = std::make_shared(); + // node->inode = nullptr; /* FIXME: ??? */ + // node->fsi = fsi; + // node->Flags.MountPoint = true; + + // node->Name = Name; + // node->Path = fs->NormalizePath(Parent, Parent->Path + "/" + Name); + // node->Parent = Parent; + // Parent->Children.push_back(node); + // return {node, 0}; + } + int Virtual::Umount(Node &node) { if (!node->Flags.MountPoint) diff --git a/Kernel/include/fs/vfs.hpp b/Kernel/include/fs/vfs.hpp index 0cabf605..e35fec1a 100644 --- a/Kernel/include/fs/vfs.hpp +++ b/Kernel/include/fs/vfs.hpp @@ -128,25 +128,25 @@ namespace vfs /** * @brief Read directory entries - * + * * @note This function includes "." and ".." - * - * @param Target - * @param Buffer - * @param Size - * @param Offset - * @param Entries - * @return + * + * @param Target + * @param Buffer + * @param Size + * @param Offset + * @param Entries + * @return */ ssize_t ReadDirectory(Node &Target, kdirent *Buffer, size_t Size, off_t Offset, off_t Entries); /** * @brief Read directory entries - * + * * @note This function does NOT include "." and ".." - * - * @param Target - * @return + * + * @param Target + * @return */ std::list ReadDirectory(Node &Target); @@ -165,8 +165,76 @@ namespace vfs #pragma region Mounting + FileSystemInfo *Probe(FileSystemDevice *Device); + + FileSystemInfo *Probe(Node &node) + { + FileSystemDevice dev; + dev.inode.node = node->inode; + dev.inode.ops = &node->fsi->Ops; + dev.Block = nullptr; + return this->Probe(&dev); + } + + FileSystemInfo *Probe(BlockDevice *Device) + { + FileSystemDevice dev; + dev.inode.node = nullptr; + dev.inode.ops = nullptr; + dev.Block = Device; + return this->Probe(&dev); + } + + /** + * @brief Mount a filesystem + * + * + * + * @param Parent Parent Node + * @param inode Inode from the filesystem as root of the mount point + * @param Name Name of the mount point + * @param fsi FileSystemInfo structure + * @return eNode{Node, errno} + */ eNode Mount(Node &Parent, Inode *inode, std::string Name, FileSystemInfo *fsi); + /** + * @brief Mount a filesystem + * + * + * + * @param Parent Parent Node + * @param Name Name of the mount point + * @param fsi FileSystemInfo structure + * @param Device Device to mount + * @return eNode{Node, errno} + */ + eNode Mount(Node &Parent, std::string Name, FileSystemInfo *fsi, FileSystemDevice *Device); + + /** + * Wrapper for Mount(Node &Parent, std::string Name, FileSystemInfo *fsi, FileSystemDevice *Device) + */ + eNode Mount(Node &Parent, std::string Name, FileSystemInfo *fsi, Node Device) + { + FileSystemDevice dev; + dev.inode.node = Device->inode; + dev.inode.ops = &Device->fsi->Ops; + dev.Block = nullptr; + return this->Mount(Parent, Name, fsi, &dev); + } + + /** + * Wrapper for Mount(Node &Parent, std::string Name, FileSystemInfo *fsi, FileSystemDevice *Device) + */ + eNode Mount(Node &Parent, std::string Name, FileSystemInfo *fsi, BlockDevice *Device) + { + FileSystemDevice dev; + dev.inode.node = nullptr; + dev.inode.ops = nullptr; + dev.Block = Device; + return this->Mount(Parent, Name, fsi, &dev); + } + int Umount(Node &node); int Umount(Node &Parent, std::string Name); diff --git a/Kernel/include/interface/fs.h b/Kernel/include/interface/fs.h index c55969d5..4e4b3a58 100644 --- a/Kernel/include/interface/fs.h +++ b/Kernel/include/interface/fs.h @@ -323,6 +323,28 @@ struct InodeOperations } __attribute__((packed)); struct FileSystemInfo; + +struct FileSystemDevice +{ + struct + { + /** + * @brief Inode + * + * If the device is a block device, this will be NULL. + */ + struct Inode *node; + struct InodeOperations *ops; + } inode; + + /** + * @brief Block Device + * + * If the device is a block device, this will be non-NULL. + */ + struct BlockDevice *Block; +}; + struct SuperBlockOperations { int (*AllocateInode)(struct FileSystemInfo *Info, struct Inode **Result); @@ -360,7 +382,7 @@ struct SuperBlockOperations * * @return Zero on success, otherwise an error code. */ - int (*Probe)(void *Device); + int (*Probe)(struct FileSystemDevice *Device); /** * Mount the filesystem. @@ -369,11 +391,11 @@ struct SuperBlockOperations * * @param FS Filesystem to mount. * @param Root Pointer to the root inode. - * @param Device Device to mount. + * @param Device Device to mount. This pointer will be undefined after the function returns! * * @return Zero on success, otherwise an error code. */ - int (*Mount)(struct FileSystemInfo *FS, struct Inode **Root, void *Device); + int (*Mount)(struct FileSystemInfo *FS, struct Inode **Root, struct FileSystemDevice *Device); /** * Unmount the filesystem. diff --git a/Kernel/subsystem/linux/init.cpp b/Kernel/subsystem/linux/init.cpp index faaddb3a..a3c7bbbc 100644 --- a/Kernel/subsystem/linux/init.cpp +++ b/Kernel/subsystem/linux/init.cpp @@ -18,7 +18,6 @@ #include #include "../../kernel.h" -#include #include namespace Subsystem::Linux @@ -78,13 +77,21 @@ namespace Subsystem::Linux debug("path=%s", uPath); ini_destroy(ini); - if (fs->Lookup(root, uPath) != false) + eNode ret = fs->Lookup(root, uPath); + if (ret != false) { - root = fs->Lookup(root, uPath); - - // if (TestAndInitializeUSTAR(moduleAddress, moduleSize, 0)) - // { - // } + Node tar = ret; + FileSystemInfo *fsi = fs->Probe(tar); + if (fsi == nullptr) + { + warn("Couldn't probe rootfs %s", uPath); + __CreateStubRoot(); + } + else + { + Node mnt = fs->Lookup(root, "/mnt"); + auto node = fs->Mount(mnt, "linux", fsi, tar); + } } else { diff --git a/Kernel/subsystem/windows/init.cpp b/Kernel/subsystem/windows/init.cpp index 43fa881e..5468bac3 100644 --- a/Kernel/subsystem/windows/init.cpp +++ b/Kernel/subsystem/windows/init.cpp @@ -18,6 +18,7 @@ #include #include "../../kernel.h" +#include namespace Subsystem::Windows { @@ -47,6 +48,50 @@ namespace Subsystem::Windows { if (fs->RootExists(2) == false) { + Node root = fs->GetRoot(0); + Node cfg = fs->Lookup(root, "/sys/cfg/subsystem/windows"); + if (cfg) + { + struct kstat st; + fs->Stat(cfg, &st); + + std::unique_ptr buf(new char[st.Size]); + fs->Read(cfg, buf.get(), st.Size, 0); + + ini_t *ini = ini_load(buf.get(), NULL); + int section = ini_find_section(ini, "rootfs", NULL); + int pathIdx = ini_find_property(ini, section, "path", NULL); + const char *uPath = ini_property_value(ini, section, pathIdx); + debug("path=%s", uPath); + ini_destroy(ini); + + eNode ret = fs->Lookup(root, uPath); + if (ret != false) + { + Node tar = ret; + FileSystemInfo *fsi = fs->Probe(tar); + if (fsi == nullptr) + { + warn("Couldn't probe rootfs %s", uPath); + __CreateStubRoot(); + } + else + { + Node mnt = fs->Lookup(root, "/mnt"); + auto node = fs->Mount(mnt, "windows", fsi, tar); + } + } + else + { + warn("Couldn't find rootfs path %s", uPath); + __CreateStubRoot(); + } + } + else + { + warn("Couldn't open /sys/cfg/subsystem/windows"); + __CreateStubRoot(); + } } } }