2020-01-18 11:38:21 +03:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2018-10-10 12:53:07 +03:00
# include <AK/Bitmap.h>
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 {
2020-02-08 04:26:33 +03:00
static const size_t max_link_count = 65535 ;
2019-09-30 12:20:51 +03:00
static const size_t max_block_size = 4096 ;
2019-03-02 03:50:34 +03:00
static const ssize_t max_inline_symlink_length = 60 ;
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 } ;
} ;
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
{
2020-04-06 11:54:21 +03:00
return adopt ( * 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
{
2019-02-20 15:09:59 +03:00
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
{
2019-11-03 15:56:55 +03:00
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 ) {
klog ( ) < < " ext2fs: super block magic: " < < String : : format ( " %x " , super_block . s_magic ) < < " (super block size: " < < sizeof ( ext2_super_block ) < < " ) " ;
}
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 ) {
klog ( ) < < " ext2fs: " < < super_block . s_inodes_count < < " inodes, " < < super_block . s_blocks_count < < " blocks " ;
klog ( ) < < " ext2fs: block size = " < < EXT2_BLOCK_SIZE ( & super_block ) ;
klog ( ) < < " ext2fs: first data block = " < < super_block . s_first_data_block ;
klog ( ) < < " ext2fs: inodes per block = " < < inodes_per_block ( ) ;
klog ( ) < < " ext2fs: inodes per group = " < < inodes_per_group ( ) ;
klog ( ) < < " ext2fs: free inodes = " < < super_block . s_free_inodes_count ;
klog ( ) < < " ext2fs: desc per block = " < < EXT2_DESC_PER_BLOCK ( & super_block ) ;
klog ( ) < < " ext2fs: desc size = " < < EXT2_DESC_SIZE ( & super_block ) ;
}
2018-10-10 12:53:07 +03:00
2019-01-31 19:31:23 +03:00
set_block_size ( EXT2_BLOCK_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 ) {
2020-03-01 22:45:39 +03:00
klog ( ) < < " 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-01-20 23:11:01 +03:00
auto result = read_blocks ( first_block_of_bgdt , blocks_to_read , buffer ) ;
if ( result . is_error ( ) ) {
// 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 ) ;
klog ( ) < < " ext2fs: group[ " < < i < < " ] { block_bitmap: " < < group . bg_block_bitmap < < " , inode_bitmap: " < < group . bg_inode_bitmap < < " , inode_table: " < < group . bg_inode_table < < " } " ;
}
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-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
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-01-21 01:11:17 +03:00
KResult Ext2FS : : write_block_list_for_inode ( InodeIndex inode_index , ext2_inode & e2inode , const Vector < BlockIndex > & blocks )
2019-01-23 04:45:25 +03:00
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2020-11-23 14:30:12 +03:00
if ( blocks . is_empty ( ) ) {
e2inode . i_blocks = 0 ;
memset ( e2inode . i_block , 0 , sizeof ( e2inode . i_block ) ) ;
write_ext2_inode ( inode_index , e2inode ) ;
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.
2020-05-23 14:59:11 +03:00
auto old_block_count = ceil_div ( static_cast < size_t > ( e2inode . i_size ) , block_size ( ) ) ;
2019-01-23 04:45:25 +03:00
2019-03-27 16:24:37 +03:00
auto old_shape = compute_block_list_shape ( old_block_count ) ;
2019-01-23 04:45:25 +03:00
auto new_shape = compute_block_list_shape ( blocks . size ( ) ) ;
Vector < BlockIndex > new_meta_blocks ;
if ( new_shape . meta_blocks > old_shape . meta_blocks ) {
new_meta_blocks = allocate_blocks ( group_index_from_inode ( inode_index ) , new_shape . meta_blocks - old_shape . meta_blocks ) ;
}
2019-01-31 19:31:23 +03:00
e2inode . i_blocks = ( blocks . size ( ) + new_shape . meta_blocks ) * ( block_size ( ) / 512 ) ;
2019-01-23 17:43:29 +03:00
2019-03-27 16:24:37 +03:00
bool inode_dirty = false ;
2019-01-23 04:45:25 +03:00
unsigned output_block_index = 0 ;
unsigned remaining_blocks = blocks . size ( ) ;
for ( unsigned i = 0 ; i < new_shape . direct_blocks ; + + i ) {
2019-03-27 16:24:37 +03:00
if ( e2inode . i_block [ i ] ! = blocks [ output_block_index ] )
inode_dirty = true ;
2021-02-12 13:59:27 +03:00
e2inode . i_block [ i ] = blocks [ 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 ;
}
2019-03-27 16:24:37 +03:00
if ( inode_dirty ) {
2021-02-12 01:05:16 +03:00
if constexpr ( EXT2_DEBUG ) {
dbgln ( " Ext2FS: Writing {} direct block(s) to i_block array of inode {} " , min ( ( size_t ) EXT2_NDIR_BLOCKS , blocks . size ( ) ) , inode_index ) ;
for ( size_t i = 0 ; i < min ( ( size_t ) EXT2_NDIR_BLOCKS , blocks . size ( ) ) ; + + i )
dbgln ( " + {} " , blocks [ i ] ) ;
}
2019-03-27 16:24:37 +03:00
write_ext2_inode ( inode_index , e2inode ) ;
inode_dirty = false ;
}
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
2019-09-28 07:31:40 +03:00
const unsigned entries_per_block = EXT2_ADDR_PER_BLOCK ( & super_block ( ) ) ;
bool ind_block_new = ! e2inode . i_block [ EXT2_IND_BLOCK ] ;
if ( ind_block_new ) {
2019-03-27 16:24:37 +03:00
BlockIndex new_indirect_block = new_meta_blocks . take_last ( ) ;
if ( e2inode . i_block [ EXT2_IND_BLOCK ] ! = new_indirect_block )
inode_dirty = true ;
2021-02-12 13:59:27 +03:00
e2inode . i_block [ EXT2_IND_BLOCK ] = new_indirect_block . value ( ) ;
2019-03-27 16:24:37 +03:00
if ( inode_dirty ) {
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: Adding the indirect block to i_block array of inode {} " , inode_index ) ;
2019-03-27 16:24:37 +03:00
write_ext2_inode ( inode_index , e2inode ) ;
inode_dirty = false ;
}
2019-01-23 04:45:25 +03:00
}
2019-03-27 16:24:37 +03:00
if ( old_shape . indirect_blocks = = new_shape . indirect_blocks ) {
// No need to update the singly indirect block array.
remaining_blocks - = new_shape . indirect_blocks ;
2019-09-28 07:31:40 +03:00
output_block_index + = new_shape . indirect_blocks ;
2019-03-27 16:24:37 +03:00
} else {
2019-01-31 19:31:23 +03:00
auto block_contents = ByteBuffer : : create_uninitialized ( block_size ( ) ) ;
2020-09-15 13:24:14 +03:00
OutputMemoryStream stream { block_contents } ;
2021-02-23 22:42:32 +03:00
VERIFY ( new_shape . indirect_blocks < = entries_per_block ) ;
2019-01-23 04:45:25 +03:00
for ( unsigned i = 0 ; i < new_shape . indirect_blocks ; + + i ) {
2021-02-12 13:59:27 +03:00
stream < < blocks [ output_block_index + + ] . value ( ) ;
2019-01-23 04:45:25 +03:00
- - remaining_blocks ;
}
2020-09-15 13:24:14 +03:00
2019-01-23 04:45:25 +03:00
stream . fill_to_end ( 0 ) ;
2020-09-15 13:24:14 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( stream . data ( ) ) ;
2021-01-21 01:11:17 +03:00
auto result = write_block ( e2inode . i_block [ EXT2_IND_BLOCK ] , buffer , stream . size ( ) ) ;
if ( result . is_error ( ) )
return result ;
2019-01-23 04:45:25 +03:00
}
2019-09-28 07:31:40 +03:00
if ( ! remaining_blocks )
2021-01-21 01:11:17 +03:00
return KSuccess ;
2019-09-28 07:31:40 +03:00
bool dind_block_dirty = false ;
bool dind_block_new = ! e2inode . i_block [ EXT2_DIND_BLOCK ] ;
if ( dind_block_new ) {
BlockIndex new_dindirect_block = new_meta_blocks . take_last ( ) ;
if ( e2inode . i_block [ EXT2_DIND_BLOCK ] ! = new_dindirect_block )
inode_dirty = true ;
2021-02-12 13:59:27 +03:00
e2inode . i_block [ EXT2_DIND_BLOCK ] = new_dindirect_block . value ( ) ;
2019-09-28 07:31:40 +03:00
if ( inode_dirty ) {
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: Adding the doubly-indirect block to i_block array of inode {} " , inode_index ) ;
2019-09-28 07:31:40 +03:00
write_ext2_inode ( inode_index , e2inode ) ;
inode_dirty = false ;
}
}
if ( old_shape . doubly_indirect_blocks = = new_shape . doubly_indirect_blocks ) {
// No need to update the doubly indirect block data.
remaining_blocks - = new_shape . doubly_indirect_blocks ;
output_block_index + = new_shape . doubly_indirect_blocks ;
} else {
2021-02-13 21:05:57 +03:00
unsigned indirect_block_count = divide_rounded_up ( new_shape . doubly_indirect_blocks , entries_per_block ) ;
2019-09-28 07:31:40 +03:00
2019-09-30 12:04:30 +03:00
auto dind_block_contents = ByteBuffer : : create_uninitialized ( block_size ( ) ) ;
2019-09-28 07:31:40 +03:00
if ( dind_block_new ) {
2021-02-21 13:14:43 +03:00
dind_block_contents . zero_fill ( ) ;
2019-09-28 07:31:40 +03:00
dind_block_dirty = true ;
2020-05-18 21:55:08 +03:00
} else {
2020-09-12 06:11:07 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( dind_block_contents . data ( ) ) ;
2021-01-20 23:11:01 +03:00
auto result = read_block ( e2inode . i_block [ EXT2_DIND_BLOCK ] , & buffer , block_size ( ) ) ;
if ( result . is_error ( ) ) {
dbgln ( " Ext2FS: write_block_list_for_inode had error: {} " , result . error ( ) ) ;
2021-01-21 01:11:17 +03:00
return result ;
2021-01-20 23:11:01 +03:00
}
2019-09-28 07:31:40 +03:00
}
2019-09-30 09:57:01 +03:00
auto * dind_block_as_pointers = ( unsigned * ) dind_block_contents . data ( ) ;
2019-09-28 07:31:40 +03:00
2021-02-23 22:42:32 +03:00
VERIFY ( indirect_block_count < = entries_per_block ) ;
2019-09-28 07:31:40 +03:00
for ( unsigned i = 0 ; i < indirect_block_count ; + + i ) {
bool ind_block_dirty = false ;
BlockIndex indirect_block_index = dind_block_as_pointers [ i ] ;
bool ind_block_new = ! indirect_block_index ;
if ( ind_block_new ) {
indirect_block_index = new_meta_blocks . take_last ( ) ;
2021-02-12 13:59:27 +03:00
dind_block_as_pointers [ i ] = indirect_block_index . value ( ) ;
2019-09-28 07:31:40 +03:00
dind_block_dirty = true ;
}
2019-09-30 12:04:30 +03:00
auto ind_block_contents = ByteBuffer : : create_uninitialized ( block_size ( ) ) ;
2019-09-28 07:31:40 +03:00
if ( ind_block_new ) {
2021-02-21 13:14:43 +03:00
ind_block_contents . zero_fill ( ) ;
2019-09-28 07:31:40 +03:00
ind_block_dirty = true ;
2020-05-18 21:55:08 +03:00
} else {
2020-09-12 06:11:07 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( ind_block_contents . data ( ) ) ;
2021-01-20 23:11:01 +03:00
auto result = read_block ( indirect_block_index , & buffer , block_size ( ) ) ;
if ( result . is_error ( ) ) {
dbgln ( " Ext2FS: write_block_list_for_inode had error: {} " , result . error ( ) ) ;
2021-01-21 01:11:17 +03:00
return result ;
2021-01-20 23:11:01 +03:00
}
2019-09-28 07:31:40 +03:00
}
2019-09-30 09:57:01 +03:00
auto * ind_block_as_pointers = ( unsigned * ) ind_block_contents . data ( ) ;
2019-09-28 07:31:40 +03:00
unsigned entries_to_write = new_shape . doubly_indirect_blocks - ( i * entries_per_block ) ;
if ( entries_to_write > entries_per_block )
entries_to_write = entries_per_block ;
2021-02-23 22:42:32 +03:00
VERIFY ( entries_to_write < = entries_per_block ) ;
2019-09-28 07:31:40 +03:00
for ( unsigned j = 0 ; j < entries_to_write ; + + j ) {
BlockIndex output_block = blocks [ output_block_index + + ] ;
if ( ind_block_as_pointers [ j ] ! = output_block ) {
2021-02-12 13:59:27 +03:00
ind_block_as_pointers [ j ] = output_block . value ( ) ;
2019-09-28 07:31:40 +03:00
ind_block_dirty = true ;
}
- - remaining_blocks ;
}
for ( unsigned j = entries_to_write ; j < entries_per_block ; + + j ) {
if ( ind_block_as_pointers [ j ] ! = 0 ) {
ind_block_as_pointers [ j ] = 0 ;
ind_block_dirty = true ;
}
}
if ( ind_block_dirty ) {
2020-09-12 06:11:07 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( ind_block_contents . data ( ) ) ;
int err = write_block ( indirect_block_index , buffer , block_size ( ) ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( err > = 0 ) ;
2019-09-28 07:31:40 +03:00
}
}
for ( unsigned i = indirect_block_count ; i < entries_per_block ; + + i ) {
if ( dind_block_as_pointers [ i ] ! = 0 ) {
dind_block_as_pointers [ i ] = 0 ;
dind_block_dirty = true ;
}
}
if ( dind_block_dirty ) {
2020-09-12 06:11:07 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( dind_block_contents . data ( ) ) ;
int err = write_block ( e2inode . i_block [ EXT2_DIND_BLOCK ] , buffer , block_size ( ) ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( err > = 0 ) ;
2019-09-28 07:31:40 +03:00
}
}
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
// FIXME: Implement!
2021-01-09 20:51:44 +03:00
dbgln ( " we don't know how to write tind ext2fs blocks yet! " ) ;
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2019-01-23 04:45:25 +03:00
}
2019-05-25 18:23:17 +03:00
Vector < Ext2FS : : BlockIndex > Ext2FS : : block_list_for_inode ( const ext2_inode & e2inode , bool include_block_list_blocks ) const
2020-02-21 19:48:50 +03:00
{
auto block_list = block_list_for_inode_impl ( e2inode , include_block_list_blocks ) ;
while ( ! block_list . is_empty ( ) & & block_list . last ( ) = = 0 )
block_list . take_last ( ) ;
return block_list ;
}
Vector < Ext2FS : : BlockIndex > Ext2FS : : block_list_for_inode_impl ( const ext2_inode & e2inode , bool include_block_list_blocks ) const
2018-10-10 12:53:07 +03:00
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2019-01-31 19:31:23 +03:00
unsigned entries_per_block = EXT2_ADDR_PER_BLOCK ( & super_block ( ) ) ;
2018-10-10 12:53:07 +03:00
2020-05-23 14:59:11 +03:00
unsigned block_count = ceil_div ( static_cast < size_t > ( e2inode . i_size ) , 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.
if ( is_symlink ( e2inode . i_mode ) & & e2inode . i_blocks = = 0 )
block_count = 0 ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS::block_list_for_inode(): i_size={}, i_blocks={}, block_count={} " , 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 ) {
auto shape = compute_block_list_shape ( block_count ) ;
blocks_remaining + = shape . meta_blocks ;
}
2019-05-25 18:23:17 +03:00
Vector < BlockIndex > list ;
2020-02-21 19:48:50 +03:00
auto add_block = [ & ] ( BlockIndex bi ) {
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
// be called before block_list_for_inode_impl finishes.
2021-02-12 13:59:27 +03:00
auto process_block_array = [ & ] ( BlockIndex 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 ;
2020-11-24 20:38:41 +03:00
u32 array [ count ] ;
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( ( u8 * ) array ) ;
2021-01-20 23:11:01 +03:00
auto result = read_block ( array_block_index , & buffer , sizeof ( array ) , 0 ) ;
if ( result . is_error ( ) ) {
// FIXME: Stop here and propagate this error.
dbgln ( " Ext2FS: block_list_for_inode_impl had error: {} " , result . error ( ) ) ;
}
2021-02-12 13:59:27 +03:00
for ( unsigned i = 0 ; i < count ; + + i )
callback ( BlockIndex ( array [ i ] ) ) ;
2018-10-10 12:53:07 +03:00
} ;
2021-02-12 13:59:27 +03:00
process_block_array ( e2inode . i_block [ EXT2_IND_BLOCK ] , [ & ] ( BlockIndex 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-12 13:59:27 +03:00
process_block_array ( e2inode . i_block [ EXT2_DIND_BLOCK ] , [ & ] ( BlockIndex block_index ) {
process_block_array ( block_index , [ & ] ( BlockIndex 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-12 13:59:27 +03:00
process_block_array ( e2inode . i_block [ EXT2_TIND_BLOCK ] , [ & ] ( BlockIndex block_index ) {
process_block_array ( block_index , [ & ] ( BlockIndex block_index2 ) {
process_block_array ( block_index2 , [ & ] ( BlockIndex 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 )
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( inode . m_raw_inode . i_links_count = = 0 ) ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: Inode {} has no more links, time to delete! " , 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.
2019-01-22 22:48:48 +03:00
auto block_list = block_list_for_inode ( inode . m_raw_inode , true ) ;
2020-02-21 19:48:50 +03:00
for ( auto block_index : block_list ) {
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 ( ) ) {
auto result = set_block_allocation_state ( block_index , false ) ;
if ( result . is_error ( ) ) {
dbgln ( " Ext2FS: Failed to deallocate block {} in free_inode() " , block_index ) ;
}
}
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-01-09 23:54:27 +03:00
dbgln ( " Ext2FS: Decremented bg_used_dirs_count to {} " , bgd . bg_used_dirs_count ) ;
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 ) ) ;
inode . m_raw_inode . i_dtime = kgettimeofday ( ) . tv_sec ;
write_ext2_inode ( inode . index ( ) , inode . m_raw_inode ) ;
// Mark the inode as free.
2021-02-26 01:07:24 +03:00
auto result = set_inode_allocation_state ( inode . index ( ) , false ) ;
if ( result . is_error ( ) ) {
dbgln ( " Ext2FS: Failed to free inode {} " , inode . index ( ) ) ;
}
2019-01-28 06:16:01 +03:00
}
void Ext2FS : : flush_block_group_descriptor_table ( )
{
2019-02-20 15:09:59 +03:00
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-01-20 23:11:01 +03:00
auto result = write_blocks ( first_block_of_bgdt , blocks_to_write , buffer ) ;
if ( result . is_error ( ) )
dbgln ( " Ext2FS: flush_block_group_descriptor_table had error: {} " , result . error ( ) ) ;
2019-01-22 22:48:48 +03:00
}
2019-11-02 13:36:56 +03:00
void Ext2FS : : flush_writes ( )
{
2019-11-03 15:56:55 +03:00
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-01-20 23:11:01 +03:00
auto result = write_block ( cached_bitmap - > bitmap_block_index , buffer , block_size ( ) ) ;
if ( result . is_error ( ) ) {
dbgln ( " Ext2FS: flush_writes() had error {} " , result . error ( ) ) ;
}
2019-11-02 14:18:43 +03:00
cached_bitmap - > dirty = false ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Flushed bitmap block {} " , 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
}
2019-01-01 05:16:36 +03:00
InodeMetadata Ext2FSInode : : metadata ( ) const
{
2020-01-03 19:47:31 +03:00
LOCKER ( m_lock ) ;
2019-01-01 05:16:36 +03:00
InodeMetadata metadata ;
metadata . inode = identifier ( ) ;
metadata . size = m_raw_inode . i_size ;
metadata . mode = m_raw_inode . i_mode ;
metadata . uid = m_raw_inode . i_uid ;
metadata . gid = m_raw_inode . i_gid ;
2019-01-31 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 ( )
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: flush_metadata for inode {} " , index ( ) ) ;
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
{
2019-02-20 23:41:53 +03:00
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
2019-01-01 05:35:33 +03:00
if ( ! get_inode_allocation_state ( inode . index ( ) ) ) {
m_inode_cache . set ( inode . index ( ) , nullptr ) ;
return nullptr ;
}
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
2019-02-03 06:05:30 +03:00
auto new_inode = adopt ( * 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-01-20 23:11:01 +03:00
auto result = read_block ( block_index , & buffer , sizeof ( ext2_inode ) , offset ) ;
if ( result . is_error ( ) ) {
// 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 ;
}
2020-09-12 06:11:07 +03:00
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 ;
// 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 ) )
return - EFAULT ;
2018-11-13 15:02:39 +03:00
return nread ;
}
2019-02-20 15:09:59 +03:00
Locker fs_locker ( fs ( ) . m_lock ) ;
2019-11-03 12:22:09 +03:00
if ( m_block_list . is_empty ( ) )
m_block_list = fs ( ) . block_list_for_inode ( m_raw_inode ) ;
2018-11-13 15:02:39 +03:00
2018-12-21 04:10:45 +03:00
if ( m_block_list . is_empty ( ) ) {
2021-02-12 11:18:47 +03:00
dmesgln ( " Ext2FS: read_bytes: empty block list for inode {} " , index ( ) ) ;
2018-11-13 15:02:39 +03:00
return - EIO ;
}
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
2020-02-25 16:49:47 +03:00
size_t first_block_logical_index = offset / block_size ;
size_t 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 ;
2020-02-25 16:49:47 +03:00
size_t remaining_count = min ( ( off_t ) count , ( off_t ) size ( ) - offset ) ;
2018-11-13 15:02:39 +03:00
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_VERY_DEBUG , " Ext2FS: Reading up to {} bytes, {} bytes into inode {} to {} " , count , offset , index ( ) , buffer . user_or_kernel_ptr ( ) ) ;
2018-11-13 15:02:39 +03:00
2020-02-25 16:49:47 +03:00
for ( size_t bi = first_block_logical_index ; remaining_count & & bi < = last_block_logical_index ; + + bi ) {
2020-02-21 21:07:23 +03:00
auto block_index = m_block_list [ bi ] ;
2021-02-23 22:42:32 +03:00
VERIFY ( block_index . value ( ) ) ;
2020-05-18 21:55:08 +03:00
size_t offset_into_block = ( bi = = first_block_logical_index ) ? offset_into_first_block : 0 ;
size_t num_bytes_to_copy = min ( block_size - offset_into_block , remaining_count ) ;
2020-09-12 06:11:07 +03:00
auto buffer_offset = buffer . offset ( nread ) ;
2020-12-29 02:05:37 +03:00
int err = fs ( ) . read_block ( block_index , & buffer_offset , num_bytes_to_copy , offset_into_block , allow_cache ) ;
2020-09-12 06:11:07 +03:00
if ( err < 0 ) {
2021-02-12 13:59:27 +03:00
dmesgln ( " Ext2FS: read_bytes: read_block({}) failed (bi: {}) " , block_index . value ( ) , bi ) ;
2020-09-12 06:11:07 +03:00
return err ;
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
{
2019-07-03 22:17:35 +03:00
u64 old_size = size ( ) ;
2019-11-02 18:22:29 +03:00
if ( old_size = = new_size )
return KSuccess ;
u64 block_size = fs ( ) . block_size ( ) ;
2020-02-25 16:49:47 +03:00
size_t blocks_needed_before = ceil_div ( old_size , block_size ) ;
size_t 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 ) {
dbgln ( " Ext2FSInode::resize(): blocks needed before (size was {}): {} " , old_size , blocks_needed_before ) ;
dbgln ( " Ext2FSInode::resize(): blocks needed after (size is {}): {} " , new_size , blocks_needed_after ) ;
}
2019-04-28 23:07:25 +03:00
2019-11-02 14:53:31 +03:00
if ( blocks_needed_after > blocks_needed_before ) {
u32 additional_blocks_needed = blocks_needed_after - blocks_needed_before ;
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
}
2020-11-24 13:49:46 +03:00
Vector < Ext2FS : : BlockIndex > block_list ;
if ( ! m_block_list . is_empty ( ) )
block_list = m_block_list ;
else
2020-11-24 18:14:24 +03:00
block_list = fs ( ) . block_list_for_inode ( m_raw_inode ) ;
2020-11-24 13:49:46 +03:00
2019-04-28 23:07:25 +03:00
if ( blocks_needed_after > blocks_needed_before ) {
auto new_blocks = fs ( ) . allocate_blocks ( fs ( ) . group_index_from_inode ( index ( ) ) , blocks_needed_after - blocks_needed_before ) ;
block_list . append ( move ( new_blocks ) ) ;
} else if ( blocks_needed_after < blocks_needed_before ) {
2021-02-12 01:05:16 +03:00
if constexpr ( EXT2_DEBUG ) {
dbgln ( " Ext2FS: Shrinking inode {}. Old block list is {} entries: " , index ( ) , block_list . size ( ) ) ;
for ( auto block_index : block_list ) {
dbgln ( " # {} " , block_index ) ;
}
2019-05-25 18:23:17 +03:00
}
2019-04-28 23:07:25 +03:00
while ( block_list . size ( ) ! = blocks_needed_after ) {
auto block_index = block_list . take_last ( ) ;
2021-02-26 01:07:24 +03:00
if ( block_index . value ( ) ) {
auto result = fs ( ) . set_block_allocation_state ( block_index , false ) ;
if ( result . is_error ( ) ) {
dbgln ( " Ext2FS: Failed to free block {} in Ext2Inode::resize() " , block_index ) ;
return result ;
}
}
2019-04-28 23:07:25 +03:00
}
}
2021-01-21 01:11:17 +03:00
auto result = fs ( ) . write_block_list_for_inode ( index ( ) , m_raw_inode , block_list ) ;
if ( result . is_error ( ) )
return result ;
2019-04-28 23:07:25 +03:00
m_raw_inode . i_size = new_size ;
set_metadata_dirty ( true ) ;
m_block_list = move ( block_list ) ;
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.
size_t bytes_to_clear = new_size - old_size ;
size_t 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 ) {
auto nwritten = write_bytes ( clear_from , min ( sizeof ( zero_buffer ) , bytes_to_clear ) , UserOrKernelBuffer : : for_kernel_buffer ( zero_buffer ) , nullptr ) ;
if ( nwritten < 0 )
2021-01-21 01:11:17 +03:00
return KResult ( ( ErrnoCode ) - nwritten ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( nwritten ! = 0 ) ;
2021-01-10 00:05:05 +03:00
bytes_to_clear - = nwritten ;
clear_from + = nwritten ;
}
}
2019-11-02 14:53:31 +03:00
return KSuccess ;
2019-04-28 23:07:25 +03:00
}
2020-09-12 06:11:07 +03:00
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
2019-02-20 15:09:59 +03:00
Locker inode_locker ( m_lock ) ;
Locker fs_locker ( fs ( ) . m_lock ) ;
2019-01-23 06:29:56 +03:00
2020-04-04 20:46:55 +03:00
auto result = prepare_to_write_data ( ) ;
if ( result . is_error ( ) )
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-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: write_bytes poking into i_block array for inline symlink '{}' ({} bytes) " , 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 ) )
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 ( ) ;
2020-02-25 16:49:47 +03:00
const size_t block_size = fs ( ) . block_size ( ) ;
2019-07-03 22:17:35 +03:00
u64 old_size = size ( ) ;
u64 new_size = max ( static_cast < u64 > ( offset ) + count , ( u64 ) size ( ) ) ;
2019-01-23 06:29:56 +03:00
2019-11-02 14:53:31 +03:00
auto resize_result = resize ( new_size ) ;
if ( resize_result . is_error ( ) )
return resize_result ;
2019-01-23 06:29:56 +03:00
2019-11-03 12:22:09 +03:00
if ( m_block_list . is_empty ( ) )
m_block_list = fs ( ) . block_list_for_inode ( m_raw_inode ) ;
if ( m_block_list . is_empty ( ) ) {
2021-01-09 23:54:27 +03:00
dbgln ( " Ext2FSInode::write_bytes(): empty block list for inode {} " , index ( ) ) ;
2019-11-03 12:22:09 +03:00
return - EIO ;
}
2020-02-25 16:49:47 +03:00
size_t first_block_logical_index = offset / block_size ;
size_t 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 ;
2020-02-25 16:49:47 +03:00
size_t remaining_count = min ( ( off_t ) count , ( off_t ) new_size - offset ) ;
2019-01-23 06:29:56 +03:00
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_VERY_DEBUG , " Ext2FS: Writing {} bytes, {} bytes into inode {} from {} " , count , offset , index ( ) , data . user_or_kernel_ptr ( ) ) ;
2019-01-23 06:29:56 +03:00
2020-02-25 16:49:47 +03:00
for ( size_t bi = first_block_logical_index ; remaining_count & & bi < = last_block_logical_index ; + + bi ) {
size_t offset_into_block = ( bi = = first_block_logical_index ) ? offset_into_first_block : 0 ;
size_t num_bytes_to_copy = min ( block_size - offset_into_block , remaining_count ) ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: Writing block {} (offset_into_block: {}) " , m_block_list [ bi ] , offset_into_block ) ;
result = fs ( ) . write_block ( m_block_list [ bi ] , data . offset ( nwritten ) , num_bytes_to_copy , offset_into_block , allow_cache ) ;
2021-01-21 01:51:14 +03:00
if ( result . is_error ( ) ) {
2021-01-09 23:54:27 +03:00
dbgln ( " Ext2FS: write_block({}) failed (bi: {}) " , m_block_list [ bi ] , 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-02-12 01:05:16 +03:00
dbgln_if ( EXT2_VERY_DEBUG , " Ext2FS: After write, i_size={}, i_blocks={} ({} blocks in list) " , m_raw_inode . i_size , m_raw_inode . i_blocks , m_block_list . size ( ) ) ;
2019-01-23 06:29:56 +03:00
2019-02-05 10:17:46 +03:00
if ( old_size ! = new_size )
inode_size_changed ( old_size , new_size ) ;
inode_contents_changed ( offset , count , data ) ;
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 ;
}
}
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
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( is_directory ( ) ) ;
2018-11-14 01:44:54 +03:00
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_VERY_DEBUG , " Ext2FS: Traversing as directory: {} " , index ( ) ) ;
2018-11-14 01:44:54 +03:00
2020-05-26 10:36:11 +03:00
auto buffer_or = read_entire ( ) ;
if ( buffer_or . is_error ( ) )
return buffer_or . error ( ) ;
2020-12-18 16:10:10 +03:00
auto & buffer = * buffer_or . value ( ) ;
2019-09-30 09:57:01 +03:00
auto * entry = reinterpret_cast < ext2_dir_entry_2 * > ( buffer . data ( ) ) ;
2018-11-14 01:44:54 +03:00
2018-12-21 04:10:45 +03:00
while ( entry < buffer . end_pointer ( ) ) {
2018-11-14 01:44:54 +03:00
if ( entry - > inode ! = 0 ) {
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2Inode::traverse_as_directory: {}, name_len: {}, rec_len: {}, file_type: {}, name: {} " , entry - > inode , entry - > name_len , entry - > rec_len , entry - > file_type , StringView ( entry - > name , entry - > name_len ) ) ;
2020-08-29 21:25:01 +03:00
if ( ! callback ( { { entry - > name , entry - > name_len } , { fsid ( ) , entry - > inode } , entry - > file_type } ) )
2018-11-14 01:44:54 +03:00
break ;
}
entry = ( ext2_dir_entry_2 * ) ( ( char * ) entry + entry - > rec_len ) ;
}
2020-05-26 10:36:11 +03:00
return KSuccess ;
2018-11-14 01:44:54 +03:00
}
2021-02-02 18:06:03 +03:00
KResult Ext2FSInode : : write_directory ( const Vector < Ext2FSDirectoryEntry > & entries )
2019-06-09 13:46:23 +03:00
{
LOCKER ( m_lock ) ;
int directory_size = 0 ;
2020-01-25 16:28:39 +03:00
for ( auto & entry : entries )
2020-08-18 19:01:42 +03:00
directory_size + = EXT2_DIR_REC_LEN ( entry . name . length ( ) ) ;
2019-06-09 13:46:23 +03:00
auto block_size = fs ( ) . block_size ( ) ;
2020-05-23 14:59:11 +03:00
int blocks_needed = ceil_div ( static_cast < size_t > ( directory_size ) , block_size ) ;
2019-06-09 13:46:23 +03:00
int occupied_size = blocks_needed * block_size ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: New directory inode {} contents to write (size {}, occupied {}): " , index ( ) , directory_size , occupied_size ) ;
2019-06-09 13:46:23 +03:00
auto directory_data = ByteBuffer : : create_uninitialized ( occupied_size ) ;
2020-09-15 13:24:14 +03:00
OutputMemoryStream stream { directory_data } ;
2019-06-09 13:46:23 +03:00
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 ] ;
2020-08-18 19:01:42 +03:00
int record_length = EXT2_DIR_REC_LEN ( entry . name . length ( ) ) ;
2019-06-09 13:46:23 +03:00
if ( i = = entries . size ( ) - 1 )
record_length + = occupied_size - directory_size ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " * Inode: {}, name_len: {}, rec_len: {}, file_type: {}, name: {} " , entry . inode_index , u16 ( entry . name . length ( ) ) , u16 ( 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 ( ) ) ;
2019-07-03 22:17:35 +03:00
stream < < u16 ( 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 ( ) ;
2019-06-09 13:46:23 +03:00
2020-08-18 19:01:42 +03:00
int padding = 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
}
stream . fill_to_end ( 0 ) ;
2020-09-15 13:24:14 +03:00
auto buffer = UserOrKernelBuffer : : for_kernel_buffer ( stream . data ( ) ) ;
ssize_t nwritten = write_bytes ( 0 , stream . size ( ) , buffer , nullptr ) ;
2020-02-20 14:54:15 +03:00
if ( nwritten < 0 )
2021-02-02 18:06:03 +03:00
return KResult ( ( ErrnoCode ) - nwritten ) ;
2019-11-13 11:42:24 +03:00
set_metadata_dirty ( true ) ;
2021-02-02 18:06:03 +03:00
if ( static_cast < size_t > ( nwritten ) ! = directory_data . size ( ) )
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
{
2019-02-20 15:09:59 +03:00
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-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode::add_child: Adding inode {} with name '{}' and mode {:o} to directory {} " , 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-01-09 23:54:27 +03:00
dbgln ( " Ext2FSInode::add_child: Name '{}' already exists in inode {} " , name , index ( ) ) ;
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 ( ) ) ;
2020-09-19 17:39:52 +03:00
did_add_child ( child . identifier ( ) ) ;
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
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode::remove_child('{}') in inode {} " , name , index ( ) ) ;
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 } ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FSInode::remove_child(): Removing '{}' in directory {} " , name , index ( ) ) ;
2019-01-22 09:03:44 +03:00
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
2020-09-19 17:39:52 +03:00
did_remove_child ( child_id ) ;
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
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
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-12 13:59:27 +03:00
auto Ext2FS : : allocate_blocks ( GroupIndex preferred_group_index , size_t count ) - > Vector < BlockIndex >
2019-09-22 19:34:52 +03:00
{
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 )
return { } ;
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 ) ;
auto & cached_bitmap = get_bitmap_block ( bgd . bg_block_bitmap ) ;
int blocks_in_group = min ( blocks_per_group ( ) , super_block ( ) . s_blocks_count ) ;
auto block_bitmap = Bitmap : : wrap ( cached_bitmap . buffer . data ( ) , blocks_in_group ) ;
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-02-26 01:07:24 +03:00
auto result = set_block_allocation_state ( block_index , true ) ;
if ( result . is_error ( ) ) {
// FIXME: We need to bail out of here somehow.
dbgln ( " Ext2FS: Failed to allocate block {} in allocate_blocks() " , block_index ) ;
}
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-12 11:18:47 +03:00
InodeIndex Ext2FS : : find_a_free_inode ( GroupIndex preferred_group )
2018-10-10 12:53:07 +03:00
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: find_a_free_inode(preferred_group: {}) " , preferred_group ) ;
2018-10-10 12:53:07 +03:00
2021-02-12 15:33:58 +03:00
GroupIndex group_index ;
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-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 ) {
if ( is_suitable_group ( i ) )
2019-04-23 16:07:07 +03:00
group_index = i ;
2018-10-10 12:53:07 +03:00
}
}
2019-04-23 16:07:07 +03:00
if ( ! group_index ) {
2021-02-02 18:24:26 +03:00
dmesgln ( " Ext2FS: find_a_free_inode: no suitable group found for new inode " ) ;
2018-10-10 12:53:07 +03:00
return 0 ;
}
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: find_a_free_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 11:18:47 +03:00
InodeIndex first_free_inode_in_group = 0 ;
2019-04-23 16:07:07 +03:00
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
2019-11-02 14:34:18 +03:00
auto & cached_bitmap = get_bitmap_block ( bgd . bg_inode_bitmap ) ;
auto inode_bitmap = Bitmap : : wrap ( cached_bitmap . buffer . data ( ) , 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-12 11:18:47 +03:00
first_free_inode_in_group = InodeIndex ( first_inode_in_group . value ( ) + i ) ;
2019-04-23 16:07:07 +03:00
break ;
}
2018-10-10 12:53:07 +03:00
2019-02-26 00:06:55 +03:00
if ( ! first_free_inode_in_group ) {
2020-03-01 22:45:39 +03:00
klog ( ) < < " Ext2FS: first_free_inode_in_group returned no inode, despite bgd claiming there are inodes :( " ;
2018-10-10 12:53:07 +03:00
return 0 ;
}
2021-02-12 11:18:47 +03:00
InodeIndex inode = first_free_inode_in_group ;
2021-02-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: found suitable inode {} " , inode ) ;
2018-10-10 12:53:07 +03:00
2021-02-23 22:42:32 +03:00
VERIFY ( get_inode_allocation_state ( inode ) = = false ) ;
2018-10-10 12:53:07 +03:00
return inode ;
}
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
}
2019-01-01 05:35:33 +03:00
bool Ext2FS : : get_inode_allocation_state ( InodeIndex index ) const
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2019-01-01 05:35:33 +03:00
if ( index = = 0 )
return true ;
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
auto & cached_bitmap = const_cast < Ext2FS & > ( * this ) . get_bitmap_block ( bgd . bg_inode_bitmap ) ;
return cached_bitmap . 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 01:07:24 +03:00
auto & cached_bitmap = get_bitmap_block ( bitmap_block ) ;
bool current_state = cached_bitmap . bitmap ( blocks_per_group ( ) ) . get ( bit_index ) ;
VERIFY ( current_state ! = new_state ) ;
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 )
{
LOCKER ( m_lock ) ;
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 ;
}
2019-11-02 14:26:20 +03:00
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 )
return * cached_bitmap ;
}
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 ( ) ) ;
2020-12-29 02:05:37 +03:00
int err = read_block ( bitmap_block_index , & buffer , block_size ( ) ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( err > = 0 ) ;
2019-11-02 14:18:43 +03:00
m_cached_bitmaps . append ( make < CachedBitmap > ( bitmap_block_index , move ( block ) ) ) ;
return * m_cached_bitmaps . last ( ) ;
}
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 ) ;
2019-02-20 15:09:59 +03:00
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
{
2019-02-20 15:09:59 +03:00
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-02-02 18:06:03 +03:00
auto result = static_cast < Ext2FSInode & > ( * inode ) . write_directory ( entries ) ;
if ( result . is_error ( ) )
return result ;
2018-10-16 01:35:03 +03:00
2021-02-02 18:37:52 +03:00
result = parent_inode . increment_link_count ( ) ;
2020-02-08 04:34:22 +03:00
if ( result . is_error ( ) )
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
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2020-01-15 23:58:03 +03:00
2021-02-02 18:37:52 +03:00
if ( parent_inode . m_raw_inode . i_links_count = = 0 )
2021-01-21 01:11:17 +03:00
return ENOENT ;
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-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: Adding inode '{}' (mode {:o}) to parent directory {} " , name , mode , parent_inode . index ( ) ) ;
2018-10-10 12:53:07 +03:00
// NOTE: This doesn't commit the inode allocation just yet!
2021-02-02 18:24:26 +03:00
auto inode_id = find_a_free_inode ( ) ;
2018-12-25 01:44:46 +03:00
if ( ! inode_id ) {
2020-03-01 22:45:39 +03:00
klog ( ) < < " Ext2FS: create_inode: allocate_inode failed " ;
2021-01-21 01:11:17 +03:00
return ENOSPC ;
2018-10-16 01:35:03 +03:00
}
2018-10-10 12:53:07 +03:00
// Looks like we're good, time to update the inode bitmap and group+global inode counters.
2021-02-26 01:07:24 +03:00
auto result = set_inode_allocation_state ( inode_id , true ) ;
if ( result . is_error ( ) )
return result ;
2018-10-10 12:53:07 +03:00
2021-02-26 01:07:24 +03:00
auto now = kgettimeofday ( ) ;
2021-02-21 13:16:16 +03:00
ext2_inode e2inode { } ;
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 ;
2019-03-25 04:06:57 +03:00
e2inode . i_atime = now . tv_sec ;
e2inode . i_ctime = now . tv_sec ;
e2inode . i_mtime = now . tv_sec ;
2019-01-31 06:17:16 +03:00
e2inode . i_dtime = 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-12 01:05:16 +03:00
dbgln_if ( EXT2_DEBUG , " Ext2FS: writing initial metadata for inode {} " , inode_id ) ;
2019-01-31 06:17:16 +03:00
e2inode . i_flags = 0 ;
2021-02-26 01:07:24 +03:00
auto success = write_ext2_inode ( inode_id , e2inode ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( success ) ;
2018-10-10 12:53:07 +03:00
2019-02-20 23:41:53 +03:00
// We might have cached the fact that this inode didn't exist. Wipe the slate.
m_inode_cache . remove ( inode_id ) ;
2019-11-03 12:22:09 +03:00
auto inode = get_inode ( { fsid ( ) , inode_id } ) ;
2020-06-24 23:35:56 +03:00
2021-02-26 01:07:24 +03:00
result = parent_inode . add_child ( * inode , name , mode ) ;
2021-01-21 01:11:17 +03:00
if ( result . is_error ( ) )
return result ;
2020-06-24 23:35:56 +03:00
2020-02-08 13:58:28 +03:00
return 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
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
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-01-01 06:24:28 +03:00
if ( ! populate_lookup_cache ( ) )
return { } ;
2018-11-15 18:34:36 +03:00
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 } ) ;
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
2019-01-23 08:53:01 +03:00
int Ext2FSInode : : set_atime ( time_t t )
2019-01-01 05:16:36 +03:00
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2019-01-01 05:16:36 +03:00
if ( fs ( ) . is_readonly ( ) )
return - EROFS ;
m_raw_inode . i_atime = t ;
set_metadata_dirty ( true ) ;
return 0 ;
}
2019-01-23 08:53:01 +03:00
int Ext2FSInode : : set_ctime ( time_t t )
2019-01-01 05:16:36 +03:00
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2019-01-01 05:16:36 +03:00
if ( fs ( ) . is_readonly ( ) )
return - EROFS ;
m_raw_inode . i_ctime = t ;
set_metadata_dirty ( true ) ;
return 0 ;
}
2019-01-23 08:53:01 +03:00
int Ext2FSInode : : set_mtime ( time_t t )
2019-01-01 05:16:36 +03:00
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2019-01-01 05:16:36 +03:00
if ( fs ( ) . is_readonly ( ) )
return - EROFS ;
m_raw_inode . i_mtime = t ;
set_metadata_dirty ( true ) ;
return 0 ;
}
2020-02-08 04:26:33 +03:00
KResult Ext2FSInode : : increment_link_count ( )
2019-01-01 05:16:36 +03:00
{
2019-02-20 15:09:59 +03:00
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 ;
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
{
2019-02-20 15:09:59 +03:00
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 ) ;
2019-01-01 05:16:36 +03:00
- - m_raw_inode . i_links_count ;
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 ( ) ) ;
2019-01-01 05:16:36 +03:00
set_metadata_dirty ( true ) ;
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 )
{
2019-02-20 23:41:53 +03:00
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 ( ) ) ;
2019-01-28 06:16:01 +03:00
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
{
2019-02-20 15:09:59 +03:00
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 )
{
LOCKER ( m_lock ) ;
if ( m_raw_inode . i_uid = = uid & & m_raw_inode . i_gid = = gid )
return KSuccess ;
m_raw_inode . i_uid = uid ;
m_raw_inode . i_gid = gid ;
set_metadata_dirty ( true ) ;
return KSuccess ;
}
2020-02-08 14:07:04 +03:00
KResult Ext2FSInode : : truncate ( u64 size )
2019-03-27 18:42:30 +03:00
{
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 ;
2019-11-02 14:53:31 +03:00
auto result = resize ( size ) ;
if ( result . is_error ( ) )
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 )
{
LOCKER ( m_lock ) ;
if ( m_block_list . is_empty ( ) )
m_block_list = fs ( ) . block_list_for_inode ( m_raw_inode ) ;
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
{
LOCKER ( m_lock ) ;
return super_block ( ) . s_blocks_count ;
}
unsigned Ext2FS : : free_block_count ( ) const
{
LOCKER ( m_lock ) ;
return super_block ( ) . s_free_blocks_count ;
}
unsigned Ext2FS : : total_inode_count ( ) const
{
LOCKER ( m_lock ) ;
return super_block ( ) . s_inodes_count ;
}
unsigned Ext2FS : : free_inode_count ( ) const
{
LOCKER ( m_lock ) ;
return super_block ( ) . s_free_inodes_count ;
}
2019-08-11 16:56:39 +03:00
KResult Ext2FS : : prepare_to_unmount ( ) const
{
2019-08-17 14:49:37 +03:00
LOCKER ( m_lock ) ;
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
}