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>
2018-10-24 13:43:52 +03:00
# include <AK/BufferStream.h>
2020-02-16 03:50:16 +03:00
# include <AK/HashMap.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>
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
2018-10-17 12:47:14 +03:00
//#define EXT2_DEBUG
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 ;
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 ;
}
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 )
: FileBackedFS ( 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 ) ;
2020-04-06 16:50:24 +03:00
ASSERT ( ( sizeof ( ext2_super_block ) % logical_block_size ( ) ) = = 0 ) ;
bool success = raw_write_blocks ( 2 , ( sizeof ( ext2_super_block ) / logical_block_size ( ) ) , ( const u8 * ) & m_super_block ) ;
2019-02-11 13:38:14 +03:00
ASSERT ( 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?
2019-05-25 18:23:17 +03:00
ASSERT ( group_index < = m_block_group_count ) ;
2019-11-03 02:10:24 +03:00
return block_group_descriptors ( ) [ group_index - 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 ) ;
2020-04-06 16:50:24 +03:00
ASSERT ( ( sizeof ( ext2_super_block ) % logical_block_size ( ) ) = = 0 ) ;
bool success = raw_read_blocks ( 2 , ( sizeof ( ext2_super_block ) / logical_block_size ( ) ) , ( u8 * ) & m_super_block ) ;
2019-11-02 13:49:11 +03:00
ASSERT ( success ) ;
2019-01-31 19:31:23 +03:00
auto & super_block = this - > super_block ( ) ;
2018-10-24 14:38:53 +03:00
# ifdef EXT2_DEBUG
2020-03-01 22:45:39 +03:00
klog ( ) < < " ext2fs: super block magic: " < < String : : format ( " %x " , super_block . s_magic ) < < " (super block size: " < < sizeof ( ext2_super_block ) < < " ) " ;
2018-10-24 14:38:53 +03:00
# endif
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 ;
2018-10-24 14:38:53 +03:00
# ifdef EXT2_DEBUG
2020-03-01 22:45:39 +03:00
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-24 14:38:53 +03:00
# endif
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
2019-09-30 12:20:51 +03:00
ASSERT ( block_size ( ) < = ( int ) max_block_size ) ;
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 ;
}
2019-11-03 02:10:24 +03:00
unsigned blocks_to_read = ceil_div ( m_block_group_count * ( unsigned ) sizeof ( ext2_group_desc ) , block_size ( ) ) ;
BlockIndex first_block_of_bgdt = block_size ( ) = = 1024 ? 2 : 1 ;
2020-01-26 12:16:05 +03:00
m_cached_group_descriptor_table = KBuffer : : create_with_size ( block_size ( ) * blocks_to_read , Region : : Access : : Read | Region : : Access : : Write , " Ext2FS: Block group descriptors " ) ;
2019-11-03 02:10:24 +03:00
read_blocks ( first_block_of_bgdt , blocks_to_read , m_cached_group_descriptor_table . value ( ) . data ( ) ) ;
2018-10-31 23:31:56 +03:00
2018-10-24 14:38:53 +03:00
# ifdef EXT2_DEBUG
2019-01-31 19:31:23 +03:00
for ( unsigned i = 1 ; i < = m_block_group_count ; + + i ) {
2018-12-29 05:36:22 +03:00
auto & group = group_descriptor ( i ) ;
2020-03-01 22:45:39 +03:00
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
}
2018-10-24 14:38:53 +03:00
# endif
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
}
2018-12-03 02:20:00 +03:00
InodeIdentifier Ext2FS : : root_inode ( ) const
2018-10-10 12:53:07 +03:00
{
2019-01-23 07:38:54 +03:00
return { fsid ( ) , EXT2_ROOT_INO } ;
2018-10-10 12:53:07 +03:00
}
2019-09-30 12:20:51 +03:00
bool Ext2FS : : read_block_containing_inode ( unsigned inode , unsigned & block_index , unsigned & offset , u8 * buffer ) 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
2018-12-03 02:20:00 +03:00
offset = ( ( inode - 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
2019-09-30 12:20:51 +03:00
return read_block ( block_index , buffer ) ;
2018-10-10 12:53:07 +03:00
}
2019-01-23 04:45:25 +03:00
Ext2FS : : BlockListShape Ext2FS : : compute_block_list_shape ( unsigned blocks )
{
BlockListShape shape ;
const unsigned entries_per_block = EXT2_ADDR_PER_BLOCK ( & super_block ( ) ) ;
unsigned blocks_remaining = blocks ;
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 ) ;
blocks_remaining - = shape . indirect_blocks ;
shape . meta_blocks + = 1 ;
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 ) ;
blocks_remaining - = shape . doubly_indirect_blocks ;
2019-09-28 07:31:40 +03:00
shape . meta_blocks + = 1 ;
shape . meta_blocks + = shape . doubly_indirect_blocks / entries_per_block ;
if ( ( shape . doubly_indirect_blocks % entries_per_block ) ! = 0 )
shape . meta_blocks + = 1 ;
2019-01-23 04:45:25 +03:00
if ( ! blocks_remaining )
return shape ;
2019-09-28 07:31:40 +03:00
dbg ( ) < < " we don't know how to compute tind ext2fs blocks yet! " ;
ASSERT_NOT_REACHED ( ) ;
2019-01-23 04:45:25 +03:00
shape . triply_indirect_blocks = min ( blocks_remaining , entries_per_block * entries_per_block * entries_per_block ) ;
blocks_remaining - = shape . triply_indirect_blocks ;
2019-09-28 07:31:40 +03:00
if ( ! blocks_remaining )
return shape ;
ASSERT_NOT_REACHED ( ) ;
return { } ;
2019-01-23 04:45:25 +03:00
}
2019-01-23 06:29:56 +03:00
bool 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 ) ;
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.
auto old_block_count = ceil_div ( 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 ;
e2inode . i_block [ i ] = blocks [ output_block_index ] ;
+ + output_block_index ;
2019-01-23 04:45:25 +03:00
- - remaining_blocks ;
}
2019-03-27 16:24:37 +03:00
if ( inode_dirty ) {
2019-04-28 23:07:25 +03:00
# ifdef EXT2_DEBUG
2020-02-29 14:51:44 +03:00
dbg ( ) < < " Ext2FS: Writing " < < min ( ( size_t ) EXT2_NDIR_BLOCKS , blocks . size ( ) ) < < " direct block(s) to i_block array of inode " < < inode_index ;
for ( size_t i = 0 ; i < min ( ( size_t ) EXT2_NDIR_BLOCKS , blocks . size ( ) ) ; + + i )
2020-02-24 21:24:29 +03:00
dbg ( ) < < " + " < < blocks [ i ] ;
2019-04-28 23:07:25 +03:00
# endif
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 )
return true ;
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 ;
e2inode . i_block [ EXT2_IND_BLOCK ] = new_indirect_block ;
if ( inode_dirty ) {
2019-09-28 07:31:40 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: Adding the indirect block to i_block array of inode " < < inode_index ;
2019-09-28 07:31:40 +03:00
# endif
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 ( ) ) ;
2019-01-23 04:45:25 +03:00
BufferStream stream ( block_contents ) ;
2019-09-28 07:31:40 +03:00
ASSERT ( 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 ) {
stream < < blocks [ output_block_index + + ] ;
- - remaining_blocks ;
}
stream . fill_to_end ( 0 ) ;
2019-09-30 12:20:51 +03:00
bool success = write_block ( e2inode . i_block [ EXT2_IND_BLOCK ] , block_contents . data ( ) ) ;
2019-02-11 13:38:14 +03:00
ASSERT ( success ) ;
2019-01-23 04:45:25 +03:00
}
2019-09-28 07:31:40 +03:00
if ( ! remaining_blocks )
return true ;
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 ;
e2inode . i_block [ EXT2_DIND_BLOCK ] = new_dindirect_block ;
if ( inode_dirty ) {
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: Adding the doubly-indirect block to i_block array of inode " < < inode_index ;
2019-09-28 07:31:40 +03:00
# endif
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 {
unsigned indirect_block_count = new_shape . doubly_indirect_blocks / entries_per_block ;
if ( ( new_shape . doubly_indirect_blocks % entries_per_block ) ! = 0 )
indirect_block_count + + ;
2019-09-30 12:04:30 +03:00
auto dind_block_contents = ByteBuffer : : create_uninitialized ( block_size ( ) ) ;
read_block ( e2inode . i_block [ EXT2_DIND_BLOCK ] , dind_block_contents . data ( ) ) ;
2019-09-28 07:31:40 +03:00
if ( dind_block_new ) {
2019-09-30 09:57:01 +03:00
memset ( dind_block_contents . data ( ) , 0 , dind_block_contents . size ( ) ) ;
2019-09-28 07:31:40 +03:00
dind_block_dirty = true ;
}
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
ASSERT ( indirect_block_count < = entries_per_block ) ;
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 ( ) ;
dind_block_as_pointers [ i ] = indirect_block_index ;
dind_block_dirty = true ;
}
2019-09-30 12:04:30 +03:00
auto ind_block_contents = ByteBuffer : : create_uninitialized ( block_size ( ) ) ;
read_block ( indirect_block_index , ind_block_contents . data ( ) ) ;
2019-09-28 07:31:40 +03:00
if ( ind_block_new ) {
2019-09-30 09:57:01 +03:00
memset ( ind_block_contents . data ( ) , 0 , dind_block_contents . size ( ) ) ;
2019-09-28 07:31:40 +03:00
ind_block_dirty = true ;
}
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 ;
ASSERT ( entries_to_write < = entries_per_block ) ;
for ( unsigned j = 0 ; j < entries_to_write ; + + j ) {
BlockIndex output_block = blocks [ output_block_index + + ] ;
if ( ind_block_as_pointers [ j ] ! = output_block ) {
ind_block_as_pointers [ j ] = output_block ;
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 ) {
2019-09-30 12:20:51 +03:00
bool success = write_block ( indirect_block_index , ind_block_contents . data ( ) ) ;
2019-09-28 07:31:40 +03:00
ASSERT ( success ) ;
}
}
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 ) {
2019-09-30 12:20:51 +03:00
bool success = write_block ( e2inode . i_block [ EXT2_DIND_BLOCK ] , dind_block_contents . data ( ) ) ;
2019-09-28 07:31:40 +03:00
ASSERT ( success ) ;
}
}
2019-01-23 04:45:25 +03:00
if ( ! remaining_blocks )
return true ;
// FIXME: Implement!
2019-09-28 07:31:40 +03:00
dbg ( ) < < " we don't know how to write tind ext2fs blocks yet! " ;
2019-01-23 04:45:25 +03:00
ASSERT_NOT_REACHED ( ) ;
}
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-02-21 21:07:23 +03:00
unsigned block_count = ceil_div ( 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 ;
2019-05-25 20:19:43 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS::block_list_for_inode(): i_size= " < < e2inode . i_size < < " , i_blocks= " < < e2inode . i_blocks < < " , block_count= " < < block_count ;
2019-05-25 20:19:43 +03:00
# endif
2019-05-25 18:23:17 +03:00
unsigned blocks_remaining = block_count ;
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 ;
2019-06-07 12:43:58 +03:00
auto process_block_array = [ & ] ( unsigned array_block_index , auto & & callback ) {
2019-01-22 18:34:24 +03:00
if ( include_block_list_blocks )
2019-01-31 19:31:23 +03:00
callback ( array_block_index ) ;
2019-09-30 12:04:30 +03:00
auto array_block = ByteBuffer : : create_uninitialized ( block_size ( ) ) ;
read_block ( array_block_index , array_block . data ( ) ) ;
2019-01-31 19:31:23 +03:00
ASSERT ( array_block ) ;
2019-09-30 09:57:01 +03:00
auto * array = reinterpret_cast < const __u32 * > ( array_block . data ( ) ) ;
2019-05-25 18:23:17 +03:00
unsigned count = min ( blocks_remaining , entries_per_block ) ;
2020-02-21 19:48:50 +03:00
for ( BlockIndex i = 0 ; i < count ; + + i )
2018-10-10 12:53:07 +03:00
callback ( array [ i ] ) ;
} ;
2020-02-21 19:48:50 +03:00
process_block_array ( e2inode . i_block [ EXT2_IND_BLOCK ] , [ & ] ( unsigned block_index ) {
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-02-21 19:48:50 +03:00
process_block_array ( e2inode . i_block [ EXT2_DIND_BLOCK ] , [ & ] ( unsigned block_index ) {
process_block_array ( block_index , [ & ] ( unsigned block_index2 ) {
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 ;
2020-02-21 19:48:50 +03:00
process_block_array ( e2inode . i_block [ EXT2_TIND_BLOCK ] , [ & ] ( unsigned block_index ) {
process_block_array ( block_index , [ & ] ( unsigned block_index2 ) {
process_block_array ( block_index2 , [ & ] ( unsigned block_index3 ) {
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 ) ;
2019-01-22 22:48:48 +03:00
ASSERT ( inode . m_raw_inode . i_links_count = = 0 ) ;
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: Inode " < < inode . identifier ( ) < < " has no more links, time to delete! " ;
2019-07-21 19:34:26 +03:00
# endif
2019-01-22 22:48:48 +03:00
2019-03-25 04:06:57 +03:00
struct timeval now ;
kgettimeofday ( now ) ;
inode . m_raw_inode . i_dtime = now . tv_sec ;
2019-01-22 22:48:48 +03:00
write_ext2_inode ( inode . index ( ) , inode . m_raw_inode ) ;
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 ) {
2020-04-05 15:48:58 +03:00
ASSERT ( block_index < = super_block ( ) . s_blocks_count ) ;
2020-02-21 19:48:50 +03:00
if ( block_index )
set_block_allocation_state ( block_index , false ) ;
}
2019-01-22 22:48:48 +03:00
set_inode_allocation_state ( inode . index ( ) , false ) ;
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 ;
2020-01-25 16:28:39 +03:00
dbg ( ) < < " 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
}
}
void Ext2FS : : flush_block_group_descriptor_table ( )
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2019-01-31 19:31:23 +03:00
unsigned blocks_to_write = ceil_div ( m_block_group_count * ( unsigned ) sizeof ( ext2_group_desc ) , block_size ( ) ) ;
unsigned first_block_of_bgdt = block_size ( ) = = 1024 ? 2 : 1 ;
2019-11-03 02:10:24 +03:00
write_blocks ( first_block_of_bgdt , blocks_to_write , ( const u8 * ) block_group_descriptors ( ) ) ;
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 ) {
write_block ( cached_bitmap - > bitmap_block_index , cached_bitmap - > buffer . data ( ) ) ;
cached_bitmap - > dirty = false ;
# ifdef EXT2_DEBUG
dbg ( ) < < " Flushed bitmap block " < < cached_bitmap - > bitmap_block_index ;
# endif
}
}
2019-11-04 14:23:19 +03:00
2020-04-06 11:54:21 +03:00
FileBackedFS : : 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
}
2019-02-03 06:05:30 +03:00
Ext2FSInode : : Ext2FSInode ( Ext2FS & fs , unsigned 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 ) ;
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: flush_metadata for inode " < < identifier ( ) ;
2019-07-21 19:34:26 +03:00
# endif
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 ) ;
2019-01-23 07:38:54 +03:00
ASSERT ( 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 ;
}
2018-12-25 02:32:57 +03:00
unsigned block_index ;
unsigned offset ;
2019-09-30 12:20:51 +03:00
u8 block [ max_block_size ] ;
if ( ! read_block_containing_inode ( inode . index ( ) , block_index , offset , block ) )
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 ( ) ) ) ;
2019-09-30 12:20:51 +03:00
memcpy ( & new_inode - > m_raw_inode , reinterpret_cast < ext2_inode * > ( block + offset ) , sizeof ( ext2_inode ) ) ;
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 ;
}
2019-11-05 21:35:12 +03:00
ssize_t Ext2FSInode : : read_bytes ( off_t offset , ssize_t count , u8 * 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 ) ;
2018-11-13 15:02:39 +03:00
ASSERT ( offset > = 0 ) ;
if ( m_raw_inode . i_size = = 0 )
return 0 ;
// Symbolic links shorter than 60 characters are store inline inside the i_block array.
// This avoids wasting an entire block on short links. (Most links are short.)
if ( is_symlink ( ) & & size ( ) < max_inline_symlink_length ) {
2020-01-16 00:04:57 +03:00
ASSERT ( offset = = 0 ) ;
2019-01-23 08:53:01 +03:00
ssize_t nread = min ( ( off_t ) size ( ) - offset , static_cast < off_t > ( count ) ) ;
2019-07-03 22:17:35 +03:00
memcpy ( buffer , ( ( const u8 * ) m_raw_inode . i_block ) + offset , ( size_t ) nread ) ;
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 ( ) ) {
2020-03-01 22:45:39 +03:00
klog ( ) < < " ext2fs: read_bytes: empty block list for inode " < < index ( ) ;
2018-11-13 15:02:39 +03:00
return - EIO ;
}
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 ) ;
2019-07-03 22:17:35 +03:00
u8 * out = buffer ;
2018-11-13 15:02:39 +03:00
# ifdef EXT2_DEBUG
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: Reading up to " < < count < < " bytes " < < offset < < " bytes into inode " < < identifier ( ) < < " to " < < ( const void * ) buffer ;
2018-11-13 15:02:39 +03:00
# endif
2019-09-30 12:20:51 +03:00
u8 block [ max_block_size ] ;
2019-09-30 12:04:30 +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 ] ;
ASSERT ( block_index ) ;
bool success = fs ( ) . read_block ( block_index , block , description ) ;
2019-09-30 12:04:30 +03:00
if ( ! success ) {
2020-03-01 22:45:39 +03:00
klog ( ) < < " ext2fs: read_bytes: read_block( " < < block_index < < " ) failed (lbi: " < < bi < < " ) " ;
2018-11-13 15:02:39 +03:00
return - EIO ;
}
2020-02-25 16:49:47 +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 ) ;
2019-09-30 12:04:30 +03:00
memcpy ( out , block + offset_into_block , num_bytes_to_copy ) ;
2018-11-13 15:02:39 +03:00
remaining_count - = num_bytes_to_copy ;
nread + = num_bytes_to_copy ;
out + = 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
2019-04-28 23:14:37 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FSInode::resize(): blocks needed before (size was " < < old_size < < " ): " < < blocks_needed_before ;
dbg ( ) < < " Ext2FSInode::resize(): blocks needed after (size is " < < new_size < < " ): " < < blocks_needed_after ;
2019-04-28 23:14:37 +03:00
# endif
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 )
return KResult ( - ENOSPC ) ;
}
2019-04-28 23:07:25 +03:00
auto block_list = fs ( ) . block_list_for_inode ( m_raw_inode ) ;
if ( blocks_needed_after > blocks_needed_before ) {
auto new_blocks = fs ( ) . allocate_blocks ( fs ( ) . group_index_from_inode ( index ( ) ) , blocks_needed_after - blocks_needed_before ) ;
block_list . append ( move ( new_blocks ) ) ;
} else if ( blocks_needed_after < blocks_needed_before ) {
2019-05-25 18:23:17 +03:00
# ifdef EXT2_DEBUG
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: Shrinking inode " < < identifier ( ) < < " . Old block list is " < < block_list . size ( ) < < " entries: " ;
2019-05-25 18:23:17 +03:00
for ( auto block_index : block_list ) {
2020-01-25 16:28:39 +03:00
dbg ( ) < < " # " < < block_index ;
2019-05-25 18:23:17 +03:00
}
# endif
2019-04-28 23:07:25 +03:00
while ( block_list . size ( ) ! = blocks_needed_after ) {
auto block_index = block_list . take_last ( ) ;
2020-02-21 19:48:50 +03:00
if ( block_index )
fs ( ) . set_block_allocation_state ( block_index , false ) ;
2019-04-28 23:07:25 +03:00
}
}
bool success = fs ( ) . write_block_list_for_inode ( index ( ) , m_raw_inode , block_list ) ;
if ( ! success )
2019-11-02 14:53:31 +03:00
return KResult ( - EIO ) ;
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 ) ;
2019-11-02 14:53:31 +03:00
return KSuccess ;
2019-04-28 23:07:25 +03:00
}
2019-11-05 21:35:12 +03:00
ssize_t Ext2FSInode : : write_bytes ( off_t offset , ssize_t count , const u8 * data , FileDescription * description )
2019-01-23 06:29:56 +03:00
{
2019-03-02 03:50:34 +03:00
ASSERT ( offset > = 0 ) ;
ASSERT ( count > = 0 ) ;
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 ( ) ) {
2020-01-16 00:04:57 +03:00
ASSERT ( 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 ) {
2019-03-02 03:50:34 +03:00
# ifdef EXT2_DEBUG
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: write_bytes poking into i_block array for inline symlink ' " < < StringView ( data , count ) < < " ' ( " < < count < < " bytes) " ;
2019-03-02 03:50:34 +03:00
# endif
2019-07-03 22:17:35 +03:00
memcpy ( ( ( u8 * ) m_raw_inode . i_block ) + offset , data , ( size_t ) count ) ;
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-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 ( ) ) {
dbg ( ) < < " Ext2FSInode::write_bytes(): empty block list for inode " < < index ( ) ;
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
2020-02-25 16:49:47 +03:00
size_t last_logical_block_index_in_file = new_size / block_size ;
2019-03-02 03:50:34 +03:00
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-07-03 22:17:35 +03:00
const u8 * in = data ;
2019-01-23 06:29:56 +03:00
# ifdef EXT2_DEBUG
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: Writing " < < count < < " bytes " < < offset < < " bytes into inode " < < identifier ( ) < < " from " < < ( const void * ) data ;
2019-01-23 06:29:56 +03:00
# endif
auto buffer_block = ByteBuffer : : create_uninitialized ( block_size ) ;
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 ) ;
2019-01-23 06:29:56 +03:00
ByteBuffer block ;
2019-03-02 03:50:34 +03:00
if ( offset_into_block ! = 0 | | num_bytes_to_copy ! = block_size ) {
2019-09-30 12:04:30 +03:00
block = ByteBuffer : : create_uninitialized ( block_size ) ;
2019-11-05 21:35:12 +03:00
bool success = fs ( ) . read_block ( m_block_list [ bi ] , block . data ( ) , description ) ;
2019-09-30 12:04:30 +03:00
if ( ! success ) {
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: In write_bytes, read_block( " < < m_block_list [ bi ] < < " ) failed (bi: " < < bi < < " ) " ;
2019-01-23 06:29:56 +03:00
return - EIO ;
}
} else
block = buffer_block ;
2019-09-30 09:57:01 +03:00
memcpy ( block . data ( ) + offset_into_block , in , num_bytes_to_copy ) ;
2019-03-02 03:50:34 +03:00
if ( bi = = last_logical_block_index_in_file & & num_bytes_to_copy < block_size ) {
2020-02-25 16:49:47 +03:00
size_t padding_start = new_size % block_size ;
size_t padding_bytes = block_size - padding_start ;
2019-03-02 03:50:34 +03:00
# ifdef EXT2_DEBUG
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: Padding last block of file with zero x " < < padding_bytes < < " (new_size= " < < new_size < < " , offset_into_block= " < < offset_into_block < < " , num_bytes_to_copy= " < < num_bytes_to_copy < < " ) " ;
2019-03-02 03:50:34 +03:00
# endif
2019-09-30 09:57:01 +03:00
memset ( block . data ( ) + padding_start , 0 , padding_bytes ) ;
2019-03-02 03:50:34 +03:00
}
2019-01-23 06:29:56 +03:00
# ifdef EXT2_DEBUG
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: Writing block " < < m_block_list [ bi ] < < " (offset_into_block: " < < offset_into_block < < " ) " ;
2019-01-23 06:29:56 +03:00
# endif
2019-11-05 21:35:12 +03:00
bool success = fs ( ) . write_block ( m_block_list [ bi ] , block . data ( ) , description ) ;
2019-01-23 06:29:56 +03:00
if ( ! success ) {
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: write_block( " < < m_block_list [ bi ] < < " ) failed (bi: " < < bi < < " ) " ;
2019-02-11 13:38:14 +03:00
ASSERT_NOT_REACHED ( ) ;
2019-01-23 06:29:56 +03:00
return - EIO ;
}
remaining_count - = num_bytes_to_copy ;
nwritten + = num_bytes_to_copy ;
in + = num_bytes_to_copy ;
}
# ifdef EXT2_DEBUG
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: After write, i_size= " < < m_raw_inode . i_size < < " , i_blocks= " < < m_raw_inode . i_blocks < < " ( " < < m_block_list . size ( ) < < " blocks in list) " ;
2019-01-23 06:29:56 +03:00
# endif
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 ;
}
2019-01-28 06:16:01 +03:00
bool Ext2FSInode : : traverse_as_directory ( Function < bool ( const FS : : DirectoryEntry & ) > callback ) const
2018-11-14 01:44:54 +03:00
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2019-10-02 14:37:44 +03:00
ASSERT ( is_directory ( ) ) ;
2018-11-14 01:44:54 +03:00
# ifdef EXT2_DEBUG
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: Traversing as directory: " < < identifier ( ) ;
2018-11-14 01:44:54 +03:00
# endif
auto buffer = read_entire ( ) ;
ASSERT ( buffer ) ;
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 ) {
# ifdef EXT2_DEBUG
2020-02-29 14:51:44 +03:00
dbg ( ) < < " Ext2Inode::traverse_as_directory: " < < entry - > inode < < " , name_len: " < < entry - > name_len < < " , rec_len: " < < entry - > rec_len < < " , file_type: " < < entry - > file_type < < " , name: " < < String ( entry - > name , entry - > name_len ) ;
2018-11-14 01:44:54 +03:00
# endif
if ( ! callback ( { entry - > name , entry - > name_len , { fsid ( ) , entry - > inode } , entry - > file_type } ) )
break ;
}
entry = ( ext2_dir_entry_2 * ) ( ( char * ) entry + entry - > rec_len ) ;
}
return true ;
}
2019-06-09 13:46:23 +03:00
bool Ext2FSInode : : write_directory ( const Vector < FS : : DirectoryEntry > & entries )
{
LOCKER ( m_lock ) ;
int directory_size = 0 ;
2020-01-25 16:28:39 +03:00
for ( auto & entry : entries )
2019-06-09 13:46:23 +03:00
directory_size + = EXT2_DIR_REC_LEN ( entry . name_length ) ;
auto block_size = fs ( ) . block_size ( ) ;
int blocks_needed = ceil_div ( directory_size , block_size ) ;
int occupied_size = blocks_needed * block_size ;
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-01-25 16:28:39 +03:00
dbg ( ) < < " Ext2FS: New directory inode " < < identifier ( ) < < " contents to write (size " < < directory_size < < " , occupied " < < occupied_size < < " ): " ;
2019-07-21 19:34:26 +03:00
# endif
2019-06-09 13:46:23 +03:00
auto directory_data = ByteBuffer : : create_uninitialized ( occupied_size ) ;
BufferStream stream ( directory_data ) ;
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 ] ;
int record_length = EXT2_DIR_REC_LEN ( entry . name_length ) ;
if ( i = = entries . size ( ) - 1 )
record_length + = occupied_size - directory_size ;
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-01-25 16:28:39 +03:00
dbg ( ) < < " * Inode: " < < entry . inode
< < " , name_len: " < < u16 ( entry . name_length )
< < " , rec_len: " < < u16 ( record_length )
< < " , file_type: " < < u8 ( entry . file_type )
< < " , name: " < < entry . name ;
2019-07-21 19:34:26 +03:00
# endif
2019-06-09 13:46:23 +03:00
2019-07-03 22:17:35 +03:00
stream < < u32 ( entry . inode . index ( ) ) ;
stream < < u16 ( record_length ) ;
stream < < u8 ( entry . name_length ) ;
stream < < u8 ( entry . file_type ) ;
2019-06-09 13:46:23 +03:00
stream < < entry . name ;
int padding = record_length - entry . name_length - 8 ;
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 ) ;
2019-09-30 09:57:01 +03:00
ssize_t nwritten = write_bytes ( 0 , directory_data . size ( ) , directory_data . data ( ) , nullptr ) ;
2020-02-20 14:54:15 +03:00
if ( nwritten < 0 )
return false ;
2019-11-13 11:42:24 +03:00
set_metadata_dirty ( true ) ;
2020-02-20 14:54:15 +03:00
return static_cast < size_t > ( nwritten ) = = directory_data . size ( ) ;
2019-06-09 13:46:23 +03:00
}
2019-06-09 11:25:19 +03:00
KResult Ext2FSInode : : add_child ( InodeIdentifier child_id , 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 ) ;
2018-12-25 02:27:39 +03:00
ASSERT ( is_directory ( ) ) ;
2018-10-10 12:53:07 +03:00
2019-09-10 22:04:27 +03:00
if ( name . length ( ) > EXT2_NAME_LEN )
return KResult ( - ENAMETOOLONG ) ;
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-04-05 15:48:58 +03:00
dbg ( ) < < " Ext2FSInode::add_child(): Adding inode " < < child_id . index ( ) < < " with name ' " < < name < < " ' and mode " < < mode < < " to directory " < < index ( ) ;
2019-07-21 19:34:26 +03:00
# endif
2018-10-10 12:53:07 +03:00
2018-12-25 02:27:39 +03:00
Vector < FS : : DirectoryEntry > entries ;
bool name_already_exists = false ;
2019-06-07 12:43:58 +03:00
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 ;
}
entries . append ( entry ) ;
return true ;
} ) ;
2018-12-25 02:27:39 +03:00
if ( name_already_exists ) {
2019-07-08 16:38:44 +03:00
dbg ( ) < < " Ext2FSInode::add_child(): Name ' " < < name < < " ' already exists in inode " < < index ( ) ;
2019-02-27 17:31:26 +03:00
return KResult ( - EEXIST ) ;
2018-10-10 12:53:07 +03:00
}
2019-02-21 15:26:40 +03:00
auto child_inode = fs ( ) . get_inode ( child_id ) ;
2020-02-08 04:26:33 +03:00
if ( child_inode ) {
auto result = child_inode - > increment_link_count ( ) ;
if ( result . is_error ( ) )
return result ;
}
2019-02-21 15:26:40 +03:00
2019-08-01 17:31:05 +03:00
entries . empend ( name . characters_without_null_termination ( ) , name . length ( ) , child_id , to_ext2_file_type ( mode ) ) ;
2019-06-09 13:46:23 +03:00
bool success = write_directory ( entries ) ;
2019-02-21 15:26:40 +03:00
if ( success )
2019-01-22 02:58:13 +03:00
m_lookup_cache . set ( name , child_id . index ( ) ) ;
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 ) ;
2019-01-28 06:16:01 +03:00
# ifdef EXT2_DEBUG
2019-07-08 16:38:44 +03:00
dbg ( ) < < " Ext2FSInode::remove_child( " < < name < < " ) in inode " < < index ( ) ;
2019-01-28 06:16:01 +03:00
# endif
2019-01-22 09:03:44 +03:00
ASSERT ( is_directory ( ) ) ;
2019-02-27 16:11:25 +03:00
auto it = m_lookup_cache . find ( name ) ;
if ( it = = m_lookup_cache . end ( ) )
return KResult ( - 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 } ;
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2019-07-08 16:38:44 +03:00
dbg ( ) < < " Ext2FSInode::remove_child(): Removing ' " < < name < < " ' in directory " < < index ( ) ;
2019-07-21 19:34:26 +03:00
# endif
2019-01-22 09:03:44 +03:00
Vector < FS : : DirectoryEntry > entries ;
2019-06-07 12:43:58 +03:00
traverse_as_directory ( [ & ] ( auto & entry ) {
2019-06-12 16:25:28 +03:00
if ( name ! = entry . name )
2019-02-21 15:26:40 +03:00
entries . append ( entry ) ;
2019-01-22 09:03:44 +03:00
return true ;
} ) ;
2019-06-09 13:46:23 +03:00
bool success = write_directory ( entries ) ;
2019-01-22 09:03:44 +03:00
if ( ! success ) {
2019-06-09 13:46:23 +03:00
// FIXME: Plumb error from write_directory().
2019-02-27 16:11:25 +03:00
return KResult ( - EIO ) ;
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 ) ;
child_inode - > decrement_link_count ( ) ;
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
}
2018-12-03 02:20:00 +03:00
bool Ext2FS : : write_ext2_inode ( unsigned inode , const ext2_inode & e2inode )
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 block_index ;
2018-10-10 12:53:07 +03:00
unsigned offset ;
2019-09-30 12:20:51 +03:00
u8 block [ max_block_size ] ;
if ( ! read_block_containing_inode ( inode , block_index , offset , block ) )
2018-10-10 12:53:07 +03:00
return false ;
2019-09-30 12:20:51 +03:00
memcpy ( reinterpret_cast < ext2_inode * > ( block + offset ) , & e2inode , inode_size ( ) ) ;
2019-02-11 13:38:14 +03:00
bool success = write_block ( block_index , block ) ;
ASSERT ( success ) ;
return success ;
2018-10-10 12:53:07 +03:00
}
2020-02-25 16:49:47 +03:00
Vector < Ext2FS : : BlockIndex > Ext2FS : : allocate_blocks ( GroupIndex preferred_group_index , size_t count )
2019-09-22 19:34:52 +03:00
{
LOCKER ( m_lock ) ;
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: allocate_blocks(preferred group: " < < preferred_group_index < < " , count: " < < count < < " ) " ;
2019-09-22 19:34:52 +03:00
# endif
if ( count = = 0 )
return { } ;
Vector < BlockIndex > blocks ;
2019-09-28 07:31:40 +03:00
# ifdef EXT2_DEBUG
2019-09-22 19:34:52 +03:00
dbg ( ) < < " Ext2FS: allocate_blocks: " ;
2019-09-28 07:31:40 +03:00
# endif
2019-09-22 19:34:52 +03:00
blocks . ensure_capacity ( count ) ;
2020-01-26 11:48:24 +03:00
GroupIndex group_index = preferred_group_index ;
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 ;
for ( ; group_index < m_block_group_count ; + + group_index ) {
if ( group_descriptor ( group_index ) . bg_free_blocks_count ) {
found_a_group = true ;
break ;
}
}
}
2020-03-12 00:06:17 +03:00
2020-01-26 11:48:24 +03:00
ASSERT ( found_a_group ) ;
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 ) ;
BlockIndex first_block_in_group = ( group_index - 1 ) * blocks_per_group ( ) + first_block_index ( ) ;
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 ) ;
ASSERT ( first_unset_bit_index . has_value ( ) ) ;
2019-09-28 07:31:40 +03:00
# ifdef EXT2_DEBUG
2020-01-26 11:48:24 +03:00
dbg ( ) < < " Ext2FS: allocating free region of size: " < < free_region_size < < " [ " < < group_index < < " ] " ;
2019-09-28 07:31:40 +03:00
# endif
2020-02-24 11:55:46 +03:00
for ( size_t i = 0 ; i < free_region_size ; + + i ) {
BlockIndex block_index = ( first_unset_bit_index . value ( ) + i ) + first_block_in_group ;
2020-01-26 11:48:24 +03:00
set_block_allocation_state ( block_index , true ) ;
blocks . unchecked_append ( block_index ) ;
# ifdef EXT2_DEBUG
dbg ( ) < < " allocated > " < < block_index ;
# endif
}
2018-10-16 01:35:03 +03:00
}
2019-09-22 19:34:52 +03:00
ASSERT ( blocks . size ( ) = = count ) ;
2018-10-16 01:35:03 +03:00
return blocks ;
}
2019-11-17 21:17:10 +03:00
unsigned Ext2FS : : find_a_free_inode ( GroupIndex preferred_group , off_t expected_size )
2018-10-10 12:53:07 +03:00
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: find_a_free_inode(preferred_group: " < < preferred_group < < " , expected_size: " < < String : : format ( " %ld " , expected_size ) < < " ) " ;
2019-07-21 19:34:26 +03:00
# endif
2018-10-10 12:53:07 +03:00
2019-01-31 19:31:23 +03:00
unsigned needed_blocks = ceil_div ( expected_size , block_size ( ) ) ;
2018-10-10 12:53:07 +03:00
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: minimum needed blocks: " < < needed_blocks ;
2019-07-21 19:34:26 +03:00
# endif
2018-10-10 12:53:07 +03:00
2019-04-23 16:07:07 +03:00
unsigned group_index = 0 ;
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.
2019-06-07 12:43:58 +03:00
auto is_suitable_group = [ this , needed_blocks ] ( GroupIndex group_index ) {
2019-05-25 18:23:17 +03:00
auto & bgd = group_descriptor ( group_index ) ;
2019-01-31 19:31:23 +03:00
return bgd . bg_free_inodes_count & & bgd . bg_free_blocks_count > = needed_blocks ;
2018-10-10 12:53:07 +03:00
} ;
2019-01-31 19:31:23 +03:00
if ( preferred_group & & 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 ) {
2020-03-01 22:45:39 +03:00
klog ( ) < < " Ext2FS: find_a_free_inode: no suitable group found for new inode with " < < needed_blocks < < " blocks needed :( " ;
2018-10-10 12:53:07 +03:00
return 0 ;
}
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: find_a_free_inode: found suitable group [ " < < group_index < < " ] for new inode with " < < needed_blocks < < " blocks needed :^) " ;
2019-07-21 19:34:26 +03:00
# endif
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 ) ;
2019-02-26 00:06:55 +03:00
unsigned first_free_inode_in_group = 0 ;
2019-04-23 16:07:07 +03:00
unsigned first_inode_in_group = ( group_index - 1 ) * inodes_per_group ( ) + 1 ;
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 ;
first_free_inode_in_group = first_inode_in_group + i ;
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 ;
}
2019-02-26 00:06:55 +03:00
unsigned inode = first_free_inode_in_group ;
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: found suitable inode " < < inode ;
2019-07-21 19:34:26 +03:00
# endif
2018-10-10 12:53:07 +03:00
2019-02-16 01:24:01 +03:00
ASSERT ( 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 ;
return ( block_index - 1 ) / blocks_per_group ( ) + 1 ;
}
2018-12-03 02:20:00 +03:00
unsigned Ext2FS : : group_index_from_inode ( unsigned inode ) const
2018-10-10 12:53:07 +03:00
{
if ( ! inode )
return 0 ;
2018-12-03 02:20:00 +03:00
return ( inode - 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 ;
2019-02-11 14:46:56 +03:00
unsigned group_index = group_index_from_inode ( index ) ;
auto & bgd = group_descriptor ( group_index ) ;
unsigned index_in_group = index - ( ( group_index - 1 ) * inodes_per_group ( ) ) ;
2019-04-23 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
}
2019-05-25 18:23:17 +03:00
bool Ext2FS : : set_inode_allocation_state ( InodeIndex inode_index , bool new_state )
2018-10-10 12:53:07 +03:00
{
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2019-05-25 18:23:17 +03:00
unsigned group_index = group_index_from_inode ( inode_index ) ;
2019-02-11 14:46:56 +03:00
auto & bgd = group_descriptor ( group_index ) ;
2019-05-25 18:23:17 +03:00
unsigned index_in_group = inode_index - ( ( group_index - 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 = get_bitmap_block ( bgd . bg_inode_bitmap ) ;
bool current_state = cached_bitmap . bitmap ( inodes_per_group ( ) ) . get ( bit_index ) ;
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: set_inode_allocation_state( " < < inode_index < < " ) " < < String : : format ( " %u " , current_state ) < < " -> " < < String : : format ( " %u " , new_state ) ;
2019-07-21 19:34:26 +03:00
# endif
2018-10-10 12:53:07 +03:00
2019-05-25 18:23:17 +03:00
if ( current_state = = new_state ) {
2019-04-23 17:21:07 +03:00
ASSERT_NOT_REACHED ( ) ;
2018-10-10 12:53:07 +03:00
return true ;
2019-04-23 17:21:07 +03:00
}
2018-10-10 12:53:07 +03:00
2019-11-02 14:34:18 +03:00
cached_bitmap . bitmap ( inodes_per_group ( ) ) . set ( bit_index , new_state ) ;
cached_bitmap . dirty = true ;
2018-10-10 12:53:07 +03:00
// Update superblock
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: superblock free inode count " < < m_super_block . s_free_inodes_count < < " -> " < < ( m_super_block . s_free_inodes_count - 1 ) ;
2019-07-21 19:34:26 +03:00
# endif
2019-05-25 18:23:17 +03:00
if ( new_state )
2019-11-02 13:49:11 +03:00
- - m_super_block . s_free_inodes_count ;
2018-10-10 12:53:07 +03:00
else
2019-11-02 13:49:11 +03:00
+ + m_super_block . s_free_inodes_count ;
2019-11-02 13:36:56 +03:00
m_super_block_dirty = true ;
2018-10-10 12:53:07 +03:00
// Update BGD
2019-01-31 19:31:23 +03:00
auto & mutable_bgd = const_cast < ext2_group_desc & > ( bgd ) ;
2019-05-25 18:23:17 +03:00
if ( new_state )
2019-01-31 19:31:23 +03:00
- - mutable_bgd . bg_free_inodes_count ;
2018-10-10 12:53:07 +03:00
else
2019-01-31 19:31:23 +03:00
+ + mutable_bgd . bg_free_inodes_count ;
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: group free inode count " < < bgd . bg_free_inodes_count < < " -> " < < ( bgd . bg_free_inodes_count - 1 ) ;
2019-07-21 19:34:26 +03:00
# endif
2018-10-10 12:53:07 +03:00
2019-11-02 13:36:56 +03:00
m_block_group_descriptors_dirty = true ;
2018-10-16 01:35:03 +03:00
return true ;
}
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 " ) ;
2019-11-02 14:18:43 +03:00
bool success = read_block ( bitmap_block_index , block . data ( ) ) ;
ASSERT ( success ) ;
m_cached_bitmaps . append ( make < CachedBitmap > ( bitmap_block_index , move ( block ) ) ) ;
return * m_cached_bitmaps . last ( ) ;
}
2019-02-16 01:24:01 +03:00
bool Ext2FS : : set_block_allocation_state ( BlockIndex block_index , bool new_state )
2018-10-16 01:35:03 +03:00
{
2020-02-21 19:48:50 +03:00
ASSERT ( block_index ! = 0 ) ;
2019-02-20 15:09:59 +03:00
LOCKER ( m_lock ) ;
2019-04-25 23:05:32 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: set_block_allocation_state(block= " < < block_index < < " , state= " < < String : : format ( " %u " , new_state ) < < " ) " ;
2019-04-25 23:05:32 +03:00
# endif
2019-11-02 14:18:43 +03:00
GroupIndex group_index = group_index_from_block_index ( block_index ) ;
2019-02-16 01:24:01 +03:00
auto & bgd = group_descriptor ( group_index ) ;
2019-09-22 19:34:52 +03:00
BlockIndex index_in_group = ( block_index - first_block_index ( ) ) - ( ( group_index - 1 ) * blocks_per_group ( ) ) ;
2019-04-23 17:38:45 +03:00
unsigned bit_index = index_in_group % blocks_per_group ( ) ;
2019-11-02 14:18:43 +03:00
2019-11-02 14:26:20 +03:00
auto & cached_bitmap = get_bitmap_block ( bgd . bg_block_bitmap ) ;
2019-11-02 14:18:43 +03:00
bool current_state = cached_bitmap . bitmap ( blocks_per_group ( ) ) . get ( bit_index ) ;
2019-04-25 23:05:32 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: block " < < block_index < < " state: " < < String : : format ( " %u " , current_state ) < < " -> " < < String : : format ( " %u " , new_state ) < < " (in bitmap block " < < bgd . bg_block_bitmap < < " ) " ;
2019-07-21 19:34:26 +03:00
# endif
2018-10-16 01:35:03 +03:00
2019-04-23 17:21:07 +03:00
if ( current_state = = new_state ) {
ASSERT_NOT_REACHED ( ) ;
2018-10-16 01:35:03 +03:00
return true ;
2019-04-23 17:21:07 +03:00
}
2018-10-16 01:35:03 +03:00
2019-11-02 14:18:43 +03:00
cached_bitmap . bitmap ( blocks_per_group ( ) ) . set ( bit_index , new_state ) ;
cached_bitmap . dirty = true ;
2018-10-16 01:35:03 +03:00
// Update superblock
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: superblock free block count " < < m_super_block . s_free_blocks_count < < " -> " < < ( m_super_block . s_free_blocks_count - 1 ) ;
2019-07-21 19:34:26 +03:00
# endif
2019-01-31 19:31:23 +03:00
if ( new_state )
2019-11-02 13:49:11 +03:00
- - m_super_block . s_free_blocks_count ;
2018-10-16 01:35:03 +03:00
else
2019-11-02 13:49:11 +03:00
+ + m_super_block . s_free_blocks_count ;
2019-11-02 13:36:56 +03:00
m_super_block_dirty = true ;
2018-10-16 01:35:03 +03:00
// Update BGD
2019-01-31 19:31:23 +03:00
auto & mutable_bgd = const_cast < ext2_group_desc & > ( bgd ) ;
if ( new_state )
- - mutable_bgd . bg_free_blocks_count ;
2018-10-16 01:35:03 +03:00
else
2019-01-31 19:31:23 +03:00
+ + mutable_bgd . bg_free_blocks_count ;
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: group " < < group_index < < " free block count " < < bgd . bg_free_blocks_count < < " -> " < < ( bgd . bg_free_blocks_count - 1 ) ;
2019-07-21 19:34:26 +03:00
# endif
2018-10-16 01:35:03 +03:00
2019-11-02 13:36:56 +03:00
m_block_group_descriptors_dirty = true ;
2018-10-10 12:53:07 +03:00
return true ;
}
2020-02-08 04:34:22 +03:00
KResult Ext2FS : : create_directory ( InodeIdentifier parent_id , 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 ) ;
2019-01-23 07:38:54 +03:00
ASSERT ( parent_id . fsid ( ) = = fsid ( ) ) ;
2018-10-16 01:35:03 +03:00
// Fix up the mode to definitely be a directory.
// FIXME: This is a bit on the hackish side.
mode & = ~ 0170000 ;
mode | = 0040000 ;
// NOTE: When creating a new directory, make the size 1 block.
// There's probably a better strategy here, but this works for now.
2020-02-08 13:58:28 +03:00
auto inode_or_error = create_inode ( parent_id , name , mode , block_size ( ) , 0 , uid , gid ) ;
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
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-29 14:51:44 +03:00
dbg ( ) < < " Ext2FS: create_directory: created new directory named ' " < < name < < " ' with inode " < < inode - > identifier ( ) ;
2019-07-21 19:34:26 +03:00
# endif
2018-10-16 01:35:03 +03:00
Vector < DirectoryEntry > entries ;
2019-08-01 17:31:05 +03:00
entries . empend ( " . " , inode - > identifier ( ) , EXT2_FT_DIR ) ;
entries . empend ( " .. " , parent_id , EXT2_FT_DIR ) ;
2018-10-16 01:35:03 +03:00
2019-06-09 13:46:23 +03:00
bool success = static_cast < Ext2FSInode & > ( * inode ) . write_directory ( entries ) ;
2018-10-16 01:35:03 +03:00
ASSERT ( success ) ;
2018-12-25 01:58:00 +03:00
auto parent_inode = get_inode ( parent_id ) ;
2020-02-08 04:34:22 +03:00
auto result = parent_inode - > increment_link_count ( ) ;
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-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: incremented bg_used_dirs_count " < < bgd . bg_used_dirs_count - 1 < < " -> " < < bgd . bg_used_dirs_count ;
2019-07-21 19:34:26 +03:00
# endif
2018-10-16 01:35:03 +03:00
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
}
2020-02-08 13:58:28 +03:00
KResultOr < NonnullRefPtr < Inode > > Ext2FS : : create_inode ( InodeIdentifier parent_id , const String & name , mode_t mode , off_t size , 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 ) ;
2019-01-23 07:38:54 +03:00
ASSERT ( parent_id . fsid ( ) = = fsid ( ) ) ;
2018-12-25 02:27:39 +03:00
auto parent_inode = get_inode ( parent_id ) ;
2020-01-15 23:58:03 +03:00
ASSERT ( parent_inode ) ;
2020-02-08 13:58:28 +03:00
if ( static_cast < const Ext2FSInode & > ( * parent_inode ) . m_raw_inode . i_links_count = = 0 )
return KResult ( - ENOENT ) ;
2018-10-10 12:53:07 +03:00
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-29 14:51:44 +03:00
dbg ( ) < < " Ext2FS: Adding inode ' " < < name < < " ' (mode " < < String : : format ( " %o " , mode ) < < " ) to parent directory " < < parent_inode - > identifier ( ) ;
2019-07-21 19:34:26 +03:00
# endif
2018-10-10 12:53:07 +03:00
2020-02-25 16:49:47 +03:00
size_t needed_blocks = ceil_div ( size , block_size ( ) ) ;
2019-09-22 19:44:05 +03:00
if ( ( size_t ) needed_blocks > super_block ( ) . s_free_blocks_count ) {
2019-09-22 19:34:52 +03:00
dbg ( ) < < " Ext2FS: create_inode: not enough free blocks " ;
2020-02-08 13:58:28 +03:00
return KResult ( - ENOSPC ) ;
2019-09-22 19:34:52 +03:00
}
2018-10-10 12:53:07 +03:00
// NOTE: This doesn't commit the inode allocation just yet!
2019-11-17 21:17:10 +03:00
auto inode_id = find_a_free_inode ( 0 , size ) ;
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 " ;
2020-02-08 13:58:28 +03:00
return KResult ( - ENOSPC ) ;
2018-10-16 01:35:03 +03:00
}
2018-10-10 12:53:07 +03:00
// Try adding it to the directory first, in case the name is already in use.
2019-06-09 13:48:34 +03:00
auto result = parent_inode - > add_child ( { fsid ( ) , inode_id } , name , mode ) ;
2020-02-08 13:58:28 +03:00
if ( result . is_error ( ) )
return result ;
2018-10-10 12:53:07 +03:00
2019-09-22 19:50:24 +03:00
auto blocks = allocate_blocks ( group_index_from_inode ( inode_id ) , needed_blocks ) ;
ASSERT ( blocks . size ( ) = = needed_blocks ) ;
2018-10-10 12:53:07 +03:00
// Looks like we're good, time to update the inode bitmap and group+global inode counters.
2019-02-27 17:31:26 +03:00
bool success = set_inode_allocation_state ( inode_id , true ) ;
2018-10-10 12:53:07 +03:00
ASSERT ( success ) ;
2019-01-31 19:31:23 +03:00
unsigned initial_links_count ;
if ( is_directory ( mode ) )
initial_links_count = 2 ; // (parent directory + "." entry in self)
2018-10-16 01:35:03 +03:00
else
2019-01-31 19:31:23 +03:00
initial_links_count = 1 ;
2018-10-16 01:35:03 +03:00
2019-03-25 04:06:57 +03:00
struct timeval now ;
kgettimeofday ( now ) ;
2019-01-31 06:17:16 +03:00
ext2_inode e2inode ;
memset ( & e2inode , 0 , sizeof ( ext2_inode ) ) ;
e2inode . i_mode = mode ;
2020-01-03 22:13:21 +03:00
e2inode . i_uid = uid ;
e2inode . i_gid = gid ;
2019-01-31 06:17:16 +03:00
e2inode . i_size = size ;
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 ;
2019-01-31 19:31:23 +03:00
e2inode . i_links_count = initial_links_count ;
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 ;
2019-01-31 06:17:16 +03:00
success = write_block_list_for_inode ( inode_id , e2inode , blocks ) ;
2019-01-23 04:45:25 +03:00
ASSERT ( success ) ;
2018-10-16 01:35:03 +03:00
2019-07-21 19:34:26 +03:00
# ifdef EXT2_DEBUG
2020-02-24 21:24:29 +03:00
dbg ( ) < < " Ext2FS: writing initial metadata for inode " < < inode_id ;
2019-07-21 19:34:26 +03:00
# endif
2019-01-31 06:17:16 +03:00
e2inode . i_flags = 0 ;
success = write_ext2_inode ( inode_id , e2inode ) ;
2018-10-10 12:53:07 +03:00
ASSERT ( success ) ;
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 } ) ;
// If we've already computed a block list, no sense in throwing it away.
static_cast < Ext2FSInode & > ( * inode ) . m_block_list = move ( blocks ) ;
2020-02-08 13:58:28 +03:00
return inode . release_nonnull ( ) ;
2018-10-10 12:53:07 +03:00
}
2019-01-28 06:16:01 +03:00
void 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 ( ) )
return ;
2018-11-15 19:04:55 +03:00
HashMap < String , unsigned > children ;
2019-06-07 12:43:58 +03:00
traverse_as_directory ( [ & children ] ( auto & entry ) {
2018-11-15 19:04:55 +03:00
children . set ( String ( entry . name , entry . name_length ) , entry . inode . index ( ) ) ;
return true ;
} ) ;
2018-12-21 04:10:45 +03:00
if ( ! m_lookup_cache . is_empty ( ) )
2018-11-15 19:04:55 +03:00
return ;
m_lookup_cache = move ( children ) ;
}
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
{
ASSERT ( is_directory ( ) ) ;
populate_lookup_cache ( ) ;
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 ( ) )
2020-02-08 04:26:33 +03:00
return KResult ( - EROFS ) ;
if ( m_raw_inode . i_links_count = = max_link_count )
return KResult ( - 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 ( ) )
2020-02-08 04:26:33 +03:00
return KResult ( - EROFS ) ;
2019-01-22 09:03:44 +03:00
ASSERT ( 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
size_t Ext2FSInode : : directory_entry_count ( ) const
{
ASSERT ( is_directory ( ) ) ;
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 ;
}
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 )
2019-08-11 16:56:39 +03:00
return KResult ( - EBUSY ) ;
}
m_inode_cache . clear ( ) ;
return KSuccess ;
}
2020-02-16 03:27:42 +03:00
}