366 lines
14 KiB
C
366 lines
14 KiB
C
/// @file ext2.c
|
|
/// @author Enrico Fraccaroli (enry.frak@gmail.com)
|
|
/// @brief EXT2 driver.
|
|
/// @version 0.1
|
|
/// @date 2021-12-13
|
|
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
|
|
/// See LICENSE.md for details.
|
|
|
|
#include "fs/ext2.h"
|
|
|
|
#define __DEBUG_HEADER__ "[EXT2 ]"
|
|
#define __DEBUG_LEVEL__ 100
|
|
|
|
#include "process/scheduler.h"
|
|
#include "process/process.h"
|
|
#include "klib/spinlock.h"
|
|
#include "fs/vfs_types.h"
|
|
#include "sys/errno.h"
|
|
#include "io/debug.h"
|
|
#include "fs/vfs.h"
|
|
#include "string.h"
|
|
#include "stdio.h"
|
|
#include "fcntl.h"
|
|
|
|
#define EXT2_SUPERBLOCK_MAGIC 0xEF53
|
|
#define EXT2_INDIRECT_BLOCKS 12
|
|
#define EXT2_NAME_LEN 255
|
|
|
|
/// @brief The superblock contains all the information about the configuration
|
|
/// of the filesystem.
|
|
/// @details The primary copy of the superblock is stored at an offset of 1024
|
|
/// bytes from the start of the device, and it is essential to mounting the
|
|
/// filesystem. Since it is so important, backup copies of the superblock are
|
|
/// stored in block groups throughout the filesystem.
|
|
typedef struct ext2_superblock_t {
|
|
/// @brief Total number of inodes in file system.
|
|
uint32_t inodes_count;
|
|
/// @brief Total number of blocks in file system
|
|
uint32_t blocks_count;
|
|
/// @brief Number of blocks reserved for superuser.
|
|
uint32_t r_blocks_count;
|
|
/// @brief Total number of unallocated blocks.
|
|
uint32_t free_blocks_count;
|
|
/// @brief Total number of unallocated inodes.
|
|
uint32_t free_inodes_count;
|
|
/// @brief Block number of the block containing the superblock.
|
|
uint32_t first_data_block;
|
|
/// @brief The number to shift 1024 to the left by to obtain the block size
|
|
/// (log2 (block size) - 10).
|
|
uint32_t log_block_size;
|
|
/// @brief The number to shift 1,024 to the left by to obtain the fragment
|
|
/// size (log2 (fragment size) - 10).
|
|
uint32_t log_frag_size;
|
|
/// @brief Number of blocks in each block group.
|
|
uint32_t blocks_per_group;
|
|
/// @brief Number of fragments in each block group.
|
|
uint32_t frags_per_group;
|
|
/// @brief Number of inodes in each block group.
|
|
uint32_t inodes_per_group;
|
|
/// @brief Last mount time (in POSIX time).
|
|
uint32_t mtime;
|
|
/// @brief Last written time (in POSIX time).
|
|
uint32_t wtime;
|
|
/// @brief Number of times the volume has been mounted since its last
|
|
/// consistency check (fsck).
|
|
uint16_t mnt_count;
|
|
/// @brief Number of mounts allowed before a consistency check (fsck) must
|
|
/// be done.
|
|
uint16_t max_mnt_count;
|
|
/// @brief Ext2 signature (0xef53), used to help confirm the presence of
|
|
/// Ext2 on a volume.
|
|
uint16_t magic;
|
|
/// @brief File system state.
|
|
uint16_t state;
|
|
/// @brief What to do when an error is detected.
|
|
uint16_t errors;
|
|
/// @brief Minor portion of version (combine with Major portion below to
|
|
/// construct full version field).
|
|
uint16_t minor_rev_level;
|
|
/// @brief POSIX time of last consistency check (fsck).
|
|
uint32_t lastcheck;
|
|
/// @brief Interval (in POSIX time) between forced consistency checks
|
|
/// (fsck).
|
|
uint32_t checkinterval;
|
|
/// @brief Operating system ID from which the filesystem on this volume was
|
|
/// created.
|
|
uint32_t creator_os;
|
|
/// @brief Major portion of version (combine with Minor portion above to
|
|
/// construct full version field).
|
|
uint32_t rev_level;
|
|
/// @brief User ID that can use reserved blocks.
|
|
uint16_t def_resuid;
|
|
/// @brief Group ID that can use reserved blocks.
|
|
uint16_t def_resgid;
|
|
|
|
// == Extended Superblock Fields ==========================================
|
|
/// @brief First non-reserved inode in file system. (In versions < 1.0, this
|
|
/// is fixed as 11)
|
|
uint32_t first_ino;
|
|
/// @brief Size of each inode structure in bytes. (In versions < 1.0, this
|
|
/// is fixed as 128)
|
|
uint16_t inode_size;
|
|
/// @brief Block group that this superblock is part of (if backup copy).
|
|
uint16_t block_group_nr;
|
|
/// @brief Optional features present (features that are not required to read
|
|
/// or write, but usually result in a performance increase).
|
|
uint32_t feature_compat;
|
|
/// @brief Required features present (features that are required to be
|
|
/// supported to read or write)
|
|
uint32_t feature_incompat;
|
|
/// @brief Features that if not supported, the volume must be mounted
|
|
/// read-only).
|
|
uint32_t feature_ro_compat;
|
|
/// @brief File system ID (what is output by blkid).
|
|
uint8_t uuid[16];
|
|
/// @brief Volume name (C-style string: characters terminated by a 0 byte).
|
|
uint8_t volume_name[16];
|
|
/// @brief Path volume was last mounted to (C-style string: characters
|
|
/// terminated by a 0 byte).
|
|
uint8_t last_mounted[64];
|
|
/// @brief Compression algorithms used.
|
|
uint32_t algo_bitmap;
|
|
|
|
// == Performance Hints ===================================================
|
|
/// @brief Number of blocks to preallocate for files.
|
|
uint8_t prealloc_blocks;
|
|
/// @brief Number of blocks to preallocate for directories.
|
|
uint8_t prealloc_dir_blocks;
|
|
/// @brief (Unused)
|
|
uint16_t padding0;
|
|
|
|
// == Journaling Support ==================================================
|
|
/// @brief Journal ID
|
|
uint8_t journal_uuid[16];
|
|
/// @brief Inode number of journal file.
|
|
uint32_t journal_inum;
|
|
/// @brief Device number of journal file.
|
|
uint32_t jounral_dev;
|
|
/// @brief Start of list of inodes to delete.
|
|
uint32_t last_orphan;
|
|
|
|
// == Directory Indexing Support ==========================================
|
|
/// @brief HTree hash seed.
|
|
uint32_t hash_seed[4];
|
|
/// @brief Ddefault hash version to use.
|
|
uint8_t def_hash_version;
|
|
/// @brief Padding.
|
|
uint16_t padding1;
|
|
/// @brief Padding.
|
|
uint8_t padding2;
|
|
|
|
// == Other Options =======================================================
|
|
/// @brief The default mount options for the file system.
|
|
uint32_t default_mount_options;
|
|
/// @brief The ID of the first meta block group.
|
|
uint32_t first_meta_block_group_id;
|
|
/// @brief Reserved.
|
|
uint8_t reserved[760];
|
|
} ext2_superblock_t;
|
|
|
|
/// @brief
|
|
typedef struct ext2_group_descriptor_t {
|
|
/// @brief The block number of the block bitmap for this Block Group
|
|
uint32_t block_bitmap;
|
|
/// @brief The block number of the inode allocation bitmap for this Block Group.
|
|
uint32_t inode_bitmap;
|
|
/// @brief The block number of the starting block for the inode table for this Block Group.
|
|
uint32_t inode_table;
|
|
/// @brief Number of free blocks.
|
|
uint16_t free_blocks_count;
|
|
/// @brief Number of free inodes.
|
|
uint16_t free_inodes_count;
|
|
/// @brief Number of used directories.
|
|
uint16_t used_dirs_count;
|
|
/// @brief Padding.
|
|
uint16_t pad;
|
|
/// @brief Reserved.
|
|
uint32_t reserved[3];
|
|
} ext2_group_descriptor_t;
|
|
|
|
/// @brief The ext2 inode.
|
|
typedef struct ext2_inode_t {
|
|
/// @brief File mode
|
|
uint16_t mode;
|
|
/// @brief The user identifiers of the owners.
|
|
uint16_t uid;
|
|
/// @brief The size of the file in bytes.
|
|
uint32_t size;
|
|
/// @brief The time that the inode was accessed.
|
|
uint32_t atime;
|
|
/// @brief The time that the inode was created.
|
|
uint32_t ctime;
|
|
/// @brief The time that the inode was modified the last time.
|
|
uint32_t mtime;
|
|
/// @brief The time that the inode was deleted.
|
|
uint32_t dtime;
|
|
/// @brief The group identifiers of the owners.
|
|
uint16_t gid;
|
|
/// @brief Number of hard links.
|
|
uint16_t links_count;
|
|
/// @brief Blocks count.
|
|
uint32_t blocks_count;
|
|
/// @brief File flags.
|
|
uint32_t flags;
|
|
/// @brief OS dependant value.
|
|
uint32_t osd1;
|
|
union {
|
|
struct datablocks {
|
|
uint32_t dir_blocks[EXT2_INDIRECT_BLOCKS];
|
|
uint32_t indir_block;
|
|
uint32_t double_indir_block;
|
|
uint32_t triple_indir_block;
|
|
} blocks;
|
|
char symlink[60];
|
|
} data;
|
|
/// @brief Value used to indicate the file version (used by NFS).
|
|
uint32_t generation;
|
|
/// @brief Value indicating the block number containing the extended attributes.
|
|
uint32_t file_acl;
|
|
/// @brief For regular files this 32bit value contains the high 32 bits of the 64bit file size.
|
|
uint32_t dir_acl;
|
|
/// @brief Value indicating the location of the file fragment.
|
|
uint32_t fragment_addr;
|
|
/// @brief OS dependant structure.
|
|
uint32_t osd2[3];
|
|
} ext2_inode_t;
|
|
|
|
/// @brief The header of an ext2 directory entry.
|
|
typedef struct ext2_dirent_t {
|
|
/// @brief Inode number.
|
|
uint32_t inode;
|
|
/// @brief Directory entry length
|
|
uint16_t direntlen;
|
|
/// @brief Name length
|
|
uint8_t namelen;
|
|
/// @brief File type.
|
|
uint8_t filetype;
|
|
/// @brief File name.
|
|
char name[EXT2_NAME_LEN];
|
|
} ext2_dirent_t;
|
|
|
|
static const char *uuid_to_string(uint8_t uuid[16])
|
|
{
|
|
static char s[33] = { 0 };
|
|
sprintf(s, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
|
|
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
|
|
return s;
|
|
}
|
|
|
|
static const char *time_to_string(uint32_t time)
|
|
{
|
|
static char s[250] = { 0 };
|
|
tm_t *tm = localtime(&time);
|
|
sprintf(s, "%2d/%2d %2d:%2d", tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
|
|
return s;
|
|
}
|
|
|
|
static void ext2_dump_superblock(ext2_superblock_t *sb)
|
|
{
|
|
pr_debug("inodes_count : %d\n", sb->inodes_count);
|
|
pr_debug("blocks_count : %d\n", sb->blocks_count);
|
|
pr_debug("r_blocks_count : %d\n", sb->r_blocks_count);
|
|
pr_debug("free_blocks_count : %d\n", sb->free_blocks_count);
|
|
pr_debug("free_inodes_count : %d\n", sb->free_inodes_count);
|
|
pr_debug("first_data_block : %d\n", sb->first_data_block);
|
|
pr_debug("log_block_size : %d\n", sb->log_block_size);
|
|
pr_debug("log_frag_size : %d\n", sb->log_frag_size);
|
|
pr_debug("blocks_per_group : %d\n", sb->blocks_per_group);
|
|
pr_debug("frags_per_group : %d\n", sb->frags_per_group);
|
|
pr_debug("inodes_per_group : %d\n", sb->inodes_per_group);
|
|
pr_debug("mtime : %s\n", time_to_string(sb->mtime));
|
|
pr_debug("wtime : %s\n", time_to_string(sb->wtime));
|
|
pr_debug("mnt_count : %d\n", sb->mnt_count);
|
|
pr_debug("max_mnt_count : %d\n", sb->max_mnt_count);
|
|
pr_debug("magic : 0x%0x\n", sb->magic);
|
|
pr_debug("state : %d\n", sb->state);
|
|
pr_debug("errors : %d\n", sb->errors);
|
|
pr_debug("minor_rev_level : %d\n", sb->minor_rev_level);
|
|
pr_debug("lastcheck : %s\n", time_to_string(sb->lastcheck));
|
|
pr_debug("checkinterval : %d\n", sb->checkinterval);
|
|
pr_debug("creator_os : %d\n", sb->creator_os);
|
|
pr_debug("rev_level : %d\n", sb->rev_level);
|
|
pr_debug("def_resuid : %d\n", sb->def_resuid);
|
|
pr_debug("def_resgid : %d\n", sb->def_resgid);
|
|
pr_debug("first_ino : %d\n", sb->first_ino);
|
|
pr_debug("inode_size : %d\n", sb->inode_size);
|
|
pr_debug("block_group_nr : %d\n", sb->block_group_nr);
|
|
pr_debug("feature_compat : %d\n", sb->feature_compat);
|
|
pr_debug("feature_incompat : %d\n", sb->feature_incompat);
|
|
pr_debug("feature_ro_compat : %d\n", sb->feature_ro_compat);
|
|
pr_debug("uuid : %s\n", uuid_to_string(sb->uuid));
|
|
pr_debug("volume_name : %s\n", (char *)sb->volume_name);
|
|
pr_debug("last_mounted : %s\n", (char *)sb->last_mounted);
|
|
pr_debug("algo_bitmap : %d\n", sb->algo_bitmap);
|
|
pr_debug("prealloc_blocks : %d\n", sb->prealloc_blocks);
|
|
pr_debug("prealloc_dir_blocks : %d\n", sb->prealloc_dir_blocks);
|
|
pr_debug("journal_uuid : %s\n", uuid_to_string(sb->journal_uuid));
|
|
pr_debug("journal_inum : %d\n", sb->journal_inum);
|
|
pr_debug("jounral_dev : %d\n", sb->jounral_dev);
|
|
pr_debug("last_orphan : %d\n", sb->last_orphan);
|
|
pr_debug("hash_seed : %u %u %u %u\n", sb->hash_seed[0], sb->hash_seed[1], sb->hash_seed[2], sb->hash_seed[3]);
|
|
pr_debug("def_hash_version : %d\n", sb->def_hash_version);
|
|
pr_debug("default_mount_options : %d\n", sb->default_mount_options);
|
|
pr_debug("first_meta_bg : %d\n", sb->first_meta_block_group_id);
|
|
}
|
|
|
|
static vfs_file_t *ext2_mount_callback(const char *path, const char *device)
|
|
{
|
|
vfs_file_t *dev = vfs_open(device, 0, 0);
|
|
if (dev && (dev->flags == DT_BLK)) {
|
|
pr_debug("Correctly found block device.\n");
|
|
ext2_superblock_t sb;
|
|
memset(&sb, 0, sizeof(ext2_superblock_t));
|
|
if (vfs_read(dev, &sb, 1024, sizeof(ext2_superblock_t)) == -1) {
|
|
pr_err("Failed to read the superblock table at 1024.\n");
|
|
return NULL;
|
|
}
|
|
if (sb.magic != EXT2_SUPERBLOCK_MAGIC) {
|
|
pr_err("Wrong magic number, it is not an EXT2 filesystem.\n");
|
|
return NULL;
|
|
}
|
|
ext2_dump_superblock(&sb);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/// Filesystem general operations.
|
|
static vfs_sys_operations_t ext2_sys_operations = {
|
|
.mkdir_f = NULL,
|
|
.rmdir_f = NULL,
|
|
.stat_f = NULL
|
|
};
|
|
|
|
/// Filesystem file operations.
|
|
static vfs_file_operations_t ext2_fs_operations = {
|
|
.open_f = NULL,
|
|
.unlink_f = NULL,
|
|
.close_f = NULL,
|
|
.read_f = NULL,
|
|
.write_f = NULL,
|
|
.lseek_f = NULL,
|
|
.stat_f = NULL,
|
|
.ioctl_f = NULL,
|
|
.getdents_f = NULL
|
|
};
|
|
|
|
/// Filesystem information.
|
|
static file_system_type ext2_file_system_type = {
|
|
.name = "ext2",
|
|
.fs_flags = 0,
|
|
.mount = ext2_mount_callback
|
|
};
|
|
|
|
int ext2_initialize(void)
|
|
{
|
|
// Register the filesystem.
|
|
vfs_register_filesystem(&ext2_file_system_type);
|
|
return 0;
|
|
}
|
|
|
|
int ext2_finalize(void)
|
|
{
|
|
vfs_unregister_filesystem(&ext2_file_system_type);
|
|
return 0;
|
|
} |