2020-01-18 11:38:21 +03:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
2021-05-12 22:17:51 +03:00
* Copyright ( c ) 2021 , sin - ack < sin - ack @ protonmail . com >
2020-01-18 11:38:21 +03:00
*
2021-04-22 11:24:48 +03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 11:38:21 +03:00
*/
2020-02-16 03:50:16 +03:00
# include <AK/HashMap.h>
2020-09-15 13:24:14 +03:00
# include <AK/MemoryStream.h>
2019-06-07 12:43:58 +03:00
# include <AK/StdLibExtras.h>
2020-03-23 15:45:10 +03:00
# include <AK/StringView.h>
2021-01-25 18:07:10 +03:00
# include <Kernel/Debug.h>
2020-02-16 03:50:16 +03:00
# include <Kernel/Devices/BlockDevice.h>
2019-06-07 20:29:34 +03:00
# include <Kernel/FileSystem/Ext2FileSystem.h>
2019-11-05 21:35:12 +03:00
# include <Kernel/FileSystem/FileDescription.h>
2019-06-07 20:29:34 +03:00
# include <Kernel/FileSystem/ext2_fs.h>
2019-02-21 17:45:31 +03:00
# include <Kernel/Process.h>
2019-06-07 20:29:34 +03:00
# include <Kernel/UnixTypes.h>
2019-06-07 12:43:58 +03:00
# include <LibC/errno_numbers.h>
2018-10-10 12:53:07 +03:00
2020-02-16 03:27:42 +03:00
namespace Kernel {
2021-05-19 17:35:09 +03:00
static constexpr size_t max_block_size = 4096 ;
static constexpr ssize_t max_inline_symlink_length = 60 ;
2019-03-02 03:50:34 +03:00
2020-08-18 19:01:42 +03:00
struct Ext2FSDirectoryEntry {
String name ;
2021-02-12 11:18:47 +03:00
InodeIndex inode_index { 0 } ;
2020-08-18 19:01:42 +03:00
u8 file_type { 0 } ;
2021-05-07 15:47:38 +03:00
u16 record_length { 0 } ;
2020-08-18 19:01:42 +03:00
} ;
2019-07-03 22:17:35 +03:00
static u8 to_ext2_file_type ( mode_t mode )
2019-05-31 18:41:33 +03:00
{
if ( is_regular_file ( mode ) )
return EXT2_FT_REG_FILE ;
if ( is_directory ( mode ) )
return EXT2_FT_DIR ;
if ( is_character_device ( mode ) )
return EXT2_FT_CHRDEV ;
if ( is_block_device ( mode ) )
return EXT2_FT_BLKDEV ;
if ( is_fifo ( mode ) )
return EXT2_FT_FIFO ;
if ( is_socket ( mode ) )
return EXT2_FT_SOCK ;
if ( is_symlink ( mode ) )
return EXT2_FT_SYMLINK ;
return EXT2_FT_UNKNOWN ;
}
2021-02-13 21:05:04 +03:00
static unsigned divide_rounded_up ( unsigned a , unsigned b )
{
return ( a / b ) + ( a % b ! = 0 ) ;
}
2020-04-06 11:54:21 +03:00
NonnullRefPtr < Ext2FS > Ext2FS : : create ( FileDescription & file_description )
2018-10-10 12:53:07 +03:00
{
2021-04-23 17:46:57 +03:00
return adopt_ref ( * new Ext2FS ( file_description ) ) ;
2018-10-10 12:53:07 +03:00
}
2020-04-06 11:54:21 +03:00
Ext2FS : : Ext2FS ( FileDescription & file_description )
2020-07-02 12:48:08 +03:00
: BlockBasedFS ( file_description )
2018-10-10 12:53:07 +03:00
{
}
2018-11-15 19:13:10 +03:00
Ext2FS : : ~ Ext2FS ( )
2018-10-10 12:53:07 +03:00
{
}
2019-11-02 13:49:11 +03:00
bool Ext2FS : : flush_super_block ( )
2018-10-10 12:53:07 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( ( sizeof ( ext2_super_block ) % logical_block_size ( ) ) = = 0 ) ;
2020-09-12 06:11:07 +03:00
auto super_block_buffer = UserOrKernelBuffer : : for_kernel_buffer ( ( u8 * ) & m_super_block ) ;
bool success = raw_write_blocks ( 2 , ( sizeof ( ext2_super_block ) / logical_block_size ( ) ) , super_block_buffer ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( success ) ;
2018-10-10 12:53:07 +03:00
return true ;
}
2019-05-25 18:23:17 +03:00
const ext2_group_desc & Ext2FS : : group_descriptor ( GroupIndex group_index ) const
2018-10-10 12:53:07 +03:00
{
// FIXME: Should this fail gracefully somehow?
2021-02-23 22:42:32 +03:00
VERIFY ( group_index < = m_block_group_count ) ;
VERIFY ( group_index > 0 ) ;
2021-02-12 15:33:58 +03:00
return block_group_descriptors ( ) [ group_index . value ( ) - 1 ] ;
2018-10-10 12:53:07 +03:00
}
2018-11-15 19:13:10 +03:00
bool Ext2FS : : initialize ( )
2018-10-10 12:53:07 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( ( sizeof ( ext2_super_block ) % logical_block_size ( ) ) = = 0 ) ;
2020-09-12 06:11:07 +03:00
auto super_block_buffer = UserOrKernelBuffer : : for_kernel_buffer ( ( u8 * ) & m_super_block ) ;
bool success = raw_read_blocks ( 2 , ( sizeof ( ext2_super_block ) / logical_block_size ( ) ) , super_block_buffer ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( success ) ;
2019-11-02 13:49:11 +03:00
2019-01-31 19:31:23 +03:00
auto & super_block = this - > super_block ( ) ;
2021-02-12 01:05:16 +03:00
if constexpr ( EXT2_DEBUG ) {
2021-03-09 22:17:57 +03:00
dmesgln ( " Ext2FS: super block magic: {:04x} (super block size: {}) " , super_block . s_magic , sizeof ( ext2_super_block ) ) ;
2021-02-12 01:05:16 +03:00
}
2019-01-31 19:31:23 +03:00
if ( super_block . s_magic ! = EXT2_SUPER_MAGIC )
2018-10-10 12:53:07 +03:00
return false ;
2021-02-12 01:05:16 +03:00
if constexpr ( EXT2_DEBUG ) {
2021-03-09 22:17:57 +03:00
dmesgln ( " Ext2FS: {} inodes, {} blocks " , super_block . s_inodes_count , super_block . s_blocks_count ) ;
dmesgln ( " Ext2FS: Block size: {} " , EXT2_BLOCK_SIZE ( & super_block ) ) ;
dmesgln ( " Ext2FS: First data block: {} " , super_block . s_first_data_block ) ;
dmesgln ( " Ext2FS: Inodes per block: {} " , inodes_per_block ( ) ) ;
dmesgln ( " Ext2FS: Inodes per group: {} " , inodes_per_group ( ) ) ;
dmesgln ( " Ext2FS: Free inodes: {} " , super_block . s_free_inodes_count ) ;
dmesgln ( " Ext2FS: Descriptors per block: {} " , EXT2_DESC_PER_BLOCK ( & super_block ) ) ;
dmesgln ( " Ext2FS: Descriptor size: {} " , EXT2_DESC_SIZE ( & super_block ) ) ;
2021-02-12 01:05:16 +03:00
}
2018-10-10 12:53:07 +03:00
2019-01-31 19:31:23 +03:00
set_block_size ( EXT2_BLOCK_SIZE ( & super_block ) ) ;
2021-05-18 22:04:52 +03:00
set_fragment_size ( EXT2_FRAG_SIZE ( & super_block ) ) ;
2018-10-10 12:53:07 +03:00
2021-02-23 22:42:32 +03:00
VERIFY ( block_size ( ) < = ( int ) max_block_size ) ;
2019-09-30 12:20:51 +03:00
2019-01-31 19:31:23 +03:00
m_block_group_count = ceil_div ( super_block . s_blocks_count , super_block . s_blocks_per_group ) ;
2018-10-10 12:53:07 +03:00
2019-01-31 19:31:23 +03:00
if ( m_block_group_count = = 0 ) {
2021-03-09 22:17:57 +03:00
dmesgln ( " Ext2FS: no block groups :( " ) ;
2018-10-10 12:53:07 +03:00
return false ;
}
2020-05-23 14:59:11 +03:00
unsigned blocks_to_read = ceil_div ( m_block_group_count * sizeof ( ext2_group_desc ) , block_size ( ) ) ;
2019-11-03 02:10:24 +03:00
BlockIndex first_block_of_bgdt = block_size ( ) = = 1024 ? 2 : 1 ;
2020-12-18 15:18:47 +03:00
m_cached_group_descriptor_table = KBuffer : : try_create_with_size ( block_size ( ) * blocks_to_read , Region : : Access : : Read | Region : : Access : : Write , " Ext2FS: Block group descriptors " ) ;
if ( ! m_cached_group_descriptor_table ) {
dbgln ( " Ext2FS: Failed to allocate memory for group descriptor table " ) ;
return false ;
}
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( m_cached_group_descriptor_table - > data ( ) ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = read_blocks ( first_block_of_bgdt , blocks_to_read , buffer ) ; result . is_error ( ) ) {
2021-01-20 23:11:01 +03:00
// FIXME: Propagate the error
dbgln ( " Ext2FS: initialize had error: {} " , result . error ( ) ) ;
return false ;
}
2018-10-31 23:31:56 +03:00
2021-02-12 01:05:16 +03:00
if constexpr ( EXT2_DEBUG ) {
for ( unsigned i = 1 ; i < = m_block_group_count ; + + i ) {
auto & group = group_descriptor ( i ) ;
2021-03-09 22:17:57 +03:00
dbgln ( " Ext2FS: group[{}] ( block_bitmap: {}, inode_bitmap: {}, inode_table: {} ) " , i , group . bg_block_bitmap , group . bg_inode_bitmap , group . bg_inode_table ) ;
2021-02-12 01:05:16 +03:00
}
2018-10-10 12:53:07 +03:00
}
return true ;
}
2018-11-15 19:13:10 +03:00
const char * Ext2FS : : class_name ( ) const
2018-10-10 12:53:07 +03:00
{
2019-01-31 08:13:55 +03:00
return " Ext2FS " ;
2018-10-10 12:53:07 +03:00
}
2020-06-24 23:35:56 +03:00
NonnullRefPtr < Inode > Ext2FS : : root_inode ( ) const
2018-10-10 12:53:07 +03:00
{
2020-06-24 23:35:56 +03:00
return * get_inode ( { fsid ( ) , EXT2_ROOT_INO } ) ;
2018-10-10 12:53:07 +03:00
}
2021-02-12 13:59:27 +03:00
bool Ext2FS : : find_block_containing_inode ( InodeIndex inode , BlockIndex & block_index , unsigned & offset ) const
2018-10-10 12:53:07 +03:00
{
2019-01-31 19:31:23 +03:00
auto & super_block = this - > super_block ( ) ;
2018-10-10 12:53:07 +03:00
2019-01-31 19:31:23 +03:00
if ( inode ! = EXT2_ROOT_INO & & inode < EXT2_FIRST_INO ( & super_block ) )
2019-09-30 12:20:51 +03:00
return false ;
2018-10-10 12:53:07 +03:00
2019-01-31 19:31:23 +03:00
if ( inode > super_block . s_inodes_count )
2019-09-30 12:20:51 +03:00
return false ;
2018-10-10 12:53:07 +03:00
2018-12-03 02:20:00 +03:00
auto & bgd = group_descriptor ( group_index_from_inode ( inode ) ) ;
2018-10-10 12:53:07 +03:00
2021-02-12 11:18:47 +03:00
offset = ( ( inode . value ( ) - 1 ) % inodes_per_group ( ) ) * inode_size ( ) ;
2019-01-31 19:31:23 +03:00
block_index = bgd . bg_inode_table + ( offset > > EXT2_BLOCK_SIZE_BITS ( & super_block ) ) ;
offset & = block_size ( ) - 1 ;
2018-10-10 12:53:07 +03:00
2020-05-18 21:55:08 +03:00
return true ;
2018-10-10 12:53:07 +03:00
}
2020-11-07 18:45:03 +03:00
Ext2FS : : BlockListShape Ext2FS : : compute_block_list_shape ( unsigned blocks ) const
2019-01-23 04:45:25 +03:00
{
BlockListShape shape ;
const unsigned entries_per_block = EXT2_ADDR_PER_BLOCK ( & super_block ( ) ) ;
unsigned blocks_remaining = blocks ;
2019-09-28 07:31:40 +03:00
2019-01-23 04:45:25 +03:00
shape . direct_blocks = min ( ( unsigned ) EXT2_NDIR_BLOCKS , blocks_remaining ) ;
blocks_remaining - = shape . direct_blocks ;
if ( ! blocks_remaining )
return shape ;
2019-09-28 07:31:40 +03:00
2019-01-23 04:45:25 +03:00
shape . indirect_blocks = min ( blocks_remaining , entries_per_block ) ;
shape . meta_blocks + = 1 ;
2021-02-13 21:05:04 +03:00
blocks_remaining - = shape . indirect_blocks ;
2019-01-23 04:45:25 +03:00
if ( ! blocks_remaining )
return shape ;
2019-09-28 07:31:40 +03:00
2019-01-23 04:45:25 +03:00
shape . doubly_indirect_blocks = min ( blocks_remaining , entries_per_block * entries_per_block ) ;
2019-09-28 07:31:40 +03:00
shape . meta_blocks + = 1 ;
2021-02-13 21:05:04 +03:00
shape . meta_blocks + = divide_rounded_up ( shape . doubly_indirect_blocks , entries_per_block ) ;
blocks_remaining - = shape . doubly_indirect_blocks ;
2019-01-23 04:45:25 +03:00
if ( ! blocks_remaining )
return shape ;
2019-09-28 07:31:40 +03:00
2019-01-23 04:45:25 +03:00
shape . triply_indirect_blocks = min ( blocks_remaining , entries_per_block * entries_per_block * entries_per_block ) ;
2021-02-13 21:05:04 +03:00
shape . meta_blocks + = 1 ;
shape . meta_blocks + = divide_rounded_up ( shape . triply_indirect_blocks , entries_per_block * entries_per_block ) ;
shape . meta_blocks + = divide_rounded_up ( shape . triply_indirect_blocks , entries_per_block ) ;
2019-01-23 04:45:25 +03:00
blocks_remaining - = shape . triply_indirect_blocks ;
2021-02-23 22:42:32 +03:00
VERIFY ( blocks_remaining = = 0 ) ;
2021-02-13 21:05:04 +03:00
return shape ;
2019-01-23 04:45:25 +03:00
}
2021-04-29 23:23:52 +03:00
KResult Ext2FSInode : : write_indirect_block ( BlockBasedFS : : BlockIndex block , Span < BlockBasedFS : : BlockIndex > blocks_indices )
2021-03-11 21:22:13 +03:00
{
const auto entries_per_block = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ;
2021-04-29 23:23:52 +03:00
VERIFY ( blocks_indices . size ( ) < = entries_per_block ) ;
2021-03-11 21:22:13 +03:00
auto block_contents = ByteBuffer : : create_uninitialized ( fs ( ) . block_size ( ) ) ;
OutputMemoryStream stream { block_contents } ;
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( stream . data ( ) ) ;
2021-04-29 23:23:52 +03:00
VERIFY ( blocks_indices . size ( ) < = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ) ;
for ( unsigned i = 0 ; i < blocks_indices . size ( ) ; + + i )
stream < < static_cast < u32 > ( blocks_indices [ i ] . value ( ) ) ;
2021-03-11 21:22:13 +03:00
stream . fill_to_end ( 0 ) ;
return fs ( ) . write_block ( block , buffer , stream . size ( ) ) ;
}
2021-04-29 23:23:52 +03:00
KResult Ext2FSInode : : grow_doubly_indirect_block ( BlockBasedFS : : BlockIndex block , size_t old_blocks_length , Span < BlockBasedFS : : BlockIndex > blocks_indices , Vector < Ext2FS : : BlockIndex > & new_meta_blocks , unsigned & meta_blocks )
2021-03-11 21:22:13 +03:00
{
const auto entries_per_block = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ;
const auto entries_per_doubly_indirect_block = entries_per_block * entries_per_block ;
const auto old_indirect_blocks_length = divide_rounded_up ( old_blocks_length , entries_per_block ) ;
2021-04-29 23:23:52 +03:00
const auto new_indirect_blocks_length = divide_rounded_up ( blocks_indices . size ( ) , entries_per_block ) ;
VERIFY ( blocks_indices . size ( ) > 0 ) ;
VERIFY ( blocks_indices . size ( ) > old_blocks_length ) ;
VERIFY ( blocks_indices . size ( ) < = entries_per_doubly_indirect_block ) ;
2021-03-11 21:22:13 +03:00
auto block_contents = ByteBuffer : : create_uninitialized ( fs ( ) . block_size ( ) ) ;
auto * block_as_pointers = ( unsigned * ) block_contents . data ( ) ;
OutputMemoryStream stream { block_contents } ;
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( stream . data ( ) ) ;
if ( old_blocks_length > 0 ) {
2021-04-11 01:25:26 +03:00
if ( auto result = fs ( ) . read_block ( block , & buffer , fs ( ) . block_size ( ) ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
}
// Grow the doubly indirect block.
for ( unsigned i = 0 ; i < old_indirect_blocks_length ; i + + )
2021-03-19 00:58:21 +03:00
stream < < static_cast < u32 > ( block_as_pointers [ i ] ) ;
2021-03-11 21:22:13 +03:00
for ( unsigned i = old_indirect_blocks_length ; i < new_indirect_blocks_length ; i + + ) {
auto new_block = new_meta_blocks . take_last ( ) . value ( ) ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::grow_doubly_indirect_block(): Allocating indirect block {} at index {} " , identifier ( ) , new_block , i ) ;
2021-03-19 00:58:21 +03:00
stream < < static_cast < u32 > ( new_block ) ;
2021-03-11 21:22:13 +03:00
meta_blocks + + ;
}
stream . fill_to_end ( 0 ) ;
// Write out the indirect blocks.
for ( unsigned i = old_blocks_length / entries_per_block ; i < new_indirect_blocks_length ; i + + ) {
const auto offset_block = i * entries_per_block ;
2021-04-29 23:23:52 +03:00
if ( auto result = write_indirect_block ( block_as_pointers [ i ] , blocks_indices . slice ( offset_block , min ( blocks_indices . size ( ) - offset_block , entries_per_block ) ) ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
}
// Write out the doubly indirect block.
return fs ( ) . write_block ( block , buffer , stream . size ( ) ) ;
}
KResult Ext2FSInode : : shrink_doubly_indirect_block ( BlockBasedFS : : BlockIndex block , size_t old_blocks_length , size_t new_blocks_length , unsigned & meta_blocks )
{
const auto entries_per_block = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ;
const auto entries_per_doubly_indirect_block = entries_per_block * entries_per_block ;
const auto old_indirect_blocks_length = divide_rounded_up ( old_blocks_length , entries_per_block ) ;
const auto new_indirect_blocks_length = divide_rounded_up ( new_blocks_length , entries_per_block ) ;
VERIFY ( old_blocks_length > 0 ) ;
VERIFY ( old_blocks_length > = new_blocks_length ) ;
VERIFY ( new_blocks_length < = entries_per_doubly_indirect_block ) ;
auto block_contents = ByteBuffer : : create_uninitialized ( fs ( ) . block_size ( ) ) ;
auto * block_as_pointers = ( unsigned * ) block_contents . data ( ) ;
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( reinterpret_cast < u8 * > ( block_as_pointers ) ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = fs ( ) . read_block ( block , & buffer , fs ( ) . block_size ( ) ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
// Free the unused indirect blocks.
for ( unsigned i = new_indirect_blocks_length ; i < old_indirect_blocks_length ; i + + ) {
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::shrink_doubly_indirect_block(): Freeing indirect block {} at index {} " , identifier ( ) , block_as_pointers [ i ] , i ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = fs ( ) . set_block_allocation_state ( block_as_pointers [ i ] , false ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
meta_blocks - - ;
}
// Free the doubly indirect block if no longer needed.
if ( new_blocks_length = = 0 ) {
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::shrink_doubly_indirect_block(): Freeing doubly indirect block {} " , identifier ( ) , block ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = fs ( ) . set_block_allocation_state ( block , false ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
meta_blocks - - ;
}
return KSuccess ;
}
2021-04-29 23:23:52 +03:00
KResult Ext2FSInode : : grow_triply_indirect_block ( BlockBasedFS : : BlockIndex block , size_t old_blocks_length , Span < BlockBasedFS : : BlockIndex > blocks_indices , Vector < Ext2FS : : BlockIndex > & new_meta_blocks , unsigned & meta_blocks )
2021-03-11 21:22:13 +03:00
{
const auto entries_per_block = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ;
const auto entries_per_doubly_indirect_block = entries_per_block * entries_per_block ;
const auto entries_per_triply_indirect_block = entries_per_block * entries_per_block ;
const auto old_doubly_indirect_blocks_length = divide_rounded_up ( old_blocks_length , entries_per_doubly_indirect_block ) ;
2021-04-29 23:23:52 +03:00
const auto new_doubly_indirect_blocks_length = divide_rounded_up ( blocks_indices . size ( ) , entries_per_doubly_indirect_block ) ;
VERIFY ( blocks_indices . size ( ) > 0 ) ;
VERIFY ( blocks_indices . size ( ) > old_blocks_length ) ;
VERIFY ( blocks_indices . size ( ) < = entries_per_triply_indirect_block ) ;
2021-03-11 21:22:13 +03:00
auto block_contents = ByteBuffer : : create_uninitialized ( fs ( ) . block_size ( ) ) ;
auto * block_as_pointers = ( unsigned * ) block_contents . data ( ) ;
OutputMemoryStream stream { block_contents } ;
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( stream . data ( ) ) ;
if ( old_blocks_length > 0 ) {
2021-04-11 01:25:26 +03:00
if ( auto result = fs ( ) . read_block ( block , & buffer , fs ( ) . block_size ( ) ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
}
// Grow the triply indirect block.
for ( unsigned i = 0 ; i < old_doubly_indirect_blocks_length ; i + + )
2021-03-19 00:58:21 +03:00
stream < < static_cast < u32 > ( block_as_pointers [ i ] ) ;
2021-03-11 21:22:13 +03:00
for ( unsigned i = old_doubly_indirect_blocks_length ; i < new_doubly_indirect_blocks_length ; i + + ) {
auto new_block = new_meta_blocks . take_last ( ) . value ( ) ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::grow_triply_indirect_block(): Allocating doubly indirect block {} at index {} " , identifier ( ) , new_block , i ) ;
2021-03-19 00:58:21 +03:00
stream < < static_cast < u32 > ( new_block ) ;
2021-03-11 21:22:13 +03:00
meta_blocks + + ;
}
stream . fill_to_end ( 0 ) ;
// Write out the doubly indirect blocks.
for ( unsigned i = old_blocks_length / entries_per_doubly_indirect_block ; i < new_doubly_indirect_blocks_length ; i + + ) {
const auto processed_blocks = i * entries_per_doubly_indirect_block ;
const auto old_doubly_indirect_blocks_length = min ( old_blocks_length > processed_blocks ? old_blocks_length - processed_blocks : 0 , entries_per_doubly_indirect_block ) ;
2021-04-29 23:23:52 +03:00
const auto new_doubly_indirect_blocks_length = min ( blocks_indices . size ( ) > processed_blocks ? blocks_indices . size ( ) - processed_blocks : 0 , entries_per_doubly_indirect_block ) ;
if ( auto result = grow_doubly_indirect_block ( block_as_pointers [ i ] , old_doubly_indirect_blocks_length , blocks_indices . slice ( processed_blocks , new_doubly_indirect_blocks_length ) , new_meta_blocks , meta_blocks ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
}
// Write out the triply indirect block.
return fs ( ) . write_block ( block , buffer , stream . size ( ) ) ;
}
KResult Ext2FSInode : : shrink_triply_indirect_block ( BlockBasedFS : : BlockIndex block , size_t old_blocks_length , size_t new_blocks_length , unsigned & meta_blocks )
{
const auto entries_per_block = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ;
const auto entries_per_doubly_indirect_block = entries_per_block * entries_per_block ;
const auto entries_per_triply_indirect_block = entries_per_doubly_indirect_block * entries_per_block ;
const auto old_triply_indirect_blocks_length = divide_rounded_up ( old_blocks_length , entries_per_doubly_indirect_block ) ;
const auto new_triply_indirect_blocks_length = new_blocks_length / entries_per_doubly_indirect_block ;
VERIFY ( old_blocks_length > 0 ) ;
VERIFY ( old_blocks_length > = new_blocks_length ) ;
VERIFY ( new_blocks_length < = entries_per_triply_indirect_block ) ;
auto block_contents = ByteBuffer : : create_uninitialized ( fs ( ) . block_size ( ) ) ;
auto * block_as_pointers = ( unsigned * ) block_contents . data ( ) ;
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( reinterpret_cast < u8 * > ( block_as_pointers ) ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = fs ( ) . read_block ( block , & buffer , fs ( ) . block_size ( ) ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
// Shrink the doubly indirect blocks.
for ( unsigned i = new_triply_indirect_blocks_length ; i < old_triply_indirect_blocks_length ; i + + ) {
const auto processed_blocks = i * entries_per_doubly_indirect_block ;
const auto old_doubly_indirect_blocks_length = min ( old_blocks_length > processed_blocks ? old_blocks_length - processed_blocks : 0 , entries_per_doubly_indirect_block ) ;
const auto new_doubly_indirect_blocks_length = min ( new_blocks_length > processed_blocks ? new_blocks_length - processed_blocks : 0 , entries_per_doubly_indirect_block ) ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::shrink_triply_indirect_block(): Shrinking doubly indirect block {} at index {} " , identifier ( ) , block_as_pointers [ i ] , i ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = shrink_doubly_indirect_block ( block_as_pointers [ i ] , old_doubly_indirect_blocks_length , new_doubly_indirect_blocks_length , meta_blocks ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
}
// Free the triply indirect block if no longer needed.
if ( new_blocks_length = = 0 ) {
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::shrink_triply_indirect_block(): Freeing triply indirect block {} " , identifier ( ) , block ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = fs ( ) . set_block_allocation_state ( block , false ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
meta_blocks - - ;
}
return KSuccess ;
}
2021-02-26 19:57:38 +03:00
KResult Ext2FSInode : : flush_block_list ( )
2019-01-23 04:45:25 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-02-20 15:09:59 +03:00
2021-02-26 19:57:38 +03:00
if ( m_block_list . is_empty ( ) ) {
m_raw_inode . i_blocks = 0 ;
memset ( m_raw_inode . i_block , 0 , sizeof ( m_raw_inode . i_block ) ) ;
2021-02-26 20:24:40 +03:00
set_metadata_dirty ( true ) ;
2021-01-21 01:11:17 +03:00
return KSuccess ;
2020-11-23 14:30:12 +03:00
}
2019-03-27 16:24:37 +03:00
// NOTE: There is a mismatch between i_blocks and blocks.size() since i_blocks includes meta blocks and blocks.size() does not.
2021-03-17 20:35:42 +03:00
const auto old_block_count = ceil_div ( size ( ) , static_cast < u64 > ( fs ( ) . block_size ( ) ) ) ;
2019-01-23 04:45:25 +03:00
2021-02-26 19:57:38 +03:00
auto old_shape = fs ( ) . compute_block_list_shape ( old_block_count ) ;
2021-03-11 21:22:13 +03:00
const auto new_shape = fs ( ) . compute_block_list_shape ( m_block_list . size ( ) ) ;
2019-01-23 04:45:25 +03:00
2021-02-26 19:57:38 +03:00
Vector < Ext2FS : : BlockIndex > new_meta_blocks ;
2019-01-23 04:45:25 +03:00
if ( new_shape . meta_blocks > old_shape . meta_blocks ) {
2021-02-26 19:57:38 +03:00
auto blocks_or_error = fs ( ) . allocate_blocks ( fs ( ) . group_index_from_inode ( index ( ) ) , new_shape . meta_blocks - old_shape . meta_blocks ) ;
2021-02-26 10:35:40 +03:00
if ( blocks_or_error . is_error ( ) )
return blocks_or_error . error ( ) ;
new_meta_blocks = blocks_or_error . release_value ( ) ;
2019-01-23 04:45:25 +03:00
}
2021-02-26 19:57:38 +03:00
m_raw_inode . i_blocks = ( m_block_list . size ( ) + new_shape . meta_blocks ) * ( fs ( ) . block_size ( ) / 512 ) ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::flush_block_list(): Old shape=({};{};{};{}:{}), new shape=({};{};{};{}:{}) " , identifier ( ) , old_shape . direct_blocks , old_shape . indirect_blocks , old_shape . doubly_indirect_blocks , old_shape . triply_indirect_blocks , old_shape . meta_blocks , new_shape . direct_blocks , new_shape . indirect_blocks , new_shape . doubly_indirect_blocks , new_shape . triply_indirect_blocks , new_shape . meta_blocks ) ;
2019-03-27 16:24:37 +03:00
2019-01-23 04:45:25 +03:00
unsigned output_block_index = 0 ;
2021-02-26 19:57:38 +03:00
unsigned remaining_blocks = m_block_list . size ( ) ;
2021-03-11 21:22:13 +03:00
// Deal with direct blocks.
bool inode_dirty = false ;
VERIFY ( new_shape . direct_blocks < = EXT2_NDIR_BLOCKS ) ;
2019-01-23 04:45:25 +03:00
for ( unsigned i = 0 ; i < new_shape . direct_blocks ; + + i ) {
2021-03-19 00:58:21 +03:00
if ( BlockBasedFS : : BlockIndex ( m_raw_inode . i_block [ i ] ) ! = m_block_list [ output_block_index ] )
2019-03-27 16:24:37 +03:00
inode_dirty = true ;
2021-02-26 19:57:38 +03:00
m_raw_inode . i_block [ i ] = m_block_list [ output_block_index ] . value ( ) ;
2019-03-27 16:24:37 +03:00
+ + output_block_index ;
2019-01-23 04:45:25 +03:00
- - remaining_blocks ;
}
2021-05-07 19:43:59 +03:00
// e2fsck considers all blocks reachable through any of the pointers in
// m_raw_inode.i_block as part of this inode regardless of the value in
// m_raw_inode.i_size. When it finds more blocks than the amount that
// is indicated by i_size or i_blocks it offers to repair the filesystem
// by changing those values. That will actually cause further corruption.
// So we must zero all pointers to blocks that are now unused.
for ( unsigned i = new_shape . direct_blocks ; i < EXT2_NDIR_BLOCKS ; + + i ) {
m_raw_inode . i_block [ i ] = 0 ;
}
2019-03-27 16:24:37 +03:00
if ( inode_dirty ) {
2021-02-12 01:05:16 +03:00
if constexpr ( EXT2_DEBUG ) {
2021-03-13 16:59:00 +03:00
dbgln ( " Ext2FSInode[{}]::flush_block_list(): Writing {} direct block(s) to i_block array of inode {} " , identifier ( ) , min ( ( size_t ) EXT2_NDIR_BLOCKS , m_block_list . size ( ) ) , index ( ) ) ;
2021-02-26 19:57:38 +03:00
for ( size_t i = 0 ; i < min ( ( size_t ) EXT2_NDIR_BLOCKS , m_block_list . size ( ) ) ; + + i )
dbgln ( " + {} " , m_block_list [ i ] ) ;
2021-02-12 01:05:16 +03:00
}
2021-02-26 20:24:40 +03:00
set_metadata_dirty ( true ) ;
2019-03-27 16:24:37 +03:00
}
2019-01-23 04:45:25 +03:00
2021-03-11 21:22:13 +03:00
// Deal with indirect blocks.
if ( old_shape . indirect_blocks ! = new_shape . indirect_blocks ) {
if ( new_shape . indirect_blocks > old_shape . indirect_blocks ) {
// Write out the indirect block.
if ( old_shape . indirect_blocks = = 0 ) {
auto new_block = new_meta_blocks . take_last ( ) . value ( ) ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::flush_block_list(): Allocating indirect block: {} " , identifier ( ) , new_block ) ;
2021-03-11 21:22:13 +03:00
m_raw_inode . i_block [ EXT2_IND_BLOCK ] = new_block ;
set_metadata_dirty ( true ) ;
old_shape . meta_blocks + + ;
}
2019-09-28 07:31:40 +03:00
2021-04-11 01:25:26 +03:00
if ( auto result = write_indirect_block ( m_raw_inode . i_block [ EXT2_IND_BLOCK ] , m_block_list . span ( ) . slice ( output_block_index , new_shape . indirect_blocks ) ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
} else if ( ( new_shape . indirect_blocks = = 0 ) & & ( old_shape . indirect_blocks ! = 0 ) ) {
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::flush_block_list(): Freeing indirect block: {} " , identifier ( ) , m_raw_inode . i_block [ EXT2_IND_BLOCK ] ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = fs ( ) . set_block_allocation_state ( m_raw_inode . i_block [ EXT2_IND_BLOCK ] , false ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
old_shape . meta_blocks - - ;
2021-05-07 19:43:59 +03:00
m_raw_inode . i_block [ EXT2_IND_BLOCK ] = 0 ;
2019-09-28 07:31:40 +03:00
}
}
2021-03-11 21:22:13 +03:00
remaining_blocks - = new_shape . indirect_blocks ;
output_block_index + = new_shape . indirect_blocks ;
if ( old_shape . doubly_indirect_blocks ! = new_shape . doubly_indirect_blocks ) {
// Write out the doubly indirect block.
if ( new_shape . doubly_indirect_blocks > old_shape . doubly_indirect_blocks ) {
if ( old_shape . doubly_indirect_blocks = = 0 ) {
auto new_block = new_meta_blocks . take_last ( ) . value ( ) ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::flush_block_list(): Allocating doubly indirect block: {} " , identifier ( ) , new_block ) ;
2021-03-11 21:22:13 +03:00
m_raw_inode . i_block [ EXT2_DIND_BLOCK ] = new_block ;
set_metadata_dirty ( true ) ;
old_shape . meta_blocks + + ;
}
2021-04-11 01:25:26 +03:00
if ( auto result = grow_doubly_indirect_block ( m_raw_inode . i_block [ EXT2_DIND_BLOCK ] , old_shape . doubly_indirect_blocks , m_block_list . span ( ) . slice ( output_block_index , new_shape . doubly_indirect_blocks ) , new_meta_blocks , old_shape . meta_blocks ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
2020-05-18 21:55:08 +03:00
} else {
2021-04-11 01:25:26 +03:00
if ( auto result = shrink_doubly_indirect_block ( m_raw_inode . i_block [ EXT2_DIND_BLOCK ] , old_shape . doubly_indirect_blocks , new_shape . doubly_indirect_blocks , old_shape . meta_blocks ) ; result . is_error ( ) )
2021-01-21 01:11:17 +03:00
return result ;
2021-05-07 19:43:59 +03:00
if ( new_shape . doubly_indirect_blocks = = 0 )
m_raw_inode . i_block [ EXT2_DIND_BLOCK ] = 0 ;
2019-09-28 07:31:40 +03:00
}
2021-03-11 21:22:13 +03:00
}
2019-09-28 07:31:40 +03:00
2021-03-11 21:22:13 +03:00
remaining_blocks - = new_shape . doubly_indirect_blocks ;
output_block_index + = new_shape . doubly_indirect_blocks ;
if ( old_shape . triply_indirect_blocks ! = new_shape . triply_indirect_blocks ) {
// Write out the triply indirect block.
if ( new_shape . triply_indirect_blocks > old_shape . triply_indirect_blocks ) {
if ( old_shape . triply_indirect_blocks = = 0 ) {
auto new_block = new_meta_blocks . take_last ( ) . value ( ) ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::flush_block_list(): Allocating triply indirect block: {} " , identifier ( ) , new_block ) ;
2021-03-11 21:22:13 +03:00
m_raw_inode . i_block [ EXT2_TIND_BLOCK ] = new_block ;
set_metadata_dirty ( true ) ;
old_shape . meta_blocks + + ;
2019-09-28 07:31:40 +03:00
}
2021-04-11 01:25:26 +03:00
if ( auto result = grow_triply_indirect_block ( m_raw_inode . i_block [ EXT2_TIND_BLOCK ] , old_shape . triply_indirect_blocks , m_block_list . span ( ) . slice ( output_block_index , new_shape . triply_indirect_blocks ) , new_meta_blocks , old_shape . meta_blocks ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
} else {
2021-04-11 01:25:26 +03:00
if ( auto result = shrink_triply_indirect_block ( m_raw_inode . i_block [ EXT2_TIND_BLOCK ] , old_shape . triply_indirect_blocks , new_shape . triply_indirect_blocks , old_shape . meta_blocks ) ; result . is_error ( ) )
2021-03-11 21:22:13 +03:00
return result ;
2021-05-07 19:43:59 +03:00
if ( new_shape . triply_indirect_blocks = = 0 )
m_raw_inode . i_block [ EXT2_TIND_BLOCK ] = 0 ;
2019-09-28 07:31:40 +03:00
}
}
2021-03-11 21:22:13 +03:00
remaining_blocks - = new_shape . triply_indirect_blocks ;
output_block_index + = new_shape . triply_indirect_blocks ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::flush_block_list(): New meta blocks count at {}, expecting {} " , identifier ( ) , old_shape . meta_blocks , new_shape . meta_blocks ) ;
2021-03-11 21:22:13 +03:00
VERIFY ( new_meta_blocks . size ( ) = = 0 ) ;
VERIFY ( old_shape . meta_blocks = = new_shape . meta_blocks ) ;
2019-01-23 04:45:25 +03:00
if ( ! remaining_blocks )
2021-01-21 01:11:17 +03:00
return KSuccess ;
2019-01-23 04:45:25 +03:00
2021-03-11 21:22:13 +03:00
dbgln ( " we don't know how to write qind ext2fs blocks, they don't exist anyway! " ) ;
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2019-01-23 04:45:25 +03:00
}
2021-02-26 20:14:02 +03:00
Vector < Ext2FS : : BlockIndex > Ext2FSInode : : compute_block_list ( ) const
2020-02-21 19:48:50 +03:00
{
2021-02-26 20:14:02 +03:00
return compute_block_list_impl ( false ) ;
}
Vector < Ext2FS : : BlockIndex > Ext2FSInode : : compute_block_list_with_meta_blocks ( ) const
{
return compute_block_list_impl ( true ) ;
}
Vector < Ext2FS : : BlockIndex > Ext2FSInode : : compute_block_list_impl ( bool include_block_list_blocks ) const
{
// FIXME: This is really awkwardly factored.. foo_impl_internal :|
auto block_list = compute_block_list_impl_internal ( m_raw_inode , include_block_list_blocks ) ;
2020-02-21 19:48:50 +03:00
while ( ! block_list . is_empty ( ) & & block_list . last ( ) = = 0 )
block_list . take_last ( ) ;
return block_list ;
}
2021-02-26 20:14:02 +03:00
Vector < Ext2FS : : BlockIndex > Ext2FSInode : : compute_block_list_impl_internal ( const ext2_inode & e2inode , bool include_block_list_blocks ) const
2018-10-10 12:53:07 +03:00
{
2021-02-26 20:14:02 +03:00
unsigned entries_per_block = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ;
2018-10-10 12:53:07 +03:00
2021-03-17 20:35:42 +03:00
unsigned block_count = ceil_div ( size ( ) , static_cast < u64 > ( fs ( ) . block_size ( ) ) ) ;
2019-05-25 20:19:43 +03:00
2020-04-05 15:48:58 +03:00
// If we are handling a symbolic link, the path is stored in the 60 bytes in
// the inode that are used for the 12 direct and 3 indirect block pointers,
// If the path is longer than 60 characters, a block is allocated, and the
// block contains the destination path. The file size corresponds to the
// path length of the destination.
2021-02-26 20:14:02 +03:00
if ( : : is_symlink ( e2inode . i_mode ) & & e2inode . i_blocks = = 0 )
2020-04-05 15:48:58 +03:00
block_count = 0 ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::block_list_for_inode(): i_size={}, i_blocks={}, block_count={} " , identifier ( ) , e2inode . i_size , e2inode . i_blocks , block_count ) ;
2019-05-25 20:19:43 +03:00
2019-05-25 18:23:17 +03:00
unsigned blocks_remaining = block_count ;
2020-11-07 18:45:03 +03:00
if ( include_block_list_blocks ) {
2021-02-26 20:14:02 +03:00
auto shape = fs ( ) . compute_block_list_shape ( block_count ) ;
2020-11-07 18:45:03 +03:00
blocks_remaining + = shape . meta_blocks ;
}
2021-02-26 20:14:02 +03:00
Vector < Ext2FS : : BlockIndex > list ;
2020-02-21 19:48:50 +03:00
2021-02-26 20:14:02 +03:00
auto add_block = [ & ] ( auto bi ) {
2020-02-21 19:48:50 +03:00
if ( blocks_remaining ) {
list . append ( bi ) ;
- - blocks_remaining ;
}
} ;
2019-01-22 18:34:24 +03:00
if ( include_block_list_blocks ) {
// This seems like an excessive over-estimate but w/e.
2019-05-25 18:23:17 +03:00
list . ensure_capacity ( blocks_remaining * 2 ) ;
2019-01-22 18:34:24 +03:00
} else {
2019-05-25 18:23:17 +03:00
list . ensure_capacity ( blocks_remaining ) ;
2019-01-22 18:34:24 +03:00
}
2018-10-10 12:53:07 +03:00
2019-01-31 19:31:23 +03:00
unsigned direct_count = min ( block_count , ( unsigned ) EXT2_NDIR_BLOCKS ) ;
for ( unsigned i = 0 ; i < direct_count ; + + i ) {
2019-05-25 20:19:43 +03:00
auto block_index = e2inode . i_block [ i ] ;
2020-02-21 19:48:50 +03:00
add_block ( block_index ) ;
2018-10-10 12:53:07 +03:00
}
2019-05-25 18:23:17 +03:00
if ( ! blocks_remaining )
2018-10-10 12:53:07 +03:00
return list ;
2020-08-29 15:07:21 +03:00
// Don't need to make copy of add_block, since this capture will only
2021-02-26 20:14:02 +03:00
// be called before compute_block_list_impl_internal finishes.
auto process_block_array = [ & ] ( auto array_block_index , auto & & callback ) {
2019-01-22 18:34:24 +03:00
if ( include_block_list_blocks )
2020-05-22 00:36:05 +03:00
add_block ( array_block_index ) ;
2020-11-24 20:38:41 +03:00
auto count = min ( blocks_remaining , entries_per_block ) ;
2021-02-05 22:03:49 +03:00
if ( ! count )
return ;
2021-03-10 18:24:01 +03:00
size_t read_size = count * sizeof ( u32 ) ;
auto array_storage = ByteBuffer : : create_uninitialized ( read_size ) ;
auto * array = ( u32 * ) array_storage . data ( ) ;
2020-11-24 20:38:41 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( ( u8 * ) array ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = fs ( ) . read_block ( array_block_index , & buffer , read_size , 0 ) ; result . is_error ( ) ) {
2021-01-20 23:11:01 +03:00
// FIXME: Stop here and propagate this error.
2021-03-13 16:59:00 +03:00
dbgln ( " Ext2FSInode[{}]::compute_block_list_impl_internal(): Error: {} " , identifier ( ) , result . error ( ) ) ;
2021-01-20 23:11:01 +03:00
}
2021-02-12 13:59:27 +03:00
for ( unsigned i = 0 ; i < count ; + + i )
2021-02-26 20:14:02 +03:00
callback ( Ext2FS : : BlockIndex ( array [ i ] ) ) ;
2018-10-10 12:53:07 +03:00
} ;
2021-02-26 20:14:02 +03:00
process_block_array ( e2inode . i_block [ EXT2_IND_BLOCK ] , [ & ] ( auto block_index ) {
2020-02-21 19:48:50 +03:00
add_block ( block_index ) ;
2018-10-10 12:53:07 +03:00
} ) ;
2019-05-25 18:23:17 +03:00
if ( ! blocks_remaining )
2018-10-10 12:53:07 +03:00
return list ;
2021-02-26 20:14:02 +03:00
process_block_array ( e2inode . i_block [ EXT2_DIND_BLOCK ] , [ & ] ( auto block_index ) {
process_block_array ( block_index , [ & ] ( auto block_index2 ) {
2020-02-21 19:48:50 +03:00
add_block ( block_index2 ) ;
2018-10-10 12:53:07 +03:00
} ) ;
} ) ;
2019-05-25 18:23:17 +03:00
if ( ! blocks_remaining )
2018-10-10 12:53:07 +03:00
return list ;
2021-02-26 20:14:02 +03:00
process_block_array ( e2inode . i_block [ EXT2_TIND_BLOCK ] , [ & ] ( auto block_index ) {
process_block_array ( block_index , [ & ] ( auto block_index2 ) {
process_block_array ( block_index2 , [ & ] ( auto block_index3 ) {
2020-02-21 19:48:50 +03:00
add_block ( block_index3 ) ;
2018-10-10 12:53:07 +03:00
} ) ;
} ) ;
} ) ;
return list ;
}
2019-01-22 22:48:48 +03:00
void Ext2FS : : free_inode ( Ext2FSInode & inode )
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( inode . m_raw_inode . i_links_count = = 0 ) ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS[{}]::free_inode(): Inode {} has no more links, time to delete! " , fsid ( ) , inode . index ( ) ) ;
2019-01-22 22:48:48 +03:00
2021-01-17 18:56:07 +03:00
// Mark all blocks used by this inode as free.
2021-02-26 20:14:02 +03:00
for ( auto block_index : inode . compute_block_list_with_meta_blocks ( ) ) {
2021-02-23 22:42:32 +03:00
VERIFY ( block_index < = super_block ( ) . s_blocks_count ) ;
2021-02-26 01:07:24 +03:00
if ( block_index . value ( ) ) {
2021-04-11 01:25:26 +03:00
if ( auto result = set_block_allocation_state ( block_index , false ) ; result . is_error ( ) ) {
2021-03-13 16:59:00 +03:00
dbgln ( " Ext2FS[{}]::free_inode(): Failed to deallocate block {} for inode {} " , fsid ( ) , block_index , inode . index ( ) ) ;
2021-02-26 01:07:24 +03:00
}
}
2020-02-21 19:48:50 +03:00
}
2019-01-22 22:48:48 +03:00
2021-01-17 18:56:07 +03:00
// If the inode being freed is a directory, update block group directory counter.
2019-01-28 06:16:01 +03: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 ;
2021-04-20 15:01:38 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS[{}]::free_inode(): Decremented bg_used_dirs_count to {} for inode {} " , fsid ( ) , bgd . bg_used_dirs_count , inode . index ( ) ) ;
2019-11-02 13:36:56 +03:00
m_block_group_descriptors_dirty = true ;
2019-01-28 06:16:01 +03:00
}
2021-01-17 18:56:07 +03:00
// NOTE: After this point, the inode metadata is wiped.
memset ( & inode . m_raw_inode , 0 , sizeof ( ext2_inode ) ) ;
2021-02-28 04:18:48 +03:00
inode . m_raw_inode . i_dtime = kgettimeofday ( ) . to_truncated_seconds ( ) ;
2021-01-17 18:56:07 +03:00
write_ext2_inode ( inode . index ( ) , inode . m_raw_inode ) ;
// Mark the inode as free.
2021-04-11 01:25:26 +03:00
if ( auto result = set_inode_allocation_state ( inode . index ( ) , false ) ; result . is_error ( ) )
2021-03-13 16:59:00 +03:00
dbgln ( " Ext2FS[{}]::free_inode(): Failed to free inode {}: {} " , fsid ( ) , inode . index ( ) , result . error ( ) ) ;
2019-01-28 06:16:01 +03:00
}
void Ext2FS : : flush_block_group_descriptor_table ( )
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2020-05-23 14:59:11 +03:00
unsigned blocks_to_write = ceil_div ( m_block_group_count * sizeof ( ext2_group_desc ) , block_size ( ) ) ;
2019-01-31 19:31:23 +03:00
unsigned first_block_of_bgdt = block_size ( ) = = 1024 ? 2 : 1 ;
2020-09-12 06:11:07 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( ( u8 * ) block_group_descriptors ( ) ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = write_blocks ( first_block_of_bgdt , blocks_to_write , buffer ) ; result . is_error ( ) )
2021-03-13 16:59:00 +03:00
dbgln ( " Ext2FS[{}]::flush_block_group_descriptor_table(): Failed to write blocks: {} " , fsid ( ) , result . error ( ) ) ;
2019-01-22 22:48:48 +03:00
}
2019-11-02 13:36:56 +03:00
void Ext2FS : : flush_writes ( )
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-11-02 13:36:56 +03:00
if ( m_super_block_dirty ) {
2019-11-02 13:49:11 +03:00
flush_super_block ( ) ;
2019-11-02 13:36:56 +03:00
m_super_block_dirty = false ;
}
if ( m_block_group_descriptors_dirty ) {
flush_block_group_descriptor_table ( ) ;
m_block_group_descriptors_dirty = false ;
}
2019-11-02 14:18:43 +03:00
for ( auto & cached_bitmap : m_cached_bitmaps ) {
if ( cached_bitmap - > dirty ) {
2020-09-12 06:11:07 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( cached_bitmap - > buffer . data ( ) ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = write_block ( cached_bitmap - > bitmap_block_index , buffer , block_size ( ) ) ; result . is_error ( ) ) {
2021-03-13 16:59:00 +03:00
dbgln ( " Ext2FS[{}]::flush_writes(): Failed to write blocks: {} " , fsid ( ) , result . error ( ) ) ;
2021-01-20 23:11:01 +03:00
}
2019-11-02 14:18:43 +03:00
cached_bitmap - > dirty = false ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS[{}]::flush_writes(): Flushed bitmap block {} " , fsid ( ) , cached_bitmap - > bitmap_block_index ) ;
2019-11-02 14:18:43 +03:00
}
}
2019-11-04 14:23:19 +03:00
2020-07-02 12:48:08 +03:00
BlockBasedFS : : flush_writes ( ) ;
2019-11-04 14:23:19 +03:00
// Uncache Inodes that are only kept alive by the index-to-inode lookup cache.
2019-11-04 18:31:46 +03:00
// We don't uncache Inodes that are being watched by at least one InodeWatcher.
2019-11-04 14:23:19 +03:00
// FIXME: It would be better to keep a capped number of Inodes around.
// The problem is that they are quite heavy objects, and use a lot of heap memory
// for their (child name lookup) and (block list) caches.
Vector < InodeIndex > unused_inodes ;
for ( auto & it : m_inode_cache ) {
if ( it . value - > ref_count ( ) ! = 1 )
continue ;
2019-11-04 18:31:46 +03:00
if ( it . value - > has_watchers ( ) )
continue ;
2019-11-04 14:23:19 +03:00
unused_inodes . append ( it . key ) ;
}
for ( auto index : unused_inodes )
uncache_inode ( index ) ;
2019-11-02 13:36:56 +03:00
}
2021-02-12 11:18:47 +03:00
Ext2FSInode : : Ext2FSInode ( Ext2FS & fs , InodeIndex index )
2018-12-19 23:18:28 +03:00
: Inode ( fs , index )
2018-11-13 15:02:39 +03:00
{
}
2018-11-15 19:13:10 +03:00
Ext2FSInode : : ~ Ext2FSInode ( )
2018-11-13 15:02:39 +03:00
{
2019-01-22 22:48:48 +03:00
if ( m_raw_inode . i_links_count = = 0 )
fs ( ) . free_inode ( * this ) ;
2018-11-13 15:02:39 +03:00
}
2021-03-17 20:35:42 +03:00
u64 Ext2FSInode : : size ( ) const
{
if ( Kernel : : is_regular_file ( m_raw_inode . i_mode ) & & ( ( u32 ) fs ( ) . get_features_readonly ( ) & ( u32 ) Ext2FS : : FeaturesReadOnly : : FileSize64bits ) )
return static_cast < u64 > ( m_raw_inode . i_dir_acl ) < < 32 | m_raw_inode . i_size ;
return m_raw_inode . i_size ;
}
2019-01-01 05:16:36 +03:00
InodeMetadata Ext2FSInode : : metadata ( ) const
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-01-01 05:16:36 +03:00
InodeMetadata metadata ;
metadata . inode = identifier ( ) ;
2021-03-17 20:35:42 +03:00
metadata . size = size ( ) ;
2019-01-01 05:16:36 +03:00
metadata . mode = m_raw_inode . i_mode ;
metadata . uid = m_raw_inode . i_uid ;
metadata . gid = m_raw_inode . i_gid ;
2019-01-31 19:31:23 +03:00
metadata . link_count = m_raw_inode . i_links_count ;
2019-01-01 05:16:36 +03: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 19:31:23 +03:00
metadata . block_size = fs ( ) . block_size ( ) ;
metadata . block_count = m_raw_inode . i_blocks ;
2018-11-13 15:32:16 +03:00
2020-02-16 03:27:42 +03:00
if ( Kernel : : is_character_device ( m_raw_inode . i_mode ) | | Kernel : : is_block_device ( m_raw_inode . i_mode ) ) {
2018-11-13 15:32:16 +03:00
unsigned dev = m_raw_inode . i_block [ 0 ] ;
2019-07-31 18:24:54 +03:00
if ( ! dev )
dev = m_raw_inode . i_block [ 1 ] ;
2019-02-16 11:57:42 +03:00
metadata . major_device = ( dev & 0xfff00 ) > > 8 ;
metadata . minor_device = ( dev & 0xff ) | ( ( dev > > 12 ) & 0xfff00 ) ;
2018-11-13 15:32:16 +03:00
}
2019-01-01 05:16:36 +03:00
return metadata ;
2018-11-13 15:32:16 +03:00
}
2018-12-19 23:56:45 +03:00
void Ext2FSInode : : flush_metadata ( )
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::flush_metadata(): Flushing inode " , identifier ( ) ) ;
2018-12-19 23:56:45 +03:00
fs ( ) . write_ext2_inode ( index ( ) , m_raw_inode ) ;
2019-01-01 05:55:13 +03:00
if ( is_directory ( ) ) {
2019-01-28 06:16:01 +03: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 05:55:13 +03:00
}
2018-12-20 00:28:09 +03:00
set_metadata_dirty ( false ) ;
2018-12-19 23:56:45 +03:00
}
2019-06-21 19:37:47 +03:00
RefPtr < Inode > Ext2FS : : get_inode ( InodeIdentifier inode ) const
2018-11-13 15:02:39 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( inode . fsid ( ) = = fsid ( ) ) ;
2019-02-20 23:41:53 +03:00
2018-11-13 15:02:39 +03:00
{
auto it = m_inode_cache . find ( inode . index ( ) ) ;
if ( it ! = m_inode_cache . end ( ) )
return ( * it ) . value ;
}
2018-12-25 02:32:57 +03:00
2021-02-26 10:35:40 +03:00
auto state_or_error = get_inode_allocation_state ( inode . index ( ) ) ;
if ( state_or_error . is_error ( ) )
return { } ;
if ( ! state_or_error . value ( ) ) {
2019-01-01 05:35:33 +03:00
m_inode_cache . set ( inode . index ( ) , nullptr ) ;
2021-02-26 10:35:40 +03:00
return { } ;
2019-01-01 05:35:33 +03:00
}
2021-02-12 13:59:27 +03:00
BlockIndex block_index ;
2018-12-25 02:32:57 +03:00
unsigned offset ;
2020-05-18 21:55:08 +03:00
if ( ! find_block_containing_inode ( inode . index ( ) , block_index , offset ) )
2019-06-07 12:43:58 +03:00
return { } ;
2018-12-25 02:32:57 +03:00
2021-04-23 17:46:57 +03:00
auto new_inode = adopt_ref ( * new Ext2FSInode ( const_cast < Ext2FS & > ( * this ) , inode . index ( ) ) ) ;
2020-09-12 06:11:07 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( reinterpret_cast < u8 * > ( & new_inode - > m_raw_inode ) ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = read_block ( block_index , & buffer , sizeof ( ext2_inode ) , offset ) ; result . is_error ( ) ) {
2021-01-20 23:11:01 +03:00
// FIXME: Propagate the actual error.
return nullptr ;
}
2019-07-11 16:38:47 +03:00
m_inode_cache . set ( inode . index ( ) , new_inode ) ;
2018-11-13 15:02:39 +03:00
return new_inode ;
}
2021-05-02 00:29:39 +03:00
KResultOr < ssize_t > Ext2FSInode : : read_bytes ( off_t offset , ssize_t count , UserOrKernelBuffer & buffer , FileDescription * description ) const
2018-11-13 15:02:39 +03:00
{
2019-02-20 15:09:59 +03:00
Locker inode_locker ( m_lock ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( offset > = 0 ) ;
2018-11-13 15:02:39 +03:00
if ( m_raw_inode . i_size = = 0 )
return 0 ;
2021-04-04 18:21:07 +03:00
if ( static_cast < u64 > ( offset ) > = size ( ) )
return 0 ;
2018-11-13 15:02:39 +03:00
// 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 ) {
2021-02-23 22:42:32 +03:00
VERIFY ( offset = = 0 ) ;
2019-01-23 08:53:01 +03:00
ssize_t nread = min ( ( off_t ) size ( ) - offset , static_cast < off_t > ( count ) ) ;
2020-09-12 06:11:07 +03:00
if ( ! buffer . write ( ( ( const u8 * ) m_raw_inode . i_block ) + offset , ( size_t ) nread ) )
2021-05-02 00:29:39 +03:00
return EFAULT ;
2018-11-13 15:02:39 +03:00
return nread ;
}
2019-11-03 12:22:09 +03:00
if ( m_block_list . is_empty ( ) )
2021-02-26 20:14:02 +03:00
m_block_list = compute_block_list ( ) ;
2018-11-13 15:02:39 +03:00
2018-12-21 04:10:45 +03:00
if ( m_block_list . is_empty ( ) ) {
2021-03-13 16:59:00 +03:00
dmesgln ( " Ext2FSInode[{}]::read_bytes(): Empty block list " , identifier ( ) ) ;
2021-05-02 00:29:39 +03:00
return EIO ;
2018-11-13 15:02:39 +03:00
}
2020-05-18 21:55:08 +03:00
bool allow_cache = ! description | | ! description - > is_direct ( ) ;
2019-04-23 14:00:53 +03:00
const int block_size = fs ( ) . block_size ( ) ;
2018-11-13 15:02:39 +03:00
2021-03-19 00:58:21 +03:00
BlockBasedFS : : BlockIndex first_block_logical_index = offset / block_size ;
BlockBasedFS : : BlockIndex last_block_logical_index = ( offset + count ) / block_size ;
2018-11-13 15:02:39 +03:00
if ( last_block_logical_index > = m_block_list . size ( ) )
last_block_logical_index = m_block_list . size ( ) - 1 ;
2019-04-23 14:00:53 +03:00
int offset_into_first_block = offset % block_size ;
2018-11-13 15:02:39 +03:00
2018-12-03 01:34:50 +03:00
ssize_t nread = 0 ;
2021-03-19 00:58:21 +03:00
auto remaining_count = min ( ( off_t ) count , ( off_t ) size ( ) - offset ) ;
2018-11-13 15:02:39 +03:00
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_VERY_DEBUG , " Ext2FSInode[{}]::read_bytes(): Reading up to {} bytes, {} bytes into inode to {} " , identifier ( ) , count , offset , buffer . user_or_kernel_ptr ( ) ) ;
2018-11-13 15:02:39 +03:00
2021-03-19 00:58:21 +03:00
for ( auto bi = first_block_logical_index ; remaining_count & & bi < = last_block_logical_index ; bi = bi . value ( ) + 1 ) {
auto block_index = m_block_list [ bi . value ( ) ] ;
2020-05-18 21:55:08 +03:00
size_t offset_into_block = ( bi = = first_block_logical_index ) ? offset_into_first_block : 0 ;
2021-05-02 02:32:04 +03:00
size_t num_bytes_to_copy = min ( ( size_t ) block_size - offset_into_block , ( size_t ) remaining_count ) ;
2020-09-12 06:11:07 +03:00
auto buffer_offset = buffer . offset ( nread ) ;
2021-04-10 12:05:19 +03:00
if ( block_index . value ( ) = = 0 ) {
// This is a hole, act as if it's filled with zeroes.
if ( ! buffer_offset . memset ( 0 , num_bytes_to_copy ) )
2021-05-02 00:29:39 +03:00
return EFAULT ;
2021-04-10 12:05:19 +03:00
} else {
2021-04-11 01:25:26 +03:00
if ( auto result = fs ( ) . read_block ( block_index , & buffer_offset , num_bytes_to_copy , offset_into_block , allow_cache ) ; result . is_error ( ) ) {
2021-04-10 12:05:19 +03:00
dmesgln ( " Ext2FSInode[{}]::read_bytes(): Failed to read block {} (index {}) " , identifier ( ) , block_index . value ( ) , bi ) ;
return result . error ( ) ;
}
2018-11-13 15:02:39 +03:00
}
remaining_count - = num_bytes_to_copy ;
nread + = num_bytes_to_copy ;
}
return nread ;
}
2019-11-02 14:53:31 +03:00
KResult Ext2FSInode : : resize ( u64 new_size )
2019-04-28 23:07:25 +03:00
{
2021-03-19 00:58:21 +03:00
auto old_size = size ( ) ;
2019-11-02 18:22:29 +03:00
if ( old_size = = new_size )
return KSuccess ;
2021-03-17 20:35:42 +03:00
if ( ! ( ( u32 ) fs ( ) . get_features_readonly ( ) & ( u32 ) Ext2FS : : FeaturesReadOnly : : FileSize64bits ) & & ( new_size > = static_cast < u32 > ( - 1 ) ) )
return ENOSPC ;
2019-11-02 18:22:29 +03:00
u64 block_size = fs ( ) . block_size ( ) ;
2021-03-19 00:58:21 +03:00
auto blocks_needed_before = ceil_div ( old_size , block_size ) ;
auto blocks_needed_after = ceil_div ( new_size , block_size ) ;
2019-04-28 23:07:25 +03:00
2021-02-12 01:05:16 +03:00
if constexpr ( EXT2_DEBUG ) {
2021-03-13 16:59:00 +03:00
dbgln ( " Ext2FSInode[{}]::resize(): Blocks needed before (size was {}): {} " , identifier ( ) , old_size , blocks_needed_before ) ;
dbgln ( " Ext2FSInode[{}]::resize(): Blocks needed after (size is {}): {} " , identifier ( ) , new_size , blocks_needed_after ) ;
2021-02-12 01:05:16 +03:00
}
2019-04-28 23:07:25 +03:00
2019-11-02 14:53:31 +03:00
if ( blocks_needed_after > blocks_needed_before ) {
2021-03-19 00:58:21 +03:00
auto additional_blocks_needed = blocks_needed_after - blocks_needed_before ;
2019-11-02 14:53:31 +03:00
if ( additional_blocks_needed > fs ( ) . super_block ( ) . s_free_blocks_count )
2021-01-21 01:11:17 +03:00
return ENOSPC ;
2019-11-02 14:53:31 +03:00
}
2021-03-10 20:47:11 +03:00
if ( m_block_list . is_empty ( ) )
m_block_list = this - > compute_block_list ( ) ;
2020-11-24 13:49:46 +03:00
2019-04-28 23:07:25 +03:00
if ( blocks_needed_after > blocks_needed_before ) {
2021-02-26 10:35:40 +03:00
auto blocks_or_error = fs ( ) . allocate_blocks ( fs ( ) . group_index_from_inode ( index ( ) ) , blocks_needed_after - blocks_needed_before ) ;
if ( blocks_or_error . is_error ( ) )
return blocks_or_error . error ( ) ;
2021-04-30 13:21:05 +03:00
if ( ! m_block_list . try_append ( blocks_or_error . release_value ( ) ) )
return ENOMEM ;
2019-04-28 23:07:25 +03:00
} else if ( blocks_needed_after < blocks_needed_before ) {
2021-03-13 16:59:00 +03:00
if constexpr ( EXT2_VERY_DEBUG ) {
dbgln ( " Ext2FSInode[{}]::resize(): Shrinking inode, old block list is {} entries: " , identifier ( ) , m_block_list . size ( ) ) ;
2021-03-10 20:47:11 +03:00
for ( auto block_index : m_block_list ) {
2021-02-12 01:05:16 +03:00
dbgln ( " # {} " , block_index ) ;
}
2019-05-25 18:23:17 +03:00
}
2021-03-10 20:47:11 +03:00
while ( m_block_list . size ( ) ! = blocks_needed_after ) {
auto block_index = m_block_list . take_last ( ) ;
2021-02-26 01:07:24 +03:00
if ( block_index . value ( ) ) {
2021-04-11 01:25:26 +03:00
if ( auto result = fs ( ) . set_block_allocation_state ( block_index , false ) ; result . is_error ( ) ) {
2021-03-13 16:59:00 +03:00
dbgln ( " Ext2FSInode[{}]::resize(): Failed to free block {}: {} " , identifier ( ) , block_index , result . error ( ) ) ;
2021-02-26 01:07:24 +03:00
return result ;
}
}
2019-04-28 23:07:25 +03:00
}
}
2021-04-11 01:25:26 +03:00
if ( auto result = flush_block_list ( ) ; result . is_error ( ) )
2021-01-21 01:11:17 +03:00
return result ;
2019-04-28 23:07:25 +03:00
m_raw_inode . i_size = new_size ;
2021-03-17 20:35:42 +03:00
if ( Kernel : : is_regular_file ( m_raw_inode . i_mode ) )
m_raw_inode . i_dir_acl = new_size > > 32 ;
2019-04-28 23:07:25 +03:00
set_metadata_dirty ( true ) ;
2021-01-10 00:05:05 +03:00
if ( new_size > old_size ) {
// If we're growing the inode, make sure we zero out all the new space.
// FIXME: There are definitely more efficient ways to achieve this.
2021-03-19 00:58:21 +03:00
auto bytes_to_clear = new_size - old_size ;
auto clear_from = old_size ;
2021-02-21 13:16:16 +03:00
u8 zero_buffer [ PAGE_SIZE ] { } ;
2021-01-10 00:05:05 +03:00
while ( bytes_to_clear ) {
2021-05-02 00:29:39 +03:00
auto result = write_bytes ( clear_from , min ( static_cast < u64 > ( sizeof ( zero_buffer ) ) , bytes_to_clear ) , UserOrKernelBuffer : : for_kernel_buffer ( zero_buffer ) , nullptr ) ;
if ( result . is_error ( ) )
return result . error ( ) ;
VERIFY ( result . value ( ) ! = 0 ) ;
bytes_to_clear - = result . value ( ) ;
clear_from + = result . value ( ) ;
2021-01-10 00:05:05 +03:00
}
}
2019-11-02 14:53:31 +03:00
return KSuccess ;
2019-04-28 23:07:25 +03:00
}
2021-05-02 00:29:39 +03:00
KResultOr < ssize_t > Ext2FSInode : : write_bytes ( off_t offset , ssize_t count , const UserOrKernelBuffer & data , FileDescription * description )
2019-01-23 06:29:56 +03:00
{
2021-02-23 22:42:32 +03:00
VERIFY ( offset > = 0 ) ;
VERIFY ( count > = 0 ) ;
2019-03-02 03:50:34 +03:00
2021-05-07 21:30:23 +03:00
if ( count = = 0 )
return 0 ;
2019-02-20 15:09:59 +03:00
Locker inode_locker ( m_lock ) ;
2019-01-23 06:29:56 +03:00
2021-04-11 01:25:26 +03:00
if ( auto result = prepare_to_write_data ( ) ; result . is_error ( ) )
2020-04-04 20:46:55 +03:00
return result ;
2019-03-02 03:50:34 +03:00
if ( is_symlink ( ) ) {
2021-02-23 22:42:32 +03:00
VERIFY ( offset = = 0 ) ;
2019-11-17 21:12:29 +03:00
if ( max ( ( size_t ) ( offset + count ) , ( size_t ) m_raw_inode . i_size ) < max_inline_symlink_length ) {
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::write_bytes(): Poking into i_block array for inline symlink '{}' ({} bytes) " , identifier ( ) , data . copy_into_string ( count ) , count ) ;
2020-09-12 06:11:07 +03:00
if ( ! data . read ( ( ( u8 * ) m_raw_inode . i_block ) + offset , ( size_t ) count ) )
2021-05-02 00:29:39 +03:00
return EFAULT ;
2019-11-17 21:12:29 +03:00
if ( ( size_t ) ( offset + count ) > ( size_t ) m_raw_inode . i_size )
2019-03-02 03:50:34 +03:00
m_raw_inode . i_size = offset + count ;
set_metadata_dirty ( true ) ;
return count ;
}
}
2019-01-23 06:29:56 +03:00
2020-05-18 21:55:08 +03:00
bool allow_cache = ! description | | ! description - > is_direct ( ) ;
2021-03-19 00:58:21 +03:00
const auto block_size = fs ( ) . block_size ( ) ;
auto new_size = max ( static_cast < u64 > ( offset ) + count , size ( ) ) ;
2019-01-23 06:29:56 +03:00
2021-04-11 01:25:26 +03:00
if ( auto result = resize ( new_size ) ; result . is_error ( ) )
return result ;
2019-01-23 06:29:56 +03:00
2019-11-03 12:22:09 +03:00
if ( m_block_list . is_empty ( ) )
2021-02-26 20:14:02 +03:00
m_block_list = compute_block_list ( ) ;
2019-11-03 12:22:09 +03:00
if ( m_block_list . is_empty ( ) ) {
2021-03-13 16:59:00 +03:00
dbgln ( " Ext2FSInode[{}]::write_bytes(): Empty block list " , identifier ( ) ) ;
2021-05-02 00:29:39 +03:00
return EIO ;
2019-11-03 12:22:09 +03:00
}
2021-03-19 00:58:21 +03:00
BlockBasedFS : : BlockIndex first_block_logical_index = offset / block_size ;
BlockBasedFS : : BlockIndex last_block_logical_index = ( offset + count ) / block_size ;
2019-04-28 23:07:25 +03:00
if ( last_block_logical_index > = m_block_list . size ( ) )
last_block_logical_index = m_block_list . size ( ) - 1 ;
2019-01-23 06:29:56 +03:00
2020-02-25 16:49:47 +03:00
size_t offset_into_first_block = offset % block_size ;
2019-01-23 06:29:56 +03:00
ssize_t nwritten = 0 ;
2021-03-19 00:58:21 +03:00
auto remaining_count = min ( ( off_t ) count , ( off_t ) new_size - offset ) ;
2019-01-23 06:29:56 +03:00
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_VERY_DEBUG , " Ext2FSInode[{}]::write_bytes(): Writing {} bytes, {} bytes into inode from {} " , identifier ( ) , count , offset , data . user_or_kernel_ptr ( ) ) ;
2019-01-23 06:29:56 +03:00
2021-03-19 00:58:21 +03:00
for ( auto bi = first_block_logical_index ; remaining_count & & bi < = last_block_logical_index ; bi = bi . value ( ) + 1 ) {
2020-02-25 16:49:47 +03:00
size_t offset_into_block = ( bi = = first_block_logical_index ) ? offset_into_first_block : 0 ;
2021-05-02 02:32:04 +03:00
size_t num_bytes_to_copy = min ( ( size_t ) block_size - offset_into_block , ( size_t ) remaining_count ) ;
2021-03-19 00:58:21 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::write_bytes(): Writing block {} (offset_into_block: {}) " , identifier ( ) , m_block_list [ bi . value ( ) ] , offset_into_block ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = fs ( ) . write_block ( m_block_list [ bi . value ( ) ] , data . offset ( nwritten ) , num_bytes_to_copy , offset_into_block , allow_cache ) ; result . is_error ( ) ) {
2021-03-19 00:58:21 +03:00
dbgln ( " Ext2FSInode[{}]::write_bytes(): Failed to write block {} (index {}) " , identifier ( ) , m_block_list [ bi . value ( ) ] , bi ) ;
2021-01-21 01:51:14 +03:00
return result ;
2019-01-23 06:29:56 +03:00
}
remaining_count - = num_bytes_to_copy ;
nwritten + = num_bytes_to_copy ;
}
2021-05-12 22:17:51 +03:00
did_modify_contents ( ) ;
2021-03-17 20:35:42 +03:00
dbgln_if ( EXT2_VERY_DEBUG , " Ext2FSInode[{}]::write_bytes(): After write, i_size={}, i_blocks={} ({} blocks in list) " , identifier ( ) , size ( ) , m_raw_inode . i_blocks , m_block_list . size ( ) ) ;
2019-01-23 06:29:56 +03:00
return nwritten ;
}
2020-08-29 21:25:01 +03:00
u8 Ext2FS : : internal_file_type_to_directory_entry_type ( const DirectoryEntryView & entry ) const
2020-08-28 16:15:51 +03:00
{
switch ( entry . file_type ) {
case EXT2_FT_REG_FILE :
return DT_REG ;
case EXT2_FT_DIR :
return DT_DIR ;
case EXT2_FT_CHRDEV :
return DT_CHR ;
case EXT2_FT_BLKDEV :
return DT_BLK ;
case EXT2_FT_FIFO :
return DT_FIFO ;
case EXT2_FT_SOCK :
2020-08-29 21:25:01 +03:00
return DT_SOCK ;
2020-08-28 16:15:51 +03:00
case EXT2_FT_SYMLINK :
return DT_LNK ;
default :
return DT_UNKNOWN ;
}
}
2021-03-17 20:35:42 +03:00
Ext2FS : : FeaturesReadOnly Ext2FS : : get_features_readonly ( ) const
{
if ( m_super_block . s_rev_level > 0 )
return static_cast < Ext2FS : : FeaturesReadOnly > ( m_super_block . s_feature_ro_compat ) ;
return Ext2FS : : FeaturesReadOnly : : None ;
}
2020-08-18 13:41:27 +03:00
KResult Ext2FSInode : : traverse_as_directory ( Function < bool ( const FS : : DirectoryEntryView & ) > callback ) const
2018-11-14 01:44:54 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( is_directory ( ) ) ;
2018-11-14 01:44:54 +03:00
2021-05-08 19:28:54 +03:00
u8 buffer [ max_block_size ] ;
auto buf = UserOrKernelBuffer : : for_kernel_buffer ( buffer ) ;
auto block_size = fs ( ) . block_size ( ) ;
bool allow_cache = true ;
if ( m_block_list . is_empty ( ) )
m_block_list = compute_block_list ( ) ;
// Directory entries are guaranteed not to span multiple blocks,
// so we can iterate over blocks separately.
for ( auto & block_index : m_block_list ) {
VERIFY ( block_index . value ( ) ! = 0 ) ;
if ( auto result = fs ( ) . read_block ( block_index , & buf , block_size , 0 , allow_cache ) ; result . is_error ( ) ) {
return result ;
}
auto * entry = reinterpret_cast < ext2_dir_entry_2 * > ( buffer ) ;
auto * entries_end = reinterpret_cast < ext2_dir_entry_2 * > ( buffer + block_size ) ;
while ( entry < entries_end ) {
if ( entry - > inode ! = 0 ) {
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::traverse_as_directory(): inode {}, name_len: {}, rec_len: {}, file_type: {}, name: {} " , identifier ( ) , entry - > inode , entry - > name_len , entry - > rec_len , entry - > file_type , StringView ( entry - > name , entry - > name_len ) ) ;
if ( ! callback ( { { entry - > name , entry - > name_len } , { fsid ( ) , entry - > inode } , entry - > file_type } ) )
return KSuccess ;
}
entry = ( ext2_dir_entry_2 * ) ( ( char * ) entry + entry - > rec_len ) ;
2018-11-14 01:44:54 +03:00
}
}
2020-05-26 10:36:11 +03:00
return KSuccess ;
2018-11-14 01:44:54 +03:00
}
2021-05-07 15:47:38 +03:00
KResult Ext2FSInode : : write_directory ( Vector < Ext2FSDirectoryEntry > & entries )
2019-06-09 13:46:23 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-06-09 13:46:23 +03:00
auto block_size = fs ( ) . block_size ( ) ;
2021-05-07 15:47:38 +03:00
// Calculate directory size and record length of entries so that
// the following constraints are met:
// - All used blocks must be entirely filled.
// - Entries are aligned on a 4-byte boundary.
// - No entry may span multiple blocks.
size_t directory_size = 0 ;
size_t space_in_block = block_size ;
2020-02-25 16:49:47 +03:00
for ( size_t i = 0 ; i < entries . size ( ) ; + + i ) {
2019-06-09 13:46:23 +03:00
auto & entry = entries [ i ] ;
2021-05-07 15:47:38 +03:00
entry . record_length = EXT2_DIR_REC_LEN ( entry . name . length ( ) ) ;
space_in_block - = entry . record_length ;
if ( i + 1 < entries . size ( ) ) {
if ( EXT2_DIR_REC_LEN ( entries [ i + 1 ] . name . length ( ) ) > space_in_block ) {
entry . record_length + = space_in_block ;
space_in_block = block_size ;
}
} else {
entry . record_length + = space_in_block ;
}
directory_size + = entry . record_length ;
}
2019-06-09 13:46:23 +03:00
2021-05-07 15:47:38 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::write_directory(): New directory contents to write (size {}): " , identifier ( ) , directory_size ) ;
2019-06-09 13:46:23 +03:00
2021-05-07 15:47:38 +03:00
auto directory_data = ByteBuffer : : create_uninitialized ( directory_size ) ;
OutputMemoryStream stream { directory_data } ;
for ( auto & entry : entries ) {
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::write_directory(): Writing inode: {}, name_len: {}, rec_len: {}, file_type: {}, name: {} " , identifier ( ) , entry . inode_index , u16 ( entry . name . length ( ) ) , u16 ( entry . record_length ) , u8 ( entry . file_type ) , entry . name ) ;
2019-06-09 13:46:23 +03:00
2021-02-12 11:18:47 +03:00
stream < < u32 ( entry . inode_index . value ( ) ) ;
2021-05-07 15:47:38 +03:00
stream < < u16 ( entry . record_length ) ;
2020-08-18 19:01:42 +03:00
stream < < u8 ( entry . name . length ( ) ) ;
2019-07-03 22:17:35 +03:00
stream < < u8 ( entry . file_type ) ;
2020-09-15 13:24:14 +03:00
stream < < entry . name . bytes ( ) ;
2021-05-07 15:47:38 +03:00
int padding = entry . record_length - entry . name . length ( ) - 8 ;
2019-06-09 13:46:23 +03:00
for ( int j = 0 ; j < padding ; + + j )
2019-07-03 22:17:35 +03:00
stream < < u8 ( 0 ) ;
2019-06-09 13:46:23 +03:00
}
2021-05-07 15:47:38 +03:00
VERIFY ( stream . is_end ( ) ) ;
2019-06-09 13:46:23 +03:00
2021-05-06 18:53:59 +03:00
if ( auto result = resize ( stream . size ( ) ) ; result . is_error ( ) )
return result ;
2020-09-15 13:24:14 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( stream . data ( ) ) ;
2021-05-02 00:29:39 +03:00
auto result = write_bytes ( 0 , stream . size ( ) , buffer , nullptr ) ;
if ( result . is_error ( ) )
return result . error ( ) ;
2019-11-13 11:42:24 +03:00
set_metadata_dirty ( true ) ;
2021-05-02 00:29:39 +03:00
if ( static_cast < size_t > ( result . value ( ) ) ! = directory_data . size ( ) )
2021-02-02 18:06:03 +03:00
return EIO ;
return KSuccess ;
2019-06-09 13:46:23 +03:00
}
2020-06-24 23:35:56 +03:00
KResultOr < NonnullRefPtr < Inode > > Ext2FSInode : : create_child ( const String & name , mode_t mode , dev_t dev , uid_t uid , gid_t gid )
{
2021-01-23 17:29:58 +03:00
if ( : : is_directory ( mode ) )
2021-02-02 18:37:52 +03:00
return fs ( ) . create_directory ( * this , name , mode , uid , gid ) ;
return fs ( ) . create_inode ( * this , name , mode , dev , uid , gid ) ;
2020-06-24 23:35:56 +03:00
}
KResult Ext2FSInode : : add_child ( Inode & child , const StringView & name , mode_t mode )
2018-10-10 12:53:07 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( is_directory ( ) ) ;
2018-10-10 12:53:07 +03:00
2019-09-10 22:04:27 +03:00
if ( name . length ( ) > EXT2_NAME_LEN )
2021-01-21 01:11:17 +03:00
return ENAMETOOLONG ;
2019-09-10 22:04:27 +03:00
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::add_child(): Adding inode {} with name '{}' and mode {:o} to directory {} " , identifier ( ) , child . index ( ) , name , mode , index ( ) ) ;
2018-10-10 12:53:07 +03:00
2020-08-18 19:01:42 +03:00
Vector < Ext2FSDirectoryEntry > entries ;
2018-12-25 02:27:39 +03:00
bool name_already_exists = false ;
2020-05-26 10:36:11 +03:00
KResult result = traverse_as_directory ( [ & ] ( auto & entry ) {
2019-06-12 16:25:28 +03:00
if ( name = = entry . name ) {
2018-12-25 02:27:39 +03:00
name_already_exists = true ;
2018-10-10 12:53:07 +03:00
return false ;
}
2021-02-12 00:45:50 +03:00
entries . append ( { entry . name , entry . inode . index ( ) , entry . file_type } ) ;
2018-10-10 12:53:07 +03:00
return true ;
} ) ;
2020-05-26 10:36:11 +03:00
if ( result . is_error ( ) )
return result ;
2018-12-25 02:27:39 +03:00
if ( name_already_exists ) {
2021-03-13 16:59:00 +03:00
dbgln ( " Ext2FSInode[{}]::add_child(): Name '{}' already exists " , identifier ( ) , name ) ;
2021-01-21 01:11:17 +03:00
return EEXIST ;
2018-10-10 12:53:07 +03:00
}
2020-06-24 23:35:56 +03:00
result = child . increment_link_count ( ) ;
if ( result . is_error ( ) )
return result ;
2019-02-21 15:26:40 +03:00
2021-02-12 00:45:50 +03:00
entries . empend ( name , child . index ( ) , to_ext2_file_type ( mode ) ) ;
2021-02-02 18:06:03 +03:00
result = write_directory ( entries ) ;
if ( result . is_error ( ) )
return result ;
2020-07-04 14:36:55 +03:00
2021-02-02 18:06:03 +03:00
m_lookup_cache . set ( name , child . index ( ) ) ;
2021-05-12 22:17:51 +03:00
did_add_child ( child . identifier ( ) , name ) ;
2019-02-27 17:31:26 +03:00
return KSuccess ;
2018-10-10 12:53:07 +03:00
}
2019-06-09 11:25:19 +03:00
KResult Ext2FSInode : : remove_child ( const StringView & name )
2019-01-22 09:03:44 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::remove_child(): Removing '{}' " , identifier ( ) , name ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( is_directory ( ) ) ;
2019-01-22 09:03:44 +03:00
2019-02-27 16:11:25 +03:00
auto it = m_lookup_cache . find ( name ) ;
if ( it = = m_lookup_cache . end ( ) )
2021-01-21 01:11:17 +03:00
return ENOENT ;
2019-11-17 20:59:14 +03:00
auto child_inode_index = ( * it ) . value ;
2019-02-27 16:11:25 +03:00
2019-01-22 09:03:44 +03:00
InodeIdentifier child_id { fsid ( ) , child_inode_index } ;
2020-08-18 19:01:42 +03:00
Vector < Ext2FSDirectoryEntry > entries ;
2020-05-26 10:36:11 +03:00
KResult result = traverse_as_directory ( [ & ] ( auto & entry ) {
2019-06-12 16:25:28 +03:00
if ( name ! = entry . name )
2021-02-12 00:45:50 +03:00
entries . append ( { entry . name , entry . inode . index ( ) , entry . file_type } ) ;
2019-01-22 09:03:44 +03:00
return true ;
} ) ;
2020-05-26 10:36:11 +03:00
if ( result . is_error ( ) )
return result ;
2019-01-22 09:03:44 +03:00
2021-02-02 18:06:03 +03:00
result = write_directory ( entries ) ;
if ( result . is_error ( ) )
return result ;
2019-01-22 09:03:44 +03:00
2019-02-27 16:11:25 +03:00
m_lookup_cache . remove ( name ) ;
2019-01-22 09:03:44 +03:00
auto child_inode = fs ( ) . get_inode ( child_id ) ;
2020-08-05 12:07:31 +03:00
result = child_inode - > decrement_link_count ( ) ;
if ( result . is_error ( ) )
return result ;
2020-07-04 14:36:55 +03:00
2021-05-12 22:17:51 +03:00
did_remove_child ( child_id , name ) ;
2019-02-27 16:11:25 +03:00
return KSuccess ;
2019-01-22 09:03:44 +03:00
}
2018-12-03 02:20:00 +03:00
unsigned Ext2FS : : inodes_per_block ( ) const
2018-10-10 12:53:07 +03:00
{
2018-12-03 02:20:00 +03:00
return EXT2_INODES_PER_BLOCK ( & super_block ( ) ) ;
2018-10-10 12:53:07 +03:00
}
2018-12-03 02:20:00 +03:00
unsigned Ext2FS : : inodes_per_group ( ) const
2018-10-10 12:53:07 +03:00
{
2018-12-03 02:20:00 +03:00
return EXT2_INODES_PER_GROUP ( & super_block ( ) ) ;
2018-10-10 12:53:07 +03:00
}
2018-12-03 02:20:00 +03:00
unsigned Ext2FS : : inode_size ( ) const
2018-10-10 12:53:07 +03:00
{
2018-12-03 02:20:00 +03:00
return EXT2_INODE_SIZE ( & super_block ( ) ) ;
2018-10-10 12:53:07 +03:00
}
2018-12-03 02:20:00 +03:00
unsigned Ext2FS : : blocks_per_group ( ) const
2018-10-10 12:53:07 +03:00
{
2018-12-03 02:20:00 +03:00
return EXT2_BLOCKS_PER_GROUP ( & super_block ( ) ) ;
2018-10-10 12:53:07 +03:00
}
2021-02-12 11:18:47 +03:00
bool Ext2FS : : write_ext2_inode ( InodeIndex inode , const ext2_inode & e2inode )
2018-10-10 12:53:07 +03:00
{
2021-02-12 13:59:27 +03:00
BlockIndex block_index ;
2018-10-10 12:53:07 +03:00
unsigned offset ;
2020-05-18 21:55:08 +03:00
if ( ! find_block_containing_inode ( inode , block_index , offset ) )
2018-10-10 12:53:07 +03:00
return false ;
2020-09-12 06:11:07 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( const_cast < u8 * > ( ( const u8 * ) & e2inode ) ) ;
return write_block ( block_index , buffer , inode_size ( ) , offset ) > = 0 ;
2018-10-10 12:53:07 +03:00
}
2021-02-26 10:35:40 +03:00
auto Ext2FS : : allocate_blocks ( GroupIndex preferred_group_index , size_t count ) - > KResultOr < Vector < BlockIndex > >
2019-09-22 19:34:52 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: allocate_blocks(preferred group: {}, count {}) " , preferred_group_index , count ) ;
2019-09-22 19:34:52 +03:00
if ( count = = 0 )
2021-02-26 10:35:40 +03:00
return Vector < BlockIndex > { } ;
2019-09-22 19:34:52 +03:00
Vector < BlockIndex > blocks ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: allocate_blocks: " ) ;
2019-09-22 19:34:52 +03:00
blocks . ensure_capacity ( count ) ;
2020-01-26 11:48:24 +03:00
2021-02-12 15:33:58 +03:00
auto group_index = preferred_group_index ;
2020-01-26 11:48:24 +03:00
if ( ! group_descriptor ( preferred_group_index ) . bg_free_blocks_count ) {
group_index = 1 ;
}
while ( blocks . size ( ) < count ) {
2020-03-12 00:06:17 +03:00
bool found_a_group = false ;
2020-01-26 11:48:24 +03:00
if ( group_descriptor ( group_index ) . bg_free_blocks_count ) {
found_a_group = true ;
} else {
if ( group_index = = preferred_group_index )
group_index = 1 ;
2021-02-12 15:33:58 +03:00
for ( ; group_index < = m_block_group_count ; group_index = GroupIndex { group_index . value ( ) + 1 } ) {
2020-01-26 11:48:24 +03:00
if ( group_descriptor ( group_index ) . bg_free_blocks_count ) {
found_a_group = true ;
break ;
}
}
}
2020-03-12 00:06:17 +03:00
2021-02-23 22:42:32 +03:00
VERIFY ( found_a_group ) ;
2020-01-26 11:48:24 +03:00
auto & bgd = group_descriptor ( group_index ) ;
2021-02-26 10:35:40 +03:00
auto cached_bitmap_or_error = get_bitmap_block ( bgd . bg_block_bitmap ) ;
if ( cached_bitmap_or_error . is_error ( ) )
return cached_bitmap_or_error . error ( ) ;
auto & cached_bitmap = * cached_bitmap_or_error . value ( ) ;
2020-01-26 11:48:24 +03:00
int blocks_in_group = min ( blocks_per_group ( ) , super_block ( ) . s_blocks_count ) ;
2021-03-04 12:58:40 +03:00
auto block_bitmap = cached_bitmap . bitmap ( blocks_in_group ) ;
2020-01-26 11:48:24 +03:00
2021-02-12 15:33:58 +03:00
BlockIndex first_block_in_group = ( group_index . value ( ) - 1 ) * blocks_per_group ( ) + first_block_index ( ) . value ( ) ;
2020-02-24 11:55:46 +03:00
size_t free_region_size = 0 ;
auto first_unset_bit_index = block_bitmap . find_longest_range_of_unset_bits ( count - blocks . size ( ) , free_region_size ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( first_unset_bit_index . has_value ( ) ) ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: allocating free region of size: {} [{}] " , free_region_size , group_index ) ;
2020-02-24 11:55:46 +03:00
for ( size_t i = 0 ; i < free_region_size ; + + i ) {
2021-02-12 13:59:27 +03:00
BlockIndex block_index = ( first_unset_bit_index . value ( ) + i ) + first_block_in_group . value ( ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = set_block_allocation_state ( block_index , true ) ; result . is_error ( ) ) {
2021-02-26 01:07:24 +03:00
dbgln ( " Ext2FS: Failed to allocate block {} in allocate_blocks() " , block_index ) ;
2021-02-26 10:35:40 +03:00
return result ;
2021-02-26 01:07:24 +03:00
}
2020-01-26 11:48:24 +03:00
blocks . unchecked_append ( block_index ) ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " allocated > {} " , block_index ) ;
2020-01-26 11:48:24 +03:00
}
2018-10-16 01:35:03 +03:00
}
2021-02-23 22:42:32 +03:00
VERIFY ( blocks . size ( ) = = count ) ;
2018-10-16 01:35:03 +03:00
return blocks ;
}
2021-02-26 14:03:14 +03:00
KResultOr < InodeIndex > Ext2FS : : allocate_inode ( GroupIndex preferred_group )
2018-10-10 12:53:07 +03:00
{
2021-02-26 14:03:14 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: allocate_inode(preferred_group: {}) " , preferred_group ) ;
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2018-10-10 12:53:07 +03:00
2019-11-17 20:59:14 +03:00
// FIXME: We shouldn't refuse to allocate an inode if there is no group that can house the whole thing.
// In those cases we should just spread it across multiple groups.
2021-02-12 15:33:58 +03:00
auto is_suitable_group = [ this ] ( auto group_index ) {
2019-05-25 18:23:17 +03:00
auto & bgd = group_descriptor ( group_index ) ;
2021-02-02 18:24:26 +03:00
return bgd . bg_free_inodes_count & & bgd . bg_free_blocks_count > = 1 ;
2018-10-10 12:53:07 +03:00
} ;
2021-02-26 14:03:14 +03:00
GroupIndex group_index ;
2021-02-12 15:33:58 +03:00
if ( preferred_group . value ( ) & & is_suitable_group ( preferred_group ) ) {
2019-04-23 16:07:07 +03:00
group_index = preferred_group ;
2018-10-10 12:53:07 +03:00
} else {
2019-01-31 19:31:23 +03:00
for ( unsigned i = 1 ; i < = m_block_group_count ; + + i ) {
2021-02-26 14:03:14 +03:00
if ( is_suitable_group ( i ) ) {
2019-04-23 16:07:07 +03:00
group_index = i ;
2021-02-26 14:03:14 +03:00
break ;
}
2018-10-10 12:53:07 +03:00
}
}
2019-04-23 16:07:07 +03:00
if ( ! group_index ) {
2021-02-26 14:03:14 +03:00
dmesgln ( " Ext2FS: allocate_inode: no suitable group found for new inode " ) ;
2021-02-26 10:35:40 +03:00
return ENOSPC ;
2018-10-10 12:53:07 +03:00
}
2021-02-26 14:03:14 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: allocate_inode: found suitable group [{}] for new inode :^) " , group_index ) ;
2018-10-10 12:53:07 +03:00
2019-04-23 16:07:07 +03:00
auto & bgd = group_descriptor ( group_index ) ;
unsigned inodes_in_group = min ( inodes_per_group ( ) , super_block ( ) . s_inodes_count ) ;
2021-02-12 15:33:58 +03:00
InodeIndex first_inode_in_group = ( group_index . value ( ) - 1 ) * inodes_per_group ( ) + 1 ;
2019-04-23 16:07:07 +03:00
2021-02-26 10:35:40 +03:00
auto cached_bitmap_or_error = get_bitmap_block ( bgd . bg_inode_bitmap ) ;
if ( cached_bitmap_or_error . is_error ( ) )
return cached_bitmap_or_error . error ( ) ;
auto & cached_bitmap = * cached_bitmap_or_error . value ( ) ;
2021-02-26 14:03:14 +03:00
auto inode_bitmap = cached_bitmap . bitmap ( inodes_in_group ) ;
2020-02-24 11:55:46 +03:00
for ( size_t i = 0 ; i < inode_bitmap . size ( ) ; + + i ) {
2019-04-23 16:07:07 +03:00
if ( inode_bitmap . get ( i ) )
continue ;
2021-02-26 14:03:14 +03:00
inode_bitmap . set ( i , true ) ;
2018-10-10 12:53:07 +03:00
2021-02-26 14:03:14 +03:00
auto inode_index = InodeIndex ( first_inode_in_group . value ( ) + i ) ;
2018-10-10 12:53:07 +03:00
2021-02-26 14:03:14 +03:00
cached_bitmap . dirty = true ;
m_super_block . s_free_inodes_count - - ;
m_super_block_dirty = true ;
const_cast < ext2_group_desc & > ( bgd ) . bg_free_inodes_count - - ;
m_block_group_descriptors_dirty = true ;
2018-10-10 12:53:07 +03:00
2021-02-26 14:03:14 +03:00
// In case the inode cache had this cached as "non-existent", uncache that info.
m_inode_cache . remove ( inode_index . value ( ) ) ;
return inode_index ;
}
dmesgln ( " Ext2FS: allocate_inode found no available inode, despite bgd claiming there are inodes :( " ) ;
return EIO ;
2018-10-10 12:53:07 +03:00
}
2019-02-16 01:24:01 +03:00
Ext2FS : : GroupIndex Ext2FS : : group_index_from_block_index ( BlockIndex block_index ) const
{
if ( ! block_index )
return 0 ;
2021-02-12 13:59:27 +03:00
return ( block_index . value ( ) - 1 ) / blocks_per_group ( ) + 1 ;
2019-02-16 01:24:01 +03:00
}
2021-02-12 15:33:58 +03:00
auto Ext2FS : : group_index_from_inode ( InodeIndex inode ) const - > GroupIndex
2018-10-10 12:53:07 +03:00
{
if ( ! inode )
return 0 ;
2021-02-12 11:18:47 +03:00
return ( inode . value ( ) - 1 ) / inodes_per_group ( ) + 1 ;
2018-10-10 12:53:07 +03:00
}
2021-02-26 10:35:40 +03:00
KResultOr < bool > Ext2FS : : get_inode_allocation_state ( InodeIndex index ) const
2019-01-01 05:35:33 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-01-01 05:35:33 +03:00
if ( index = = 0 )
2021-02-26 10:35:40 +03:00
return EINVAL ;
2021-02-12 15:33:58 +03:00
auto group_index = group_index_from_inode ( index ) ;
2019-02-11 14:46:56 +03:00
auto & bgd = group_descriptor ( group_index ) ;
2021-02-12 15:33:58 +03:00
unsigned index_in_group = index . value ( ) - ( ( group_index . value ( ) - 1 ) * inodes_per_group ( ) ) ;
2019-04-23 17:21:07 +03:00
unsigned bit_index = ( index_in_group - 1 ) % inodes_per_group ( ) ;
2019-11-02 14:34:18 +03:00
2021-02-26 10:35:40 +03:00
auto cached_bitmap_or_error = const_cast < Ext2FS & > ( * this ) . get_bitmap_block ( bgd . bg_inode_bitmap ) ;
if ( cached_bitmap_or_error . is_error ( ) )
return cached_bitmap_or_error . error ( ) ;
return cached_bitmap_or_error . value ( ) - > bitmap ( inodes_per_group ( ) ) . get ( bit_index ) ;
2019-01-01 05:35:33 +03:00
}
2021-02-26 01:07:24 +03:00
KResult Ext2FS : : update_bitmap_block ( BlockIndex bitmap_block , size_t bit_index , bool new_state , u32 & super_block_counter , u16 & group_descriptor_counter )
2018-10-10 12:53:07 +03:00
{
2021-02-26 10:35:40 +03:00
auto cached_bitmap_or_error = get_bitmap_block ( bitmap_block ) ;
if ( cached_bitmap_or_error . is_error ( ) )
return cached_bitmap_or_error . error ( ) ;
auto & cached_bitmap = * cached_bitmap_or_error . value ( ) ;
2021-02-26 01:07:24 +03:00
bool current_state = cached_bitmap . bitmap ( blocks_per_group ( ) ) . get ( bit_index ) ;
2021-02-26 10:35:40 +03:00
if ( current_state = = new_state ) {
dbgln ( " Ext2FS: Bit {} in bitmap block {} had unexpected state {} " , bit_index , bitmap_block , current_state ) ;
return EIO ;
}
2021-02-26 01:07:24 +03:00
cached_bitmap . bitmap ( blocks_per_group ( ) ) . set ( bit_index , new_state ) ;
cached_bitmap . dirty = true ;
2018-10-10 12:53:07 +03:00
2021-02-26 01:07:24 +03:00
if ( new_state ) {
- - super_block_counter ;
- - group_descriptor_counter ;
} else {
+ + super_block_counter ;
+ + group_descriptor_counter ;
2019-04-23 17:21:07 +03:00
}
2018-10-10 12:53:07 +03:00
2019-11-02 13:36:56 +03:00
m_super_block_dirty = true ;
2021-02-26 01:07:24 +03:00
m_block_group_descriptors_dirty = true ;
return KSuccess ;
}
2018-10-10 12:53:07 +03:00
2021-02-26 01:07:24 +03:00
KResult Ext2FS : : set_inode_allocation_state ( InodeIndex inode_index , bool new_state )
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2021-02-26 01:07:24 +03:00
auto group_index = group_index_from_inode ( inode_index ) ;
unsigned index_in_group = inode_index . value ( ) - ( ( group_index . value ( ) - 1 ) * inodes_per_group ( ) ) ;
unsigned bit_index = ( index_in_group - 1 ) % inodes_per_group ( ) ;
2018-10-10 12:53:07 +03:00
2021-02-26 01:07:24 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: set_inode_allocation_state: Inode {} -> {} " , inode_index , new_state ) ;
auto & bgd = const_cast < ext2_group_desc & > ( group_descriptor ( group_index ) ) ;
return update_bitmap_block ( bgd . bg_inode_bitmap , bit_index , new_state , m_super_block . s_free_inodes_count , bgd . bg_free_inodes_count ) ;
2018-10-16 01:35:03 +03:00
}
2019-09-22 19:34:52 +03:00
Ext2FS : : BlockIndex Ext2FS : : first_block_index ( ) const
{
return block_size ( ) = = 1024 ? 1 : 0 ;
}
2021-02-26 10:35:40 +03:00
KResultOr < Ext2FS : : CachedBitmap * > Ext2FS : : get_bitmap_block ( BlockIndex bitmap_block_index )
2019-11-02 14:18:43 +03:00
{
for ( auto & cached_bitmap : m_cached_bitmaps ) {
if ( cached_bitmap - > bitmap_block_index = = bitmap_block_index )
2021-02-26 10:35:40 +03:00
return cached_bitmap ;
2019-11-02 14:18:43 +03:00
}
2020-01-26 12:16:05 +03:00
auto block = KBuffer : : create_with_size ( block_size ( ) , Region : : Access : : Read | Region : : Access : : Write , " Ext2FS: Cached bitmap block " ) ;
2020-09-12 06:11:07 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( block . data ( ) ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = read_block ( bitmap_block_index , & buffer , block_size ( ) ) ; result . is_error ( ) ) {
2021-02-26 10:35:40 +03:00
dbgln ( " Ext2FS: Failed to load bitmap block {} " , bitmap_block_index ) ;
return result ;
}
2021-05-13 09:01:55 +03:00
auto new_bitmap = adopt_own_if_nonnull ( new CachedBitmap ( bitmap_block_index , move ( block ) ) ) ;
if ( ! new_bitmap )
return ENOMEM ;
if ( ! m_cached_bitmaps . try_append ( move ( new_bitmap ) ) )
2021-04-30 13:21:05 +03:00
return ENOMEM ;
2021-02-26 10:35:40 +03:00
return m_cached_bitmaps . last ( ) ;
2019-11-02 14:18:43 +03:00
}
2021-02-26 01:07:24 +03:00
KResult Ext2FS : : set_block_allocation_state ( BlockIndex block_index , bool new_state )
2018-10-16 01:35:03 +03:00
{
2021-02-23 22:42:32 +03:00
VERIFY ( block_index ! = 0 ) ;
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-11-02 14:18:43 +03:00
2021-02-12 15:33:58 +03:00
auto group_index = group_index_from_block_index ( block_index ) ;
unsigned index_in_group = ( block_index . value ( ) - first_block_index ( ) . value ( ) ) - ( ( group_index . value ( ) - 1 ) * blocks_per_group ( ) ) ;
2019-04-23 17:38:45 +03:00
unsigned bit_index = index_in_group % blocks_per_group ( ) ;
2021-02-26 01:07:24 +03:00
auto & bgd = const_cast < ext2_group_desc & > ( group_descriptor ( group_index ) ) ;
2019-11-02 14:18:43 +03:00
2021-02-26 01:07:24 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: Block {} state -> {} (in bitmap block {}) " , block_index , new_state , bgd . bg_block_bitmap ) ;
return update_bitmap_block ( bgd . bg_block_bitmap , bit_index , new_state , m_super_block . s_free_blocks_count , bgd . bg_free_blocks_count ) ;
2018-10-10 12:53:07 +03:00
}
2021-02-02 18:37:52 +03:00
KResult Ext2FS : : create_directory ( Ext2FSInode & parent_inode , const String & name , mode_t mode , uid_t uid , gid_t gid )
2018-10-16 01:35:03 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( is_directory ( mode ) ) ;
2018-10-16 01:35:03 +03:00
2021-02-02 18:37:52 +03:00
auto inode_or_error = create_inode ( parent_inode , name , mode , 0 , uid , gid ) ;
2020-02-08 13:58:28 +03:00
if ( inode_or_error . is_error ( ) )
return inode_or_error . error ( ) ;
auto & inode = inode_or_error . value ( ) ;
2018-10-16 01:35:03 +03:00
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: create_directory: created new directory named '{} with inode {} " , name , inode - > index ( ) ) ;
2018-10-16 01:35:03 +03:00
2020-08-18 19:01:42 +03:00
Vector < Ext2FSDirectoryEntry > entries ;
2021-02-12 00:45:50 +03:00
entries . empend ( " . " , inode - > index ( ) , static_cast < u8 > ( EXT2_FT_DIR ) ) ;
entries . empend ( " .. " , parent_inode . index ( ) , static_cast < u8 > ( EXT2_FT_DIR ) ) ;
2018-10-16 01:35:03 +03:00
2021-04-11 01:25:26 +03:00
if ( auto result = static_cast < Ext2FSInode & > ( * inode ) . write_directory ( entries ) ; result . is_error ( ) )
2021-02-02 18:06:03 +03:00
return result ;
2018-10-16 01:35:03 +03:00
2021-04-11 01:25:26 +03:00
if ( auto result = parent_inode . increment_link_count ( ) ; result . is_error ( ) )
2020-02-08 04:34:22 +03:00
return result ;
2018-10-16 01:35:03 +03:00
2018-12-25 01:44:46 +03:00
auto & bgd = const_cast < ext2_group_desc & > ( group_descriptor ( group_index_from_inode ( inode - > identifier ( ) . index ( ) ) ) ) ;
2018-10-16 01:35:03 +03:00
+ + bgd . bg_used_dirs_count ;
2019-11-02 13:36:56 +03:00
m_block_group_descriptors_dirty = true ;
2018-10-16 01:35:03 +03:00
2020-02-08 04:34:22 +03:00
return KSuccess ;
2018-10-16 01:35:03 +03:00
}
2021-02-02 18:37:52 +03:00
KResultOr < NonnullRefPtr < Inode > > Ext2FS : : create_inode ( Ext2FSInode & parent_inode , const String & name , mode_t mode , dev_t dev , uid_t uid , gid_t gid )
2018-10-10 12:53:07 +03:00
{
2020-09-28 22:50:07 +03:00
if ( name . length ( ) > EXT2_NAME_LEN )
2021-01-21 01:11:17 +03:00
return ENAMETOOLONG ;
2020-09-28 22:50:07 +03:00
2021-02-26 14:03:14 +03:00
if ( parent_inode . m_raw_inode . i_links_count = = 0 )
return ENOENT ;
2018-10-10 12:53:07 +03:00
2021-02-21 13:16:16 +03:00
ext2_inode e2inode { } ;
2021-02-28 04:18:48 +03:00
auto now = kgettimeofday ( ) . to_truncated_seconds ( ) ;
2019-01-31 06:17:16 +03:00
e2inode . i_mode = mode ;
2020-01-03 22:13:21 +03:00
e2inode . i_uid = uid ;
e2inode . i_gid = gid ;
2021-02-02 18:24:26 +03:00
e2inode . i_size = 0 ;
2021-02-28 04:18:48 +03:00
e2inode . i_atime = now ;
e2inode . i_ctime = now ;
e2inode . i_mtime = now ;
2019-01-31 06:17:16 +03:00
e2inode . i_dtime = 0 ;
2021-02-26 14:03:14 +03:00
e2inode . i_flags = 0 ;
2020-08-18 22:53:58 +03:00
// For directories, add +1 link count for the "." entry in self.
e2inode . i_links_count = is_directory ( mode ) ;
2019-01-31 06:17:16 +03:00
2019-05-03 23:59:58 +03:00
if ( is_character_device ( mode ) )
e2inode . i_block [ 0 ] = dev ;
else if ( is_block_device ( mode ) )
e2inode . i_block [ 1 ] = dev ;
2021-02-26 14:03:14 +03:00
auto inode_id = allocate_inode ( ) ;
if ( inode_id . is_error ( ) )
return inode_id . error ( ) ;
2021-02-12 01:05:16 +03:00
2021-02-26 14:03:14 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: writing initial metadata for inode {} " , inode_id . value ( ) ) ;
auto success = write_ext2_inode ( inode_id . value ( ) , e2inode ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( success ) ;
2018-10-10 12:53:07 +03:00
2021-02-26 14:03:14 +03:00
auto new_inode = get_inode ( { fsid ( ) , inode_id . value ( ) } ) ;
VERIFY ( new_inode ) ;
2020-06-24 23:35:56 +03:00
2021-02-26 14:03:14 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: Adding inode '{}' (mode {:o}) to parent directory {} " , name , mode , parent_inode . index ( ) ) ;
2021-04-11 01:25:26 +03:00
if ( auto result = parent_inode . add_child ( * new_inode , name , mode ) ; result . is_error ( ) )
2021-01-21 01:11:17 +03:00
return result ;
2021-02-26 14:03:14 +03:00
return new_inode . release_nonnull ( ) ;
2018-10-10 12:53:07 +03:00
}
2021-01-01 06:24:28 +03:00
bool Ext2FSInode : : populate_lookup_cache ( ) const
2018-11-15 18:34:36 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-02-20 15:09:59 +03:00
if ( ! m_lookup_cache . is_empty ( ) )
2021-01-01 06:24:28 +03:00
return true ;
2021-02-12 11:18:47 +03:00
HashMap < String , InodeIndex > children ;
2018-11-15 19:04:55 +03:00
2020-08-05 12:13:30 +03:00
KResult result = traverse_as_directory ( [ & children ] ( auto & entry ) {
2020-08-18 13:41:27 +03:00
children . set ( entry . name , entry . inode . index ( ) ) ;
2018-11-15 19:04:55 +03:00
return true ;
} ) ;
2021-01-01 06:24:28 +03:00
if ( ! result . is_success ( ) )
return false ;
2020-08-05 12:13:30 +03:00
2018-12-21 04:10:45 +03:00
if ( ! m_lookup_cache . is_empty ( ) )
2021-01-01 06:24:28 +03:00
return false ;
2018-11-15 19:04:55 +03:00
m_lookup_cache = move ( children ) ;
2021-01-01 06:24:28 +03:00
return true ;
2018-11-15 19:04:55 +03:00
}
2018-11-15 18:34:36 +03:00
2020-02-01 11:23:46 +03:00
RefPtr < Inode > Ext2FSInode : : lookup ( StringView name )
2018-11-15 19:04:55 +03:00
{
2021-02-23 22:42:32 +03:00
VERIFY ( is_directory ( ) ) ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]:lookup(): Looking up '{}' " , identifier ( ) , name ) ;
2021-01-01 06:24:28 +03:00
if ( ! populate_lookup_cache ( ) )
return { } ;
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-08-24 23:39:12 +03:00
auto it = m_lookup_cache . find ( name . hash ( ) , [ & ] ( auto & entry ) { return entry . key = = name ; } ) ;
2018-11-15 19:04:55 +03:00
if ( it ! = m_lookup_cache . end ( ) )
2020-02-01 11:23:46 +03:00
return fs ( ) . get_inode ( { fsid ( ) , ( * it ) . value } ) ;
2021-03-13 16:59:00 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]:lookup(): '{}' not found " , identifier ( ) , name ) ;
2019-06-07 12:43:58 +03:00
return { } ;
2018-11-15 19:04:55 +03:00
}
2018-11-15 18:34:36 +03:00
2019-06-21 16:29:31 +03:00
void Ext2FSInode : : one_ref_left ( )
2019-01-01 04:38:09 +03:00
{
2019-01-01 04:52:21 +03:00
// FIXME: I would like to not live forever, but uncached Ext2FS is fucking painful right now.
2019-01-01 04:38:09 +03:00
}
2019-01-01 05:16:36 +03:00
2021-04-30 16:51:06 +03:00
KResult Ext2FSInode : : set_atime ( time_t t )
2019-01-01 05:16:36 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-01-01 05:16:36 +03:00
if ( fs ( ) . is_readonly ( ) )
2021-04-30 16:51:06 +03:00
return EROFS ;
2019-01-01 05:16:36 +03:00
m_raw_inode . i_atime = t ;
set_metadata_dirty ( true ) ;
2021-04-30 16:51:06 +03:00
return KSuccess ;
2019-01-01 05:16:36 +03:00
}
2021-04-30 16:51:06 +03:00
KResult Ext2FSInode : : set_ctime ( time_t t )
2019-01-01 05:16:36 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-01-01 05:16:36 +03:00
if ( fs ( ) . is_readonly ( ) )
2021-04-30 16:51:06 +03:00
return EROFS ;
2019-01-01 05:16:36 +03:00
m_raw_inode . i_ctime = t ;
set_metadata_dirty ( true ) ;
2021-04-30 16:51:06 +03:00
return KSuccess ;
2019-01-01 05:16:36 +03:00
}
2021-04-30 16:51:06 +03:00
KResult Ext2FSInode : : set_mtime ( time_t t )
2019-01-01 05:16:36 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-01-01 05:16:36 +03:00
if ( fs ( ) . is_readonly ( ) )
2021-04-30 16:51:06 +03:00
return EROFS ;
2019-01-01 05:16:36 +03:00
m_raw_inode . i_mtime = t ;
set_metadata_dirty ( true ) ;
2021-04-30 16:51:06 +03:00
return KSuccess ;
2019-01-01 05:16:36 +03:00
}
2020-02-08 04:26:33 +03:00
KResult Ext2FSInode : : increment_link_count ( )
2019-01-01 05:16:36 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-01-01 05:16:36 +03:00
if ( fs ( ) . is_readonly ( ) )
2021-01-21 01:11:17 +03:00
return EROFS ;
2021-05-19 17:35:09 +03:00
constexpr size_t max_link_count = 65535 ;
2020-02-08 04:26:33 +03:00
if ( m_raw_inode . i_links_count = = max_link_count )
2021-01-21 01:11:17 +03:00
return EMLINK ;
2019-01-01 05:16:36 +03:00
+ + m_raw_inode . i_links_count ;
set_metadata_dirty ( true ) ;
2020-02-08 04:26:33 +03:00
return KSuccess ;
2019-01-01 05:16:36 +03:00
}
2020-02-08 04:26:33 +03:00
KResult Ext2FSInode : : decrement_link_count ( )
2019-01-01 05:16:36 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-01-01 05:16:36 +03:00
if ( fs ( ) . is_readonly ( ) )
2021-01-21 01:11:17 +03:00
return EROFS ;
2021-02-23 22:42:32 +03:00
VERIFY ( m_raw_inode . i_links_count ) ;
2021-05-12 22:17:51 +03:00
2019-01-01 05:16:36 +03:00
- - m_raw_inode . i_links_count ;
2021-05-12 22:17:51 +03:00
set_metadata_dirty ( true ) ;
if ( m_raw_inode . i_links_count = = 0 )
did_delete_self ( ) ;
2020-01-15 23:58:03 +03:00
if ( ref_count ( ) = = 1 & & m_raw_inode . i_links_count = = 0 )
2019-01-22 09:03:44 +03:00
fs ( ) . uncache_inode ( index ( ) ) ;
2021-05-12 22:17:51 +03:00
2020-02-08 04:26:33 +03:00
return KSuccess ;
2019-01-01 05:16:36 +03:00
}
2019-01-22 09:03:44 +03:00
void Ext2FS : : uncache_inode ( InodeIndex index )
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-02-21 15:26:40 +03:00
m_inode_cache . remove ( index ) ;
2019-01-22 09:03:44 +03:00
}
2019-01-28 06:16:01 +03:00
2020-08-05 11:00:18 +03:00
KResultOr < size_t > Ext2FSInode : : directory_entry_count ( ) const
2019-01-28 06:16:01 +03:00
{
2021-02-23 22:42:32 +03:00
VERIFY ( is_directory ( ) ) ;
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-02-27 19:07:34 +03:00
populate_lookup_cache ( ) ;
2019-01-28 06:16:01 +03:00
return m_lookup_cache . size ( ) ;
}
2019-01-29 06:55:08 +03:00
2019-02-25 22:47:56 +03:00
KResult Ext2FSInode : : chmod ( mode_t mode )
2019-01-29 06:55:08 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-01-29 06:55:08 +03:00
if ( m_raw_inode . i_mode = = mode )
2019-02-25 22:47:56 +03:00
return KSuccess ;
2019-01-29 06:55:08 +03:00
m_raw_inode . i_mode = mode ;
set_metadata_dirty ( true ) ;
2019-02-25 22:47:56 +03:00
return KSuccess ;
2019-01-29 06:55:08 +03:00
}
2019-02-21 16:48:00 +03:00
2019-02-27 14:32:53 +03:00
KResult Ext2FSInode : : chown ( uid_t uid , gid_t gid )
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-02-27 14:32:53 +03:00
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 ;
}
2020-02-08 14:07:04 +03:00
KResult Ext2FSInode : : truncate ( u64 size )
2019-03-27 18:42:30 +03:00
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2020-02-08 14:07:04 +03:00
if ( static_cast < u64 > ( m_raw_inode . i_size ) = = size )
2019-03-27 18:42:30 +03:00
return KSuccess ;
2021-04-11 01:25:26 +03:00
if ( auto result = resize ( size ) ; result . is_error ( ) )
2019-11-02 14:53:31 +03:00
return result ;
2019-03-27 18:42:30 +03:00
set_metadata_dirty ( true ) ;
return KSuccess ;
}
2021-01-30 23:12:49 +03:00
KResultOr < int > Ext2FSInode : : get_block_address ( int index )
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2021-01-30 23:12:49 +03:00
if ( m_block_list . is_empty ( ) )
2021-02-26 20:14:02 +03:00
m_block_list = compute_block_list ( ) ;
2021-01-30 23:12:49 +03:00
if ( index < 0 | | ( size_t ) index > = m_block_list . size ( ) )
return 0 ;
2021-02-12 13:59:27 +03:00
return m_block_list [ index ] . value ( ) ;
2021-01-30 23:12:49 +03:00
}
2019-02-21 16:48:00 +03:00
unsigned Ext2FS : : total_block_count ( ) const
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-02-21 16:48:00 +03:00
return super_block ( ) . s_blocks_count ;
}
unsigned Ext2FS : : free_block_count ( ) const
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-02-21 16:48:00 +03:00
return super_block ( ) . s_free_blocks_count ;
}
unsigned Ext2FS : : total_inode_count ( ) const
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-02-21 16:48:00 +03:00
return super_block ( ) . s_inodes_count ;
}
unsigned Ext2FS : : free_inode_count ( ) const
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-02-21 16:48:00 +03:00
return super_block ( ) . s_free_inodes_count ;
}
2019-08-11 16:56:39 +03:00
KResult Ext2FS : : prepare_to_unmount ( ) const
{
2021-04-25 01:27:32 +03:00
Locker locker ( m_lock ) ;
2019-08-17 14:49:37 +03:00
for ( auto & it : m_inode_cache ) {
if ( it . value - > ref_count ( ) > 1 )
2021-01-21 01:11:17 +03:00
return EBUSY ;
2019-08-11 16:56:39 +03:00
}
m_inode_cache . clear ( ) ;
return KSuccess ;
}
2020-02-16 03:27:42 +03:00
}