Untitled

 avatar
unknown
plain_text
18 days ago
15 kB
5
Indexable
#pragma once

#include "ide.h"
#include "shared.h"
#include "block_io.h"
#include "debug.h"

struct Inode {
    uint16_t i_mode;
    uint16_t i_uid;
    uint32_t i_size;
    uint32_t i_atime;
    uint32_t i_ctime;
    uint32_t i_mtime;
    uint32_t i_dtime;
    uint16_t i_gid;
    uint16_t i_links_count;
    uint32_t i_blocks;
    uint32_t i_flags;
    uint32_t i_osd1;
    uint32_t i_block[15];
    uint32_t i_generation;
    uint32_t i_file_acl;
    uint32_t i_dir_acl;
    uint32_t i_faddr;
    uint8_t  i_osd2[12];
};

struct Superblock {
    uint32_t s_inodes_count;
    uint32_t s_blocks_count;
    uint32_t s_r_blocks_count;
    uint32_t s_free_blocks_count;
    uint32_t s_free_inodes_count;
    uint32_t s_first_data_block;
    uint32_t s_log_block_size;
    uint32_t s_log_frag_size;
    uint32_t s_blocks_per_group;
    uint32_t s_frags_per_group;
    uint32_t s_inodes_per_group;
    uint32_t s_mtime;
    uint32_t s_wtime;
    uint16_t s_mnt_count;
    uint16_t s_max_mnt_count;
    uint16_t s_magic;
    uint16_t s_state;
    uint16_t s_errors;
    uint16_t s_minor_rev_level;
    uint32_t s_lastcheck;
    uint32_t s_checkinterval;
    uint32_t s_creator_os;
    uint32_t s_rev_level;
    uint16_t s_def_resuid;
    uint16_t s_def_resgid;

    // extended 
    uint32_t s_first_ino;
    uint16_t s_inode_size;
    uint16_t s_block_group_nr;
    uint32_t s_feature_compat;
    uint32_t s_feature_incompat;
    uint32_t s_feature_ro_compat;
    uint8_t  s_uuid[16];
    char     s_volume_name[16];
    char     s_last_mounted[64];
    uint32_t s_algorithm_usage_bitmap;
    uint8_t  s_prealloc_blocks;
    uint8_t  s_prealloc_dir_blocks;
    uint16_t s_reserved_gdt_blocks;
    uint8_t  s_journal_uuid[16];
    uint32_t s_journal_inum;
    uint32_t s_journal_dev;
    uint32_t s_last_orphan;
    uint32_t s_hash_seed[4];
    uint8_t  s_def_hash_version;
    uint8_t  s_reserved_char_pad;
    uint16_t s_reserved_word_pad;
    uint32_t s_default_mount_opts;
    uint32_t s_first_meta_bg;
    uint32_t s_mkfs_time;
    uint32_t s_jnl_blocks[17];
};

struct BlockGD {
    uint32_t bg_block_bitmap;
    uint32_t bg_inode_bitmap;
    uint32_t bg_inode_table;
    uint16_t bg_free_blocks_count;
    uint16_t bg_free_inodes_count;
    uint16_t bg_used_dirs_count;
    uint16_t bg_pad;
    uint32_t bg_reserved[3];
};

struct DirEntry {
    uint32_t inode;
    uint16_t rec_len;
    uint8_t name_len;
    uint8_t file_type;
    char name[255];
};

// A wrapper around an i-node
class Node : public BlockIO { // we implement BlockIO because we
    // represent data

public:
    Inode* currInode;
    StrongPtr<Ide> currIde;
    uint32_t number;

    Node(Inode* inode, uint32_t num, StrongPtr<Ide> ide, uint32_t block_size)
        : BlockIO(block_size), currInode(inode), currIde(ide), number(num) {
    }


    // How many bytes does this i-node represent
    //    - for a file, the size of the file
    //    - for a directory, implementation dependent
    //    - for a symbolic link, the length of the name
    uint32_t size_in_bytes() override {
        return currInode->i_size;
    }

    // read the given block (panics if the block number is not valid)
    // remember that block size is defined by the file system not the device
    void read_block(uint32_t number, char* buffer) override;

    inline uint16_t get_type() {
        return currInode->i_mode & 0xF000;
    }

    // true if this node is a directory
    bool is_dir() {
        return (currInode->i_mode & 0xF000) == 0x4000;
    }

    // true if this node is a file
    bool is_file() {
        return (currInode->i_mode & 0xF000) == 0x8000;
    }

    // true if this node is a symbolic link
    bool is_symlink() {
        return (currInode->i_mode & 0xF000) == 0xA000;
    }

