Untitled
unknown
plain_text
a year ago
15 kB
9
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