333 lines
12 KiB
C
333 lines
12 KiB
C
|
|
|
|
// lookup a file name in a directory and store the directory entry for it
|
|
int ext2_dir_lookup(vfs_mount_t *mount, u32_t dir_inode, char *fileName, ext2_dir_entry_t *direntry)
|
|
{
|
|
ext2_open_dir_t *dir = ext2_open_dir(mount, dir_inode);
|
|
if (!dir)
|
|
return -1; // bad directory inode number
|
|
ext2_dir_entry_t dentry;
|
|
while (!ext2_dir_read_entry(mount, dir, &dentry))
|
|
{
|
|
char *dentryName = kcalloc(1, dentry.name_length + 1);
|
|
memcpy(dentryName, dentry.name, dentry.name_length);
|
|
int res = strcmp(fileName, dentryName);
|
|
kfree(dentryName);
|
|
if (!res)
|
|
{
|
|
*direntry = dentry;
|
|
ext2_close_dir(mount, dir);
|
|
return 0;
|
|
}
|
|
}
|
|
ext2_close_dir(mount, dir);
|
|
return -2;
|
|
}
|
|
|
|
// open a directory by inode number for reading
|
|
ext2_open_dir_t *ext2_open_dir(vfs_mount_t *mount, u32_t inode_number)
|
|
{
|
|
ext2_open_dir_t *open_dir = New(ext2_open_dir_t);
|
|
ext2_open_inode_t *open_inode = ext2_open_inode(mount, inode_number);
|
|
if (!open_inode)
|
|
{
|
|
kfree(open_dir);
|
|
return NULL;
|
|
}
|
|
if ((open_inode->inode.i_mode & EXT2_I_MODE_TYPE_MASK) != EXT2_I_MODE_DIR)
|
|
{
|
|
ext2_close_inode(mount, open_inode);
|
|
kfree(open_dir);
|
|
return NULL;
|
|
}
|
|
open_dir->open_inode = open_inode;
|
|
open_dir->position = 0;
|
|
return open_dir;
|
|
}
|
|
|
|
int ext2_dir_read_entry(vfs_mount_t *mount, ext2_open_dir_t *open_dir, ext2_dir_entry_t *dentry)
|
|
{
|
|
ext2_super_block_t *super = mount->super;
|
|
if (open_dir->position >= open_dir->open_inode->inode.i_size)
|
|
return -1; // EOF
|
|
u32_t dir_block = open_dir->position >> (10 + super->s_log_block_size);
|
|
char *block = kmalloc(2048 << super->s_log_block_size);
|
|
ext2_inode_seek(mount, open_dir->open_inode, dir_block);
|
|
if (ext2_read_inode_block(mount, open_dir->open_inode, block))
|
|
ext2_read_inode_block(mount, open_dir->open_inode, block + (1024 << super->s_log_block_size));
|
|
ext2_dir_entry_t *dir_entry = (ext2_dir_entry_t *)(block + open_dir->position % (1024 << super->s_log_block_size));
|
|
if (!dir_entry->inode)
|
|
{
|
|
kfree(block);
|
|
return -2; // EOF
|
|
}
|
|
memcpy(dentry, dir_entry, min(dir_entry->length, sizeof(ext2_dir_entry_t)));
|
|
open_dir->position += dir_entry->length;
|
|
kfree(block);
|
|
return 0;
|
|
}
|
|
|
|
int ext2_close_dir(vfs_mount_t *mount, ext2_open_dir_t *open_dir)
|
|
{
|
|
ext2_close_inode(mount, open_dir->open_inode);
|
|
kfree(open_dir);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// open an inode for reading
|
|
ext2_open_inode_t *ext2_open_inode(vfs_mount_t *mount, u32_t inode_number)
|
|
{
|
|
ext2_open_inode_t *open_inode = New(ext2_open_inode_t);
|
|
if ( ext2_read_inode(mount, inode_number, &(open_inode->inode)) )
|
|
{
|
|
kfree(open_inode);
|
|
return NULL;
|
|
}
|
|
open_inode->block = 0;
|
|
open_inode->block_pointers = NULL;
|
|
open_inode->block_pointers_start = 0;
|
|
mount->refs++;
|
|
return open_inode;
|
|
}
|
|
|
|
|
|
// seek to a certain block of an open inode
|
|
int ext2_inode_seek(vfs_mount_t *mount, ext2_open_inode_t *open_inode, u32_t block_number)
|
|
{
|
|
ext2_super_block_t *super = mount->super;
|
|
if (open_inode->inode.i_size <= (block_number << (10 + super->s_log_block_size)))
|
|
return -1; // at or past EOF
|
|
open_inode->block = block_number;
|
|
return 0;
|
|
}
|
|
|
|
// returns number of bytes read
|
|
int ext2_read_inode_block(vfs_mount_t *mount, ext2_open_inode_t *open_inode, void *block)
|
|
{
|
|
ext2_super_block_t *super = mount->super;
|
|
if (open_inode->inode.i_size <= (open_inode->block << (10 + super->s_log_block_size)))
|
|
return 0; // at or past EOF
|
|
u32_t leftover_bytes = open_inode->inode.i_size - (open_inode->block << (10 + super->s_log_block_size));
|
|
u32_t block_number = ext2_block_number(mount, open_inode);
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(block_number, super),
|
|
2 << super->s_log_block_size, block);
|
|
open_inode->block++;
|
|
return min(leftover_bytes, 1024 << super->s_log_block_size);
|
|
}
|
|
|
|
|
|
// close an open inode
|
|
int ext2_close_inode(vfs_mount_t *mount, ext2_open_inode_t *open_inode)
|
|
{
|
|
mount->refs--;
|
|
if (open_inode->block_pointers)
|
|
kfree(open_inode->block_pointers); // free the block pointers cache
|
|
kfree(open_inode);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// transform open_inode->block (a relative block number) to an absolute block number for the filesystem
|
|
u32_t ext2_block_number(vfs_mount_t *mount, ext2_open_inode_t *open_inode)
|
|
{
|
|
if (open_inode->block < 12)
|
|
return open_inode->inode.i_block[open_inode->block];
|
|
ext2_super_block_t *super = mount->super;
|
|
int pointersPerBlock = 256 << super->s_log_block_size;
|
|
if (open_inode->block_pointers && // there is a block pointers cache block allocated
|
|
(open_inode->block >= open_inode->block_pointers_start) && // and the block number is in it
|
|
(open_inode->block < (open_inode->block_pointers_start + pointersPerBlock)))
|
|
return open_inode->block_pointers[open_inode->block - open_inode->block_pointers_start];
|
|
u32_t rel_block = open_inode->block - 12;
|
|
if (!open_inode->block_pointers)
|
|
open_inode->block_pointers = kmalloc(pointersPerBlock << 2);
|
|
if (rel_block < pointersPerBlock) // indirect block in i_block[12]
|
|
{
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(open_inode->inode.i_block[12], super),
|
|
2 << super->s_log_block_size, open_inode->block_pointers);
|
|
open_inode->block_pointers_start = 12;
|
|
return open_inode->block_pointers[rel_block];
|
|
}
|
|
rel_block -= pointersPerBlock;
|
|
if (rel_block < (pointersPerBlock * pointersPerBlock)) // double-indirect block in i_block[13]
|
|
{
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(open_inode->inode.i_block[13], super),
|
|
2 << super->s_log_block_size, open_inode->block_pointers);
|
|
u32_t real_block = open_inode->block_pointers[rel_block / pointersPerBlock];
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(real_block, super),
|
|
2 << super->s_log_block_size, open_inode->block_pointers);
|
|
open_inode->block_pointers_start = 12 + pointersPerBlock + rel_block - (rel_block % pointersPerBlock);
|
|
return open_inode->block_pointers[rel_block % pointersPerBlock];
|
|
}
|
|
// this code shouldn't run unless we are dealing with a 65+mb file ...
|
|
rel_block -= pointersPerBlock * pointersPerBlock;
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(open_inode->inode.i_block[14], super),
|
|
2 << super->s_log_block_size, open_inode->block_pointers);
|
|
u32_t index_1 = rel_block / (pointersPerBlock * pointersPerBlock);
|
|
u32_t leftover_1 = rel_block % (pointersPerBlock * pointersPerBlock);
|
|
u32_t block_1 = open_inode->block_pointers[index_1];
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(block_1, super),
|
|
2 << super->s_log_block_size, open_inode->block_pointers);
|
|
u32_t index_2 = leftover_1 / pointersPerBlock;
|
|
u32_t leftover_2 = leftover_1 % pointersPerBlock;
|
|
u32_t block_2 = open_inode->block_pointers[index_2];
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(block_2, super),
|
|
2 << super->s_log_block_size, open_inode->block_pointers);
|
|
open_inode->block_pointers_start = 12 + (pointersPerBlock + 1) * pointersPerBlock + rel_block - (rel_block % pointersPerBlock);
|
|
return open_inode->block_pointers[leftover_2];
|
|
}
|
|
|
|
|
|
int ext2_resize_inode(vfs_mount_t *mount, u32_t inode_number, u32_t new_size)
|
|
{
|
|
ext2_inode_t inode;
|
|
if ( ext2_read_inode(mount, inode_number, &inode) )
|
|
return -1;
|
|
ext2_super_block_t *super = mount->super;
|
|
int current_blocks = (inode.i_size + (1024 << super->s_log_block_size) - 1) >> (10 + super->s_log_block_size);
|
|
int new_blocks = (new_size + (1024 << super->s_log_block_size) - 1) >> (10 + super->s_log_block_size);
|
|
if ( new_blocks == current_blocks )
|
|
return 0;
|
|
|
|
// TODO: resize
|
|
u32_t block_size = 1024 << super->s_log_block_size;
|
|
u32_t pointers_per_block = block_size >> 2;
|
|
u32_t *pointer_cache1 = kmalloc(3 * block_size);
|
|
u32_t *pointer_cache2 = pointer_cache1 + pointers_per_block;
|
|
u32_t *pointer_cache3 = pointer_cache2 + pointers_per_block;
|
|
u32_t c1_start, c2_start, c3_start;
|
|
c1_start = c2_start = c3_start = 0;
|
|
while (new_blocks < current_blocks) // delete, decrease current_blocks
|
|
{
|
|
current_blocks--;
|
|
// now delete block number current_blocks
|
|
|
|
}
|
|
|
|
while (current_blocks < new_blocks) // add, increase current_blocks
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
/***************** VFS INTERFACE FUNCTIONS *******************/
|
|
|
|
|
|
|
|
// VFS interface function to open a directory
|
|
int ext2__open_dir(vfs_mount_t *mount, u32_t inode_number, vfs_open_file_t *dir)
|
|
{
|
|
if (ext2_inode_status(mount, inode_number) != 1)
|
|
return -1;
|
|
ext2_open_dir_t *open_dir = ext2_open_dir(mount, inode_number);
|
|
if (!open_dir)
|
|
return -2;
|
|
dir->fs_data = open_dir;
|
|
return 0;
|
|
}
|
|
|
|
// VFS interface function to read a directory entry from an open directory
|
|
int ext2__read_dir(vfs_mount_t *mount, vfs_open_file_t *dir, vfs_dir_entry_t *dentry)
|
|
{
|
|
ext2_dir_entry_t t_dentry;
|
|
int status = ext2_dir_read_entry(mount, dir->fs_data, &t_dentry);
|
|
if (status)
|
|
return status;
|
|
memcpy(dentry->name, t_dentry.name, t_dentry.name_length);
|
|
dentry->name[t_dentry.name_length] = 0;
|
|
dentry->inode_number = t_dentry.inode;
|
|
return 0;
|
|
}
|
|
|
|
// VFS interface function to close an open directory
|
|
int ext2__close_dir(vfs_mount_t *mount, vfs_open_file_t *dir)
|
|
{
|
|
return ext2_close_dir(mount, dir->fs_data);
|
|
}
|
|
|
|
|
|
// VFS interface function to open a file for reading a byte at a time
|
|
int ext2__open_file(vfs_mount_t *mount, u32_t inode_number, vfs_open_file_t *open_file)
|
|
{
|
|
if (ext2_inode_status(mount, inode_number) != 1)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
// VFS interface function to read a byte from an open file
|
|
int ext2__read_file(vfs_mount_t *mount, vfs_open_file_t *open_file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// VFS interface function to close a byte-file
|
|
int ext2__close_file(vfs_mount_t *mount, vfs_open_file_t *open_file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
// VFS interface function to open a file for reading 512-byte blocks at a time
|
|
int ext2__open_block_file(vfs_mount_t *mount, u32_t inode_number, vfs_open_file_t *open_file)
|
|
{
|
|
if (ext2_inode_status(mount, inode_number) != 1)
|
|
return -1;
|
|
ext2_open_inode_t *open_inode = ext2_open_inode(mount, inode_number);
|
|
if (!open_inode)
|
|
return -2;
|
|
if ((open_inode->inode.i_mode & EXT2_I_MODE_TYPE_MASK) != EXT2_I_MODE_FILE)
|
|
{
|
|
ext2_close_inode(mount, open_inode);
|
|
return -3;
|
|
}
|
|
ext2__open_block_file_t *open_block_file = New(ext2__open_block_file_t);
|
|
open_block_file->open_inode = open_inode;
|
|
open_block_file->block = 0;
|
|
open_file->fs_data = open_block_file;
|
|
return 0;
|
|
}
|
|
|
|
// VFS interface function to read a block from an open block file
|
|
// returns the number of bytes read
|
|
int ext2__read_block_file(vfs_mount_t *mount, vfs_open_file_t *open_file, void *buffer)
|
|
{
|
|
ext2_super_block_t *super = mount->super;
|
|
ext2__open_block_file_t *open_block_file = open_file->fs_data;
|
|
if (ext2_inode_seek(mount, open_block_file->open_inode, ext2_diskToFSBlock(open_block_file->block, super)))
|
|
return 0; // EOF
|
|
u8_t *block = kmalloc(1024 << super->s_log_block_size);
|
|
u32_t file_position_read = (1024 << super->s_log_block_size) * open_block_file->open_inode->block;
|
|
u32_t file_position_want = open_block_file->block << 9;
|
|
u32_t data_offset = file_position_want - file_position_read;
|
|
int bytes_read = ext2_read_inode_block(mount, open_block_file->open_inode, block);
|
|
if (bytes_read <= data_offset)
|
|
{
|
|
kfree(block);
|
|
return 0; // EOF
|
|
}
|
|
memcpy(buffer, block + data_offset, min(512, bytes_read - data_offset));
|
|
kfree(block);
|
|
open_block_file->block++;
|
|
return min(512, bytes_read - data_offset);
|
|
}
|
|
|
|
// VFS interface function to seek to a certain block number of an open block file
|
|
int ext2__block_file_seek(vfs_mount_t *mount, vfs_open_file_t *open_file, u32_t block_number)
|
|
{
|
|
ext2__open_block_file_t *open_block_file = open_file->fs_data;
|
|
return ext2_inode_seek(mount, open_block_file->open_inode, ext2_FSToDiskBlock(block_number, mount->super));
|
|
}
|
|
|
|
// VFS interface function to close an open block file
|
|
int ext2__close_block_file(vfs_mount_t *mount, vfs_open_file_t *open_file)
|
|
{
|
|
ext2__open_block_file_t *open_block_file = open_file->fs_data;
|
|
ext2_close_inode(mount, open_block_file->open_inode);
|
|
kfree(open_block_file);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|