    // If this node is a symbolic link, fill the buffer with
    // the name the link referes to.
    //
    // Panics if the node is not a symbolic link
    //
    // The buffer needs to be at least as big as the the value
    // returned by size_in_byte()
    void get_symbol(char* buffer);

    // Returns the number of hard links to this node
    uint32_t n_links() {
        return currInode->i_links_count;
    }

    void show(const char* msg) {
    }

    // Returns the number of entries in a directory node
    //
    // Panics if not a directory
    uint32_t entry_count();
};


// This class encapsulates the implementation of the Ext2 file system
class Ext2 {
public:
    Superblock* sb;
    StrongPtr<Node> root;
    StrongPtr<Ide> ide;
    // Mount an existing file system residing on the given device
    // Panics if the file system is invalid
    Ext2(StrongPtr<Ide> ide);

    // Returns the block size of the file system. Doesn't have
    // to match that of the underlying device
    uint32_t get_block_size() {
        return 1024 << sb->s_log_block_size;
        // block size is not stored directly, block size is 0, 1, 2, etc.
    }

    // Returns the actual size of an i-node. Ext2 specifies that
    // an i-node will have a minimum size of 128B but could have
    // more bytes for extended attributes
    uint32_t get_inode_size() {
        return sb->s_inode_size;
    }

    // Traverse "path" starting at the current node
    //
    // Follows symbolic links for intermediate nodes
    //
    // Returns StrongPtr{} (null) in case of errors. For example:
    //   - node is not a directory
    //   - the path is invalid
    //   - too many symbolic link traversals (> 100)
    //
    StrongPtr<Node> find(StrongPtr<Node> dir, const char* name) {
        // check the path if it is a relative or not
        // then check the dir

        // first check if the passed in dir is a directory or symbolic link
        if (!dir->is_dir() && !dir->is_symlink()) {
            return StrongPtr<Node>();
        }

        // get the length of the path
        int name_len = 0;
        while (name[name_len] != '\0') {
            name_len++;
        }

        // copy the given path to a mutable copy
        char* curr_name = new char[name_len + 1];
        for (int i = 0; i < name_len; i++) {
            curr_name[i] = name[i];
        }
        curr_name[name_len] = '\0';

        if (curr_name[0] == '/') {
            dir = root;
        }
        int sl_count = 0;

        while (*curr_name != '\0') {
            // skip any '/'
            while (*curr_name == '/') {
                curr_name++;
            }
            if (*curr_name == '\0') {
                break;
            }

            // get the next path
            // ex: /Users/Thomas/Downloads/File.txt -> Users will be the target
            char target_name[256];
            int j = 0;
            while (curr_name[j] != '/' && curr_name[j] != '\0' && j < 255) {
                target_name[j] = curr_name[j];
                j++;
            }
            target_name[j] = '\0';

            StrongPtr<Node> next_node;
            uint32_t entry_count = dir->entry_count();
            uint32_t offset = 0;
            bool match = false;

            if (dir->is_dir()) {
                Debug::printf("*** We are in a dir\n");
                // go through the directory entries
                for (uint32_t k = 0; k < entry_count; k++) {
                    DirEntry entry;
                    dir->read_all(offset, sizeof(DirEntry), (char* ) &entry);
                    offset += entry.rec_len;

                    Debug::printf("checking directory entry, inode=%d, name=%.*s\n", entry.inode, entry.name_len, entry.name);

                    match = (entry.name_len == (uint8_t) j); // First, check if lengths match
                    if (match) {
                        for (int l = 0; l < j; l++) {
                            if (entry.name[l] != target_name[l]) {
                                match = false;
                                break;
                            }
                        }
                    }

                    // if found then retrieve corresponding inode
                    if (match) {
                        Debug::printf("matched entry, fetching inode %d\n", entry.inode);

                        next_node = get_inode(entry.inode);

                        if (next_node == nullptr || next_node->currInode == nullptr) {
                            Debug::printf("*** ERROR: Failed to retrieve inode for %s\n", target_name);
                            //delete[] curr_name;
                            return StrongPtr<Node>();  // Return a valid null pointer instead of crashing
                        }

                        Debug::printf("successfully got inode %d\n", entry.inode);

                        if (next_node->is_file()) {
                            uint32_t file_size = next_node->size_in_bytes();
                            Debug::printf("file size: %d bytes\n", file_size);

                            if (file_size == 0) {
                                Debug::printf("ERROR: File size is 0, nothing to read!\n");
                            }
                            else {
                                char* buffer = new char[file_size + 1];  // Dynamically allocate correct size (+1 for null terminator)
                                Debug::printf("calling read_block() for file inode %d\n", next_node->number);

                                next_node->read_block(0, buffer);
                                buffer[file_size] = '\0';  // Null-terminate to avoid garbage output

                                Debug::printf("file content: %s\n", buffer);

                                //[] buffer; // Free allocated memory
                            }
                        }
                        break;
                    }
                }
                // if no matches, return a nullptr
                if (!match) {
                    //delete[] curr_name;
                    return StrongPtr<Node>();
                }
                // move to the next inode
                dir = next_node;
                curr_name += j;
                continue;
            }
            // check if it is a symlink
            if (dir->is_symlink()) {
                Debug::printf("*** We are in a symlink\n");
                sl_count++;

                if (sl_count > 100) {
                    Debug::printf("too many sym links");
                    //delete[] curr_name;
                    return StrongPtr<Node>();
                }
                // make a brand new target from the symlink
                char sl_target[256];
                dir->get_symbol(sl_target);
                if (sl_target[0] == '/') {
                    dir = root;
                }

                // copy new target into curr_name (overwrite it)
                int sl_target_len = 0;
                while (sl_target[sl_target_len] != '\0') {
                    sl_target_len++;
                }

                //delete[] curr_name;
                curr_name = new char[sl_target_len + 1];

                // now curr_name is the target of the symlink
                for (int i = 0; i < sl_target_len; i++) {
                    curr_name[i] = sl_target[i];
                }
                curr_name[sl_target_len] = '\0';

                continue;
            }
        }
        // now you are out of the while loop
        // make sure you have reaeched the correct path 
        if (!dir->is_dir() && !dir->is_symlink()) {
            // means that you have traversered throguh the whole name
            if (*curr_name == '\0') {
                //delete[] curr_name;
                return dir;
            }
            // means that you broke out of the loop without fully traversing
            else {
                //delete[] curr_name;
                return StrongPtr<Node>();
            }
        }
        if (dir == nullptr || dir->currInode == nullptr) {
            Debug::printf("ERROR: find() is returning an invalid node!\n");
            Debug::panic("invalid node returned from find()");
        }
        Debug::printf("find() successfully returning inode %d\n", dir->number);

        // delete[] curr_name;
        return dir;
    }

