hos/kernel/fs/ext2/ext2.cpp

510 lines
14 KiB
C++

// ext2.cpp
// ext2 filesystem driver for HOS
// Author: Josh Holtrop
// Date: 05/10/05
// Modified: 05/10/05
/* example ext2 filesystem block group structure containing superblock and group descriptors:
* block offset description #blocks
* ------------ ----------- -------
* 0 superblock 1
* 1 group descriptors gd_blocks = ceil(ceil[s_blocks_count / s_blocks_per_group] / (1024 >> [5 - s_log_block_size]))
* gd_blocks+1 block bitmap block 1
* gd_blocks+2 inode bitmap block 1
* gd_blocks+3 inode table itblocks = ceil[s_inodes_per_group / (1024 >> [7 - s_log_block_size])]
* gd_blocks+3+itblocks data blocks s_blocks_per_group - gd_blocks - itblocks - 3
*/
extern "C" {
#include "display/kout.h"
#include "mm/vmm.h"
#include "lang/lang.h"
}
#include "ext2.h"
#include "Ext2OpenDirectory.h"
#include "Ext2OpenFile.h"
/* initialize the ext2 filesystem driver */
int ext2_init()
{
vfs_register("ext2", ext2__mount_func);
return 0;
}
/* a function to mount an ext2 filesystem and return a pointer to it */
FileSystem *ext2__mount_func(device_t dev)
{
ext2_super_block_t *super =
(ext2_super_block_t *) New(ext2_super_block_t);
if ( (block_read(DEV_MAJOR(dev), DEV_MINOR(dev), 2, 2, super) < 1024)
|| (super->s_magic != EXT2_MAGIC) )
{
kfree(super);
return NULL;
}
Ext2fs *fs = new Ext2fs(super, dev);
kfree(super);
return fs;
}
/* constructor for an actual ext2 filesystem object */
Ext2fs::Ext2fs(ext2_super_block_t *super, device_t dev)
{
myDevice = dev;
memcpy(&mySuper, super, sizeof(ext2_super_block_t));
mySuperDirty = 0;
myNumGroups = /* ceil[blocks_count / blocks_per_group] */
(mySuper.s_blocks_count + mySuper.s_blocks_per_group - 1)
/ mySuper.s_blocks_per_group;
myGroupDescriptorsPerBlock = 1024 >> (5 - mySuper.s_log_block_size);
myGroupDescriptorBlocks = /* ceil[num_groups / groupDescriptorsPerBlock] */
(myNumGroups + myGroupDescriptorsPerBlock - 1)
/ myGroupDescriptorsPerBlock;
myInodesPerBlock = 1024 >> (7 - mySuper.s_log_block_size);
myInodeTableBlocks = /* ceil[inodes_per_group / inodePerBlock] */
(mySuper.s_inodes_per_group + myInodesPerBlock - 1)
/ myInodesPerBlock;
/* test/debug code */
OpenDirectory *od = openDirectory(2, VFS_MODE_WRITE);
kprintf("TEST: %d\n", od->unlink("link"));
kprintf("TEST: %d\n", od->create("test", VFS_FT_FILE, 0640, 0));
kprintf("TEST: %d\n", od->create("Block File baby", VFS_FT_BLOCK, 0666, 5 << 8 | 3));
kprintf("TEST: %d\n", od->create("Character-File", VFS_FT_CHAR, 0644, 130 << 8 | 42));
delete od;
}
/* this destructor destroys an ext2 filesystem that was mounted */
Ext2fs::~Ext2fs()
{
if (mySuperDirty)
block_write(DEV_MAJOR(myDevice), DEV_MINOR(myDevice), 2, 2, &mySuper);
}
/* return the total number of 512-blocks on the filesystem */
u32_t Ext2fs::totalBlocks()
{
return mySuper.s_blocks_count >> (mySuper.s_log_block_size + 1);
}
/* return the number of free 512-blocks on the filesystem */
u32_t Ext2fs::freeBlocks()
{
return mySuper.s_free_blocks_count >> (mySuper.s_log_block_size + 1);
}
/* return the total number of inodes on the filesystem */
u32_t Ext2fs::totalInodes()
{
return mySuper.s_inodes_count;
}
/* return the free number of inodes on the filesystem */
u32_t Ext2fs::freeInodes()
{
return mySuper.s_free_inodes_count;
}
/* ext2 filesystems always have a root-directory inode number of 2 */
u32_t Ext2fs::getRootInodeNumber()
{
return 2;
}
/* open a directory */
OpenDirectory *Ext2fs::openDirectory(u32_t inum, int mode)
{
ext2_inode_t inode;
readInode(inum, &inode);
if ((inode.i_mode & EXT2_I_MODE_TYPE_MASK) == EXT2_I_MODE_DIR)
return new Ext2OpenDirectory(this, inum, mode);
return NULL;
}
/* stat an inode */
int Ext2fs::stat(u32_t inum, vfs_stat_t *buf)
{
ext2_inode_t inode;
if (readInode(inum, &inode))
return -1;
buf->dev = 0;
switch(inode.i_mode & EXT2_I_MODE_TYPE_MASK)
{
case EXT2_I_MODE_FIFO: buf->type = VFS_FT_FIFO; break;
case EXT2_I_MODE_CHAR: buf->type = VFS_FT_CHAR;
buf->dev = inode.i_block[0]; break;
case EXT2_I_MODE_DIR: buf->type = VFS_FT_DIR; break;
case EXT2_I_MODE_BLOCK: buf->type = VFS_FT_BLOCK;
buf->dev = inode.i_block[0]; break;
case EXT2_I_MODE_FILE: buf->type = VFS_FT_FILE; break;
case EXT2_I_MODE_SYM: buf->type = VFS_FT_SYMLINK; break;
case EXT2_I_MODE_SOCK: buf->type = VFS_FT_SOCK; break;
default: buf->type = VFS_FT_UNKNOWN;
}
buf->size = inode.i_size;
buf->inode = inum;
buf->permissions = inode.i_mode & EXT2_I_MODE_ATTR_MASK;
buf->uid = inode.i_uid;
buf->gid = inode.i_gid;
buf->atime = inode.i_atime;
buf->mtime = inode.i_mtime;
buf->ctime = inode.i_ctime;
buf->links = inode.i_links_count;
return 0;
}
/* dereference a symbolink link */
int Ext2fs::link_deref(u32_t inum, char *buf)
{
ext2_inode_t inode;
if (readInode(inum, &inode))
return -1; /* Couldn't read inode */
if ((inode.i_mode & EXT2_I_MODE_TYPE_MASK) != EXT2_I_MODE_SYM)
return -2;
if (inode.i_size > VFS_MAX_PATH_LENGTH)
return -3;
if (inode.i_size < 61)
{
memcpy(buf, inode.i_block, inode.i_size);
buf[inode.i_size] = 0;
return 0;
}
char *block = new char[1024 << mySuper.s_log_block_size];
readBlock(inode.i_block[0], block);
memcpy(buf, block, inode.i_size);
buf[inode.i_size] = 0;
delete[] block;
return 0;
}
/*************************** internal functions ***************************/
/* read an inode from the disk into memory */
int Ext2fs::readInode(u32_t inum, ext2_inode_t *buf)
{
if (inodeStatus(inum) != 1)
return -1;
inum--; /* turn inode number into a 0-based index */
u32_t group = inum / mySuper.s_inodes_per_group;
u32_t index = inum % mySuper.s_inodes_per_group;
ext2_group_desc_t group_desc;
readGroupDescriptor(group, &group_desc);
ext2_inode_t *inode_block = new ext2_inode_t[8 << mySuper.s_log_block_size];
readBlock(group_desc.bg_inode_table +
(index >> (3 + mySuper.s_log_block_size)), inode_block);
u32_t inode_block_index = index % (8 << mySuper.s_log_block_size);
memcpy(buf, inode_block + inode_block_index, sizeof(ext2_inode_t));
delete[] inode_block;
return 0;
}
/* write an inode from memory onto the disk */
int Ext2fs::writeInode(u32_t inum, ext2_inode_t *buf)
{
if (inodeStatus(inum) != 1)
return -1;
inum--; /* turn inode number into a 0-based index */
u32_t group = inum / mySuper.s_inodes_per_group;
u32_t index = inum % mySuper.s_inodes_per_group;
ext2_group_desc_t group_desc;
readGroupDescriptor(group, &group_desc);
ext2_inode_t *inode_block = new ext2_inode_t[8 << mySuper.s_log_block_size];
u32_t block_num = group_desc.bg_inode_table +
(index >> (3 + mySuper.s_log_block_size));
readBlock(block_num, inode_block);
u32_t inode_block_index = index % (8 << mySuper.s_log_block_size);
memcpy(inode_block + inode_block_index, buf, sizeof(ext2_inode_t));
writeBlock(block_num, inode_block);
delete[] inode_block;
return 0;
}
/* read the group descriptor for a given group number */
int Ext2fs::readGroupDescriptor(u32_t group, ext2_group_desc_t *buf)
{
if (group >= myNumGroups)
return -1;
ext2_group_desc_t *gd_block =
new ext2_group_desc_t[myGroupDescriptorsPerBlock];
readBlock(mySuper.s_first_data_block + 1 +
(group / myGroupDescriptorsPerBlock), gd_block);
memcpy(buf, gd_block + (group % myGroupDescriptorsPerBlock),
sizeof(ext2_group_desc_t));
delete[] gd_block;
return 0;
}
/* write the group descriptor for a given group number */
int Ext2fs::writeGroupDescriptor(u32_t group, ext2_group_desc_t *buf)
{
if (group >= myNumGroups)
return -1;
ext2_group_desc_t *gd_block =
new ext2_group_desc_t[myGroupDescriptorsPerBlock];
u32_t block_num = mySuper.s_first_data_block + 1 +
(group / myGroupDescriptorsPerBlock);
readBlock(block_num, gd_block);
memcpy(gd_block + (group % myGroupDescriptorsPerBlock), buf,
sizeof(ext2_group_desc_t));
writeBlock(block_num, gd_block);
delete[] gd_block;
return 0;
}
/* read a data block from the block device */
int Ext2fs::readBlock(u32_t blockNum, void *buf)
{
return block_read(DEV_MAJOR(myDevice), DEV_MINOR(myDevice),
blockNum << (mySuper.s_log_block_size + 1),
2 << mySuper.s_log_block_size,
buf);
}
/* write a data block to the block device */
int Ext2fs::writeBlock(u32_t blockNum, void *buf)
{
return block_write(DEV_MAJOR(myDevice), DEV_MINOR(myDevice),
blockNum << (mySuper.s_log_block_size + 1),
2 << mySuper.s_log_block_size,
buf);
}
/* check the status of an inode (1-based inode number)
* -1: invalid inode number
* 0: free inode
* 1: allocated (used) inode
*/
int Ext2fs::inodeStatus(u32_t inum)
{
if (inum < 1 || inum > mySuper.s_inodes_count)
return -1; /* invalid inode number */
inum--; /* turn inode number into a 0-based index */
u32_t group = inum / mySuper.s_inodes_per_group;
u32_t index = inum % mySuper.s_inodes_per_group;
ext2_group_desc_t group_desc;
readGroupDescriptor(group, &group_desc);
u8_t *bitmap_block = new u8_t[1024 << mySuper.s_log_block_size];
readBlock(group_desc.bg_inode_bitmap +
(index >> (13 + mySuper.s_log_block_size)), bitmap_block);
u32_t bitmap_index = index % (8192 << mySuper.s_log_block_size);
int inode_status = (bitmap_block[bitmap_index >> 3] >> (bitmap_index & 0x7)) & 1;
delete[] bitmap_block;
return inode_status;
}
/* check the status of a block
* -1: invalid block number
* 0: free block
* 1: allocated (used) block
*/
int Ext2fs::blockStatus(u32_t bnum)
{
if (bnum < mySuper.s_first_data_block)
return 1;
if (bnum >= mySuper.s_blocks_count)
return -1; /* invalid inode number */
bnum -= mySuper.s_first_data_block;
u32_t group = bnum / mySuper.s_blocks_per_group;
u32_t index = bnum % mySuper.s_blocks_per_group;
ext2_group_desc_t group_desc;
readGroupDescriptor(group, &group_desc);
u8_t *bitmap_block = new u8_t[1024 << mySuper.s_log_block_size];
readBlock(group_desc.bg_block_bitmap +
(index >> (13 + mySuper.s_log_block_size)), bitmap_block);
u32_t bitmap_index = index % (8192 << mySuper.s_log_block_size);
int block_status = (bitmap_block[bitmap_index >> 3] >> (bitmap_index & 0x7)) & 1;
delete[] bitmap_block;
return block_status;
}
/* allocate an inode, return number (0 on failure) */
u32_t Ext2fs::allocInode()
{
ext2_group_desc_t gd;
for (unsigned int bg = 0; bg < myNumGroups; bg++)
{
readGroupDescriptor(bg, &gd);
if (gd.bg_free_inodes_count)
{
u32_t index = allocFromBitmap(gd.bg_inode_bitmap,
mySuper.s_inodes_per_group);
if (index == 0xFFFFFFFF)
return 0;
gd.bg_free_inodes_count--;
writeGroupDescriptor(bg, &gd);
mySuper.s_free_inodes_count--;
mySuperDirty = 1;
return (bg * mySuper.s_inodes_per_group) + index + 1;
}
}
return 0;
}
/* allocate a block, return number (0 on failure) */
u32_t Ext2fs::allocBlock()
{
ext2_group_desc_t gd;
for (unsigned int bg = 0; bg < myNumGroups; bg++)
{
readGroupDescriptor(bg, &gd);
if (gd.bg_free_blocks_count)
{
u32_t index = allocFromBitmap(gd.bg_block_bitmap,
mySuper.s_blocks_per_group);
if (index == 0xFFFFFFFF)
return 0;
gd.bg_free_blocks_count--;
writeGroupDescriptor(bg, &gd);
mySuper.s_free_blocks_count--;
mySuperDirty = 1;
return (bg * mySuper.s_blocks_per_group) + index +
mySuper.s_first_data_block;
}
}
return 0;
}
/* free an inode, return error code */
int Ext2fs::freeInode(u32_t inum)
{
if (inum < 11 || inum > mySuper.s_inodes_count)
return -1;
if (inodeStatus(inum) != 1)
return -2;
inum--; /* now 0-based */
ext2_group_desc_t gd;
u32_t group_num = inum / mySuper.s_inodes_per_group;
readGroupDescriptor(group_num, &gd);
freeFromBitmap(gd.bg_inode_bitmap, inum % mySuper.s_inodes_per_group);
gd.bg_free_inodes_count++;
mySuper.s_free_inodes_count++;
mySuperDirty = 1;
writeGroupDescriptor(group_num, &gd);
return 0;
}
/* free a block, return error code */
int Ext2fs::freeBlock(u32_t bnum)
{
if (bnum < mySuper.s_first_data_block || bnum >= mySuper.s_blocks_count)
return -1;
if (blockStatus(bnum) != 1)
return -2;
bnum -= mySuper.s_first_data_block; /* now 0-based */
ext2_group_desc_t gd;
u32_t group_num = bnum / mySuper.s_blocks_per_group;
readGroupDescriptor(group_num, &gd);
freeFromBitmap(gd.bg_block_bitmap, bnum % mySuper.s_blocks_per_group);
gd.bg_free_blocks_count++;
mySuper.s_free_blocks_count++;
mySuperDirty = 1;
writeGroupDescriptor(group_num, &gd);
return 0;
}
/* allocate a node from a bitmap, return the index, 0xFFFFFFFF on failure */
u32_t Ext2fs::allocFromBitmap(u32_t blockNum, u32_t maxBits)
{
u8_t *block = new u8_t[1024 << mySuper.s_log_block_size];
u8_t *blockPtr = block;
u32_t maxBytes = (maxBits + 7) >> 3;
readBlock(blockNum, block);
for (u32_t idx = 0; idx < maxBytes; idx++)
{
if (*blockPtr != 0xFF)
{
for (u32_t bit = 0; bit < 8; bit++)
{
if ( !((*blockPtr) & (1 << bit)) )
{
u32_t nodeIndex = (idx << 3) + bit;
if (nodeIndex < maxBits)
{
*blockPtr |= (1 << bit);
writeBlock(blockNum, block);
return nodeIndex;
}
return 0xFFFFFFFF;
}
}
break;
}
blockPtr++;
}
delete[] block;
return 0xFFFFFFFF;
}
/* free a node from a bitmap */
void Ext2fs::freeFromBitmap(u32_t blockNum, u32_t node)
{
u8_t *block = new u8_t[1024 << mySuper.s_log_block_size];
readBlock(blockNum, block);
u8_t *blockPtr = block + (node >> 3);
*blockPtr &= ((1 << (node & 0x7)) ^ 0xFF);
writeBlock(blockNum, block);
delete[] block;
}
/* this function is called when a file is opened for writing and truncates
* the inode. the function notifies any file handles open on this inode that
* the inode was truncated
*/
void Ext2fs::notifyTruncatedFiles(u32_t inum)
{
// TODO: notify open files
}
/* unlink an inode:
* decrement i_links_count
* if it is zero, free the inode
* if not, write the inode back
*/
void Ext2fs::unlink(u32_t inum)
{
ext2_inode_t inode;
readInode(inum, &inode);
inode.i_links_count--;
if (inode.i_links_count)
{
writeInode(inum, &inode);
return;
}
freeBlocksFromInode(inum);
freeInode(inum);
}
int Ext2fs::attemptRemoveDir(u32_t inum)
{
vfs_dir_entry_t dentry, dentry2;
OpenDirectory *od = openDirectory(inum, VFS_MODE_READ);
if (od->read(&dentry))
{
delete od;
return -1;
}
if (od->read(&dentry2))
{
delete od;
return -1;
}
if (!od->read(&dentry))
{
delete od;
return -1;
}
delete od;
unlink(dentry.inode);
unlink(dentry2.inode);
}
void Ext2fs::freeBlocksFromInode(u32_t inum)
{
}