2020-01-18 09:38:21 +01:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
2021-05-12 19:17:51 +00:00
* Copyright ( c ) 2021 , sin - ack < sin - ack @ protonmail . com >
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2023-02-07 12:43:54 +01:00
# include <AK/MemoryStream.h>
2021-09-12 11:29:28 +00:00
# include <Kernel/API/POSIX/errno.h>
2021-01-25 16:07:10 +01:00
# include <Kernel/Debug.h>
2022-10-24 10:02:37 +03:00
# include <Kernel/FileSystem/Ext2FS/Inode.h>
# include <Kernel/FileSystem/InodeMetadata.h>
2019-06-07 19:29:34 +02:00
# include <Kernel/UnixTypes.h>
2018-10-10 11:53:07 +02:00
2020-02-16 01:27:42 +01:00
namespace Kernel {
2021-06-16 16:44:15 +02:00
static constexpr size_t max_inline_symlink_length = 60 ;
2019-03-02 01:50:34 +01:00
2019-07-03 21:17:35 +02:00
static u8 to_ext2_file_type ( mode_t mode )
2019-05-31 17:41:33 +02: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-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : write_indirect_block ( BlockBasedFileSystem : : BlockIndex block , Span < BlockBasedFileSystem : : BlockIndex > blocks_indices )
2021-03-11 19:22:13 +01:00
{
2022-04-01 20:58:27 +03:00
auto const entries_per_block = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ;
2021-04-29 22:23:52 +02:00
VERIFY ( blocks_indices . size ( ) < = entries_per_block ) ;
2021-03-11 19:22:13 +01:00
2023-02-07 12:43:54 +01:00
auto block_contents = TRY ( ByteBuffer : : create_zeroed ( fs ( ) . block_size ( ) ) ) ;
FixedMemoryStream stream { block_contents . bytes ( ) } ;
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( block_contents . data ( ) ) ;
2021-03-11 19:22:13 +01:00
2021-04-29 22:23:52 +02:00
VERIFY ( blocks_indices . size ( ) < = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ) ;
for ( unsigned i = 0 ; i < blocks_indices . size ( ) ; + + i )
2023-02-07 12:43:54 +01:00
MUST ( stream . write_value < u32 > ( blocks_indices [ i ] . value ( ) ) ) ;
2021-03-11 19:22:13 +01:00
2023-02-07 12:43:54 +01:00
return fs ( ) . write_block ( block , buffer , block_contents . size ( ) ) ;
2021-03-11 19:22:13 +01:00
}
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : grow_doubly_indirect_block ( BlockBasedFileSystem : : BlockIndex block , size_t old_blocks_length , Span < BlockBasedFileSystem : : BlockIndex > blocks_indices , Vector < Ext2FS : : BlockIndex > & new_meta_blocks , unsigned & meta_blocks )
2021-03-11 19:22:13 +01:00
{
2022-04-01 20:58:27 +03:00
auto const entries_per_block = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ;
auto const entries_per_doubly_indirect_block = entries_per_block * entries_per_block ;
auto const old_indirect_blocks_length = ceil_div ( old_blocks_length , entries_per_block ) ;
auto const new_indirect_blocks_length = ceil_div ( blocks_indices . size ( ) , entries_per_block ) ;
2021-04-29 22:23:52 +02:00
VERIFY ( blocks_indices . size ( ) > 0 ) ;
VERIFY ( blocks_indices . size ( ) > old_blocks_length ) ;
VERIFY ( blocks_indices . size ( ) < = entries_per_doubly_indirect_block ) ;
2021-03-11 19:22:13 +01:00
2023-02-07 12:43:54 +01:00
auto block_contents = TRY ( ByteBuffer : : create_zeroed ( fs ( ) . block_size ( ) ) ) ;
2021-03-11 19:22:13 +01:00
auto * block_as_pointers = ( unsigned * ) block_contents . data ( ) ;
2023-02-07 12:43:54 +01:00
FixedMemoryStream stream { block_contents . bytes ( ) } ;
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( block_contents . data ( ) ) ;
2021-03-11 19:22:13 +01:00
if ( old_blocks_length > 0 ) {
2021-09-05 15:30:15 +02:00
TRY ( fs ( ) . read_block ( block , & buffer , fs ( ) . block_size ( ) ) ) ;
2021-03-11 19:22:13 +01:00
}
// Grow the doubly indirect block.
for ( unsigned i = 0 ; i < old_indirect_blocks_length ; i + + )
2023-02-07 12:43:54 +01:00
MUST ( stream . write_value < u32 > ( block_as_pointers [ i ] ) ) ;
2021-03-11 19:22:13 +01: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 14:59:00 +01:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::grow_doubly_indirect_block(): Allocating indirect block {} at index {} " , identifier ( ) , new_block , i ) ;
2023-02-07 12:43:54 +01:00
MUST ( stream . write_value < u32 > ( new_block ) ) ;
2021-03-11 19:22:13 +01:00
meta_blocks + + ;
}
// Write out the indirect blocks.
for ( unsigned i = old_blocks_length / entries_per_block ; i < new_indirect_blocks_length ; i + + ) {
2022-04-01 20:58:27 +03:00
auto const offset_block = i * entries_per_block ;
2021-09-05 15:30:15 +02:00
TRY ( write_indirect_block ( block_as_pointers [ i ] , blocks_indices . slice ( offset_block , min ( blocks_indices . size ( ) - offset_block , entries_per_block ) ) ) ) ;
2021-03-11 19:22:13 +01:00
}
// Write out the doubly indirect block.
2023-02-07 12:43:54 +01:00
return fs ( ) . write_block ( block , buffer , block_contents . size ( ) ) ;
2021-03-11 19:22:13 +01:00
}
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : shrink_doubly_indirect_block ( BlockBasedFileSystem : : BlockIndex block , size_t old_blocks_length , size_t new_blocks_length , unsigned & meta_blocks )
2021-03-11 19:22:13 +01:00
{
2022-04-01 20:58:27 +03:00
auto const entries_per_block = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ;
auto const entries_per_doubly_indirect_block = entries_per_block * entries_per_block ;
auto const old_indirect_blocks_length = ceil_div ( old_blocks_length , entries_per_block ) ;
auto const new_indirect_blocks_length = ceil_div ( new_blocks_length , entries_per_block ) ;
2021-03-11 19:22:13 +01:00
VERIFY ( old_blocks_length > 0 ) ;
VERIFY ( old_blocks_length > = new_blocks_length ) ;
VERIFY ( new_blocks_length < = entries_per_doubly_indirect_block ) ;
2022-01-20 17:47:39 +00:00
auto block_contents = TRY ( ByteBuffer : : create_uninitialized ( fs ( ) . block_size ( ) ) ) ;
2021-03-11 19:22:13 +01:00
auto * block_as_pointers = ( unsigned * ) block_contents . data ( ) ;
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( reinterpret_cast < u8 * > ( block_as_pointers ) ) ;
2021-09-05 15:30:15 +02:00
TRY ( fs ( ) . read_block ( block , & buffer , fs ( ) . block_size ( ) ) ) ;
2021-03-11 19:22:13 +01:00
// Free the unused indirect blocks.
for ( unsigned i = new_indirect_blocks_length ; i < old_indirect_blocks_length ; i + + ) {
2021-03-13 14:59:00 +01:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::shrink_doubly_indirect_block(): Freeing indirect block {} at index {} " , identifier ( ) , block_as_pointers [ i ] , i ) ;
2021-09-05 15:30:15 +02:00
TRY ( fs ( ) . set_block_allocation_state ( block_as_pointers [ i ] , false ) ) ;
2021-03-11 19:22:13 +01:00
meta_blocks - - ;
}
// Free the doubly indirect block if no longer needed.
if ( new_blocks_length = = 0 ) {
2021-03-13 14:59:00 +01:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::shrink_doubly_indirect_block(): Freeing doubly indirect block {} " , identifier ( ) , block ) ;
2021-09-05 15:30:15 +02:00
TRY ( fs ( ) . set_block_allocation_state ( block , false ) ) ;
2021-03-11 19:22:13 +01:00
meta_blocks - - ;
}
2021-11-08 00:51:39 +01:00
return { } ;
2021-03-11 19:22:13 +01:00
}
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : grow_triply_indirect_block ( BlockBasedFileSystem : : BlockIndex block , size_t old_blocks_length , Span < BlockBasedFileSystem : : BlockIndex > blocks_indices , Vector < Ext2FS : : BlockIndex > & new_meta_blocks , unsigned & meta_blocks )
2021-03-11 19:22:13 +01:00
{
2022-04-01 20:58:27 +03:00
auto const entries_per_block = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ;
auto const entries_per_doubly_indirect_block = entries_per_block * entries_per_block ;
auto const entries_per_triply_indirect_block = entries_per_block * entries_per_block ;
auto const old_doubly_indirect_blocks_length = ceil_div ( old_blocks_length , entries_per_doubly_indirect_block ) ;
auto const new_doubly_indirect_blocks_length = ceil_div ( blocks_indices . size ( ) , entries_per_doubly_indirect_block ) ;
2021-04-29 22:23:52 +02:00
VERIFY ( blocks_indices . size ( ) > 0 ) ;
VERIFY ( blocks_indices . size ( ) > old_blocks_length ) ;
VERIFY ( blocks_indices . size ( ) < = entries_per_triply_indirect_block ) ;
2021-03-11 19:22:13 +01:00
2023-02-07 12:43:54 +01:00
auto block_contents = TRY ( ByteBuffer : : create_zeroed ( fs ( ) . block_size ( ) ) ) ;
2021-03-11 19:22:13 +01:00
auto * block_as_pointers = ( unsigned * ) block_contents . data ( ) ;
2023-02-07 12:43:54 +01:00
FixedMemoryStream stream { block_contents . bytes ( ) } ;
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( block_contents . data ( ) ) ;
2021-03-11 19:22:13 +01:00
if ( old_blocks_length > 0 ) {
2021-09-05 15:30:15 +02:00
TRY ( fs ( ) . read_block ( block , & buffer , fs ( ) . block_size ( ) ) ) ;
2021-03-11 19:22:13 +01:00
}
// Grow the triply indirect block.
for ( unsigned i = 0 ; i < old_doubly_indirect_blocks_length ; i + + )
2023-02-07 12:43:54 +01:00
MUST ( stream . write_value < u32 > ( block_as_pointers [ i ] ) ) ;
2021-03-11 19:22:13 +01: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 14:59:00 +01:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::grow_triply_indirect_block(): Allocating doubly indirect block {} at index {} " , identifier ( ) , new_block , i ) ;
2023-02-07 12:43:54 +01:00
MUST ( stream . write_value < u32 > ( new_block ) ) ;
2021-03-11 19:22:13 +01:00
meta_blocks + + ;
}
// Write out the doubly indirect blocks.
for ( unsigned i = old_blocks_length / entries_per_doubly_indirect_block ; i < new_doubly_indirect_blocks_length ; i + + ) {
2022-04-01 20:58:27 +03:00
auto const processed_blocks = i * entries_per_doubly_indirect_block ;
auto const old_doubly_indirect_blocks_length = min ( old_blocks_length > processed_blocks ? old_blocks_length - processed_blocks : 0 , entries_per_doubly_indirect_block ) ;
auto const new_doubly_indirect_blocks_length = min ( blocks_indices . size ( ) > processed_blocks ? blocks_indices . size ( ) - processed_blocks : 0 , entries_per_doubly_indirect_block ) ;
2021-09-05 15:30:15 +02:00
TRY ( 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 ) ) ;
2021-03-11 19:22:13 +01:00
}
// Write out the triply indirect block.
2023-02-07 12:43:54 +01:00
return fs ( ) . write_block ( block , buffer , block_contents . size ( ) ) ;
2021-03-11 19:22:13 +01:00
}
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : shrink_triply_indirect_block ( BlockBasedFileSystem : : BlockIndex block , size_t old_blocks_length , size_t new_blocks_length , unsigned & meta_blocks )
2021-03-11 19:22:13 +01:00
{
2022-04-01 20:58:27 +03:00
auto const entries_per_block = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ;
auto const entries_per_doubly_indirect_block = entries_per_block * entries_per_block ;
auto const entries_per_triply_indirect_block = entries_per_doubly_indirect_block * entries_per_block ;
auto const old_triply_indirect_blocks_length = ceil_div ( old_blocks_length , entries_per_doubly_indirect_block ) ;
auto const new_triply_indirect_blocks_length = new_blocks_length / entries_per_doubly_indirect_block ;
2021-03-11 19:22:13 +01:00
VERIFY ( old_blocks_length > 0 ) ;
VERIFY ( old_blocks_length > = new_blocks_length ) ;
VERIFY ( new_blocks_length < = entries_per_triply_indirect_block ) ;
2022-01-20 17:47:39 +00:00
auto block_contents = TRY ( ByteBuffer : : create_uninitialized ( fs ( ) . block_size ( ) ) ) ;
2021-03-11 19:22:13 +01:00
auto * block_as_pointers = ( unsigned * ) block_contents . data ( ) ;
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( reinterpret_cast < u8 * > ( block_as_pointers ) ) ;
2021-09-05 15:30:15 +02:00
TRY ( fs ( ) . read_block ( block , & buffer , fs ( ) . block_size ( ) ) ) ;
2021-03-11 19:22:13 +01:00
// Shrink the doubly indirect blocks.
for ( unsigned i = new_triply_indirect_blocks_length ; i < old_triply_indirect_blocks_length ; i + + ) {
2022-04-01 20:58:27 +03:00
auto const processed_blocks = i * entries_per_doubly_indirect_block ;
auto const old_doubly_indirect_blocks_length = min ( old_blocks_length > processed_blocks ? old_blocks_length - processed_blocks : 0 , entries_per_doubly_indirect_block ) ;
auto const 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 14:59:00 +01:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::shrink_triply_indirect_block(): Shrinking doubly indirect block {} at index {} " , identifier ( ) , block_as_pointers [ i ] , i ) ;
2021-09-05 15:30:15 +02:00
TRY ( shrink_doubly_indirect_block ( block_as_pointers [ i ] , old_doubly_indirect_blocks_length , new_doubly_indirect_blocks_length , meta_blocks ) ) ;
2021-03-11 19:22:13 +01:00
}
// Free the triply indirect block if no longer needed.
if ( new_blocks_length = = 0 ) {
2021-03-13 14:59:00 +01:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::shrink_triply_indirect_block(): Freeing triply indirect block {} " , identifier ( ) , block ) ;
2021-09-05 15:30:15 +02:00
TRY ( fs ( ) . set_block_allocation_state ( block , false ) ) ;
2021-03-11 19:22:13 +01:00
meta_blocks - - ;
}
2021-11-08 00:51:39 +01:00
return { } ;
2021-03-11 19:22:13 +01:00
}
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : flush_block_list ( )
2019-01-23 02:45:25 +01:00
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2019-02-20 13:09:59 +01:00
2021-02-26 17:57:38 +01: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 18:24:40 +01:00
set_metadata_dirty ( true ) ;
2021-11-08 00:51:39 +01:00
return { } ;
2020-11-23 12:30:12 +01:00
}
2019-03-27 14:24:37 +01:00
// NOTE: There is a mismatch between i_blocks and blocks.size() since i_blocks includes meta blocks and blocks.size() does not.
2022-04-01 20:58:27 +03:00
auto const old_block_count = ceil_div ( size ( ) , static_cast < u64 > ( fs ( ) . block_size ( ) ) ) ;
2019-01-23 02:45:25 +01:00
2021-02-26 17:57:38 +01:00
auto old_shape = fs ( ) . compute_block_list_shape ( old_block_count ) ;
2022-04-01 20:58:27 +03:00
auto const new_shape = fs ( ) . compute_block_list_shape ( m_block_list . size ( ) ) ;
2019-01-23 02:45:25 +01:00
2021-02-26 17:57:38 +01:00
Vector < Ext2FS : : BlockIndex > new_meta_blocks ;
2019-01-23 02:45:25 +01:00
if ( new_shape . meta_blocks > old_shape . meta_blocks ) {
2021-09-05 15:30:15 +02:00
new_meta_blocks = TRY ( fs ( ) . allocate_blocks ( fs ( ) . group_index_from_inode ( index ( ) ) , new_shape . meta_blocks - old_shape . meta_blocks ) ) ;
2019-01-23 02:45:25 +01:00
}
2021-02-26 17:57:38 +01:00
m_raw_inode . i_blocks = ( m_block_list . size ( ) + new_shape . meta_blocks ) * ( fs ( ) . block_size ( ) / 512 ) ;
2021-03-13 14:59:00 +01: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 14:24:37 +01:00
2019-01-23 02:45:25 +01:00
unsigned output_block_index = 0 ;
2021-02-26 17:57:38 +01:00
unsigned remaining_blocks = m_block_list . size ( ) ;
2021-03-11 19:22:13 +01:00
// Deal with direct blocks.
bool inode_dirty = false ;
VERIFY ( new_shape . direct_blocks < = EXT2_NDIR_BLOCKS ) ;
2019-01-23 02:45:25 +01:00
for ( unsigned i = 0 ; i < new_shape . direct_blocks ; + + i ) {
2021-07-11 00:34:36 +02:00
if ( BlockBasedFileSystem : : BlockIndex ( m_raw_inode . i_block [ i ] ) ! = m_block_list [ output_block_index ] )
2019-03-27 14:24:37 +01:00
inode_dirty = true ;
2021-02-26 17:57:38 +01:00
m_raw_inode . i_block [ i ] = m_block_list [ output_block_index ] . value ( ) ;
2019-03-27 14:24:37 +01:00
+ + output_block_index ;
2019-01-23 02:45:25 +01:00
- - remaining_blocks ;
}
2021-05-07 18:43:59 +02: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 14:24:37 +01:00
if ( inode_dirty ) {
2021-02-11 23:05:16 +01:00
if constexpr ( EXT2_DEBUG ) {
2021-03-13 14:59:00 +01: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 17:57:38 +01: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-11 23:05:16 +01:00
}
2021-02-26 18:24:40 +01:00
set_metadata_dirty ( true ) ;
2019-03-27 14:24:37 +01:00
}
2019-01-23 02:45:25 +01:00
2021-03-11 19:22:13 +01: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 14:59:00 +01:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::flush_block_list(): Allocating indirect block: {} " , identifier ( ) , new_block ) ;
2021-03-11 19:22:13 +01:00
m_raw_inode . i_block [ EXT2_IND_BLOCK ] = new_block ;
set_metadata_dirty ( true ) ;
old_shape . meta_blocks + + ;
}
2019-09-28 14:31:40 +10:00
2021-09-05 15:30:15 +02:00
TRY ( write_indirect_block ( m_raw_inode . i_block [ EXT2_IND_BLOCK ] , m_block_list . span ( ) . slice ( output_block_index , new_shape . indirect_blocks ) ) ) ;
2021-03-11 19:22:13 +01:00
} else if ( ( new_shape . indirect_blocks = = 0 ) & & ( old_shape . indirect_blocks ! = 0 ) ) {
2021-03-13 14:59:00 +01:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::flush_block_list(): Freeing indirect block: {} " , identifier ( ) , m_raw_inode . i_block [ EXT2_IND_BLOCK ] ) ;
2021-09-05 15:30:15 +02:00
TRY ( fs ( ) . set_block_allocation_state ( m_raw_inode . i_block [ EXT2_IND_BLOCK ] , false ) ) ;
2021-03-11 19:22:13 +01:00
old_shape . meta_blocks - - ;
2021-05-07 18:43:59 +02:00
m_raw_inode . i_block [ EXT2_IND_BLOCK ] = 0 ;
2019-09-28 14:31:40 +10:00
}
}
2021-03-11 19:22:13 +01: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 14:59:00 +01:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::flush_block_list(): Allocating doubly indirect block: {} " , identifier ( ) , new_block ) ;
2021-03-11 19:22:13 +01:00
m_raw_inode . i_block [ EXT2_DIND_BLOCK ] = new_block ;
set_metadata_dirty ( true ) ;
old_shape . meta_blocks + + ;
}
2021-09-05 15:30:15 +02:00
TRY ( 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 ) ) ;
2020-05-18 21:55:08 +03:00
} else {
2021-09-05 15:30:15 +02:00
TRY ( 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 ) ) ;
2021-05-07 18:43:59 +02:00
if ( new_shape . doubly_indirect_blocks = = 0 )
m_raw_inode . i_block [ EXT2_DIND_BLOCK ] = 0 ;
2019-09-28 14:31:40 +10:00
}
2021-03-11 19:22:13 +01:00
}
2019-09-28 14:31:40 +10:00
2021-03-11 19:22:13 +01: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 14:59:00 +01:00
dbgln_if ( EXT2_BLOCKLIST_DEBUG , " Ext2FSInode[{}]::flush_block_list(): Allocating triply indirect block: {} " , identifier ( ) , new_block ) ;
2021-03-11 19:22:13 +01:00
m_raw_inode . i_block [ EXT2_TIND_BLOCK ] = new_block ;
set_metadata_dirty ( true ) ;
old_shape . meta_blocks + + ;
2019-09-28 14:31:40 +10:00
}
2021-09-05 15:30:15 +02:00
TRY ( 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 ) ) ;
2021-03-11 19:22:13 +01:00
} else {
2021-09-05 15:30:15 +02:00
TRY ( 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 ) ) ;
2021-05-07 18:43:59 +02:00
if ( new_shape . triply_indirect_blocks = = 0 )
m_raw_inode . i_block [ EXT2_TIND_BLOCK ] = 0 ;
2019-09-28 14:31:40 +10:00
}
}
2021-03-11 19:22:13 +01:00
remaining_blocks - = new_shape . triply_indirect_blocks ;
output_block_index + = new_shape . triply_indirect_blocks ;
2021-03-13 14:59:00 +01: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 19:22:13 +01:00
VERIFY ( new_meta_blocks . size ( ) = = 0 ) ;
VERIFY ( old_shape . meta_blocks = = new_shape . meta_blocks ) ;
2019-01-23 02:45:25 +01:00
if ( ! remaining_blocks )
2021-11-08 00:51:39 +01:00
return { } ;
2019-01-23 02:45:25 +01:00
2021-03-11 19:22:13 +01:00
dbgln ( " we don't know how to write qind ext2fs blocks, they don't exist anyway! " ) ;
2021-02-23 20:42:32 +01:00
VERIFY_NOT_REACHED ( ) ;
2019-01-23 02:45:25 +01:00
}
2021-11-10 15:52:46 +01:00
ErrorOr < Vector < Ext2FS : : BlockIndex > > Ext2FSInode : : compute_block_list ( ) const
2020-02-21 17:48:50 +01:00
{
2021-02-26 18:14:02 +01:00
return compute_block_list_impl ( false ) ;
}
2021-11-10 15:52:46 +01:00
ErrorOr < Vector < Ext2FS : : BlockIndex > > Ext2FSInode : : compute_block_list_with_meta_blocks ( ) const
2021-02-26 18:14:02 +01:00
{
return compute_block_list_impl ( true ) ;
}
2021-11-10 15:52:46 +01:00
ErrorOr < Vector < Ext2FS : : BlockIndex > > Ext2FSInode : : compute_block_list_impl ( bool include_block_list_blocks ) const
2021-02-26 18:14:02 +01:00
{
// FIXME: This is really awkwardly factored.. foo_impl_internal :|
2021-11-10 15:52:46 +01:00
auto block_list = TRY ( compute_block_list_impl_internal ( m_raw_inode , include_block_list_blocks ) ) ;
2020-02-21 17:48:50 +01:00
while ( ! block_list . is_empty ( ) & & block_list . last ( ) = = 0 )
block_list . take_last ( ) ;
return block_list ;
}
2021-11-10 15:52:46 +01:00
ErrorOr < Vector < Ext2FS : : BlockIndex > > Ext2FSInode : : compute_block_list_impl_internal ( ext2_inode const & e2inode , bool include_block_list_blocks ) const
2018-10-10 11:53:07 +02:00
{
2021-02-26 18:14:02 +01:00
unsigned entries_per_block = EXT2_ADDR_PER_BLOCK ( & fs ( ) . super_block ( ) ) ;
2018-10-10 11:53:07 +02:00
2021-03-17 18:35:42 +01:00
unsigned block_count = ceil_div ( size ( ) , static_cast < u64 > ( fs ( ) . block_size ( ) ) ) ;
2019-05-25 19:19:43 +02: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.
2022-10-24 10:02:37 +03:00
if ( Kernel : : is_symlink ( e2inode . i_mode ) & & e2inode . i_blocks = = 0 )
2020-04-05 15:48:58 +03:00
block_count = 0 ;
2021-03-13 14:59:00 +01: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 19:19:43 +02:00
2019-05-25 17:23:17 +02:00
unsigned blocks_remaining = block_count ;
2020-11-07 16:45:03 +01:00
if ( include_block_list_blocks ) {
2021-02-26 18:14:02 +01:00
auto shape = fs ( ) . compute_block_list_shape ( block_count ) ;
2020-11-07 16:45:03 +01:00
blocks_remaining + = shape . meta_blocks ;
}
2021-02-26 18:14:02 +01:00
Vector < Ext2FS : : BlockIndex > list ;
2020-02-21 17:48:50 +01:00
2021-11-10 15:52:46 +01:00
auto add_block = [ & ] ( auto bi ) - > ErrorOr < void > {
2020-02-21 17:48:50 +01:00
if ( blocks_remaining ) {
2021-11-10 15:52:46 +01:00
TRY ( list . try_append ( bi ) ) ;
2020-02-21 17:48:50 +01:00
- - blocks_remaining ;
}
2021-11-10 15:52:46 +01:00
return { } ;
2020-02-21 17:48:50 +01:00
} ;
2019-01-22 16:34:24 +01:00
if ( include_block_list_blocks ) {
// This seems like an excessive over-estimate but w/e.
2021-11-10 15:52:46 +01:00
TRY ( list . try_ensure_capacity ( blocks_remaining * 2 ) ) ;
2019-01-22 16:34:24 +01:00
} else {
2021-11-10 15:52:46 +01:00
TRY ( list . try_ensure_capacity ( blocks_remaining ) ) ;
2019-01-22 16:34:24 +01:00
}
2018-10-10 11:53:07 +02:00
2019-01-31 17:31:23 +01:00
unsigned direct_count = min ( block_count , ( unsigned ) EXT2_NDIR_BLOCKS ) ;
for ( unsigned i = 0 ; i < direct_count ; + + i ) {
2019-05-25 19:19:43 +02:00
auto block_index = e2inode . i_block [ i ] ;
2021-11-10 15:52:46 +01:00
TRY ( add_block ( block_index ) ) ;
2018-10-10 11:53:07 +02:00
}
2019-05-25 17:23:17 +02:00
if ( ! blocks_remaining )
2018-10-10 11:53:07 +02:00
return list ;
2020-08-29 14:07:21 +02:00
// Don't need to make copy of add_block, since this capture will only
2021-02-26 18:14:02 +01:00
// be called before compute_block_list_impl_internal finishes.
2021-11-10 15:52:46 +01:00
auto process_block_array = [ & ] ( auto array_block_index , auto & & callback ) - > ErrorOr < void > {
2019-01-22 16:34:24 +01:00
if ( include_block_list_blocks )
2021-11-10 15:52:46 +01:00
TRY ( add_block ( array_block_index ) ) ;
2020-11-24 18:38:41 +01:00
auto count = min ( blocks_remaining , entries_per_block ) ;
2021-02-05 20:03:49 +01:00
if ( ! count )
2021-11-10 15:52:46 +01:00
return { } ;
2021-03-10 16:24:01 +01:00
size_t read_size = count * sizeof ( u32 ) ;
2022-01-20 17:47:39 +00:00
auto array_storage = TRY ( ByteBuffer : : create_uninitialized ( read_size ) ) ;
2021-03-10 16:24:01 +01:00
auto * array = ( u32 * ) array_storage . data ( ) ;
2020-11-24 18:38:41 +01:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( ( u8 * ) array ) ;
2021-11-10 15:52:46 +01:00
TRY ( fs ( ) . read_block ( array_block_index , & buffer , read_size , 0 ) ) ;
2021-02-12 11:59:27 +01:00
for ( unsigned i = 0 ; i < count ; + + i )
2021-11-10 15:52:46 +01:00
TRY ( callback ( Ext2FS : : BlockIndex ( array [ i ] ) ) ) ;
return { } ;
2018-10-10 11:53:07 +02:00
} ;
2021-11-10 15:52:46 +01:00
TRY ( process_block_array ( e2inode . i_block [ EXT2_IND_BLOCK ] , [ & ] ( auto block_index ) - > ErrorOr < void > {
return add_block ( block_index ) ;
} ) ) ;
2018-10-10 11:53:07 +02:00
2019-05-25 17:23:17 +02:00
if ( ! blocks_remaining )
2018-10-10 11:53:07 +02:00
return list ;
2021-11-10 15:52:46 +01:00
TRY ( process_block_array ( e2inode . i_block [ EXT2_DIND_BLOCK ] , [ & ] ( auto block_index ) - > ErrorOr < void > {
return process_block_array ( block_index , [ & ] ( auto block_index2 ) - > ErrorOr < void > {
return add_block ( block_index2 ) ;
2018-10-10 11:53:07 +02:00
} ) ;
2021-11-10 15:52:46 +01:00
} ) ) ;
2018-10-10 11:53:07 +02:00
2019-05-25 17:23:17 +02:00
if ( ! blocks_remaining )
2018-10-10 11:53:07 +02:00
return list ;
2021-11-10 15:52:46 +01:00
TRY ( process_block_array ( e2inode . i_block [ EXT2_TIND_BLOCK ] , [ & ] ( auto block_index ) - > ErrorOr < void > {
return process_block_array ( block_index , [ & ] ( auto block_index2 ) - > ErrorOr < void > {
return process_block_array ( block_index2 , [ & ] ( auto block_index3 ) - > ErrorOr < void > {
return add_block ( block_index3 ) ;
2018-10-10 11:53:07 +02:00
} ) ;
} ) ;
2021-11-10 15:52:46 +01:00
} ) ) ;
2018-10-10 11:53:07 +02:00
return list ;
}
2021-02-12 09:18:47 +01:00
Ext2FSInode : : Ext2FSInode ( Ext2FS & fs , InodeIndex index )
2018-12-19 21:18:28 +01:00
: Inode ( fs , index )
2018-11-13 13:02:39 +01:00
{
}
2018-11-15 17:13:10 +01:00
Ext2FSInode : : ~ Ext2FSInode ( )
2018-11-13 13:02:39 +01:00
{
2021-10-21 17:06:38 +02:00
if ( m_raw_inode . i_links_count = = 0 ) {
// Alas, we have nowhere to propagate any errors that occur here.
( void ) fs ( ) . free_inode ( * this ) ;
}
2018-11-13 13:02:39 +01:00
}
2021-03-17 18:35:42 +01: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 03:16:36 +01:00
InodeMetadata Ext2FSInode : : metadata ( ) const
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2019-01-01 03:16:36 +01:00
InodeMetadata metadata ;
metadata . inode = identifier ( ) ;
2021-03-17 18:35:42 +01:00
metadata . size = size ( ) ;
2019-01-01 03:16:36 +01: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 17:31:23 +01:00
metadata . link_count = m_raw_inode . i_links_count ;
2023-03-13 22:11:13 +01:00
metadata . atime = UnixDateTime : : from_seconds_since_epoch ( m_raw_inode . i_atime ) ;
metadata . ctime = UnixDateTime : : from_seconds_since_epoch ( m_raw_inode . i_ctime ) ;
metadata . mtime = UnixDateTime : : from_seconds_since_epoch ( m_raw_inode . i_mtime ) ;
metadata . dtime = UnixDateTime : : from_seconds_since_epoch ( m_raw_inode . i_dtime ) ;
2019-01-31 17:31:23 +01:00
metadata . block_size = fs ( ) . block_size ( ) ;
metadata . block_count = m_raw_inode . i_blocks ;
2018-11-13 13:32:16 +01:00
2020-02-16 01:27:42 +01:00
if ( Kernel : : is_character_device ( m_raw_inode . i_mode ) | | Kernel : : is_block_device ( m_raw_inode . i_mode ) ) {
2018-11-13 13:32:16 +01:00
unsigned dev = m_raw_inode . i_block [ 0 ] ;
2019-07-31 17:24:54 +02:00
if ( ! dev )
dev = m_raw_inode . i_block [ 1 ] ;
2019-02-16 09:57:42 +01:00
metadata . major_device = ( dev & 0xfff00 ) > > 8 ;
metadata . minor_device = ( dev & 0xff ) | ( ( dev > > 12 ) & 0xfff00 ) ;
2018-11-13 13:32:16 +01:00
}
2019-01-01 03:16:36 +01:00
return metadata ;
2018-11-13 13:32:16 +01:00
}
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : flush_metadata ( )
2018-12-19 21:56:45 +01:00
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2021-03-13 14:59:00 +01:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::flush_metadata(): Flushing inode " , identifier ( ) ) ;
2021-10-21 17:03:50 +02:00
TRY ( fs ( ) . write_ext2_inode ( index ( ) , m_raw_inode ) ) ;
2019-01-01 03:55:13 +01:00
if ( is_directory ( ) ) {
2019-01-28 04:16:01 +01:00
// Unless we're about to go away permanently, invalidate the lookup cache.
if ( m_raw_inode . i_links_count ! = 0 ) {
// FIXME: This invalidation is way too hardcore. It's sad to throw away the whole cache.
m_lookup_cache . clear ( ) ;
}
2019-01-01 03:55:13 +01:00
}
2018-12-19 22:28:09 +01:00
set_metadata_dirty ( false ) ;
2021-11-08 00:51:39 +01:00
return { } ;
2018-12-19 21:56:45 +01:00
}
2022-08-06 18:24:21 +03:00
ErrorOr < void > Ext2FSInode : : compute_block_list_with_exclusive_locking ( )
{
// Note: We verify that the inode mutex is being held locked. Because only the read_bytes_locked()
// method uses this method and the mutex can be locked in shared mode when reading the Inode if
// it is an ext2 regular file, but also in exclusive mode, when the Inode is an ext2 directory and being
// traversed, we use another exclusive lock to ensure we always mutate the block list safely.
VERIFY ( m_inode_lock . is_locked ( ) ) ;
MutexLocker block_list_locker ( m_block_list_lock ) ;
if ( m_block_list . is_empty ( ) )
m_block_list = TRY ( compute_block_list ( ) ) ;
return { } ;
}
2022-08-06 04:22:20 +03:00
ErrorOr < size_t > Ext2FSInode : : read_bytes_locked ( off_t offset , size_t count , UserOrKernelBuffer & buffer , OpenFileDescription * description ) const
2018-11-13 13:02:39 +01:00
{
2022-08-06 04:22:20 +03:00
VERIFY ( m_inode_lock . is_locked ( ) ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( offset > = 0 ) ;
2018-11-13 13:02:39 +01:00
if ( m_raw_inode . i_size = = 0 )
return 0 ;
2021-04-04 17:21:07 +02:00
if ( static_cast < u64 > ( offset ) > = size ( ) )
return 0 ;
2018-11-13 13:02:39 +01: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 20:42:32 +01:00
VERIFY ( offset = = 0 ) ;
2021-06-16 16:44:15 +02:00
size_t nread = min ( ( off_t ) size ( ) - offset , static_cast < off_t > ( count ) ) ;
2022-04-01 20:58:27 +03:00
TRY ( buffer . write ( ( ( u8 const * ) m_raw_inode . i_block ) + offset , nread ) ) ;
2018-11-13 13:02:39 +01:00
return nread ;
}
2022-08-06 18:24:21 +03:00
// Note: We bypass the const declaration of this method, but this is a strong
// requirement to be able to accomplish the read operation successfully.
2022-09-26 21:34:28 +03:00
// We call this special method because it locks a separate mutex to ensure we
2022-08-06 18:24:21 +03:00
// update the block list of the inode safely, as the m_inode_lock is locked in
// shared mode.
TRY ( const_cast < Ext2FSInode & > ( * this ) . compute_block_list_with_exclusive_locking ( ) ) ;
2018-11-13 13:02:39 +01:00
2018-12-21 02:10:45 +01:00
if ( m_block_list . is_empty ( ) ) {
2021-03-13 14:59:00 +01:00
dmesgln ( " Ext2FSInode[{}]::read_bytes(): Empty block list " , identifier ( ) ) ;
2021-05-01 14:29:39 -07:00
return EIO ;
2018-11-13 13:02:39 +01:00
}
2020-05-18 21:55:08 +03:00
bool allow_cache = ! description | | ! description - > is_direct ( ) ;
2022-04-01 20:58:27 +03:00
int const block_size = fs ( ) . block_size ( ) ;
2018-11-13 13:02:39 +01:00
2021-07-11 00:34:36 +02:00
BlockBasedFileSystem : : BlockIndex first_block_logical_index = offset / block_size ;
BlockBasedFileSystem : : BlockIndex last_block_logical_index = ( offset + count ) / block_size ;
2018-11-13 13:02:39 +01:00
if ( last_block_logical_index > = m_block_list . size ( ) )
last_block_logical_index = m_block_list . size ( ) - 1 ;
2019-04-23 13:00:53 +02:00
int offset_into_first_block = offset % block_size ;
2018-11-13 13:02:39 +01:00
2021-06-16 16:44:15 +02:00
size_t nread = 0 ;
2021-03-18 22:58:21 +01:00
auto remaining_count = min ( ( off_t ) count , ( off_t ) size ( ) - offset ) ;
2018-11-13 13:02:39 +01:00
2021-03-13 14:59:00 +01: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 13:02:39 +01:00
2021-03-18 22:58:21 +01: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 01:32:04 +02:00
size_t num_bytes_to_copy = min ( ( size_t ) block_size - offset_into_block , ( size_t ) remaining_count ) ;
2020-09-11 21:11:07 -06:00
auto buffer_offset = buffer . offset ( nread ) ;
2021-04-10 11:05:19 +02:00
if ( block_index . value ( ) = = 0 ) {
// This is a hole, act as if it's filled with zeroes.
2021-09-07 12:09:52 +02:00
TRY ( buffer_offset . memset ( 0 , num_bytes_to_copy ) ) ;
2021-04-10 11:05:19 +02:00
} else {
2021-04-11 00:25:26 +02: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 11:05:19 +02:00
dmesgln ( " Ext2FSInode[{}]::read_bytes(): Failed to read block {} (index {}) " , identifier ( ) , block_index . value ( ) , bi ) ;
2021-11-08 00:51:39 +01:00
return result . release_error ( ) ;
2021-04-10 11:05:19 +02:00
}
2018-11-13 13:02:39 +01:00
}
remaining_count - = num_bytes_to_copy ;
nread + = num_bytes_to_copy ;
}
return nread ;
}
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : resize ( u64 new_size )
2019-04-28 22:07:25 +02:00
{
2021-03-18 22:58:21 +01:00
auto old_size = size ( ) ;
2019-11-02 16:22:29 +01:00
if ( old_size = = new_size )
2021-11-08 00:51:39 +01:00
return { } ;
2019-11-02 16:22:29 +01:00
2021-03-17 18:35:42 +01:00
if ( ! ( ( u32 ) fs ( ) . get_features_readonly ( ) & ( u32 ) Ext2FS : : FeaturesReadOnly : : FileSize64bits ) & & ( new_size > = static_cast < u32 > ( - 1 ) ) )
return ENOSPC ;
2019-11-02 16:22:29 +01:00
u64 block_size = fs ( ) . block_size ( ) ;
2021-03-18 22:58:21 +01:00
auto blocks_needed_before = ceil_div ( old_size , block_size ) ;
auto blocks_needed_after = ceil_div ( new_size , block_size ) ;
2019-04-28 22:07:25 +02:00
2021-02-11 23:05:16 +01:00
if constexpr ( EXT2_DEBUG ) {
2021-03-13 14:59:00 +01: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-11 23:05:16 +01:00
}
2019-04-28 22:07:25 +02:00
2019-11-02 12:53:31 +01:00
if ( blocks_needed_after > blocks_needed_before ) {
2021-03-18 22:58:21 +01:00
auto additional_blocks_needed = blocks_needed_after - blocks_needed_before ;
2019-11-02 12:53:31 +01:00
if ( additional_blocks_needed > fs ( ) . super_block ( ) . s_free_blocks_count )
2021-01-20 23:11:17 +01:00
return ENOSPC ;
2019-11-02 12:53:31 +01:00
}
2021-03-10 18:47:11 +01:00
if ( m_block_list . is_empty ( ) )
2021-11-10 15:52:46 +01:00
m_block_list = TRY ( compute_block_list ( ) ) ;
2020-11-24 11:49:46 +01:00
2019-04-28 22:07:25 +02:00
if ( blocks_needed_after > blocks_needed_before ) {
2021-09-05 15:30:15 +02:00
auto blocks = TRY ( fs ( ) . allocate_blocks ( fs ( ) . group_index_from_inode ( index ( ) ) , blocks_needed_after - blocks_needed_before ) ) ;
2021-11-10 11:55:37 +01:00
TRY ( m_block_list . try_extend ( move ( blocks ) ) ) ;
2019-04-28 22:07:25 +02:00
} else if ( blocks_needed_after < blocks_needed_before ) {
2021-03-13 14:59:00 +01:00
if constexpr ( EXT2_VERY_DEBUG ) {
dbgln ( " Ext2FSInode[{}]::resize(): Shrinking inode, old block list is {} entries: " , identifier ( ) , m_block_list . size ( ) ) ;
2021-03-10 18:47:11 +01:00
for ( auto block_index : m_block_list ) {
2021-02-11 23:05:16 +01:00
dbgln ( " # {} " , block_index ) ;
}
2019-05-25 17:23:17 +02:00
}
2021-03-10 18:47:11 +01:00
while ( m_block_list . size ( ) ! = blocks_needed_after ) {
auto block_index = m_block_list . take_last ( ) ;
2021-02-25 23:07:24 +01:00
if ( block_index . value ( ) ) {
2021-04-11 00:25:26 +02:00
if ( auto result = fs ( ) . set_block_allocation_state ( block_index , false ) ; result . is_error ( ) ) {
2021-03-13 14:59:00 +01:00
dbgln ( " Ext2FSInode[{}]::resize(): Failed to free block {}: {} " , identifier ( ) , block_index , result . error ( ) ) ;
2021-02-25 23:07:24 +01:00
return result ;
}
}
2019-04-28 22:07:25 +02:00
}
}
2021-09-05 15:30:15 +02:00
TRY ( flush_block_list ( ) ) ;
2019-04-28 22:07:25 +02:00
m_raw_inode . i_size = new_size ;
2021-03-17 18:35:42 +01:00
if ( Kernel : : is_regular_file ( m_raw_inode . i_mode ) )
m_raw_inode . i_dir_acl = new_size > > 32 ;
2019-04-28 22:07:25 +02:00
set_metadata_dirty ( true ) ;
2021-01-09 22:05:05 +01: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-18 22:58:21 +01:00
auto bytes_to_clear = new_size - old_size ;
auto clear_from = old_size ;
2021-02-21 02:16:16 -08:00
u8 zero_buffer [ PAGE_SIZE ] { } ;
2021-01-09 22:05:05 +01:00
while ( bytes_to_clear ) {
2021-09-05 15:30:15 +02:00
auto nwritten = TRY ( write_bytes ( clear_from , min ( static_cast < u64 > ( sizeof ( zero_buffer ) ) , bytes_to_clear ) , UserOrKernelBuffer : : for_kernel_buffer ( zero_buffer ) , nullptr ) ) ;
VERIFY ( nwritten ! = 0 ) ;
bytes_to_clear - = nwritten ;
clear_from + = nwritten ;
2021-01-09 22:05:05 +01:00
}
}
2021-11-08 00:51:39 +01:00
return { } ;
2019-04-28 22:07:25 +02:00
}
2022-08-06 04:22:20 +03:00
ErrorOr < size_t > Ext2FSInode : : write_bytes_locked ( off_t offset , size_t count , UserOrKernelBuffer const & data , OpenFileDescription * description )
2019-01-23 04:29:56 +01:00
{
2022-07-27 21:42:16 +03:00
VERIFY ( m_inode_lock . is_locked ( ) ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( offset > = 0 ) ;
2019-03-02 01:50:34 +01:00
2021-05-07 20:30:23 +02:00
if ( count = = 0 )
return 0 ;
2019-03-02 01:50:34 +01:00
if ( is_symlink ( ) ) {
2021-02-23 20:42:32 +01:00
VERIFY ( offset = = 0 ) ;
2019-11-17 19:12:29 +01:00
if ( max ( ( size_t ) ( offset + count ) , ( size_t ) m_raw_inode . i_size ) < max_inline_symlink_length ) {
2022-08-06 04:22:20 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::write_bytes_locked(): Poking into i_block array for inline symlink ({} bytes) " , identifier ( ) , count ) ;
2021-09-07 12:09:52 +02:00
TRY ( data . read ( ( ( u8 * ) m_raw_inode . i_block ) + offset , count ) ) ;
2019-11-17 19:12:29 +01:00
if ( ( size_t ) ( offset + count ) > ( size_t ) m_raw_inode . i_size )
2019-03-02 01:50:34 +01:00
m_raw_inode . i_size = offset + count ;
set_metadata_dirty ( true ) ;
return count ;
}
}
2019-01-23 04:29:56 +01:00
2020-05-18 21:55:08 +03:00
bool allow_cache = ! description | | ! description - > is_direct ( ) ;
2022-04-01 20:58:27 +03:00
auto const block_size = fs ( ) . block_size ( ) ;
2021-03-18 22:58:21 +01:00
auto new_size = max ( static_cast < u64 > ( offset ) + count , size ( ) ) ;
2019-01-23 04:29:56 +01:00
2021-09-05 15:30:15 +02:00
TRY ( resize ( new_size ) ) ;
2019-01-23 04:29:56 +01:00
2019-11-03 10:22:09 +01:00
if ( m_block_list . is_empty ( ) )
2021-11-10 15:52:46 +01:00
m_block_list = TRY ( compute_block_list ( ) ) ;
2019-11-03 10:22:09 +01:00
if ( m_block_list . is_empty ( ) ) {
2021-03-13 14:59:00 +01:00
dbgln ( " Ext2FSInode[{}]::write_bytes(): Empty block list " , identifier ( ) ) ;
2021-05-01 14:29:39 -07:00
return EIO ;
2019-11-03 10:22:09 +01:00
}
2021-07-11 00:34:36 +02:00
BlockBasedFileSystem : : BlockIndex first_block_logical_index = offset / block_size ;
BlockBasedFileSystem : : BlockIndex last_block_logical_index = ( offset + count ) / block_size ;
2019-04-28 22:07:25 +02:00
if ( last_block_logical_index > = m_block_list . size ( ) )
last_block_logical_index = m_block_list . size ( ) - 1 ;
2019-01-23 04:29:56 +01:00
2020-02-25 14:49:47 +01:00
size_t offset_into_first_block = offset % block_size ;
2019-01-23 04:29:56 +01:00
2021-06-16 16:44:15 +02:00
size_t nwritten = 0 ;
2021-03-18 22:58:21 +01:00
auto remaining_count = min ( ( off_t ) count , ( off_t ) new_size - offset ) ;
2019-01-23 04:29:56 +01:00
2022-08-06 04:22:20 +03:00
dbgln_if ( EXT2_VERY_DEBUG , " Ext2FSInode[{}]::write_bytes_locked(): Writing {} bytes, {} bytes into inode from {} " , identifier ( ) , count , offset , data . user_or_kernel_ptr ( ) ) ;
2019-01-23 04:29:56 +01:00
2021-03-18 22:58:21 +01:00
for ( auto bi = first_block_logical_index ; remaining_count & & bi < = last_block_logical_index ; bi = bi . value ( ) + 1 ) {
2020-02-25 14:49:47 +01:00
size_t offset_into_block = ( bi = = first_block_logical_index ) ? offset_into_first_block : 0 ;
2021-05-02 01:32:04 +02:00
size_t num_bytes_to_copy = min ( ( size_t ) block_size - offset_into_block , ( size_t ) remaining_count ) ;
2022-08-06 04:22:20 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::write_bytes_locked(): Writing block {} (offset_into_block: {}) " , identifier ( ) , m_block_list [ bi . value ( ) ] , offset_into_block ) ;
2021-04-11 00:25:26 +02: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 ( ) ) {
2022-08-06 04:22:20 +03:00
dbgln ( " Ext2FSInode[{}]::write_bytes_locked(): Failed to write block {} (index {}) " , identifier ( ) , m_block_list [ bi . value ( ) ] , bi ) ;
2021-11-08 00:51:39 +01:00
return result . release_error ( ) ;
2019-01-23 04:29:56 +01:00
}
remaining_count - = num_bytes_to_copy ;
nwritten + = num_bytes_to_copy ;
}
2021-05-12 19:17:51 +00:00
did_modify_contents ( ) ;
2022-08-06 04:22:20 +03:00
dbgln_if ( EXT2_VERY_DEBUG , " Ext2FSInode[{}]::write_bytes_locked(): After write, i_size={}, i_blocks={} ({} blocks in list) " , identifier ( ) , size ( ) , m_raw_inode . i_blocks , m_block_list . size ( ) ) ;
2019-01-23 04:29:56 +01:00
return nwritten ;
}
2021-11-10 15:42:39 +01:00
ErrorOr < void > Ext2FSInode : : traverse_as_directory ( Function < ErrorOr < void > ( FileSystem : : DirectoryEntryView const & ) > callback ) const
2018-11-13 23:44:54 +01:00
{
2021-02-23 20:42:32 +01:00
VERIFY ( is_directory ( ) ) ;
2018-11-13 23:44:54 +01:00
2021-05-08 18:28:54 +02:00
u8 buffer [ max_block_size ] ;
auto buf = UserOrKernelBuffer : : for_kernel_buffer ( buffer ) ;
auto block_size = fs ( ) . block_size ( ) ;
2021-07-16 02:39:41 +02:00
auto file_size = size ( ) ;
2021-05-08 18:28:54 +02:00
// Directory entries are guaranteed not to span multiple blocks,
// so we can iterate over blocks separately.
2021-07-16 02:39:41 +02:00
for ( u64 offset = 0 ; offset < file_size ; offset + = block_size ) {
2021-09-05 15:30:15 +02:00
TRY ( read_bytes ( offset , block_size , buf , nullptr ) ) ;
2021-07-16 02:39:41 +02:00
2022-10-24 10:02:37 +03:00
using ext2_extended_dir_entry = ext2_dir_entry_2 ;
auto * entry = reinterpret_cast < ext2_extended_dir_entry * > ( buffer ) ;
auto * entries_end = reinterpret_cast < ext2_extended_dir_entry * > ( buffer + block_size ) ;
2021-05-08 18:28:54 +02:00
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 ) ) ;
2021-11-10 15:42:39 +01:00
TRY ( callback ( { { entry - > name , entry - > name_len } , { fsid ( ) , entry - > inode } , entry - > file_type } ) ) ;
2021-05-08 18:28:54 +02:00
}
2022-10-24 10:02:37 +03:00
entry = ( ext2_extended_dir_entry * ) ( ( char * ) entry + entry - > rec_len ) ;
2018-11-13 23:44:54 +01:00
}
}
2020-05-26 00:36:11 -07:00
2021-11-08 00:51:39 +01:00
return { } ;
2018-11-13 23:44:54 +01:00
}
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : write_directory ( Vector < Ext2FSDirectoryEntry > & entries )
2019-06-09 12:46:23 +02:00
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2019-06-09 12:46:23 +02:00
auto block_size = fs ( ) . block_size ( ) ;
2021-05-07 14:47:38 +02: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 14:49:47 +01:00
for ( size_t i = 0 ; i < entries . size ( ) ; + + i ) {
2019-06-09 12:46:23 +02:00
auto & entry = entries [ i ] ;
2022-01-13 00:52:14 +02:00
entry . record_length = EXT2_DIR_REC_LEN ( entry . name - > length ( ) ) ;
2021-05-07 14:47:38 +02:00
space_in_block - = entry . record_length ;
if ( i + 1 < entries . size ( ) ) {
2022-01-13 00:52:14 +02:00
if ( EXT2_DIR_REC_LEN ( entries [ i + 1 ] . name - > length ( ) ) > space_in_block ) {
2021-05-07 14:47:38 +02:00
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 12:46:23 +02:00
2021-05-07 14:47:38 +02:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::write_directory(): New directory contents to write (size {}): " , identifier ( ) , directory_size ) ;
2019-06-09 12:46:23 +02:00
2022-01-20 17:47:39 +00:00
auto directory_data = TRY ( ByteBuffer : : create_uninitialized ( directory_size ) ) ;
2023-02-07 12:43:54 +01:00
FixedMemoryStream stream { directory_data . bytes ( ) } ;
2021-05-07 14:47:38 +02:00
for ( auto & entry : entries ) {
2022-01-13 00:52:14 +02:00
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 12:46:23 +02:00
2023-02-07 12:43:54 +01:00
MUST ( stream . write_value < u32 > ( entry . inode_index . value ( ) ) ) ;
MUST ( stream . write_value < u16 > ( entry . record_length ) ) ;
MUST ( stream . write_value < u8 > ( entry . name - > length ( ) ) ) ;
MUST ( stream . write_value < u8 > ( entry . file_type ) ) ;
2023-03-01 15:37:45 +01:00
MUST ( stream . write_until_depleted ( entry . name - > bytes ( ) ) ) ;
2022-01-13 00:52:14 +02:00
int padding = entry . record_length - entry . name - > length ( ) - 8 ;
2019-06-09 12:46:23 +02:00
for ( int j = 0 ; j < padding ; + + j )
2023-02-07 12:43:54 +01:00
MUST ( stream . write_value < u8 > ( 0 ) ) ;
2019-06-09 12:46:23 +02:00
}
2023-02-07 12:43:54 +01:00
auto serialized_bytes_count = TRY ( stream . tell ( ) ) ;
VERIFY ( serialized_bytes_count = = directory_size ) ;
2019-06-09 12:46:23 +02:00
2023-02-07 12:43:54 +01:00
TRY ( resize ( serialized_bytes_count ) ) ;
2021-05-06 17:53:59 +02:00
2023-02-07 12:43:54 +01:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( directory_data . data ( ) ) ;
auto nwritten = TRY ( write_bytes ( 0 , serialized_bytes_count , buffer , nullptr ) ) ;
2019-11-13 21:42:24 +13:00
set_metadata_dirty ( true ) ;
2021-09-05 15:30:15 +02:00
if ( nwritten ! = directory_data . size ( ) )
2021-02-02 16:06:03 +01:00
return EIO ;
2021-11-08 00:51:39 +01:00
return { } ;
2019-06-09 12:46:23 +02:00
}
2023-03-07 12:25:00 +01:00
ErrorOr < NonnullRefPtr < Inode > > Ext2FSInode : : create_child ( StringView name , mode_t mode , dev_t dev , UserID uid , GroupID gid )
2020-06-24 23:35:56 +03:00
{
2022-10-24 10:02:37 +03:00
if ( Kernel : : is_directory ( mode ) )
2021-02-02 16:37:52 +01: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
}
2021-11-11 00:55:02 +01:00
ErrorOr < void > Ext2FSInode : : add_child ( Inode & child , StringView name , mode_t mode )
2018-10-10 11:53:07 +02:00
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( is_directory ( ) ) ;
2018-10-10 11:53:07 +02:00
2019-09-10 21:04:27 +02:00
if ( name . length ( ) > EXT2_NAME_LEN )
2021-01-20 23:11:17 +01:00
return ENAMETOOLONG ;
2019-09-10 21:04:27 +02:00
2021-03-13 14:59:00 +01: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 11:53:07 +02:00
2020-08-18 18:01:42 +02:00
Vector < Ext2FSDirectoryEntry > entries ;
2021-11-10 15:42:39 +01:00
TRY ( traverse_as_directory ( [ & ] ( auto & entry ) - > ErrorOr < void > {
if ( name = = entry . name )
return EEXIST ;
2022-01-13 00:52:14 +02:00
auto entry_name = TRY ( KString : : try_create ( entry . name ) ) ;
TRY ( entries . try_append ( { move ( entry_name ) , entry . inode . index ( ) , entry . file_type } ) ) ;
2021-11-10 15:42:39 +01:00
return { } ;
2021-09-06 11:27:24 +02:00
} ) ) ;
2020-05-26 00:36:11 -07:00
2021-09-05 15:30:15 +02:00
TRY ( child . increment_link_count ( ) ) ;
2019-02-21 13:26:40 +01:00
2022-01-13 00:52:14 +02:00
auto entry_name = TRY ( KString : : try_create ( name ) ) ;
TRY ( entries . try_empend ( move ( entry_name ) , child . index ( ) , to_ext2_file_type ( mode ) ) ) ;
2020-07-04 13:36:55 +02:00
2021-09-05 15:30:15 +02:00
TRY ( write_directory ( entries ) ) ;
TRY ( populate_lookup_cache ( ) ) ;
2021-06-19 14:12:11 +01:00
2022-01-21 12:35:48 +02:00
auto cache_entry_name = TRY ( KString : : try_create ( name ) ) ;
TRY ( m_lookup_cache . try_set ( move ( cache_entry_name ) , child . index ( ) ) ) ;
2021-05-12 19:17:51 +00:00
did_add_child ( child . identifier ( ) , name ) ;
2021-11-08 00:51:39 +01:00
return { } ;
2018-10-10 11:53:07 +02:00
}
2021-11-11 00:55:02 +01:00
ErrorOr < void > Ext2FSInode : : remove_child ( StringView name )
2019-01-22 07:03:44 +01:00
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2021-03-13 14:59:00 +01:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::remove_child(): Removing '{}' " , identifier ( ) , name ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( is_directory ( ) ) ;
2019-01-22 07:03:44 +01:00
2021-09-05 15:30:15 +02:00
TRY ( populate_lookup_cache ( ) ) ;
2021-06-19 14:12:11 +01:00
2022-01-29 20:22:02 +02:00
auto it = m_lookup_cache . find ( name ) ;
2019-02-27 14:11:25 +01:00
if ( it = = m_lookup_cache . end ( ) )
2021-01-20 23:11:17 +01:00
return ENOENT ;
2019-11-17 18:59:14 +01:00
auto child_inode_index = ( * it ) . value ;
2019-02-27 14:11:25 +01:00
2019-01-22 07:03:44 +01:00
InodeIdentifier child_id { fsid ( ) , child_inode_index } ;
2020-08-18 18:01:42 +02:00
Vector < Ext2FSDirectoryEntry > entries ;
2021-11-10 15:42:39 +01:00
TRY ( traverse_as_directory ( [ & ] ( auto & entry ) - > ErrorOr < void > {
2022-01-13 00:52:14 +02:00
if ( name ! = entry . name ) {
auto entry_name = TRY ( KString : : try_create ( entry . name ) ) ;
TRY ( entries . try_append ( { move ( entry_name ) , entry . inode . index ( ) , entry . file_type } ) ) ;
}
2021-11-10 15:42:39 +01:00
return { } ;
2021-09-06 11:27:24 +02:00
} ) ) ;
2019-01-22 07:03:44 +01:00
2021-09-05 15:30:15 +02:00
TRY ( write_directory ( entries ) ) ;
2019-01-22 07:03:44 +01:00
2022-01-21 12:35:48 +02:00
m_lookup_cache . remove ( it ) ;
2019-01-22 07:03:44 +01:00
2021-09-05 18:55:55 +02:00
auto child_inode = TRY ( fs ( ) . get_inode ( child_id ) ) ;
2021-09-05 15:30:15 +02:00
TRY ( child_inode - > decrement_link_count ( ) ) ;
2020-07-04 13:36:55 +02:00
2021-05-12 19:17:51 +00:00
did_remove_child ( child_id , name ) ;
2021-11-08 00:51:39 +01:00
return { } ;
2019-01-22 07:03:44 +01:00
}
2022-10-08 11:22:12 +02:00
ErrorOr < void > Ext2FSInode : : replace_child ( StringView name , Inode & child )
{
MutexLocker locker ( m_inode_lock ) ;
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]::replace_child(): Replacing '{}' with inode {} " , identifier ( ) , name , child . index ( ) ) ;
VERIFY ( is_directory ( ) ) ;
TRY ( populate_lookup_cache ( ) ) ;
if ( name . length ( ) > EXT2_NAME_LEN )
return ENAMETOOLONG ;
Vector < Ext2FSDirectoryEntry > entries ;
Optional < InodeIndex > old_child_index ;
TRY ( traverse_as_directory ( [ & ] ( auto & entry ) - > ErrorOr < void > {
auto is_replacing_this_inode = name = = entry . name ;
auto inode_index = is_replacing_this_inode ? child . index ( ) : entry . inode . index ( ) ;
auto entry_name = TRY ( KString : : try_create ( entry . name ) ) ;
TRY ( entries . try_empend ( move ( entry_name ) , inode_index , to_ext2_file_type ( child . mode ( ) ) ) ) ;
if ( is_replacing_this_inode )
old_child_index = entry . inode . index ( ) ;
return { } ;
} ) ) ;
if ( ! old_child_index . has_value ( ) )
return ENOENT ;
auto old_child = TRY ( fs ( ) . get_inode ( { fsid ( ) , * old_child_index } ) ) ;
auto old_index_it = m_lookup_cache . find ( name ) ;
VERIFY ( old_index_it ! = m_lookup_cache . end ( ) ) ;
old_index_it - > value = child . index ( ) ;
// NOTE: Between this line and the write_directory line, all operations must
// be atomic. Any changes made should be reverted.
TRY ( child . increment_link_count ( ) ) ;
auto maybe_decrement_error = old_child - > decrement_link_count ( ) ;
if ( maybe_decrement_error . is_error ( ) ) {
old_index_it - > value = * old_child_index ;
MUST ( child . decrement_link_count ( ) ) ;
return maybe_decrement_error ;
}
// FIXME: The filesystem is left in an inconsistent state if this fails.
// Revert the changes made above if we can't write_directory.
// Ideally, decrement should be the last operation, but we currently
// can't "un-write" a directory entry list.
TRY ( write_directory ( entries ) ) ;
// TODO: Emit a did_replace_child event.
return { } ;
}
2022-08-06 18:07:03 +03:00
ErrorOr < void > Ext2FSInode : : populate_lookup_cache ( )
2018-11-15 16:34:36 +01:00
{
2022-08-06 18:07:03 +03:00
VERIFY ( m_inode_lock . is_exclusively_locked_by_current_thread ( ) ) ;
2019-02-20 13:09:59 +01:00
if ( ! m_lookup_cache . is_empty ( ) )
2021-11-08 00:51:39 +01:00
return { } ;
2022-01-21 12:35:48 +02:00
HashMap < NonnullOwnPtr < KString > , InodeIndex > children ;
2018-11-15 17:04:55 +01:00
2021-11-10 15:42:39 +01:00
TRY ( traverse_as_directory ( [ & children ] ( auto & entry ) - > ErrorOr < void > {
2022-01-21 12:35:48 +02:00
auto entry_name = TRY ( KString : : try_create ( entry . name ) ) ;
TRY ( children . try_set ( move ( entry_name ) , entry . inode . index ( ) ) ) ;
2021-11-10 15:42:39 +01:00
return { } ;
2021-09-06 11:27:24 +02:00
} ) ) ;
2020-08-05 02:13:30 -07:00
2021-06-19 14:12:11 +01:00
VERIFY ( m_lookup_cache . is_empty ( ) ) ;
2018-11-15 17:04:55 +01:00
m_lookup_cache = move ( children ) ;
2021-11-08 00:51:39 +01:00
return { } ;
2018-11-15 17:04:55 +01:00
}
2018-11-15 16:34:36 +01:00
2023-03-07 12:25:00 +01:00
ErrorOr < NonnullRefPtr < Inode > > Ext2FSInode : : lookup ( StringView name )
2018-11-15 17:04:55 +01:00
{
2021-02-23 20:42:32 +01:00
VERIFY ( is_directory ( ) ) ;
2021-03-13 14:59:00 +01:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]:lookup(): Looking up '{}' " , identifier ( ) , name ) ;
2021-07-15 23:54:48 +02:00
InodeIndex inode_index ;
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2022-08-06 18:07:03 +03:00
TRY ( populate_lookup_cache ( ) ) ;
2022-01-29 20:22:02 +02:00
auto it = m_lookup_cache . find ( name ) ;
2021-07-15 23:54:48 +02:00
if ( it = = m_lookup_cache . end ( ) ) {
dbgln_if ( EXT2_DEBUG , " Ext2FSInode[{}]:lookup(): '{}' not found " , identifier ( ) , name ) ;
2021-08-14 13:32:35 +02:00
return ENOENT ;
2021-07-15 23:54:48 +02:00
}
inode_index = it - > value ;
}
2021-08-14 13:32:35 +02:00
2021-09-05 18:55:55 +02:00
return fs ( ) . get_inode ( { fsid ( ) , inode_index } ) ;
2018-11-15 17:04:55 +01:00
}
2018-11-15 16:34:36 +01:00
2023-03-13 22:11:13 +01:00
ErrorOr < void > Ext2FSInode : : update_timestamps ( Optional < UnixDateTime > atime , Optional < UnixDateTime > ctime , Optional < UnixDateTime > mtime )
2019-01-01 03:16:36 +01:00
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2019-01-01 03:16:36 +01:00
if ( fs ( ) . is_readonly ( ) )
2021-04-30 15:51:06 +02:00
return EROFS ;
2023-01-07 13:57:33 -07:00
if ( atime . value_or ( { } ) . to_timespec ( ) . tv_sec > NumericLimits < i32 > : : max ( ) )
2022-05-02 15:26:10 -05:00
return EINVAL ;
2023-01-07 13:57:33 -07:00
if ( ctime . value_or ( { } ) . to_timespec ( ) . tv_sec > NumericLimits < i32 > : : max ( ) )
2022-08-22 13:34:22 +02:00
return EINVAL ;
2023-01-07 13:57:33 -07:00
if ( mtime . value_or ( { } ) . to_timespec ( ) . tv_sec > NumericLimits < i32 > : : max ( ) )
2022-08-22 13:34:22 +02:00
return EINVAL ;
if ( atime . has_value ( ) )
2022-11-22 21:01:45 +01:00
m_raw_inode . i_atime = atime . value ( ) . to_timespec ( ) . tv_sec ;
2022-08-22 13:34:22 +02:00
if ( ctime . has_value ( ) )
2022-11-22 21:01:45 +01:00
m_raw_inode . i_ctime = ctime . value ( ) . to_timespec ( ) . tv_sec ;
2022-08-22 13:34:22 +02:00
if ( mtime . has_value ( ) )
2022-11-22 21:01:45 +01:00
m_raw_inode . i_mtime = mtime . value ( ) . to_timespec ( ) . tv_sec ;
2019-01-01 03:16:36 +01:00
set_metadata_dirty ( true ) ;
2021-11-08 00:51:39 +01:00
return { } ;
2019-01-01 03:16:36 +01:00
}
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : increment_link_count ( )
2019-01-01 03:16:36 +01:00
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2019-01-01 03:16:36 +01:00
if ( fs ( ) . is_readonly ( ) )
2021-01-20 23:11:17 +01:00
return EROFS ;
2021-05-19 08:35:09 -06:00
constexpr size_t max_link_count = 65535 ;
2020-02-08 02:26:33 +01:00
if ( m_raw_inode . i_links_count = = max_link_count )
2021-01-20 23:11:17 +01:00
return EMLINK ;
2019-01-01 03:16:36 +01:00
+ + m_raw_inode . i_links_count ;
set_metadata_dirty ( true ) ;
2021-11-08 00:51:39 +01:00
return { } ;
2019-01-01 03:16:36 +01:00
}
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : decrement_link_count ( )
2019-01-01 03:16:36 +01:00
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2019-01-01 03:16:36 +01:00
if ( fs ( ) . is_readonly ( ) )
2021-01-20 23:11:17 +01:00
return EROFS ;
2021-02-23 20:42:32 +01:00
VERIFY ( m_raw_inode . i_links_count ) ;
2021-05-12 19:17:51 +00:00
2019-01-01 03:16:36 +01:00
- - m_raw_inode . i_links_count ;
2021-05-12 19:17:51 +00:00
set_metadata_dirty ( true ) ;
if ( m_raw_inode . i_links_count = = 0 )
did_delete_self ( ) ;
2020-01-15 21:58:03 +01:00
if ( ref_count ( ) = = 1 & & m_raw_inode . i_links_count = = 0 )
2019-01-22 07:03:44 +01:00
fs ( ) . uncache_inode ( index ( ) ) ;
2021-05-12 19:17:51 +00:00
2021-11-08 00:51:39 +01:00
return { } ;
2019-01-01 03:16:36 +01:00
}
2019-01-22 07:03:44 +01:00
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : chmod ( mode_t mode )
2019-01-29 04:55:08 +01:00
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2019-01-29 04:55:08 +01:00
if ( m_raw_inode . i_mode = = mode )
2021-11-08 00:51:39 +01:00
return { } ;
2019-01-29 04:55:08 +01:00
m_raw_inode . i_mode = mode ;
set_metadata_dirty ( true ) ;
2021-11-08 00:51:39 +01:00
return { } ;
2019-01-29 04:55:08 +01:00
}
2019-02-21 14:48:00 +01:00
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : chown ( UserID uid , GroupID gid )
2019-02-27 12:32:53 +01:00
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2019-02-27 12:32:53 +01:00
if ( m_raw_inode . i_uid = = uid & & m_raw_inode . i_gid = = gid )
2021-11-08 00:51:39 +01:00
return { } ;
2021-08-28 22:11:16 +02:00
m_raw_inode . i_uid = uid . value ( ) ;
m_raw_inode . i_gid = gid . value ( ) ;
2019-02-27 12:32:53 +01:00
set_metadata_dirty ( true ) ;
2021-11-08 00:51:39 +01:00
return { } ;
2019-02-27 12:32:53 +01:00
}
2021-11-08 00:51:39 +01:00
ErrorOr < void > Ext2FSInode : : truncate ( u64 size )
2019-03-27 16:42:30 +01:00
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2020-02-08 12:07:04 +01:00
if ( static_cast < u64 > ( m_raw_inode . i_size ) = = size )
2021-11-08 00:51:39 +01:00
return { } ;
2021-09-05 15:30:15 +02:00
TRY ( resize ( size ) ) ;
2019-03-27 16:42:30 +01:00
set_metadata_dirty ( true ) ;
2021-11-08 00:51:39 +01:00
return { } ;
2019-03-27 16:42:30 +01:00
}
2021-11-08 00:51:39 +01:00
ErrorOr < int > Ext2FSInode : : get_block_address ( int index )
2021-01-30 13:12:49 -07:00
{
2021-07-18 01:13:34 +02:00
MutexLocker locker ( m_inode_lock ) ;
2021-01-30 13:12:49 -07:00
if ( m_block_list . is_empty ( ) )
2021-11-10 15:52:46 +01:00
m_block_list = TRY ( compute_block_list ( ) ) ;
2021-01-30 13:12:49 -07:00
if ( index < 0 | | ( size_t ) index > = m_block_list . size ( ) )
return 0 ;
2021-02-12 11:59:27 +01:00
return m_block_list [ index ] . value ( ) ;
2021-01-30 13:12:49 -07:00
}
2020-02-16 01:27:42 +01:00
}