510 lines
14 KiB
C++
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)
|
|
{
|
|
|
|
}
|
|
|