    // grabs the inode for find
    StrongPtr<Node> get_inode(uint32_t number) {

        Debug::printf("getting inode %d\n", number);
        // compute block group and the index directly
        uint32_t block_group = (number - 1) / sb->s_inodes_per_group;
        uint32_t index = (number - 1) % sb->s_inodes_per_group;

        // compute the block size 
        uint32_t block_size = get_block_size();

        // compute offset to the bgdt
        // offset = (superblock number + 1) * block_size
        uint32_t bgdt_offset = (block_size == 1024) ? 1024 * 2 : block_size;
        uint32_t bgdt_entry = bgdt_offset + (block_group * sizeof(BlockGD));

        // read the bgdt
        BlockGD bgd;
        this->ide->read_all(bgdt_entry, sizeof(BlockGD), (char*)&bgd);

        // figure out the inode's location based on the inode table
        uint32_t inode_table_block = bgd.bg_inode_table;
        uint32_t inode_offset = (inode_table_block * block_size) + (index * sizeof(Inode));

        // make a new inode object and read from disk
        Inode* ret_inode = new Inode();
        this->ide->read_all(inode_offset, sizeof(Inode), (char*)ret_inode);

        Debug::printf("read inode %d: mode=0x%x, size=%d, blocks=%d, flags=0x%x\n",
            number, ret_inode->i_mode, ret_inode->i_size, ret_inode->i_blocks, ret_inode->i_flags);

        // return a node with the inode
        StrongPtr<Node> node = StrongPtr<Node>(new Node(ret_inode, number, ide, get_block_size()));
        if (node == nullptr || node->currInode == nullptr) {
            Debug::printf("ERROR: get_inode() returned an invalid node for inode %d!\n", number);
            Debug::panic("invalid inode retrieved");
        }
        Debug::printf("trying to read block 0 for inode %d (file content block=%d)\n", number, node->currInode->i_block[0]);

        Debug::printf("i_block[] for inode %d: [%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d]\n",
            number, node->currInode->i_block[0], node->currInode->i_block[1], node->currInode->i_block[2], node->currInode->i_block[3],
            node->currInode->i_block[4], node->currInode->i_block[5], node->currInode->i_block[6], node->currInode->i_block[7],
            node->currInode->i_block[8], node->currInode->i_block[9], node->currInode->i_block[10], node->currInode->i_block[11],
            node->currInode->i_block[12], node->currInode->i_block[13], node->currInode->i_block[14]);

        return node;
    }
};
Editor is loading...
Leave a Comment