2018-10-10 11:53:07 +02:00
# include "Ext2FileSystem.h"
# include "ext2_fs.h"
2018-10-14 22:57:41 +02:00
# include "UnixTypes.h"
2019-01-22 16:34:24 +01:00
# include "RTC.h"
2018-10-10 11:53:07 +02:00
# include <AK/Bitmap.h>
2018-12-04 00:27:16 +01:00
# include <AK/StdLibExtras.h>
2018-10-24 12:43:52 +02:00
# include <AK/BufferStream.h>
2018-11-07 21:19:47 +01:00
# include <LibC/errno_numbers.h>
2019-02-21 15:45:31 +01:00
# include <Kernel/Process.h>
2018-10-10 11:53:07 +02:00
2018-10-17 11:47:14 +02:00
//#define EXT2_DEBUG
2018-10-10 11:53:07 +02:00
2019-03-02 01:50:34 +01:00
static const ssize_t max_inline_symlink_length = 60 ;
2019-02-25 12:43:52 +01:00
Retained < Ext2FS > Ext2FS : : create ( Retained < DiskDevice > & & device )
2018-10-10 11:53:07 +02:00
{
2018-11-15 17:13:10 +01:00
return adopt ( * new Ext2FS ( move ( device ) ) ) ;
2018-10-10 11:53:07 +02:00
}
2019-02-25 12:43:52 +01:00
Ext2FS : : Ext2FS ( Retained < DiskDevice > & & device )
2018-11-15 17:13:10 +01:00
: DiskBackedFS ( move ( device ) )
2018-10-10 11:53:07 +02:00
{
}
2018-11-15 17:13:10 +01:00
Ext2FS : : ~ Ext2FS ( )
2018-10-10 11:53:07 +02:00
{
}
2018-12-03 00:20:00 +01:00
ByteBuffer Ext2FS : : read_super_block ( ) const
2018-10-10 11:53:07 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2018-12-21 02:10:45 +01:00
auto buffer = ByteBuffer : : create_uninitialized ( 1024 ) ;
2019-02-11 11:38:14 +01:00
bool success = device ( ) . read_block ( 2 , buffer . pointer ( ) ) ;
ASSERT ( success ) ;
success = device ( ) . read_block ( 3 , buffer . offset_pointer ( 512 ) ) ;
ASSERT ( success ) ;
2018-10-10 11:53:07 +02:00
return buffer ;
}
2018-12-03 00:20:00 +01:00
bool Ext2FS : : write_super_block ( const ext2_super_block & sb )
2018-10-10 11:53:07 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2018-10-10 11:53:07 +02:00
const byte * raw = ( const byte * ) & sb ;
bool success ;
2018-12-03 01:38:22 +01:00
success = device ( ) . write_block ( 2 , raw ) ;
2018-10-10 11:53:07 +02:00
ASSERT ( success ) ;
2018-12-03 01:38:22 +01:00
success = device ( ) . write_block ( 3 , raw + 512 ) ;
2018-10-10 11:53:07 +02:00
ASSERT ( success ) ;
// FIXME: This is an ugly way to refresh the superblock cache. :-|
2018-12-03 00:20:00 +01:00
super_block ( ) ;
2018-10-10 11:53:07 +02:00
return true ;
}
2018-12-03 00:20:00 +01:00
unsigned Ext2FS : : first_block_of_group ( unsigned groupIndex ) const
2018-10-10 11:53:07 +02:00
{
2018-12-03 00:20:00 +01:00
return super_block ( ) . s_first_data_block + ( groupIndex * super_block ( ) . s_blocks_per_group ) ;
2018-10-10 11:53:07 +02:00
}
2018-12-03 00:20:00 +01:00
const ext2_super_block & Ext2FS : : super_block ( ) const
2018-10-10 11:53:07 +02:00
{
2018-12-03 00:20:00 +01:00
if ( ! m_cached_super_block )
m_cached_super_block = read_super_block ( ) ;
return * reinterpret_cast < ext2_super_block * > ( m_cached_super_block . pointer ( ) ) ;
2018-10-10 11:53:07 +02:00
}
2018-12-03 00:20:00 +01:00
const ext2_group_desc & Ext2FS : : group_descriptor ( unsigned groupIndex ) const
2018-10-10 11:53:07 +02:00
{
// FIXME: Should this fail gracefully somehow?
2019-01-31 17:31:23 +01:00
ASSERT ( groupIndex < = m_block_group_count ) ;
2018-10-10 11:53:07 +02:00
2018-12-03 00:20:00 +01:00
if ( ! m_cached_group_descriptor_table ) {
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-31 17:31:23 +01:00
unsigned blocks_to_read = ceil_div ( m_block_group_count * ( unsigned ) sizeof ( ext2_group_desc ) , block_size ( ) ) ;
unsigned first_block_of_bgdt = block_size ( ) = = 1024 ? 2 : 1 ;
2018-10-24 13:38:53 +02:00
# ifdef EXT2_DEBUG
2019-01-31 17:31:23 +01:00
kprintf ( " ext2fs: block group count: %u, blocks-to-read: %u \n " , m_block_group_count , blocks_to_read ) ;
kprintf ( " ext2fs: first block of BGDT: %u \n " , first_block_of_bgdt ) ;
2018-10-24 13:38:53 +02:00
# endif
2019-01-31 17:31:23 +01:00
m_cached_group_descriptor_table = read_blocks ( first_block_of_bgdt , blocks_to_read ) ;
2018-10-10 11:53:07 +02:00
}
2018-12-03 00:20:00 +01:00
return reinterpret_cast < ext2_group_desc * > ( m_cached_group_descriptor_table . pointer ( ) ) [ groupIndex - 1 ] ;
2018-10-10 11:53:07 +02:00
}
2018-11-15 17:13:10 +01:00
bool Ext2FS : : initialize ( )
2018-10-10 11:53:07 +02:00
{
2019-01-31 17:31:23 +01:00
auto & super_block = this - > super_block ( ) ;
2018-10-24 13:38:53 +02:00
# ifdef EXT2_DEBUG
2019-01-31 17:31:23 +01:00
kprintf ( " ext2fs: super block magic: %x (super block size: %u) \n " , super_block . s_magic , sizeof ( ext2_super_block ) ) ;
2018-10-24 13:38:53 +02:00
# endif
2019-01-31 17:31:23 +01:00
if ( super_block . s_magic ! = EXT2_SUPER_MAGIC )
2018-10-10 11:53:07 +02:00
return false ;
2018-10-24 13:38:53 +02:00
# ifdef EXT2_DEBUG
2019-01-31 17:31:23 +01:00
kprintf ( " ext2fs: %u inodes, %u blocks \n " , super_block . s_inodes_count , super_block . s_blocks_count ) ;
kprintf ( " ext2fs: block size = %u \n " , EXT2_BLOCK_SIZE ( & super_block ) ) ;
kprintf ( " ext2fs: first data block = %u \n " , super_block . s_first_data_block ) ;
2018-12-29 03:36:22 +01:00
kprintf ( " ext2fs: inodes per block = %u \n " , inodes_per_block ( ) ) ;
kprintf ( " ext2fs: inodes per group = %u \n " , inodes_per_group ( ) ) ;
2019-01-31 17:31:23 +01:00
kprintf ( " ext2fs: free inodes = %u \n " , super_block . s_free_inodes_count ) ;
kprintf ( " ext2fs: desc per block = %u \n " , EXT2_DESC_PER_BLOCK ( & super_block ) ) ;
kprintf ( " ext2fs: desc size = %u \n " , EXT2_DESC_SIZE ( & super_block ) ) ;
2018-10-24 13:38:53 +02:00
# endif
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
set_block_size ( EXT2_BLOCK_SIZE ( & super_block ) ) ;
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
m_block_group_count = ceil_div ( super_block . s_blocks_count , super_block . s_blocks_per_group ) ;
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
if ( m_block_group_count = = 0 ) {
2018-10-31 20:10:39 +01:00
kprintf ( " ext2fs: no block groups :( \n " ) ;
2018-10-10 11:53:07 +02:00
return false ;
}
2018-10-31 21:31:56 +01:00
// Preheat the BGD cache.
2018-12-03 00:20:00 +01:00
group_descriptor ( 0 ) ;
2018-10-31 21:31:56 +01:00
2018-10-24 13:38:53 +02:00
# ifdef EXT2_DEBUG
2019-01-31 17:31:23 +01:00
for ( unsigned i = 1 ; i < = m_block_group_count ; + + i ) {
2018-12-29 03:36:22 +01:00
auto & group = group_descriptor ( i ) ;
2018-10-31 20:10:39 +01:00
kprintf ( " ext2fs: group[%u] { block_bitmap: %u, inode_bitmap: %u, inode_table: %u } \n " ,
2018-10-10 11:53:07 +02:00
i ,
group . bg_block_bitmap ,
group . bg_inode_bitmap ,
group . bg_inode_table ) ;
}
2018-10-24 13:38:53 +02:00
# endif
2018-10-10 11:53:07 +02:00
return true ;
}
2018-11-15 17:13:10 +01:00
const char * Ext2FS : : class_name ( ) const
2018-10-10 11:53:07 +02:00
{
2019-01-31 06:13:55 +01:00
return " Ext2FS " ;
2018-10-10 11:53:07 +02:00
}
2018-12-03 00:20:00 +01:00
InodeIdentifier Ext2FS : : root_inode ( ) const
2018-10-10 11:53:07 +02:00
{
2019-01-23 05:38:54 +01:00
return { fsid ( ) , EXT2_ROOT_INO } ;
2018-10-10 11:53:07 +02:00
}
2019-01-31 17:31:23 +01:00
ByteBuffer Ext2FS : : read_block_containing_inode ( unsigned inode , unsigned & block_index , unsigned & offset ) const
2018-10-10 11:53:07 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-31 17:31:23 +01:00
auto & super_block = this - > super_block ( ) ;
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
if ( inode ! = EXT2_ROOT_INO & & inode < EXT2_FIRST_INO ( & super_block ) )
2018-10-10 11:53:07 +02:00
return { } ;
2019-01-31 17:31:23 +01:00
if ( inode > super_block . s_inodes_count )
2018-10-10 11:53:07 +02:00
return { } ;
2018-12-03 00:20:00 +01:00
auto & bgd = group_descriptor ( group_index_from_inode ( inode ) ) ;
2018-10-10 11:53:07 +02:00
2018-12-03 00:20:00 +01:00
offset = ( ( inode - 1 ) % inodes_per_group ( ) ) * inode_size ( ) ;
2019-01-31 17:31:23 +01:00
block_index = bgd . bg_inode_table + ( offset > > EXT2_BLOCK_SIZE_BITS ( & super_block ) ) ;
offset & = block_size ( ) - 1 ;
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
return read_block ( block_index ) ;
2018-10-10 11:53:07 +02:00
}
2019-01-23 02:45:25 +01:00
Ext2FS : : BlockListShape Ext2FS : : compute_block_list_shape ( unsigned blocks )
{
BlockListShape shape ;
const unsigned entries_per_block = EXT2_ADDR_PER_BLOCK ( & super_block ( ) ) ;
unsigned blocks_remaining = blocks ;
shape . direct_blocks = min ( ( unsigned ) EXT2_NDIR_BLOCKS , blocks_remaining ) ;
blocks_remaining - = shape . direct_blocks ;
if ( ! blocks_remaining )
return shape ;
shape . indirect_blocks = min ( blocks_remaining , entries_per_block ) ;
blocks_remaining - = shape . indirect_blocks ;
shape . meta_blocks + = 1 ;
if ( ! blocks_remaining )
return shape ;
ASSERT_NOT_REACHED ( ) ;
// FIXME: Support dind/tind blocks.
shape . doubly_indirect_blocks = min ( blocks_remaining , entries_per_block * entries_per_block ) ;
blocks_remaining - = shape . doubly_indirect_blocks ;
if ( ! blocks_remaining )
return shape ;
shape . triply_indirect_blocks = min ( blocks_remaining , entries_per_block * entries_per_block * entries_per_block ) ;
blocks_remaining - = shape . triply_indirect_blocks ;
// FIXME: What do we do for files >= 16GB?
ASSERT ( ! blocks_remaining ) ;
return shape ;
}
2019-01-23 04:29:56 +01:00
bool Ext2FS : : write_block_list_for_inode ( InodeIndex inode_index , ext2_inode & e2inode , const Vector < BlockIndex > & blocks )
2019-01-23 02:45:25 +01:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-03-27 14:24:37 +01:00
// NOTE: There is a mismatch between i_blocks and blocks.size() since i_blocks includes meta blocks and blocks.size() does not.
auto old_block_count = ceil_div ( e2inode . i_size , block_size ( ) ) ;
2019-01-23 02:45:25 +01:00
2019-03-27 14:24:37 +01:00
auto old_shape = compute_block_list_shape ( old_block_count ) ;
2019-01-23 02:45:25 +01:00
auto new_shape = compute_block_list_shape ( blocks . size ( ) ) ;
Vector < BlockIndex > new_meta_blocks ;
if ( new_shape . meta_blocks > old_shape . meta_blocks ) {
new_meta_blocks = allocate_blocks ( group_index_from_inode ( inode_index ) , new_shape . meta_blocks - old_shape . meta_blocks ) ;
2019-02-15 23:24:01 +01:00
for ( auto block_index : new_meta_blocks )
set_block_allocation_state ( block_index , true ) ;
2019-01-23 02:45:25 +01:00
}
2019-01-31 17:31:23 +01:00
e2inode . i_blocks = ( blocks . size ( ) + new_shape . meta_blocks ) * ( block_size ( ) / 512 ) ;
2019-01-23 15:43:29 +01:00
2019-03-27 14:24:37 +01:00
bool inode_dirty = false ;
2019-01-23 02:45:25 +01:00
unsigned output_block_index = 0 ;
unsigned remaining_blocks = blocks . size ( ) ;
for ( unsigned i = 0 ; i < new_shape . direct_blocks ; + + i ) {
2019-03-27 14:24:37 +01:00
if ( e2inode . i_block [ i ] ! = blocks [ output_block_index ] )
inode_dirty = true ;
e2inode . i_block [ i ] = blocks [ output_block_index ] ;
+ + output_block_index ;
2019-01-23 02:45:25 +01:00
- - remaining_blocks ;
}
2019-03-27 14:24:37 +01:00
if ( inode_dirty ) {
dbgprintf ( " Ext2FS: Writing %u direct block(s) to i_block array of inode %u \n " , min ( EXT2_NDIR_BLOCKS , blocks . size ( ) ) , inode_index ) ;
write_ext2_inode ( inode_index , e2inode ) ;
inode_dirty = false ;
}
2019-01-23 02:45:25 +01:00
if ( ! remaining_blocks )
return true ;
if ( ! e2inode . i_block [ EXT2_IND_BLOCK ] ) {
2019-03-27 14:24:37 +01:00
BlockIndex new_indirect_block = new_meta_blocks . take_last ( ) ;
if ( e2inode . i_block [ EXT2_IND_BLOCK ] ! = new_indirect_block )
inode_dirty = true ;
e2inode . i_block [ EXT2_IND_BLOCK ] = new_indirect_block ;
if ( inode_dirty ) {
dbgprintf ( " Ext2FS: Adding the indirect block to i_block array of inode %u \n " , inode_index ) ;
write_ext2_inode ( inode_index , e2inode ) ;
inode_dirty = false ;
}
2019-01-23 02:45:25 +01:00
}
2019-03-27 14:24:37 +01:00
if ( old_shape . indirect_blocks = = new_shape . indirect_blocks ) {
// No need to update the singly indirect block array.
remaining_blocks - = new_shape . indirect_blocks ;
} else {
2019-01-31 17:31:23 +01:00
auto block_contents = ByteBuffer : : create_uninitialized ( block_size ( ) ) ;
2019-01-23 02:45:25 +01:00
BufferStream stream ( block_contents ) ;
ASSERT ( new_shape . indirect_blocks < = EXT2_ADDR_PER_BLOCK ( & super_block ( ) ) ) ;
for ( unsigned i = 0 ; i < new_shape . indirect_blocks ; + + i ) {
stream < < blocks [ output_block_index + + ] ;
- - remaining_blocks ;
}
stream . fill_to_end ( 0 ) ;
2019-02-11 11:38:14 +01:00
bool success = write_block ( e2inode . i_block [ EXT2_IND_BLOCK ] , block_contents ) ;
ASSERT ( success ) ;
2019-01-23 02:45:25 +01:00
}
if ( ! remaining_blocks )
return true ;
// FIXME: Implement!
ASSERT_NOT_REACHED ( ) ;
}
2019-01-22 16:34:24 +01:00
Vector < unsigned > Ext2FS : : block_list_for_inode ( const ext2_inode & e2inode , bool include_block_list_blocks ) const
2018-10-10 11:53:07 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-31 17:31:23 +01:00
unsigned entries_per_block = EXT2_ADDR_PER_BLOCK ( & super_block ( ) ) ;
2018-10-10 11:53:07 +02:00
// NOTE: i_blocks is number of 512-byte blocks, not number of fs-blocks.
2019-01-31 17:31:23 +01:00
unsigned block_count = e2inode . i_blocks / ( block_size ( ) / 512 ) ;
unsigned blocksRemaining = block_count ;
2018-10-10 11:53:07 +02:00
Vector < unsigned > list ;
2019-01-22 16:34:24 +01:00
if ( include_block_list_blocks ) {
// This seems like an excessive over-estimate but w/e.
list . ensure_capacity ( blocksRemaining * 2 ) ;
} else {
list . ensure_capacity ( blocksRemaining ) ;
}
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
unsigned direct_count = min ( block_count , ( unsigned ) EXT2_NDIR_BLOCKS ) ;
for ( unsigned i = 0 ; i < direct_count ; + + i ) {
2018-11-13 00:17:30 +01:00
list . unchecked_append ( e2inode . i_block [ i ] ) ;
2018-10-10 11:53:07 +02:00
- - blocksRemaining ;
}
if ( ! blocksRemaining )
return list ;
2019-01-31 17:31:23 +01:00
auto process_block_array = [ & ] ( unsigned array_block_index , auto & & callback ) {
2019-01-22 16:34:24 +01:00
if ( include_block_list_blocks )
2019-01-31 17:31:23 +01:00
callback ( array_block_index ) ;
auto array_block = read_block ( array_block_index ) ;
ASSERT ( array_block ) ;
auto * array = reinterpret_cast < const __u32 * > ( array_block . pointer ( ) ) ;
unsigned count = min ( blocksRemaining , entries_per_block ) ;
2018-10-10 11:53:07 +02:00
for ( unsigned i = 0 ; i < count ; + + i ) {
if ( ! array [ i ] ) {
blocksRemaining = 0 ;
return ;
}
callback ( array [ i ] ) ;
- - blocksRemaining ;
}
} ;
2019-01-31 17:31:23 +01:00
process_block_array ( e2inode . i_block [ EXT2_IND_BLOCK ] , [ & ] ( unsigned entry ) {
2018-11-13 00:17:30 +01:00
list . unchecked_append ( entry ) ;
2018-10-10 11:53:07 +02:00
} ) ;
if ( ! blocksRemaining )
return list ;
2019-01-31 17:31:23 +01:00
process_block_array ( e2inode . i_block [ EXT2_DIND_BLOCK ] , [ & ] ( unsigned entry ) {
process_block_array ( entry , [ & ] ( unsigned entry ) {
2018-11-13 00:17:30 +01:00
list . unchecked_append ( entry ) ;
2018-10-10 11:53:07 +02:00
} ) ;
} ) ;
if ( ! blocksRemaining )
return list ;
2019-01-31 17:31:23 +01:00
process_block_array ( e2inode . i_block [ EXT2_TIND_BLOCK ] , [ & ] ( unsigned entry ) {
process_block_array ( entry , [ & ] ( unsigned entry ) {
process_block_array ( entry , [ & ] ( unsigned entry ) {
2018-11-13 00:17:30 +01:00
list . unchecked_append ( entry ) ;
2018-10-10 11:53:07 +02:00
} ) ;
} ) ;
} ) ;
return list ;
}
2019-01-22 20:48:48 +01:00
void Ext2FS : : free_inode ( Ext2FSInode & inode )
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-22 20:48:48 +01:00
ASSERT ( inode . m_raw_inode . i_links_count = = 0 ) ;
dbgprintf ( " Ext2FS: inode %u has no more links, time to delete! \n " , inode . index ( ) ) ;
2019-03-25 02:06:57 +01:00
struct timeval now ;
kgettimeofday ( now ) ;
inode . m_raw_inode . i_dtime = now . tv_sec ;
2019-01-22 20:48:48 +01:00
write_ext2_inode ( inode . index ( ) , inode . m_raw_inode ) ;
auto block_list = block_list_for_inode ( inode . m_raw_inode , true ) ;
for ( auto block_index : block_list )
2019-02-15 23:24:01 +01:00
set_block_allocation_state ( block_index , false ) ;
2019-01-22 20:48:48 +01:00
set_inode_allocation_state ( inode . index ( ) , false ) ;
2019-01-28 04:16:01 +01:00
if ( inode . is_directory ( ) ) {
auto & bgd = const_cast < ext2_group_desc & > ( group_descriptor ( group_index_from_inode ( inode . index ( ) ) ) ) ;
- - bgd . bg_used_dirs_count ;
dbgprintf ( " Ext2FS: decremented bg_used_dirs_count %u -> %u \n " , bgd . bg_used_dirs_count - 1 , bgd . bg_used_dirs_count ) ;
flush_block_group_descriptor_table ( ) ;
}
}
void Ext2FS : : flush_block_group_descriptor_table ( )
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-31 17:31:23 +01:00
unsigned blocks_to_write = ceil_div ( m_block_group_count * ( unsigned ) sizeof ( ext2_group_desc ) , block_size ( ) ) ;
unsigned first_block_of_bgdt = block_size ( ) = = 1024 ? 2 : 1 ;
write_blocks ( first_block_of_bgdt , blocks_to_write , m_cached_group_descriptor_table ) ;
2019-01-22 20:48:48 +01:00
}
2019-02-03 04:05:30 +01:00
Ext2FSInode : : Ext2FSInode ( Ext2FS & fs , unsigned index )
2018-12-19 21:18:28 +01:00
: Inode ( fs , index )
2018-11-13 13:02:39 +01:00
{
}
2018-11-15 17:13:10 +01:00
Ext2FSInode : : ~ Ext2FSInode ( )
2018-11-13 13:02:39 +01:00
{
2019-01-22 20:48:48 +01:00
if ( m_raw_inode . i_links_count = = 0 )
fs ( ) . free_inode ( * this ) ;
2018-11-13 13:02:39 +01:00
}
2019-01-01 03:16:36 +01:00
InodeMetadata Ext2FSInode : : metadata ( ) const
{
2019-02-20 13:09:59 +01:00
// FIXME: This should probably take the inode lock, no?
2019-01-01 03:16:36 +01:00
InodeMetadata metadata ;
metadata . inode = identifier ( ) ;
metadata . size = m_raw_inode . i_size ;
metadata . mode = m_raw_inode . i_mode ;
metadata . uid = m_raw_inode . i_uid ;
metadata . gid = m_raw_inode . i_gid ;
2019-01-31 17:31:23 +01:00
metadata . link_count = m_raw_inode . i_links_count ;
2019-01-01 03:16:36 +01:00
metadata . atime = m_raw_inode . i_atime ;
metadata . ctime = m_raw_inode . i_ctime ;
metadata . mtime = m_raw_inode . i_mtime ;
metadata . dtime = m_raw_inode . i_dtime ;
2019-01-31 17:31:23 +01:00
metadata . block_size = fs ( ) . block_size ( ) ;
metadata . block_count = m_raw_inode . i_blocks ;
2018-11-13 13:32:16 +01:00
2019-02-16 09:57:42 +01:00
if ( : : is_character_device ( m_raw_inode . i_mode ) ) {
2018-11-13 13:32:16 +01:00
unsigned dev = m_raw_inode . i_block [ 0 ] ;
2019-01-31 17:31:23 +01:00
metadata . major_device = ( dev & 0xfff00 ) > > 8 ;
metadata . minor_device = ( dev & 0xff ) | ( ( dev > > 12 ) & 0xfff00 ) ;
2019-02-16 09:57:42 +01:00
}
if ( : : is_block_device ( m_raw_inode . i_mode ) ) {
unsigned dev = m_raw_inode . i_block [ 1 ] ;
metadata . major_device = ( dev & 0xfff00 ) > > 8 ;
metadata . minor_device = ( dev & 0xff ) | ( ( dev > > 12 ) & 0xfff00 ) ;
2018-11-13 13:32:16 +01:00
}
2019-01-01 03:16:36 +01:00
return metadata ;
2018-11-13 13:32:16 +01:00
}
2018-12-19 21:56:45 +01:00
void Ext2FSInode : : flush_metadata ( )
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2018-12-20 00:39:29 +01:00
dbgprintf ( " Ext2FSInode: flush_metadata for inode %u \n " , index ( ) ) ;
2018-12-19 21:56:45 +01:00
fs ( ) . write_ext2_inode ( index ( ) , m_raw_inode ) ;
2019-01-01 03:55:13 +01:00
if ( is_directory ( ) ) {
2019-01-28 04:16:01 +01:00
// Unless we're about to go away permanently, invalidate the lookup cache.
if ( m_raw_inode . i_links_count ! = 0 ) {
// FIXME: This invalidation is way too hardcore. It's sad to throw away the whole cache.
m_lookup_cache . clear ( ) ;
}
2019-01-01 03:55:13 +01:00
}
2018-12-19 22:28:09 +01:00
set_metadata_dirty ( false ) ;
2018-12-19 21:56:45 +01:00
}
2018-12-19 21:18:28 +01:00
RetainPtr < Inode > Ext2FS : : get_inode ( InodeIdentifier inode ) const
2018-11-13 13:02:39 +01:00
{
2019-02-20 21:41:53 +01:00
LOCKER ( m_lock ) ;
2019-01-23 05:38:54 +01:00
ASSERT ( inode . fsid ( ) = = fsid ( ) ) ;
2019-02-20 21:41:53 +01:00
2018-11-13 13:02:39 +01:00
{
auto it = m_inode_cache . find ( inode . index ( ) ) ;
if ( it ! = m_inode_cache . end ( ) )
return ( * it ) . value ;
}
2018-12-25 00:32:57 +01:00
2019-01-01 03:35:33 +01:00
if ( ! get_inode_allocation_state ( inode . index ( ) ) ) {
m_inode_cache . set ( inode . index ( ) , nullptr ) ;
return nullptr ;
}
2018-12-25 00:32:57 +01:00
unsigned block_index ;
unsigned offset ;
auto block = read_block_containing_inode ( inode . index ( ) , block_index , offset ) ;
if ( ! block )
return { } ;
2018-11-13 13:02:39 +01:00
auto it = m_inode_cache . find ( inode . index ( ) ) ;
if ( it ! = m_inode_cache . end ( ) )
return ( * it ) . value ;
2019-02-03 04:05:30 +01:00
auto new_inode = adopt ( * new Ext2FSInode ( const_cast < Ext2FS & > ( * this ) , inode . index ( ) ) ) ;
2019-02-24 14:24:04 +01:00
memcpy ( & new_inode - > m_raw_inode , reinterpret_cast < ext2_inode * > ( block . offset_pointer ( offset ) ) , sizeof ( ext2_inode ) ) ;
2019-01-31 17:31:23 +01:00
m_inode_cache . set ( inode . index ( ) , new_inode . copy_ref ( ) ) ;
2018-11-13 13:02:39 +01:00
return new_inode ;
}
2019-02-25 22:06:55 +01:00
ssize_t Ext2FSInode : : read_bytes ( off_t offset , ssize_t count , byte * buffer , FileDescriptor * ) const
2018-11-13 13:02:39 +01:00
{
2019-02-20 13:09:59 +01:00
Locker inode_locker ( m_lock ) ;
2018-11-13 13:02:39 +01:00
ASSERT ( offset > = 0 ) ;
if ( m_raw_inode . i_size = = 0 )
return 0 ;
// Symbolic links shorter than 60 characters are store inline inside the i_block array.
// This avoids wasting an entire block on short links. (Most links are short.)
if ( is_symlink ( ) & & size ( ) < max_inline_symlink_length ) {
2019-01-23 06:53:01 +01:00
ssize_t nread = min ( ( off_t ) size ( ) - offset , static_cast < off_t > ( count ) ) ;
2019-04-23 13:00:53 +02:00
memcpy ( buffer , ( ( const byte * ) m_raw_inode . i_block ) + offset , ( size_t ) nread ) ;
2018-11-13 13:02:39 +01:00
return nread ;
}
2019-02-20 13:09:59 +01:00
Locker fs_locker ( fs ( ) . m_lock ) ;
2018-12-21 02:10:45 +01:00
if ( m_block_list . is_empty ( ) ) {
2018-12-03 00:20:00 +01:00
auto block_list = fs ( ) . block_list_for_inode ( m_raw_inode ) ;
2018-11-13 13:02:39 +01:00
if ( m_block_list . size ( ) ! = block_list . size ( ) )
m_block_list = move ( block_list ) ;
}
2018-12-21 02:10:45 +01:00
if ( m_block_list . is_empty ( ) ) {
2018-11-13 13:02:39 +01:00
kprintf ( " ext2fs: read_bytes: empty block list for inode %u \n " , index ( ) ) ;
return - EIO ;
}
2019-04-23 13:00:53 +02:00
const int block_size = fs ( ) . block_size ( ) ;
2018-11-13 13:02:39 +01:00
2019-04-23 13:00:53 +02:00
int first_block_logical_index = offset / block_size ;
int last_block_logical_index = ( offset + count ) / block_size ;
2018-11-13 13:02:39 +01:00
if ( last_block_logical_index > = m_block_list . size ( ) )
last_block_logical_index = m_block_list . size ( ) - 1 ;
2019-04-23 13:00:53 +02:00
int offset_into_first_block = offset % block_size ;
2018-11-13 13:02:39 +01:00
2018-12-02 23:34:50 +01:00
ssize_t nread = 0 ;
2019-04-23 13:00:53 +02:00
int remaining_count = min ( ( off_t ) count , ( off_t ) size ( ) - offset ) ;
2018-11-13 13:02:39 +01:00
byte * out = buffer ;
# ifdef EXT2_DEBUG
2019-01-23 04:59:47 +01:00
kprintf ( " Ext2FS: Reading up to %u bytes %d bytes into inode %u:%u to %p \n " , count , offset , identifier ( ) . fsid ( ) , identifier ( ) . index ( ) , buffer ) ;
2018-12-29 03:36:22 +01:00
//kprintf("ok let's do it, read(%u, %u) -> blocks %u thru %u, oifb: %u\n", offset, count, first_block_logical_index, last_block_logical_index, offset_into_first_block);
2018-11-13 13:02:39 +01:00
# endif
2019-04-23 13:00:53 +02:00
for ( int bi = first_block_logical_index ; remaining_count & & bi < = last_block_logical_index ; + + bi ) {
2019-01-31 17:31:23 +01:00
auto block = fs ( ) . read_block ( m_block_list [ bi ] ) ;
2018-11-13 13:02:39 +01:00
if ( ! block ) {
2019-01-31 17:31:23 +01:00
kprintf ( " ext2fs: read_bytes: read_block(%u) failed (lbi: %u) \n " , m_block_list [ bi ] , bi ) ;
2018-11-13 13:02:39 +01:00
return - EIO ;
}
2019-04-23 13:00:53 +02:00
int offset_into_block = ( bi = = first_block_logical_index ) ? offset_into_first_block : 0 ;
int num_bytes_to_copy = min ( block_size - offset_into_block , remaining_count ) ;
2018-11-13 13:02:39 +01:00
memcpy ( out , block . pointer ( ) + offset_into_block , num_bytes_to_copy ) ;
remaining_count - = num_bytes_to_copy ;
nread + = num_bytes_to_copy ;
out + = num_bytes_to_copy ;
}
return nread ;
}
2019-02-25 22:06:55 +01:00
ssize_t Ext2FSInode : : write_bytes ( off_t offset , ssize_t count , const byte * data , FileDescriptor * )
2019-01-23 04:29:56 +01:00
{
2019-03-02 01:50:34 +01:00
ASSERT ( offset > = 0 ) ;
ASSERT ( count > = 0 ) ;
2019-02-20 13:09:59 +01:00
Locker inode_locker ( m_lock ) ;
Locker fs_locker ( fs ( ) . m_lock ) ;
2019-01-23 04:29:56 +01:00
2019-03-02 01:50:34 +01:00
if ( is_symlink ( ) ) {
if ( ( offset + count ) < max_inline_symlink_length ) {
# ifdef EXT2_DEBUG
dbgprintf ( " Ext2FSInode: write_bytes poking into i_block array for inline symlink '%s' (%u bytes) \n " , String ( ( const char * ) data , count ) . characters ( ) , count ) ;
# endif
memcpy ( ( ( byte * ) m_raw_inode . i_block ) + offset , data , ( size_t ) count ) ;
2019-04-23 13:00:53 +02:00
if ( ( offset + count ) > ( off_t ) m_raw_inode . i_size )
2019-03-02 01:50:34 +01:00
m_raw_inode . i_size = offset + count ;
set_metadata_dirty ( true ) ;
return count ;
}
}
2019-01-23 04:29:56 +01:00
2019-02-25 22:06:55 +01:00
const ssize_t block_size = fs ( ) . block_size ( ) ;
2019-02-05 08:17:46 +01:00
size_t old_size = size ( ) ;
2019-01-23 04:29:56 +01:00
size_t new_size = max ( static_cast < size_t > ( offset ) + count , size ( ) ) ;
2019-01-31 17:31:23 +01:00
unsigned blocks_needed_before = ceil_div ( size ( ) , block_size ) ;
unsigned blocks_needed_after = ceil_div ( new_size , block_size ) ;
2019-01-23 04:29:56 +01:00
auto block_list = fs ( ) . block_list_for_inode ( m_raw_inode ) ;
if ( blocks_needed_after > blocks_needed_before ) {
auto new_blocks = fs ( ) . allocate_blocks ( fs ( ) . group_index_from_inode ( index ( ) ) , blocks_needed_after - blocks_needed_before ) ;
for ( auto new_block_index : new_blocks )
2019-02-15 23:24:01 +01:00
fs ( ) . set_block_allocation_state ( new_block_index , true ) ;
2019-01-23 04:29:56 +01:00
block_list . append ( move ( new_blocks ) ) ;
} else if ( blocks_needed_after < blocks_needed_before ) {
// FIXME: Implement block list shrinking!
ASSERT_NOT_REACHED ( ) ;
}
2019-04-23 13:00:53 +02:00
int first_block_logical_index = offset / block_size ;
int last_block_logical_index = ( offset + count ) / block_size ;
2019-01-23 04:29:56 +01:00
if ( last_block_logical_index > = block_list . size ( ) )
last_block_logical_index = block_list . size ( ) - 1 ;
2019-04-23 13:00:53 +02:00
int offset_into_first_block = offset % block_size ;
2019-01-23 04:29:56 +01:00
2019-04-23 13:00:53 +02:00
int last_logical_block_index_in_file = size ( ) / block_size ;
2019-03-02 01:50:34 +01:00
2019-01-23 04:29:56 +01:00
ssize_t nwritten = 0 ;
2019-04-23 13:00:53 +02:00
int remaining_count = min ( ( off_t ) count , ( off_t ) new_size - offset ) ;
2019-01-23 04:29:56 +01:00
const byte * in = data ;
# ifdef EXT2_DEBUG
2019-01-23 04:59:47 +01:00
dbgprintf ( " Ext2FSInode::write_bytes: Writing %u bytes %d bytes into inode %u:%u from %p \n " , count , offset , fsid ( ) , index ( ) , data ) ;
2019-01-23 04:29:56 +01:00
# endif
auto buffer_block = ByteBuffer : : create_uninitialized ( block_size ) ;
2019-04-23 13:00:53 +02:00
for ( int bi = first_block_logical_index ; remaining_count & & bi < = last_block_logical_index ; + + bi ) {
int offset_into_block = ( bi = = first_block_logical_index ) ? offset_into_first_block : 0 ;
int num_bytes_to_copy = min ( block_size - offset_into_block , remaining_count ) ;
2019-01-23 04:29:56 +01:00
ByteBuffer block ;
2019-03-02 01:50:34 +01:00
if ( offset_into_block ! = 0 | | num_bytes_to_copy ! = block_size ) {
2019-01-31 17:31:23 +01:00
block = fs ( ) . read_block ( block_list [ bi ] ) ;
2019-01-23 04:29:56 +01:00
if ( ! block ) {
2019-01-31 17:31:23 +01:00
kprintf ( " Ext2FSInode::write_bytes: read_block(%u) failed (lbi: %u) \n " , block_list [ bi ] , bi ) ;
2019-01-23 04:29:56 +01:00
return - EIO ;
}
} else
block = buffer_block ;
memcpy ( block . pointer ( ) + offset_into_block , in , num_bytes_to_copy ) ;
2019-03-02 01:50:34 +01:00
if ( bi = = last_logical_block_index_in_file & & num_bytes_to_copy < block_size ) {
2019-04-23 13:00:53 +02:00
int padding_start = new_size % block_size ;
int padding_bytes = block_size - padding_start ;
2019-03-02 01:50:34 +01:00
# ifdef EXT2_DEBUG
dbgprintf ( " Ext2FSInode::write_bytes padding last block of file with zero x %u (new_size=%u, offset_into_block=%u, num_bytes_to_copy=%u) \n " , padding_bytes , new_size , offset_into_block , num_bytes_to_copy ) ;
# endif
memset ( block . pointer ( ) + padding_start , 0 , padding_bytes ) ;
}
2019-01-23 04:29:56 +01:00
# ifdef EXT2_DEBUG
dbgprintf ( " Ext2FSInode::write_bytes: writing block %u (offset_into_block: %u) \n " , block_list [ bi ] , offset_into_block ) ;
# endif
2019-01-31 17:31:23 +01:00
bool success = fs ( ) . write_block ( block_list [ bi ] , block ) ;
2019-01-23 04:29:56 +01:00
if ( ! success ) {
2019-01-31 17:31:23 +01:00
kprintf ( " Ext2FSInode::write_bytes: write_block(%u) failed (lbi: %u) \n " , block_list [ bi ] , bi ) ;
2019-02-11 11:38:14 +01:00
ASSERT_NOT_REACHED ( ) ;
2019-01-23 04:29:56 +01:00
return - EIO ;
}
remaining_count - = num_bytes_to_copy ;
nwritten + = num_bytes_to_copy ;
in + = num_bytes_to_copy ;
}
bool success = fs ( ) . write_block_list_for_inode ( index ( ) , m_raw_inode , block_list ) ;
ASSERT ( success ) ;
m_raw_inode . i_size = new_size ;
2019-01-23 04:59:47 +01:00
fs ( ) . write_ext2_inode ( index ( ) , m_raw_inode ) ;
2019-01-23 04:29:56 +01:00
# ifdef EXT2_DEBUG
dbgprintf ( " Ext2FSInode::write_bytes: after write, i_size=%u, i_blocks=%u (%u blocks in list) \n " , m_raw_inode . i_size , m_raw_inode . i_blocks , block_list . size ( ) ) ;
# endif
// NOTE: Make sure the cached block list is up to date!
m_block_list = move ( block_list ) ;
2019-02-05 08:17:46 +01:00
if ( old_size ! = new_size )
inode_size_changed ( old_size , new_size ) ;
inode_contents_changed ( offset , count , data ) ;
2019-01-23 04:29:56 +01:00
return nwritten ;
}
2019-01-28 04:16:01 +01:00
bool Ext2FSInode : : traverse_as_directory ( Function < bool ( const FS : : DirectoryEntry & ) > callback ) const
2018-11-13 23:44:54 +01:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-31 17:31:23 +01:00
ASSERT ( metadata ( ) . is_directory ( ) ) ;
2018-11-13 23:44:54 +01:00
# ifdef EXT2_DEBUG
kprintf ( " Ext2Inode::traverse_as_directory: inode=%u: \n " , index ( ) ) ;
# endif
auto buffer = read_entire ( ) ;
ASSERT ( buffer ) ;
auto * entry = reinterpret_cast < ext2_dir_entry_2 * > ( buffer . pointer ( ) ) ;
2018-12-21 02:10:45 +01:00
while ( entry < buffer . end_pointer ( ) ) {
2018-11-13 23:44:54 +01:00
if ( entry - > inode ! = 0 ) {
# ifdef EXT2_DEBUG
2019-01-23 04:59:47 +01:00
kprintf ( " Ext2Inode::traverse_as_directory: %u, name_len: %u, rec_len: %u, file_type: %u, name: %s \n " , entry - > inode , entry - > name_len , entry - > rec_len , entry - > file_type , String ( entry - > name , entry - > name_len ) . characters ( ) ) ;
2018-11-13 23:44:54 +01:00
# endif
if ( ! callback ( { entry - > name , entry - > name_len , { fsid ( ) , entry - > inode } , entry - > file_type } ) )
break ;
}
entry = ( ext2_dir_entry_2 * ) ( ( char * ) entry + entry - > rec_len ) ;
}
return true ;
}
2019-02-27 15:31:26 +01:00
KResult Ext2FSInode : : add_child ( InodeIdentifier child_id , const String & name , byte file_type )
2018-10-10 11:53:07 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2018-12-25 00:27:39 +01:00
ASSERT ( is_directory ( ) ) ;
2018-10-10 11:53:07 +02:00
//#ifdef EXT2_DEBUG
2018-12-25 00:27:39 +01:00
dbgprintf ( " Ext2FS: Adding inode %u with name '%s' to directory %u \n " , child_id . index ( ) , name . characters ( ) , index ( ) ) ;
2018-10-10 11:53:07 +02:00
//#endif
2018-12-25 00:27:39 +01:00
Vector < FS : : DirectoryEntry > entries ;
bool name_already_exists = false ;
traverse_as_directory ( [ & ] ( auto & entry ) {
2018-11-13 00:17:30 +01:00
if ( ! strcmp ( entry . name , name . characters ( ) ) ) {
2018-12-25 00:27:39 +01:00
name_already_exists = true ;
2018-10-10 11:53:07 +02:00
return false ;
}
entries . append ( entry ) ;
return true ;
} ) ;
2018-12-25 00:27:39 +01:00
if ( name_already_exists ) {
kprintf ( " Ext2FS: Name '%s' already exists in directory inode %u \n " , name . characters ( ) , index ( ) ) ;
2019-02-27 15:31:26 +01:00
return KResult ( - EEXIST ) ;
2018-10-10 11:53:07 +02:00
}
2019-02-21 13:26:40 +01:00
auto child_inode = fs ( ) . get_inode ( child_id ) ;
if ( child_inode )
child_inode - > increment_link_count ( ) ;
2018-12-25 00:27:39 +01:00
entries . append ( { name . characters ( ) , name . length ( ) , child_id , file_type } ) ;
2019-01-22 00:58:13 +01:00
bool success = fs ( ) . write_directory_inode ( index ( ) , move ( entries ) ) ;
2019-02-21 13:26:40 +01:00
if ( success )
2019-01-22 00:58:13 +01:00
m_lookup_cache . set ( name , child_id . index ( ) ) ;
2019-02-27 15:31:26 +01:00
return KSuccess ;
2018-10-10 11:53:07 +02:00
}
2019-02-27 14:11:25 +01:00
KResult Ext2FSInode : : remove_child ( const String & name )
2019-01-22 07:03:44 +01:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-28 04:16:01 +01:00
# ifdef EXT2_DEBUG
dbgprintf ( " Ext2FSInode::remove_child(%s) in inode %u \n " , name . characters ( ) , index ( ) ) ;
# endif
2019-01-22 07:03:44 +01:00
ASSERT ( is_directory ( ) ) ;
unsigned child_inode_index ;
2019-02-27 14:11:25 +01:00
auto it = m_lookup_cache . find ( name ) ;
if ( it = = m_lookup_cache . end ( ) )
return KResult ( - ENOENT ) ;
child_inode_index = ( * it ) . value ;
2019-01-22 07:03:44 +01:00
InodeIdentifier child_id { fsid ( ) , child_inode_index } ;
//#ifdef EXT2_DEBUG
dbgprintf ( " Ext2FS: Removing '%s' in directory %u \n " , name . characters ( ) , index ( ) ) ;
//#endif
Vector < FS : : DirectoryEntry > entries ;
traverse_as_directory ( [ & ] ( auto & entry ) {
2019-02-21 13:26:40 +01:00
if ( strcmp ( entry . name , name . characters ( ) ) ! = 0 )
entries . append ( entry ) ;
2019-01-22 07:03:44 +01:00
return true ;
} ) ;
bool success = fs ( ) . write_directory_inode ( index ( ) , move ( entries ) ) ;
if ( ! success ) {
// FIXME: Plumb error from write_directory_inode().
2019-02-27 14:11:25 +01:00
return KResult ( - EIO ) ;
2019-01-22 07:03:44 +01:00
}
2019-02-27 14:11:25 +01:00
m_lookup_cache . remove ( name ) ;
2019-01-22 07:03:44 +01:00
auto child_inode = fs ( ) . get_inode ( child_id ) ;
child_inode - > decrement_link_count ( ) ;
2019-02-27 14:11:25 +01:00
return KSuccess ;
2019-01-22 07:03:44 +01:00
}
2018-12-03 00:20:00 +01:00
bool Ext2FS : : write_directory_inode ( unsigned directoryInode , Vector < DirectoryEntry > & & entries )
2018-10-10 11:53:07 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2018-11-18 14:57:41 +01:00
dbgprintf ( " Ext2FS: New directory inode %u contents to write: \n " , directoryInode ) ;
2018-10-10 11:53:07 +02:00
2019-02-25 22:06:55 +01:00
int directory_size = 0 ;
2018-10-10 11:53:07 +02:00
for ( auto & entry : entries ) {
2018-11-18 14:57:41 +01:00
//kprintf(" - %08u %s\n", entry.inode.index(), entry.name);
2019-01-31 17:31:23 +01:00
directory_size + = EXT2_DIR_REC_LEN ( entry . name_length ) ;
2018-10-10 11:53:07 +02:00
}
2019-02-25 22:06:55 +01:00
int blocks_needed = ceil_div ( directory_size , block_size ( ) ) ;
int occupied_size = blocks_needed * block_size ( ) ;
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
dbgprintf ( " Ext2FS: directory size: %u (occupied: %u) \n " , directory_size , occupied_size ) ;
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
auto directory_data = ByteBuffer : : create_uninitialized ( occupied_size ) ;
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
BufferStream stream ( directory_data ) ;
2019-02-25 22:06:55 +01:00
for ( int i = 0 ; i < entries . size ( ) ; + + i ) {
2018-10-10 11:53:07 +02:00
auto & entry = entries [ i ] ;
2019-02-25 22:06:55 +01:00
int record_length = EXT2_DIR_REC_LEN ( entry . name_length ) ;
2018-10-10 11:53:07 +02:00
if ( i = = entries . size ( ) - 1 )
2019-01-31 17:31:23 +01:00
record_length + = occupied_size - directory_size ;
2018-10-10 11:53:07 +02:00
2018-11-18 14:57:41 +01:00
dbgprintf ( " * inode: %u " , entry . inode . index ( ) ) ;
dbgprintf ( " , name_len: %u " , word ( entry . name_length ) ) ;
2019-01-31 17:31:23 +01:00
dbgprintf ( " , rec_len: %u " , word ( record_length ) ) ;
dbgprintf ( " , file_type: %u " , byte ( entry . file_type ) ) ;
2018-11-18 14:57:41 +01:00
dbgprintf ( " , name: %s \n " , entry . name ) ;
2018-10-10 11:53:07 +02:00
stream < < dword ( entry . inode . index ( ) ) ;
2019-01-31 17:31:23 +01:00
stream < < word ( record_length ) ;
2018-11-13 00:17:30 +01:00
stream < < byte ( entry . name_length ) ;
2019-01-31 17:31:23 +01:00
stream < < byte ( entry . file_type ) ;
2018-10-10 11:53:07 +02:00
stream < < entry . name ;
2019-02-25 22:06:55 +01:00
int padding = record_length - entry . name_length - 8 ;
for ( int j = 0 ; j < padding ; + + j )
2018-10-10 11:53:07 +02:00
stream < < byte ( 0 ) ;
}
2019-01-23 02:45:25 +01:00
stream . fill_to_end ( 0 ) ;
2018-10-10 11:53:07 +02:00
2019-01-28 04:16:01 +01:00
auto directory_inode = get_inode ( { fsid ( ) , directoryInode } ) ;
2019-01-31 17:31:23 +01:00
ssize_t nwritten = directory_inode - > write_bytes ( 0 , directory_data . size ( ) , directory_data . pointer ( ) , nullptr ) ;
return nwritten = = directory_data . size ( ) ;
2018-10-10 11:53:07 +02:00
}
2018-12-03 00:20:00 +01:00
unsigned Ext2FS : : inodes_per_block ( ) const
2018-10-10 11:53:07 +02:00
{
2018-12-03 00:20:00 +01:00
return EXT2_INODES_PER_BLOCK ( & super_block ( ) ) ;
2018-10-10 11:53:07 +02:00
}
2018-12-03 00:20:00 +01:00
unsigned Ext2FS : : inodes_per_group ( ) const
2018-10-10 11:53:07 +02:00
{
2018-12-03 00:20:00 +01:00
return EXT2_INODES_PER_GROUP ( & super_block ( ) ) ;
2018-10-10 11:53:07 +02:00
}
2018-12-03 00:20:00 +01:00
unsigned Ext2FS : : inode_size ( ) const
2018-10-10 11:53:07 +02:00
{
2018-12-03 00:20:00 +01:00
return EXT2_INODE_SIZE ( & super_block ( ) ) ;
2018-10-10 11:53:07 +02:00
}
2018-12-03 00:20:00 +01:00
unsigned Ext2FS : : blocks_per_group ( ) const
2018-10-10 11:53:07 +02:00
{
2018-12-03 00:20:00 +01:00
return EXT2_BLOCKS_PER_GROUP ( & super_block ( ) ) ;
2018-10-10 11:53:07 +02:00
}
2018-12-03 00:20:00 +01:00
bool Ext2FS : : write_ext2_inode ( unsigned inode , const ext2_inode & e2inode )
2018-10-10 11:53:07 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-31 17:31:23 +01:00
unsigned block_index ;
2018-10-10 11:53:07 +02:00
unsigned offset ;
2019-01-31 17:31:23 +01:00
auto block = read_block_containing_inode ( inode , block_index , offset ) ;
2018-10-10 11:53:07 +02:00
if ( ! block )
return false ;
2018-12-21 02:10:45 +01:00
memcpy ( reinterpret_cast < ext2_inode * > ( block . offset_pointer ( offset ) ) , & e2inode , inode_size ( ) ) ;
2019-02-11 11:38:14 +01:00
bool success = write_block ( block_index , block ) ;
ASSERT ( success ) ;
return success ;
2018-10-10 11:53:07 +02:00
}
2019-04-23 14:51:47 +02:00
Vector < Ext2FS : : BlockIndex > Ext2FS : : allocate_blocks ( GroupIndex group_index , int count )
2018-10-16 00:35:03 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-04-23 14:51:47 +02:00
dbgprintf ( " Ext2FS: allocate_blocks(group: %u, count: %u) \n " , group_index , count ) ;
2019-01-22 00:58:13 +01:00
if ( count = = 0 )
return { } ;
2018-10-16 00:35:03 +02:00
2019-04-23 14:51:47 +02:00
auto & bgd = group_descriptor ( group_index ) ;
2018-10-16 00:35:03 +02:00
if ( bgd . bg_free_blocks_count < count ) {
2019-04-23 14:51:47 +02:00
kprintf ( " Ext2FS: allocate_blocks can't allocate out of group %u, wanted %u but only %u available \n " , group_index , count , bgd . bg_free_blocks_count ) ;
2018-10-16 00:35:03 +02:00
return { } ;
}
// FIXME: Implement a scan that finds consecutive blocks if possible.
Vector < BlockIndex > blocks ;
2019-04-23 14:51:47 +02:00
auto bitmap_block = read_block ( bgd . bg_block_bitmap ) ;
int blocks_in_group = min ( blocks_per_group ( ) , super_block ( ) . s_blocks_count ) ;
auto block_bitmap = Bitmap : : wrap ( bitmap_block . pointer ( ) , blocks_in_group ) ;
BlockIndex first_block_in_group = ( group_index - 1 ) * blocks_per_group ( ) + 1 ;
for ( int i = 0 ; i < block_bitmap . size ( ) ; + + i ) {
if ( ! block_bitmap . get ( i ) ) {
blocks . append ( first_block_in_group + i ) ;
if ( blocks . size ( ) = = count )
break ;
2018-10-16 00:35:03 +02:00
}
2019-04-23 14:51:47 +02:00
}
ASSERT ( blocks . size ( ) = = count ) ;
2019-01-22 00:58:13 +01:00
dbgprintf ( " Ext2FS: allocate_block found these blocks: \n " ) ;
2018-10-16 00:35:03 +02:00
for ( auto & bi : blocks ) {
2018-11-18 14:57:41 +01:00
dbgprintf ( " > %u \n " , bi ) ;
2018-10-16 00:35:03 +02:00
}
return blocks ;
}
2019-04-23 13:00:53 +02:00
unsigned Ext2FS : : allocate_inode ( GroupIndex preferred_group , off_t expected_size )
2018-10-10 11:53:07 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-31 17:31:23 +01:00
dbgprintf ( " Ext2FS: allocate_inode(preferredGroup: %u, expectedSize: %u) \n " , preferred_group , expected_size ) ;
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
unsigned needed_blocks = ceil_div ( expected_size , block_size ( ) ) ;
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
dbgprintf ( " Ext2FS: minimum needed blocks: %u \n " , needed_blocks ) ;
2018-10-10 11:53:07 +02:00
2019-04-23 15:07:07 +02:00
unsigned group_index = 0 ;
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
auto is_suitable_group = [ this , needed_blocks ] ( unsigned groupIndex ) {
2018-12-03 00:20:00 +01:00
auto & bgd = group_descriptor ( groupIndex ) ;
2019-01-31 17:31:23 +01:00
return bgd . bg_free_inodes_count & & bgd . bg_free_blocks_count > = needed_blocks ;
2018-10-10 11:53:07 +02:00
} ;
2019-01-31 17:31:23 +01:00
if ( preferred_group & & is_suitable_group ( preferred_group ) ) {
2019-04-23 15:07:07 +02:00
group_index = preferred_group ;
2018-10-10 11:53:07 +02:00
} else {
2019-01-31 17:31:23 +01:00
for ( unsigned i = 1 ; i < = m_block_group_count ; + + i ) {
if ( is_suitable_group ( i ) )
2019-04-23 15:07:07 +02:00
group_index = i ;
2018-10-10 11:53:07 +02:00
}
}
2019-04-23 15:07:07 +02:00
if ( ! group_index ) {
2019-01-31 17:31:23 +01:00
kprintf ( " Ext2FS: allocate_inode: no suitable group found for new inode with %u blocks needed :( \n " , needed_blocks ) ;
2018-10-10 11:53:07 +02:00
return 0 ;
}
2019-04-23 15:07:07 +02:00
dbgprintf ( " Ext2FS: allocate_inode: found suitable group [%u] for new inode with %u blocks needed :^) \n " , group_index , needed_blocks ) ;
2018-10-10 11:53:07 +02:00
2019-04-23 15:07:07 +02:00
auto & bgd = group_descriptor ( group_index ) ;
unsigned inodes_in_group = min ( inodes_per_group ( ) , super_block ( ) . s_inodes_count ) ;
2019-02-25 22:06:55 +01:00
unsigned first_free_inode_in_group = 0 ;
2019-04-23 15:07:07 +02:00
unsigned first_inode_in_group = ( group_index - 1 ) * inodes_per_group ( ) + 1 ;
auto bitmap_block = read_block ( bgd . bg_inode_bitmap ) ;
auto inode_bitmap = Bitmap : : wrap ( bitmap_block . data ( ) , inodes_in_group ) ;
for ( int i = 0 ; i < inode_bitmap . size ( ) ; + + i ) {
if ( inode_bitmap . get ( i ) )
continue ;
first_free_inode_in_group = first_inode_in_group + i ;
break ;
}
2018-10-10 11:53:07 +02:00
2019-02-25 22:06:55 +01:00
if ( ! first_free_inode_in_group ) {
2019-01-22 00:58:13 +01:00
kprintf ( " Ext2FS: first_free_inode_in_group returned no inode, despite bgd claiming there are inodes :( \n " ) ;
2018-10-10 11:53:07 +02:00
return 0 ;
}
2019-02-25 22:06:55 +01:00
unsigned inode = first_free_inode_in_group ;
2018-11-18 14:57:41 +01:00
dbgprintf ( " Ext2FS: found suitable inode %u \n " , inode ) ;
2018-10-10 11:53:07 +02:00
2019-02-15 23:24:01 +01:00
ASSERT ( get_inode_allocation_state ( inode ) = = false ) ;
2018-10-10 11:53:07 +02:00
// FIXME: allocate blocks if needed!
return inode ;
}
2019-02-15 23:24:01 +01:00
Ext2FS : : GroupIndex Ext2FS : : group_index_from_block_index ( BlockIndex block_index ) const
{
if ( ! block_index )
return 0 ;
return ( block_index - 1 ) / blocks_per_group ( ) + 1 ;
}
2018-12-03 00:20:00 +01:00
unsigned Ext2FS : : group_index_from_inode ( unsigned inode ) const
2018-10-10 11:53:07 +02:00
{
if ( ! inode )
return 0 ;
2018-12-03 00:20:00 +01:00
return ( inode - 1 ) / inodes_per_group ( ) + 1 ;
2018-10-10 11:53:07 +02:00
}
2019-01-01 03:35:33 +01:00
bool Ext2FS : : get_inode_allocation_state ( InodeIndex index ) const
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-01 03:35:33 +01:00
if ( index = = 0 )
return true ;
2019-02-11 12:46:56 +01:00
unsigned group_index = group_index_from_inode ( index ) ;
auto & bgd = group_descriptor ( group_index ) ;
unsigned index_in_group = index - ( ( group_index - 1 ) * inodes_per_group ( ) ) ;
2019-04-23 16:21:07 +02:00
unsigned bit_index = ( index_in_group - 1 ) % inodes_per_group ( ) ;
2019-04-23 14:51:47 +02:00
auto block = read_block ( bgd . bg_inode_bitmap ) ;
2019-01-01 03:35:33 +01:00
ASSERT ( block ) ;
2019-04-23 16:21:07 +02:00
auto bitmap = Bitmap : : wrap ( block . pointer ( ) , inodes_per_group ( ) ) ;
2019-01-01 03:55:13 +01:00
return bitmap . get ( bit_index ) ;
2019-01-01 03:35:33 +01:00
}
2019-01-01 03:55:13 +01:00
bool Ext2FS : : set_inode_allocation_state ( unsigned index , bool newState )
2018-10-10 11:53:07 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-02-11 12:46:56 +01:00
unsigned group_index = group_index_from_inode ( index ) ;
auto & bgd = group_descriptor ( group_index ) ;
unsigned index_in_group = index - ( ( group_index - 1 ) * inodes_per_group ( ) ) ;
2019-04-23 16:21:07 +02:00
unsigned bit_index = ( index_in_group - 1 ) % inodes_per_group ( ) ;
2019-04-23 14:51:47 +02:00
auto block = read_block ( bgd . bg_inode_bitmap ) ;
2018-10-10 11:53:07 +02:00
ASSERT ( block ) ;
2019-04-23 16:21:07 +02:00
auto bitmap = Bitmap : : wrap ( block . pointer ( ) , inodes_per_group ( ) ) ;
2019-01-31 17:31:23 +01:00
bool current_state = bitmap . get ( bit_index ) ;
dbgprintf ( " Ext2FS: set_inode_allocation_state(%u) %u -> %u \n " , index , current_state , newState ) ;
2018-10-10 11:53:07 +02:00
2019-04-23 16:21:07 +02:00
if ( current_state = = newState ) {
ASSERT_NOT_REACHED ( ) ;
2018-10-10 11:53:07 +02:00
return true ;
2019-04-23 16:21:07 +02:00
}
2018-10-10 11:53:07 +02:00
2019-01-01 03:55:13 +01:00
bitmap . set ( bit_index , newState ) ;
2019-04-23 14:51:47 +02:00
bool success = write_block ( bgd . bg_inode_bitmap , block ) ;
2019-02-11 11:38:14 +01:00
ASSERT ( success ) ;
2018-10-10 11:53:07 +02:00
// Update superblock
2018-12-03 00:20:00 +01:00
auto & sb = * reinterpret_cast < ext2_super_block * > ( m_cached_super_block . pointer ( ) ) ;
2018-11-18 14:57:41 +01:00
dbgprintf ( " Ext2FS: superblock free inode count %u -> %u \n " , sb . s_free_inodes_count , sb . s_free_inodes_count - 1 ) ;
2018-10-10 11:53:07 +02:00
if ( newState )
- - sb . s_free_inodes_count ;
else
+ + sb . s_free_inodes_count ;
2018-12-03 00:20:00 +01:00
write_super_block ( sb ) ;
2018-10-10 11:53:07 +02:00
// Update BGD
2019-01-31 17:31:23 +01:00
auto & mutable_bgd = const_cast < ext2_group_desc & > ( bgd ) ;
2018-10-10 11:53:07 +02:00
if ( newState )
2019-01-31 17:31:23 +01:00
- - mutable_bgd . bg_free_inodes_count ;
2018-10-10 11:53:07 +02:00
else
2019-01-31 17:31:23 +01:00
+ + mutable_bgd . bg_free_inodes_count ;
2018-11-18 14:57:41 +01:00
dbgprintf ( " Ext2FS: group free inode count %u -> %u \n " , bgd . bg_free_inodes_count , bgd . bg_free_inodes_count - 1 ) ;
2018-10-10 11:53:07 +02:00
2019-01-28 04:16:01 +01:00
flush_block_group_descriptor_table ( ) ;
2018-10-16 00:35:03 +02:00
return true ;
}
2019-02-15 23:24:01 +01:00
bool Ext2FS : : set_block_allocation_state ( BlockIndex block_index , bool new_state )
2018-10-16 00:35:03 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-02-15 23:24:01 +01:00
dbgprintf ( " Ext2FS: set_block_allocation_state(block=%u, state=%u) \n " , block_index , new_state ) ;
unsigned group_index = group_index_from_block_index ( block_index ) ;
auto & bgd = group_descriptor ( group_index ) ;
BlockIndex index_in_group = block_index - ( ( group_index - 1 ) * blocks_per_group ( ) ) ;
2019-04-23 16:21:07 +02:00
unsigned bit_index = ( index_in_group - 1 ) % blocks_per_group ( ) ;
2019-02-15 23:24:01 +01:00
dbgprintf ( " index_in_group: %u \n " , index_in_group ) ;
2019-04-23 16:21:07 +02:00
dbgprintf ( " blocks_per_group: %u \n " , blocks_per_group ( ) ) ;
2019-02-15 23:24:01 +01:00
dbgprintf ( " bit_index: %u \n " , bit_index ) ;
2019-04-23 14:51:47 +02:00
dbgprintf ( " read_block(%u) \n " , bgd . bg_block_bitmap ) ;
auto block = read_block ( bgd . bg_block_bitmap ) ;
2018-10-16 00:35:03 +02:00
ASSERT ( block ) ;
2019-04-23 16:21:07 +02:00
auto bitmap = Bitmap : : wrap ( block . pointer ( ) , blocks_per_group ( ) ) ;
2019-01-31 17:31:23 +01:00
bool current_state = bitmap . get ( bit_index ) ;
2019-02-15 23:24:01 +01:00
dbgprintf ( " Ext2FS: block %u state: %u -> %u \n " , block_index , current_state , new_state ) ;
2018-10-16 00:35:03 +02:00
2019-04-23 16:21:07 +02:00
if ( current_state = = new_state ) {
ASSERT_NOT_REACHED ( ) ;
2018-10-16 00:35:03 +02:00
return true ;
2019-04-23 16:21:07 +02:00
}
2018-10-16 00:35:03 +02:00
2019-01-31 17:31:23 +01:00
bitmap . set ( bit_index , new_state ) ;
2019-04-23 14:51:47 +02:00
bool success = write_block ( bgd . bg_block_bitmap , block ) ;
2019-02-11 11:38:14 +01:00
ASSERT ( success ) ;
2018-10-16 00:35:03 +02:00
// Update superblock
2018-12-03 00:20:00 +01:00
auto & sb = * reinterpret_cast < ext2_super_block * > ( m_cached_super_block . pointer ( ) ) ;
2018-11-18 14:57:41 +01:00
dbgprintf ( " Ext2FS: superblock free block count %u -> %u \n " , sb . s_free_blocks_count , sb . s_free_blocks_count - 1 ) ;
2019-01-31 17:31:23 +01:00
if ( new_state )
2018-10-16 00:35:03 +02:00
- - sb . s_free_blocks_count ;
else
+ + sb . s_free_blocks_count ;
2018-12-03 00:20:00 +01:00
write_super_block ( sb ) ;
2018-10-16 00:35:03 +02:00
// Update BGD
2019-01-31 17:31:23 +01:00
auto & mutable_bgd = const_cast < ext2_group_desc & > ( bgd ) ;
if ( new_state )
- - mutable_bgd . bg_free_blocks_count ;
2018-10-16 00:35:03 +02:00
else
2019-01-31 17:31:23 +01:00
+ + mutable_bgd . bg_free_blocks_count ;
2018-11-18 14:57:41 +01:00
dbgprintf ( " Ext2FS: group free block count %u -> %u \n " , bgd . bg_free_blocks_count , bgd . bg_free_blocks_count - 1 ) ;
2018-10-16 00:35:03 +02:00
2019-01-28 04:16:01 +01:00
flush_block_group_descriptor_table ( ) ;
2018-10-10 11:53:07 +02:00
return true ;
}
2019-01-23 06:53:01 +01:00
RetainPtr < Inode > Ext2FS : : create_directory ( InodeIdentifier parent_id , const String & name , mode_t mode , int & error )
2018-10-16 00:35:03 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-23 05:38:54 +01:00
ASSERT ( parent_id . fsid ( ) = = fsid ( ) ) ;
2018-10-16 00:35:03 +02:00
// Fix up the mode to definitely be a directory.
// FIXME: This is a bit on the hackish side.
mode & = ~ 0170000 ;
mode | = 0040000 ;
// NOTE: When creating a new directory, make the size 1 block.
// There's probably a better strategy here, but this works for now.
2019-01-31 17:31:23 +01:00
auto inode = create_inode ( parent_id , name , mode , block_size ( ) , error ) ;
2018-12-24 23:44:46 +01:00
if ( ! inode )
2018-12-24 23:58:00 +01:00
return nullptr ;
2018-10-16 00:35:03 +02:00
2018-12-24 23:44:46 +01:00
dbgprintf ( " Ext2FS: create_directory: created new directory named '%s' with inode %u \n " , name . characters ( ) , inode - > identifier ( ) . index ( ) ) ;
2018-10-16 00:35:03 +02:00
Vector < DirectoryEntry > entries ;
2018-12-24 23:44:46 +01:00
entries . append ( { " . " , inode - > identifier ( ) , EXT2_FT_DIR } ) ;
2018-12-24 23:58:00 +01:00
entries . append ( { " .. " , parent_id , EXT2_FT_DIR } ) ;
2018-10-16 00:35:03 +02:00
2018-12-24 23:44:46 +01:00
bool success = write_directory_inode ( inode - > identifier ( ) . index ( ) , move ( entries ) ) ;
2018-10-16 00:35:03 +02:00
ASSERT ( success ) ;
2018-12-24 23:58:00 +01:00
auto parent_inode = get_inode ( parent_id ) ;
error = parent_inode - > increment_link_count ( ) ;
if ( error < 0 )
return nullptr ;
2018-10-16 00:35:03 +02:00
2018-12-24 23:44:46 +01:00
auto & bgd = const_cast < ext2_group_desc & > ( group_descriptor ( group_index_from_inode ( inode - > identifier ( ) . index ( ) ) ) ) ;
2018-10-16 00:35:03 +02:00
+ + bgd . bg_used_dirs_count ;
2018-11-18 14:57:41 +01:00
dbgprintf ( " Ext2FS: incremented bg_used_dirs_count %u -> %u \n " , bgd . bg_used_dirs_count - 1 , bgd . bg_used_dirs_count ) ;
2018-10-16 00:35:03 +02:00
2019-01-28 04:16:01 +01:00
flush_block_group_descriptor_table ( ) ;
2018-10-16 00:35:03 +02:00
2018-11-18 14:57:41 +01:00
error = 0 ;
2018-10-16 00:35:03 +02:00
return inode ;
}
2019-04-23 13:00:53 +02:00
RetainPtr < Inode > Ext2FS : : create_inode ( InodeIdentifier parent_id , const String & name , mode_t mode , off_t size , int & error )
2018-10-10 11:53:07 +02:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-23 05:38:54 +01:00
ASSERT ( parent_id . fsid ( ) = = fsid ( ) ) ;
2018-12-25 00:27:39 +01:00
auto parent_inode = get_inode ( parent_id ) ;
2018-10-10 11:53:07 +02:00
2019-02-22 09:13:05 +01:00
dbgprintf ( " Ext2FS: Adding inode '%s' (mode %o) to parent directory %u: \n " , name . characters ( ) , mode , parent_inode - > identifier ( ) . index ( ) ) ;
2018-10-10 11:53:07 +02:00
// NOTE: This doesn't commit the inode allocation just yet!
2019-01-23 04:59:47 +01:00
auto inode_id = allocate_inode ( 0 , size ) ;
2018-12-24 23:44:46 +01:00
if ( ! inode_id ) {
2019-01-22 00:58:13 +01:00
kprintf ( " Ext2FS: create_inode: allocate_inode failed \n " ) ;
2018-11-18 14:57:41 +01:00
error = - ENOSPC ;
2018-10-16 00:35:03 +02:00
return { } ;
}
2019-01-31 17:31:23 +01:00
auto needed_blocks = ceil_div ( size , block_size ( ) ) ;
2019-01-22 00:58:13 +01:00
auto blocks = allocate_blocks ( group_index_from_inode ( inode_id ) , needed_blocks ) ;
if ( blocks . size ( ) ! = needed_blocks ) {
kprintf ( " Ext2FS: create_inode: allocate_blocks failed \n " ) ;
2018-11-18 14:57:41 +01:00
error = - ENOSPC ;
2018-10-16 00:35:03 +02:00
return { } ;
}
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
byte file_type = 0 ;
if ( is_regular_file ( mode ) )
file_type = EXT2_FT_REG_FILE ;
else if ( is_directory ( mode ) )
file_type = EXT2_FT_DIR ;
else if ( is_character_device ( mode ) )
file_type = EXT2_FT_CHRDEV ;
else if ( is_block_device ( mode ) )
file_type = EXT2_FT_BLKDEV ;
else if ( is_fifo ( mode ) )
file_type = EXT2_FT_FIFO ;
else if ( is_socket ( mode ) )
file_type = EXT2_FT_SOCK ;
else if ( is_symlink ( mode ) )
file_type = EXT2_FT_SYMLINK ;
2018-10-15 01:57:57 +02:00
2018-10-10 11:53:07 +02:00
// Try adding it to the directory first, in case the name is already in use.
2019-02-27 15:31:26 +01:00
auto result = parent_inode - > add_child ( { fsid ( ) , inode_id } , name , file_type ) ;
if ( result . is_error ( ) ) {
error = result ;
2018-10-10 11:53:07 +02:00
return { } ;
2019-02-27 15:31:26 +01:00
}
2018-10-10 11:53:07 +02:00
// Looks like we're good, time to update the inode bitmap and group+global inode counters.
2019-02-27 15:31:26 +01:00
bool success = set_inode_allocation_state ( inode_id , true ) ;
2018-10-10 11:53:07 +02:00
ASSERT ( success ) ;
2019-02-15 23:24:01 +01:00
for ( auto block_index : blocks ) {
success = set_block_allocation_state ( block_index , true ) ;
2018-10-16 00:35:03 +02:00
ASSERT ( success ) ;
}
2019-01-31 17:31:23 +01:00
unsigned initial_links_count ;
if ( is_directory ( mode ) )
initial_links_count = 2 ; // (parent directory + "." entry in self)
2018-10-16 00:35:03 +02:00
else
2019-01-31 17:31:23 +01:00
initial_links_count = 1 ;
2018-10-16 00:35:03 +02:00
2019-03-25 02:06:57 +01:00
struct timeval now ;
kgettimeofday ( now ) ;
2019-01-31 04:17:16 +01:00
ext2_inode e2inode ;
memset ( & e2inode , 0 , sizeof ( ext2_inode ) ) ;
e2inode . i_mode = mode ;
2019-03-23 22:03:17 +01:00
e2inode . i_uid = current - > process ( ) . euid ( ) ;
e2inode . i_gid = current - > process ( ) . egid ( ) ;
2019-01-31 04:17:16 +01:00
e2inode . i_size = size ;
2019-03-25 02:06:57 +01:00
e2inode . i_atime = now . tv_sec ;
e2inode . i_ctime = now . tv_sec ;
e2inode . i_mtime = now . tv_sec ;
2019-01-31 04:17:16 +01:00
e2inode . i_dtime = 0 ;
2019-01-31 17:31:23 +01:00
e2inode . i_links_count = initial_links_count ;
2019-01-31 04:17:16 +01:00
success = write_block_list_for_inode ( inode_id , e2inode , blocks ) ;
2019-01-23 02:45:25 +01:00
ASSERT ( success ) ;
2018-10-16 00:35:03 +02:00
2019-01-01 03:55:13 +01:00
dbgprintf ( " Ext2FS: writing initial metadata for inode %u \n " , inode_id ) ;
2019-01-31 04:17:16 +01:00
e2inode . i_flags = 0 ;
success = write_ext2_inode ( inode_id , e2inode ) ;
2018-10-10 11:53:07 +02:00
ASSERT ( success ) ;
2019-02-20 21:41:53 +01:00
// We might have cached the fact that this inode didn't exist. Wipe the slate.
m_inode_cache . remove ( inode_id ) ;
2019-01-23 05:38:54 +01:00
return get_inode ( { fsid ( ) , inode_id } ) ;
2018-10-10 11:53:07 +02:00
}
2019-01-04 18:37:58 +01:00
RetainPtr < Inode > Ext2FSInode : : parent ( ) const
2018-10-28 12:20:25 +01:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-04 18:37:58 +01:00
if ( m_parent_id . is_valid ( ) )
return fs ( ) . get_inode ( m_parent_id ) ;
2018-11-13 23:44:54 +01:00
2019-01-04 18:37:58 +01:00
unsigned group_index = fs ( ) . group_index_from_inode ( index ( ) ) ;
unsigned first_inode_in_group = fs ( ) . inodes_per_group ( ) * ( group_index - 1 ) ;
2018-10-28 12:20:25 +01:00
2019-02-25 16:04:08 +01:00
Vector < Retained < Ext2FSInode > > directories_in_group ;
2018-10-28 12:20:25 +01:00
2019-01-04 18:37:58 +01:00
for ( unsigned i = 0 ; i < fs ( ) . inodes_per_group ( ) ; + + i ) {
auto group_member = fs ( ) . get_inode ( { fsid ( ) , first_inode_in_group + i } ) ;
2018-11-13 23:44:54 +01:00
if ( ! group_member )
2018-10-28 12:20:25 +01:00
continue ;
2018-11-13 23:44:54 +01:00
if ( group_member - > is_directory ( ) )
2019-02-25 16:04:08 +01:00
directories_in_group . append ( * group_member ) ;
2018-10-28 12:20:25 +01:00
}
2018-11-13 23:44:54 +01:00
for ( auto & directory : directories_in_group ) {
2019-01-04 18:37:58 +01:00
if ( ! directory - > reverse_lookup ( identifier ( ) ) . is_null ( ) ) {
m_parent_id = directory - > identifier ( ) ;
2018-10-28 12:20:25 +01:00
break ;
2018-11-15 17:04:55 +01:00
}
2018-10-28 12:20:25 +01:00
}
2019-01-04 18:37:58 +01:00
ASSERT ( m_parent_id . is_valid ( ) ) ;
return fs ( ) . get_inode ( m_parent_id ) ;
2018-10-28 12:20:25 +01:00
}
2018-11-15 16:34:36 +01:00
2019-01-28 04:16:01 +01:00
void Ext2FSInode : : populate_lookup_cache ( ) const
2018-11-15 16:34:36 +01:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
if ( ! m_lookup_cache . is_empty ( ) )
return ;
2018-11-15 17:04:55 +01:00
HashMap < String , unsigned > children ;
traverse_as_directory ( [ & children ] ( auto & entry ) {
children . set ( String ( entry . name , entry . name_length ) , entry . inode . index ( ) ) ;
return true ;
} ) ;
2018-12-21 02:10:45 +01:00
if ( ! m_lookup_cache . is_empty ( ) )
2018-11-15 17:04:55 +01:00
return ;
m_lookup_cache = move ( children ) ;
}
2018-11-15 16:34:36 +01:00
2018-11-15 17:13:10 +01:00
InodeIdentifier Ext2FSInode : : lookup ( const String & name )
2018-11-15 17:04:55 +01:00
{
ASSERT ( is_directory ( ) ) ;
populate_lookup_cache ( ) ;
2018-11-15 16:34:36 +01:00
LOCKER ( m_lock ) ;
2018-11-15 17:04:55 +01:00
auto it = m_lookup_cache . find ( name ) ;
if ( it ! = m_lookup_cache . end ( ) )
2018-11-15 16:34:36 +01:00
return { fsid ( ) , ( * it ) . value } ;
2018-11-15 17:04:55 +01:00
return { } ;
}
2018-11-15 16:34:36 +01:00
2018-11-15 17:13:10 +01:00
String Ext2FSInode : : reverse_lookup ( InodeIdentifier child_id )
2018-11-15 17:04:55 +01:00
{
ASSERT ( is_directory ( ) ) ;
ASSERT ( child_id . fsid ( ) = = fsid ( ) ) ;
populate_lookup_cache ( ) ;
LOCKER ( m_lock ) ;
for ( auto it : m_lookup_cache ) {
if ( it . value = = child_id . index ( ) )
return it . key ;
}
2018-11-15 16:34:36 +01:00
return { } ;
}
2019-01-01 02:38:09 +01:00
void Ext2FSInode : : one_retain_left ( )
{
2019-01-01 02:52:21 +01:00
// FIXME: I would like to not live forever, but uncached Ext2FS is fucking painful right now.
2019-01-01 02:38:09 +01:00
}
2019-01-01 03:16:36 +01:00
2019-01-23 06:53:01 +01:00
int Ext2FSInode : : set_atime ( time_t t )
2019-01-01 03:16:36 +01:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-01 03:16:36 +01:00
if ( fs ( ) . is_readonly ( ) )
return - EROFS ;
m_raw_inode . i_atime = t ;
set_metadata_dirty ( true ) ;
return 0 ;
}
2019-01-23 06:53:01 +01:00
int Ext2FSInode : : set_ctime ( time_t t )
2019-01-01 03:16:36 +01:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-01 03:16:36 +01:00
if ( fs ( ) . is_readonly ( ) )
return - EROFS ;
m_raw_inode . i_ctime = t ;
set_metadata_dirty ( true ) ;
return 0 ;
}
2019-01-23 06:53:01 +01:00
int Ext2FSInode : : set_mtime ( time_t t )
2019-01-01 03:16:36 +01:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-01 03:16:36 +01:00
if ( fs ( ) . is_readonly ( ) )
return - EROFS ;
m_raw_inode . i_mtime = t ;
set_metadata_dirty ( true ) ;
return 0 ;
}
int Ext2FSInode : : increment_link_count ( )
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-01 03:16:36 +01:00
if ( fs ( ) . is_readonly ( ) )
return - EROFS ;
+ + m_raw_inode . i_links_count ;
set_metadata_dirty ( true ) ;
return 0 ;
}
int Ext2FSInode : : decrement_link_count ( )
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-01 03:16:36 +01:00
if ( fs ( ) . is_readonly ( ) )
return - EROFS ;
2019-01-22 07:03:44 +01:00
ASSERT ( m_raw_inode . i_links_count ) ;
2019-01-01 03:16:36 +01:00
- - m_raw_inode . i_links_count ;
2019-01-22 07:03:44 +01:00
if ( m_raw_inode . i_links_count = = 0 )
fs ( ) . uncache_inode ( index ( ) ) ;
2019-01-01 03:16:36 +01:00
set_metadata_dirty ( true ) ;
return 0 ;
}
2019-01-22 07:03:44 +01:00
void Ext2FS : : uncache_inode ( InodeIndex index )
{
2019-02-20 21:41:53 +01:00
LOCKER ( m_lock ) ;
2019-02-21 13:26:40 +01:00
m_inode_cache . remove ( index ) ;
2019-01-22 07:03:44 +01:00
}
2019-01-28 04:16:01 +01:00
size_t Ext2FSInode : : directory_entry_count ( ) const
{
ASSERT ( is_directory ( ) ) ;
LOCKER ( m_lock ) ;
2019-02-27 17:07:34 +01:00
populate_lookup_cache ( ) ;
2019-01-28 04:16:01 +01:00
return m_lookup_cache . size ( ) ;
}
2019-01-29 04:55:08 +01:00
2019-02-25 20:47:56 +01:00
KResult Ext2FSInode : : chmod ( mode_t mode )
2019-01-29 04:55:08 +01:00
{
2019-02-20 13:09:59 +01:00
LOCKER ( m_lock ) ;
2019-01-29 04:55:08 +01:00
if ( m_raw_inode . i_mode = = mode )
2019-02-25 20:47:56 +01:00
return KSuccess ;
2019-01-29 04:55:08 +01:00
m_raw_inode . i_mode = mode ;
set_metadata_dirty ( true ) ;
2019-02-25 20:47:56 +01:00
return KSuccess ;
2019-01-29 04:55:08 +01:00
}
2019-02-21 14:48:00 +01:00
2019-02-27 12:32:53 +01:00
KResult Ext2FSInode : : chown ( uid_t uid , gid_t gid )
{
LOCKER ( m_lock ) ;
if ( m_raw_inode . i_uid = = uid & & m_raw_inode . i_gid = = gid )
return KSuccess ;
m_raw_inode . i_uid = uid ;
m_raw_inode . i_gid = gid ;
set_metadata_dirty ( true ) ;
return KSuccess ;
}
2019-04-23 13:00:53 +02:00
KResult Ext2FSInode : : truncate ( off_t size )
2019-03-27 16:42:30 +01:00
{
LOCKER ( m_lock ) ;
2019-04-23 13:00:53 +02:00
if ( ( off_t ) m_raw_inode . i_size = = size )
2019-03-27 16:42:30 +01:00
return KSuccess ;
m_raw_inode . i_size = size ;
set_metadata_dirty ( true ) ;
return KSuccess ;
}
2019-02-21 14:48:00 +01:00
unsigned Ext2FS : : total_block_count ( ) const
{
LOCKER ( m_lock ) ;
return super_block ( ) . s_blocks_count ;
}
unsigned Ext2FS : : free_block_count ( ) const
{
LOCKER ( m_lock ) ;
return super_block ( ) . s_free_blocks_count ;
}
unsigned Ext2FS : : total_inode_count ( ) const
{
LOCKER ( m_lock ) ;
return super_block ( ) . s_inodes_count ;
}
unsigned Ext2FS : : free_inode_count ( ) const
{
LOCKER ( m_lock ) ;
return super_block ( ) . s_free_inodes_count ;
}