/* 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 . */ #ifndef __FENNIX_KERNEL_FILESYSTEM_H__ #define __FENNIX_KERNEL_FILESYSTEM_H__ #include #include #include #include #include #include #include #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 /** Other users have execute permission. */ #define S_IXOTH 0001 /** Other users have write permission. */ #define S_IWOTH 0002 /** Other users have read permission. */ #define S_IROTH 0004 /** Other users have read, write, and execute permissions. */ #define S_IRWXO 0007 /** Group members have execute permission. */ #define S_IXGRP 0010 /** Group members have write permission. */ #define S_IWGRP 0020 /** Group members have read permission. */ #define S_IRGRP 0040 /** Group members have read, write, and execute permissions. */ #define S_IRWXG 0070 /** The file owner has execute permission. */ #define S_IXUSR 0100 /** The file owner has write permission. */ #define S_IWUSR 0200 /** The file owner has read permission. */ #define S_IRUSR 0400 /** The file owner has read, write, * and execute permissions. */ #define S_IRWXU 0700 #define O_RDONLY 00 #define O_WRONLY 01 #define O_RDWR 02 #define O_CREAT 0100 #define O_EXCL 0200 #define O_TRUNC 01000 #define O_APPEND 02000 #define O_CLOEXEC 02000000 #define S_IFIFO 0010000 #define S_IFCHR 0020000 #define S_IFDIR 0040000 #define S_IFBLK 0060000 #define S_IFREG 0100000 #define S_IFLNK 0120000 #define S_IFSOCK 0140000 #define S_IFMT 0170000 #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) /** * @struct stat * @brief Structure holding information about a file, as returned by the stat function. * * The 'stat' structure provides information about a file, including its size, ownership, permissions, * and other attributes. It is used with the stat function to query file status. */ struct stat { /** Device ID of the file. */ dev_t st_dev; /** Inode number. */ ino_t st_ino; /** File type and mode. */ mode_t st_mode; /** Number of hard links. */ nlink_t st_nlink; /** User ID of the file's owner. */ uid_t st_uid; /** Group ID of the file's owner. */ gid_t st_gid; /** Device ID for special files. */ dev_t st_rdev; /** Size of the file in bytes. */ off_t st_size; /** Time of last access. */ time_t st_atime; /** Time of last modification. */ time_t st_mtime; /** Time of last status change. */ time_t st_ctime; /** Optimal I/O block size. */ blksize_t st_blksize; /** Number of blocks allocated. */ blkcnt_t st_blocks; /** Additional file attributes. */ mode_t st_attr; }; /** * @struct stat64 * @brief Extended structure for large file support, holding information about a file. * * The 'stat64' structure is similar to 'struct stat' but is extended to support large files on 32-bit systems. * It is used with the stat64 function for large file support. */ struct stat64 { /** Device ID of the file. */ dev_t st_dev; /** Inode number. */ ino64_t st_ino; /** File type and mode. */ mode_t st_mode; /** Number of hard links. */ nlink_t st_nlink; /** User ID of the file's owner. */ uid_t st_uid; /** Group ID of the file's owner. */ gid_t st_gid; /** Device ID for special files. */ dev_t st_rdev; /** Size of the file in bytes. */ off64_t st_size; /** Time of last access. */ time_t st_atime; /** Time of last modification. */ time_t st_mtime; /** Time of last status change. */ time_t st_ctime; /** Optimal I/O block size. */ blksize_t st_blksize; /** Number of blocks allocated. */ blkcnt64_t st_blocks; /** Additional file attributes. */ mode_t st_attr; }; static inline int ConvertFileFlags(const char *Mode) { int Flags = 0; if (strchr(Mode, '+')) Flags |= O_RDWR; else if (*Mode == 'r') Flags |= O_RDONLY; else Flags |= O_WRONLY; if (strchr(Mode, 'x')) Flags |= O_EXCL; if (strchr(Mode, 'e')) Flags |= O_CLOEXEC; if (*Mode != 'r') Flags |= O_CREAT; if (*Mode == 'w') Flags |= O_TRUNC; if (*Mode == 'a') Flags |= O_APPEND; return Flags; } namespace vfs { enum NodeType : mode_t { NODE_TYPE_NONE = 0x0, FILE = S_IFREG, DIRECTORY = S_IFDIR, CHARDEVICE = S_IFCHR, BLOCKDEVICE = S_IFBLK, PIPE = S_IFIFO, SYMLINK = S_IFLNK, MOUNTPOINT = S_IFDIR }; class RefNode; /** * Virtual filesystem node * * @note https://isocpp.org/wiki/faq/freestore-mgmt#delete-this */ class Node { private: NewLock(NodeLock); public: virtual int open(int Flags, mode_t Mode); virtual int close(); virtual size_t read(uint8_t *Buffer, size_t Size, off_t Offset); virtual size_t write(uint8_t *Buffer, size_t Size, off_t Offset); virtual int ioctl(unsigned long Request, void *Argp); // virtual int stat(struct stat *Stat); // virtual int lstat(struct stat *Stat); // virtual int fstat(struct stat *Stat); // virtual int unlink(); // virtual int mkdir(mode_t Mode); // virtual int rmdir(); // virtual int rename(const char *NewName); // virtual int chmod(mode_t Mode); // virtual int chown(uid_t User, gid_t Group); // virtual int truncate(off_t Size); // virtual int symlink(const char *Target); // virtual int readlink(char *Buffer, size_t Size); // virtual int mount(Node *Target); // virtual int umount(); typedef int (*open_t)(int, mode_t); typedef int (*close_t)(); typedef size_t (*read_t)(uint8_t *, size_t, off_t); typedef size_t (*write_t)(uint8_t *, size_t, off_t); typedef int (*ioctl_t)(unsigned long, void *); open_t open_ptr = nullptr; close_t close_ptr = nullptr; read_t read_ptr = nullptr; write_t write_ptr = nullptr; ioctl_t ioctl_ptr = nullptr; class Virtual *vFS = nullptr; Node *Parent = nullptr; const char *Name; const char *FullPath; NodeType Type; ino_t IndexNode; const char *Symlink; Node *SymlinkTarget; mode_t Mode; uid_t UserIdentifier; gid_t GroupIdentifier; dev_t DeviceMajor; dev_t DeviceMinor; time_t AccessTime; time_t ModifyTime; time_t ChangeTime; off_t Size; std::vector Children; std::vector References; RefNode *CreateReference(); void RemoveReference(RefNode *Reference); /** * Create a new node * * @param Parent The parent node * @param Name The name of the node * @param Type The type of the node * @param NoParent If true, the Parent will * be used as a hint for the parent node, but it * won't be set as the parent node. * @param fs The virtual filesystem (only if * NoParent is set) * @param Err If not nullptr, the function will * write the error code to the given address. */ Node(Node *Parent, const char *Name, NodeType Type, bool NoParent = false, Virtual *fs = nullptr, int *Err = nullptr); virtual ~Node(); }; class RefNode { private: std::atomic_int64_t FileOffset = 0; off_t FileSize = 0; Node *n; RefNode *SymlinkTo; public: void *SpecialData; decltype(FileSize) &Size = FileSize; decltype(n) &node = n; size_t read(uint8_t *Buffer, size_t Size); size_t write(uint8_t *Buffer, size_t Size); off_t seek(off_t Offset, int Whence); int ioctl(unsigned long Request, void *Argp); RefNode(Node *node); ~RefNode(); friend class Virtual; friend class FileDescriptorTable; }; class Virtual { private: Node *FileSystemRoot = nullptr; NewLock(VirtualLock); Node *GetParent(const char *Path, Node *Parent); /** @note This function is NOT thread safe */ Node *GetNodeFromPath_Unsafe(const char *Path, Node *Parent = nullptr); public: Node *nRoot = nullptr; Node *GetNodeFromPath(const char *Path, Node *Parent = nullptr); bool PathIsRelative(const char *Path); Node *GetRootNode() { return FileSystemRoot; } const char *NormalizePath(const char *Path, Node *Parent = nullptr); bool PathExists(const char *Path, Node *Parent = nullptr); Node *Create(const char *Path, NodeType Type, Node *Parent = nullptr); Node *CreateLink(const char *Path, const char *Target, Node *Parent); int Delete(const char *Path, bool Recursive = false, Node *Parent = nullptr); int Delete(Node *Path, bool Recursive = false, Node *Parent = nullptr); /** * Open a file * @param Path The path to the file, relative or absolute. The buffer shouldn't be modified while the function is running. * @param Parent Pointer to the parent node, if nullptr, the root node will be used. * @return A pointer to the vfs::ReferenceNode, or nullptr if the file doesn't exist. */ RefNode *Open(const char *Path, Node *Parent = nullptr); Node *CreateIfNotExists(const char *Path, NodeType Type, Node *Parent); Virtual(); ~Virtual(); friend class Node; }; class FileDescriptorTable { public: struct Fildes { RefNode *Handle = nullptr; mode_t Mode = 0; int Flags = 0; int Descriptor = -1; int operator==(const Fildes &other) { return this->Handle == other.Handle && this->Mode == other.Mode && this->Flags == other.Flags && this->Descriptor == other.Descriptor; } int operator!=(const Fildes &other) { return !(*this == other); } } __attribute__((packed)) nullfd; private: std::vector FileDescriptors; std::vector FildesDuplicates; vfs::Node *fdDir = nullptr; Fildes &GetFileDescriptor(int FileDescriptor); FileDescriptorTable::Fildes &GetDupFildes(int FileDescriptor); int ProbeMode(mode_t Mode, int Flags); int AddFileDescriptor(const char *AbsolutePath, mode_t Mode, int Flags); int RemoveFileDescriptor(int FileDescriptor); int GetFreeFileDescriptor(); public: Fildes &GetDescriptor(int FileDescriptor); const char *GetAbsolutePath(int FileDescriptor); std::vector &GetFileDescriptors() { return FileDescriptors; } std::vector &GetFileDescriptorsDuplicates() { return FildesDuplicates; } RefNode *GetRefNode(int FileDescriptor); int GetFlags(int FileDescriptor); int SetFlags(int FileDescriptor, int Flags); void Fork(FileDescriptorTable *Parent); int _open(const char *pathname, int flags, mode_t mode); int _creat(const char *pathname, mode_t mode); ssize_t _read(int fd, void *buf, size_t count); ssize_t _write(int fd, const void *buf, size_t count); int _close(int fd); off_t _lseek(int fd, off_t offset, int whence); int _stat(const char *pathname, struct stat *statbuf); int _fstat(int fd, struct stat *statbuf); int _lstat(const char *pathname, struct stat *statbuf); int _dup(int oldfd); int _dup2(int oldfd, int newfd); int _ioctl(int fd, unsigned long request, void *argp); FileDescriptorTable(void *Owner); ~FileDescriptorTable(); }; } int fopen(const char *pathname, const char *mode); int creat(const char *pathname, mode_t mode); ssize_t fread(int fd, void *buf, size_t count); ssize_t fwrite(int fd, const void *buf, size_t count); int fclose(int fd); off_t lseek(int fd, off_t offset, int whence); int stat(const char *pathname, struct stat *statbuf); int fstat(int fd, struct stat *statbuf); int lstat(const char *pathname, struct stat *statbuf); #endif // !__FENNIX_KERNEL_FILESYSTEM_H__