2020-04-29 08:59:28 -04:00
/*
2020-07-03 05:31:22 -04:00
* bktr . c
2020-04-29 08:59:28 -04:00
*
2020-07-07 07:55:37 -04:00
* Copyright ( c ) 2018 - 2020 , SciresM .
2022-03-17 09:58:40 -03:00
* Copyright ( c ) 2020 - 2022 , DarkMatterCore < pabloacurielz @ gmail . com > .
2020-07-03 05:31:22 -04:00
*
* This file is part of nxdumptool ( https : //github.com/DarkMatterCore/nxdumptool).
*
2021-03-25 16:26:58 -03:00
* nxdumptool is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
2020-04-29 08:59:28 -04:00
*
2021-03-25 16:26:58 -03:00
* nxdumptool is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2020-04-29 08:59:28 -04:00
*
* You should have received a copy of the GNU General Public License
2021-03-25 16:26:58 -03:00
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
2020-04-29 08:59:28 -04:00
*/
2021-03-26 01:35:14 -03:00
# include "nxdt_utils.h"
2020-07-03 05:31:22 -04:00
# include "bktr.h"
2022-06-29 08:41:58 -04:00
# include "aes.h"
2020-04-29 08:59:28 -04:00
2022-07-02 06:09:49 -04:00
/* Type definitions. */
typedef struct {
u64 offset ;
u32 stride ;
} BucketTreeStorageNodeOffset ;
typedef struct {
BucketTreeStorageNodeOffset start ;
u32 count ;
u32 index ;
} BucketTreeStorageNode ;
2022-07-02 10:07:08 -04:00
typedef struct {
BucketTreeContext * bktr_ctx ;
BucketTreeNodeHeader entry_set ;
u32 entry_index ;
void * entry ;
} BucketTreeVisitor ;
2022-07-02 06:09:49 -04:00
/* Global variables. */
static const char * g_bktrStorageTypeNames [ ] = {
[ BucketTreeStorageType_Indirect ] = " Indirect " ,
[ BucketTreeStorageType_AesCtrEx ] = " AesCtrEx " ,
[ BucketTreeStorageType_Compressed ] = " Compressed " ,
[ BucketTreeStorageType_Sparse ] = " Sparse "
} ;
2020-04-29 08:59:28 -04:00
/* Function prototypes. */
2022-07-02 06:09:49 -04:00
static const char * bktrGetStorageTypeName ( u8 storage_type ) ;
static bool bktrInitializeIndirectStorageContext ( BucketTreeContext * out , NcaFsSectionContext * nca_fs_ctx , bool is_sparse ) ;
static bool bktrInitializeAesCtrExStorageContext ( BucketTreeContext * out , NcaFsSectionContext * nca_fs_ctx ) ;
static bool bktrInitializeCompressedStorageContext ( BucketTreeContext * out , NcaFsSectionContext * nca_fs_ctx ) ;
static bool bktrVerifyBucketInfo ( NcaBucketInfo * bucket , u64 node_size , u64 entry_size ) ;
static bool bktrValidateTableOffsetNode ( const BucketTreeTable * table , u64 node_size , u64 entry_size , u32 entry_count , u64 * out_start_offset , u64 * out_end_offset ) ;
NX_INLINE bool bktrVerifyNodeHeader ( const BucketTreeNodeHeader * node_header , u32 node_index , u64 node_size , u64 entry_size ) ;
static u64 bktrQueryNodeStorageSize ( u64 node_size , u64 entry_size , u32 entry_count ) ;
static u64 bktrQueryEntryStorageSize ( u64 node_size , u64 entry_size , u32 entry_count ) ;
NX_INLINE u32 bktrGetEntryCount ( u64 node_size , u64 entry_size ) ;
NX_INLINE u32 bktrGetOffsetCount ( u64 node_size ) ;
NX_INLINE u32 bktrGetEntrySetCount ( u64 node_size , u64 entry_size , u32 entry_count ) ;
NX_INLINE u32 bktrGetNodeL2Count ( u64 node_size , u64 entry_size , u32 entry_count ) ;
NX_INLINE const void * bktrGetNodeArray ( const BucketTreeNodeHeader * node_header ) ;
NX_INLINE const u64 * bktrGetOffsetNodeArray ( const BucketTreeOffsetNode * offset_node ) ;
NX_INLINE const u64 * bktrGetOffsetNodeBegin ( const BucketTreeOffsetNode * offset_node ) ;
NX_INLINE const u64 * bktrGetOffsetNodeEnd ( const BucketTreeOffsetNode * offset_node ) ;
2022-07-02 10:07:08 -04:00
static bool bktrFindStorageEntry ( BucketTreeContext * ctx , u64 virtual_offset , BucketTreeVisitor * out_visitor ) ;
2022-07-02 06:09:49 -04:00
static bool bktrGetTreeNodeEntryIndex ( const u64 * start , const u64 * end , u64 virtual_offset , u32 * out_index ) ;
static bool bktrGetEntryNodeEntryIndex ( const BucketTreeNodeHeader * node_header , u64 node_offset , u64 entry_size , u64 virtual_offset , u32 * out_index ) ;
2022-06-29 08:41:58 -04:00
2022-07-02 06:09:49 -04:00
static bool bktrFindEntrySet ( u32 * out_index , u64 virtual_offset , u32 node_index ) ;
static const BucketTreeNodeHeader * bktrGetTreeNodeHeader ( BucketTreeContext * ctx , u32 node_index ) ;
NX_INLINE u32 bktrGetEntrySetIndex ( BucketTreeContext * ctx , u32 node_index , u32 offset_index ) ;
2020-04-29 08:59:28 -04:00
2022-07-02 10:07:08 -04:00
static bool bktrFindEntry ( BucketTreeContext * ctx , BucketTreeVisitor * out_visitor , u64 virtual_offset , u32 entry_set_index ) ;
2022-07-02 06:09:49 -04:00
static const BucketTreeNodeHeader * bktrGetEntryNodeHeader ( BucketTreeContext * ctx , u32 entry_set_index ) ;
NX_INLINE u64 bktrGetEntryNodeEntryOffset ( u64 entry_set_offset , u64 entry_size , u32 entry_index ) ;
2020-04-29 08:59:28 -04:00
2022-07-02 06:09:49 -04:00
NX_INLINE bool bktrIsExistL2 ( BucketTreeContext * ctx ) ;
NX_INLINE bool bktrIsExistOffsetL2OnL1 ( BucketTreeContext * ctx ) ;
2020-04-29 08:59:28 -04:00
2022-07-02 06:09:49 -04:00
static void bktrInitializeStorageNode ( BucketTreeStorageNode * out , u64 node_offset , u64 entry_size , u32 entry_count ) ;
static void bktrStorageNodeFind ( BucketTreeStorageNode * storage_node , const BucketTreeNodeHeader * node_header , u64 virtual_offset ) ;
NX_INLINE BucketTreeStorageNodeOffset bktrStorageNodeOffsetAdd ( BucketTreeStorageNodeOffset * ofs , u64 value ) ;
NX_INLINE u64 bktrStorageNodeOffsetSubstract ( BucketTreeStorageNodeOffset * ofs1 , BucketTreeStorageNodeOffset * ofs2 ) ;
bool bktrInitializeContext ( BucketTreeContext * out , NcaFsSectionContext * nca_fs_ctx , u8 storage_type )
2020-04-29 08:59:28 -04:00
{
2022-07-02 06:09:49 -04:00
NcaContext * nca_ctx = NULL ;
2020-04-29 08:59:28 -04:00
2022-07-02 06:09:49 -04:00
if ( ! out | | storage_type > = BucketTreeStorageType_Count | | ! nca_fs_ctx | | ! nca_fs_ctx - > enabled | | nca_fs_ctx - > section_type > = NcaFsSectionType_Invalid | | \
! ( nca_ctx = ( NcaContext * ) nca_fs_ctx - > nca_ctx ) | | ( nca_ctx - > rights_id_available & & ! nca_ctx - > titlekey_retrieved ) )
2020-04-29 08:59:28 -04:00
{
2021-03-07 20:22:49 -03:00
LOG_MSG ( " Invalid parameters! " ) ;
2020-04-29 08:59:28 -04:00
return false ;
}
2022-07-02 06:09:49 -04:00
bool success = false ;
2020-10-14 10:23:49 -03:00
/* Free output context beforehand. */
bktrFreeContext ( out ) ;
2022-07-02 06:09:49 -04:00
/* Initialize the desired storage type. */
switch ( storage_type )
2020-04-29 08:59:28 -04:00
{
2022-07-02 06:09:49 -04:00
case BucketTreeStorageType_Indirect :
case BucketTreeStorageType_Sparse :
success = bktrInitializeIndirectStorageContext ( out , nca_fs_ctx , storage_type = = BucketTreeStorageType_Sparse ) ;
break ;
case BucketTreeStorageType_AesCtrEx :
success = bktrInitializeAesCtrExStorageContext ( out , nca_fs_ctx ) ;
break ;
case BucketTreeStorageType_Compressed :
success = bktrInitializeCompressedStorageContext ( out , nca_fs_ctx ) ;
break ;
default :
break ;
2020-04-29 08:59:28 -04:00
}
2022-07-02 06:09:49 -04:00
if ( ! success ) LOG_MSG ( " Failed to initialize Bucket Tree %s storage for FS section #%u in \" %s \" . " , bktrGetStorageTypeName ( storage_type ) , nca_fs_ctx - > section_idx , \
nca_ctx - > content_id_str ) ;
2020-04-30 04:25:03 -04:00
2022-07-02 06:09:49 -04:00
return success ;
}
bool bktrReadStorage ( BucketTreeContext * ctx , void * out , u64 read_size , u64 offset )
{
if ( ! bktrIsBlockWithinStorageRange ( ctx , read_size , offset ) | | ! out )
2022-06-29 02:55:35 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Invalid parameters! " ) ;
return false ;
2022-06-29 02:55:35 -04:00
}
2022-07-02 10:07:08 -04:00
BucketTreeVisitor visitor = { 0 } ;
2022-07-02 06:09:49 -04:00
bool success = false ;
2020-04-30 04:25:03 -04:00
2022-07-02 06:09:49 -04:00
/* Find storage entry. */
2022-07-02 10:07:08 -04:00
if ( ! bktrFindStorageEntry ( ctx , offset , & visitor ) )
2020-04-29 08:59:28 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Unable to find %s storage entry for offset 0x%lX! " , bktrGetStorageTypeName ( ctx - > storage_type ) , offset ) ;
2020-07-13 02:36:17 -04:00
goto end ;
2020-04-29 08:59:28 -04:00
}
2022-07-02 06:09:49 -04:00
/* Process storage entry according to the storage type. */
switch ( ctx - > storage_type )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
case BucketTreeStorageType_Indirect :
case BucketTreeStorageType_Sparse :
2022-07-02 10:07:08 -04:00
success = bktrReadIndirectStorage ( & visitor , read_size , offset ) ;
2022-07-02 06:09:49 -04:00
break ;
case BucketTreeStorageType_AesCtrEx :
2022-07-02 10:07:08 -04:00
success = bktrReadAesCtrExStorage ( & visitor , read_size , offset ) ;
2022-07-02 06:09:49 -04:00
break ;
case BucketTreeStorageType_Compressed :
2022-07-02 10:07:08 -04:00
success = bktrReadCompressedStorage ( & visitor , read_size , offset ) ;
2022-07-02 06:09:49 -04:00
break ;
default :
break ;
2020-04-30 04:25:03 -04:00
}
2022-07-02 06:09:49 -04:00
if ( ! success ) LOG_MSG ( " Failed to read 0x%lX-byte long block at offset 0x%lX from %s storage! " , read_size , offset , bktrGetStorageTypeName ( ctx - > storage_type ) ) ;
2020-04-29 08:59:28 -04:00
2022-07-02 06:09:49 -04:00
end :
return success ;
}
2022-07-02 10:07:08 -04:00
static bool bktrReadIndirectStorage ( BucketTreeVisitor * visitor , u64 read_size , u64 offset )
2022-07-02 06:09:49 -04:00
{
2022-07-02 10:07:08 -04:00
BucketTreeContext * ctx = visitor - > bktr_ctx ;
2022-07-02 06:09:49 -04:00
NcaFsSectionContext * nca_fs_ctx = ctx - > nca_fs_ctx ;
bool is_sparse = ( ctx - > storage_type = = BucketTreeStorageType_Sparse ) ;
2022-07-02 10:07:08 -04:00
BucketTreeIndirectStorageEntry * entry = ( BucketTreeIndirectStorageEntry * ) visitor - > entry ;
2020-04-30 04:25:03 -04:00
2022-07-02 06:09:49 -04:00
/* Validate Indirect Storage entry. */
if ( ! bktrIsOffsetWithinStorageRange ( entry - > virtual_offset ) | | ( nca_fs_ctx - > section_offset + entry - > physical_offset ) > = nca_fs_ctx - > section_size )
2020-04-30 04:25:03 -04:00
{
2022-07-02 10:07:08 -04:00
LOG_MSG ( " Invalid Indirect Storage entry! (0x%lX, 0x%lX). " , entry - > virtual_offset , entry - > physical_offset ) ;
2022-07-02 06:09:49 -04:00
return false ;
2020-04-30 04:25:03 -04:00
}
2020-04-29 08:59:28 -04:00
2022-07-02 10:07:08 -04:00
/* Prepare to operate in chunks. */
2020-04-29 08:59:28 -04:00
2022-07-02 06:09:49 -04:00
2020-04-29 08:59:28 -04:00
}
2022-07-02 06:09:49 -04:00
static const char * bktrGetStorageTypeName ( u8 storage_type )
{
return ( storage_type < BucketTreeStorageType_Count ? g_bktrStorageTypeNames [ storage_type ] : NULL ) ;
}
static bool bktrInitializeIndirectStorageContext ( BucketTreeContext * out , NcaFsSectionContext * nca_fs_ctx , bool is_sparse )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
if ( ( ! is_sparse & & nca_fs_ctx - > section_type ! = NcaFsSectionType_PatchRomFs ) | | ( is_sparse & & ! nca_fs_ctx - > has_sparse_layer ) )
2020-04-30 04:25:03 -04:00
{
2021-03-07 20:22:49 -03:00
LOG_MSG ( " Invalid parameters! " ) ;
2020-04-30 04:25:03 -04:00
return false ;
}
2022-07-02 06:09:49 -04:00
NcaContext * nca_ctx = ( NcaContext * ) nca_fs_ctx - > nca_ctx ;
NcaBucketInfo * indirect_bucket = ( is_sparse ? & ( nca_fs_ctx - > header . sparse_info . bucket ) : & ( nca_fs_ctx - > header . patch_info . indirect_bucket ) ) ;
BucketTreeTable * indirect_table = NULL ;
bool success = false ;
/* Verify bucket info. */
if ( ! bktrVerifyBucketInfo ( indirect_bucket , BKTR_NODE_SIZE , BKTR_INDIRECT_ENTRY_SIZE ) )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Indirect Storage BucketInfo verification failed! (%s). " , is_sparse ? " sparse " : " patch " ) ;
goto end ;
2020-04-30 04:25:03 -04:00
}
2022-07-02 06:09:49 -04:00
/* Allocate memory for the full indirect table. */
indirect_table = calloc ( 1 , indirect_bucket - > size ) ;
if ( ! indirect_table )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Unable to allocate memory for the Indirect Storage Table! (%s). " , is_sparse ? " sparse " : " patch " ) ;
goto end ;
2020-04-30 04:25:03 -04:00
}
2022-07-02 06:09:49 -04:00
/* Read indirect storage table data. */
if ( ( ! is_sparse & & ! ncaReadFsSection ( nca_fs_ctx , indirect_table , indirect_bucket - > size , indirect_bucket - > offset ) ) | | \
( is_sparse & & ! ncaReadContentFile ( ( NcaContext * ) nca_fs_ctx - > nca_ctx , indirect_table , indirect_bucket - > size , nca_fs_ctx - > sparse_table_offset ) ) )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Failed to read Indirect Storage Table data! (%s). " , is_sparse ? " sparse " : " patch " ) ;
goto end ;
2020-04-30 04:25:03 -04:00
}
2022-07-02 06:09:49 -04:00
/* Decrypt indirect storage table, if needed. */
if ( is_sparse )
2020-05-01 00:34:30 -04:00
{
2022-07-02 06:09:49 -04:00
NcaAesCtrUpperIv sparse_upper_iv = { 0 } ;
u8 sparse_ctr [ AES_BLOCK_SIZE ] = { 0 } ;
const u8 * sparse_ctr_key = NULL ;
Aes128CtrContext sparse_ctr_ctx = { 0 } ;
/* Generate upper CTR IV. */
memcpy ( sparse_upper_iv . value , nca_fs_ctx - > header . aes_ctr_upper_iv . value , sizeof ( sparse_upper_iv . value ) ) ;
sparse_upper_iv . generation = ( ( u32 ) ( nca_fs_ctx - > header . sparse_info . generation ) < < 16 ) ;
/* Initialize partial AES CTR. */
aes128CtrInitializePartialCtr ( sparse_ctr , sparse_upper_iv . value , nca_fs_ctx - > sparse_table_offset ) ;
/* Create AES CTR context. */
sparse_ctr_key = ( nca_ctx - > rights_id_available ? nca_ctx - > titlekey : nca_ctx - > decrypted_key_area . aes_ctr ) ;
aes128CtrContextCreate ( & sparse_ctr_ctx , sparse_ctr_key , sparse_ctr ) ;
/* Decrypt indirect storage table in-place. */
aes128CtrCrypt ( & sparse_ctr_ctx , indirect_table , indirect_table , indirect_bucket - > size ) ;
2020-05-01 00:34:30 -04:00
}
2022-07-02 06:09:49 -04:00
/* Validate table offset node. */
u64 start_offset = 0 , end_offset = 0 ;
if ( ! bktrValidateTableOffsetNode ( indirect_table , BKTR_NODE_SIZE , BKTR_INDIRECT_ENTRY_SIZE , indirect_bucket - > header . entry_count , & start_offset , & end_offset ) )
2020-05-01 00:34:30 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Indirect Storage Table Offset Node validation failed! (%s). " , is_sparse ? " sparse " : " patch " ) ;
goto end ;
2020-05-01 00:34:30 -04:00
}
2022-07-02 06:09:49 -04:00
/* Update output context. */
out - > nca_fs_ctx = nca_fs_ctx ;
memcpy ( out - > bucket , indirect_bucket , sizeof ( NcaBucketInfo ) ) ;
out - > storage_type = ( is_sparse ? BucketTreeStorageType_Sparse : BucketTreeStorageType_Indirect ) ;
out - > storage_table = indirect_table ;
out - > node_size = BKTR_NODE_SIZE ;
out - > entry_size = BKTR_INDIRECT_ENTRY_SIZE ;
out - > offset_count = bktrGetOffsetCount ( BKTR_NODE_SIZE ) ;
out - > entry_set_count = bktrGetEntrySetCount ( BKTR_NODE_SIZE , BKTR_INDIRECT_ENTRY_SIZE , indirect_bucket - > header . entry_count ) ;
out - > start_offset = start_offset ;
out - > end_offset = end_offset ;
2020-05-01 00:34:30 -04:00
2022-07-02 06:09:49 -04:00
/* Update return value. */
success = true ;
2020-05-01 00:34:30 -04:00
2022-07-02 06:09:49 -04:00
end :
if ( ! success & & indirect_table ) free ( indirect_table ) ;
2020-05-01 00:34:30 -04:00
2022-07-02 06:09:49 -04:00
return success ;
2020-05-01 00:34:30 -04:00
}
2022-07-02 06:09:49 -04:00
static bool bktrInitializeAesCtrExStorageContext ( BucketTreeContext * out , NcaFsSectionContext * nca_fs_ctx )
2022-06-29 08:41:58 -04:00
{
2022-07-02 06:09:49 -04:00
if ( nca_fs_ctx - > section_type ! = NcaFsSectionType_PatchRomFs | | ! nca_fs_ctx - > header . patch_info . aes_ctr_ex_bucket . size )
2022-06-29 08:41:58 -04:00
{
LOG_MSG ( " Invalid parameters! " ) ;
return false ;
}
2022-07-02 06:09:49 -04:00
NcaContext * nca_ctx = ( NcaContext * ) nca_fs_ctx - > nca_ctx ;
NcaBucketInfo * aes_ctr_ex_bucket = & ( nca_fs_ctx - > header . patch_info . aes_ctr_ex_bucket ) ;
BucketTreeTable * aes_ctr_ex_table = NULL ;
2022-06-29 08:41:58 -04:00
bool success = false ;
2022-07-02 06:09:49 -04:00
/* Verify bucket info. */
if ( ! bktrVerifyBucketInfo ( aes_ctr_ex_bucket , BKTR_NODE_SIZE , BKTR_AES_CTR_EX_ENTRY_SIZE ) )
2022-06-29 08:41:58 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " AesCtrEx Storage BucketInfo verification failed! " ) ;
2022-06-29 08:41:58 -04:00
goto end ;
}
2022-07-02 06:09:49 -04:00
/* Allocate memory for the full AesCtrEx table. */
aes_ctr_ex_table = calloc ( 1 , aes_ctr_ex_bucket - > size ) ;
if ( ! aes_ctr_ex_table )
2022-06-29 08:41:58 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Unable to allocate memory for the AesCtrEx Storage Table! " ) ;
2022-06-29 08:41:58 -04:00
goto end ;
}
2022-07-02 06:09:49 -04:00
/* Read AesCtrEx storage table data. */
if ( ! ncaReadFsSection ( nca_fs_ctx , aes_ctr_ex_table , aes_ctr_ex_bucket - > size , aes_ctr_ex_bucket - > offset ) )
2022-06-29 08:41:58 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Failed to read AesCtrEx Storage Table data! " ) ;
goto end ;
2022-06-29 08:41:58 -04:00
}
2022-07-02 06:09:49 -04:00
/* Validate table offset node. */
u64 start_offset = 0 , end_offset = 0 ;
if ( ! bktrValidateTableOffsetNode ( aes_ctr_ex_table , BKTR_NODE_SIZE , BKTR_AES_CTR_EX_ENTRY_SIZE , aes_ctr_ex_bucket - > header . entry_count , & start_offset , & end_offset ) )
2022-06-29 08:41:58 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " AesCtrEx Storage Table Offset Node validation failed! " ) ;
goto end ;
2022-06-29 08:41:58 -04:00
}
2022-07-02 06:09:49 -04:00
/* Update output context. */
out - > nca_fs_ctx = nca_fs_ctx ;
memcpy ( out - > bucket , aes_ctr_ex_bucket , sizeof ( NcaBucketInfo ) ) ;
out - > storage_type = BucketTreeStorageType_AesCtrEx ;
out - > storage_table = aes_ctr_ex_table ;
out - > node_size = BKTR_NODE_SIZE ;
out - > entry_size = BKTR_AES_CTR_EX_ENTRY_SIZE ;
out - > offset_count = bktrGetOffsetCount ( BKTR_NODE_SIZE ) ;
out - > entry_set_count = bktrGetEntrySetCount ( BKTR_NODE_SIZE , BKTR_AES_CTR_EX_ENTRY_SIZE , aes_ctr_ex_bucket - > header . entry_count ) ;
out - > start_offset = start_offset ;
out - > end_offset = end_offset ;
2022-06-29 08:41:58 -04:00
2022-07-02 06:09:49 -04:00
/* Update return value. */
2022-06-29 08:41:58 -04:00
success = true ;
end :
2022-07-02 06:09:49 -04:00
if ( ! success & & aes_ctr_ex_table ) free ( aes_ctr_ex_table ) ;
2022-06-29 08:41:58 -04:00
return success ;
}
2022-07-02 06:09:49 -04:00
static bool bktrInitializeCompressedStorageContext ( BucketTreeContext * out , NcaFsSectionContext * nca_fs_ctx )
2022-06-29 08:41:58 -04:00
{
2022-07-02 06:09:49 -04:00
if ( ! nca_fs_ctx - > has_compression_layer )
2022-06-29 08:41:58 -04:00
{
LOG_MSG ( " Invalid parameters! " ) ;
return false ;
}
2022-07-02 06:09:49 -04:00
NcaContext * nca_ctx = ( NcaContext * ) nca_fs_ctx - > nca_ctx ;
NcaBucketInfo * compressed_bucket = & ( nca_fs_ctx - > header . compression_info . bucket ) ;
BucketTreeTable * compressed_table = NULL ;
2022-06-29 08:41:58 -04:00
bool success = false ;
2022-07-02 06:09:49 -04:00
/* Verify bucket info. */
if ( ! bktrVerifyBucketInfo ( compressed_bucket , BKTR_NODE_SIZE , BKTR_COMPRESSED_ENTRY_SIZE ) )
2022-06-29 08:41:58 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Compressed Storage BucketInfo verification failed! " ) ;
2022-06-29 08:41:58 -04:00
goto end ;
}
2022-07-02 06:09:49 -04:00
/* Allocate memory for the full Compressed table. */
compressed_table = calloc ( 1 , compressed_bucket - > size ) ;
if ( ! compressed_table )
2022-06-29 08:41:58 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Unable to allocate memory for the Compressed Storage Table! " ) ;
2022-06-29 08:41:58 -04:00
goto end ;
}
2022-07-02 06:09:49 -04:00
/* Read Compressed storage table data. */
if ( ! ncaReadFsSection ( nca_fs_ctx , compressed_table , compressed_bucket - > size , nca_fs_ctx - > compression_table_offset ) )
2022-06-29 08:41:58 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Failed to read Compressed Storage Table data! " ) ;
2022-06-29 08:41:58 -04:00
goto end ;
}
2022-07-02 06:09:49 -04:00
/* Validate table offset node. */
u64 start_offset = 0 , end_offset = 0 ;
if ( ! bktrValidateTableOffsetNode ( compressed_table , BKTR_NODE_SIZE , BKTR_COMPRESSED_ENTRY_SIZE , compressed_bucket - > header . entry_count , & start_offset , & end_offset ) )
2022-06-29 08:41:58 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Compressed Storage Table Offset Node validation failed! " ) ;
goto end ;
2022-06-29 08:41:58 -04:00
}
2022-07-02 06:09:49 -04:00
/* Update output context. */
out - > nca_fs_ctx = nca_fs_ctx ;
memcpy ( out - > bucket , compressed_bucket , sizeof ( NcaBucketInfo ) ) ;
out - > storage_type = BucketTreeStorageType_Compressed ;
out - > storage_table = compressed_table ;
out - > node_size = BKTR_NODE_SIZE ;
out - > entry_size = BKTR_COMPRESSED_ENTRY_SIZE ;
out - > offset_count = bktrGetOffsetCount ( BKTR_NODE_SIZE ) ;
out - > entry_set_count = bktrGetEntrySetCount ( BKTR_NODE_SIZE , BKTR_COMPRESSED_ENTRY_SIZE , compressed_bucket - > header . entry_count ) ;
out - > start_offset = start_offset ;
out - > end_offset = end_offset ;
2022-06-29 08:41:58 -04:00
2022-07-02 06:09:49 -04:00
/* Update return value. */
2022-06-29 08:41:58 -04:00
success = true ;
end :
2022-07-02 06:09:49 -04:00
if ( ! success & & compressed_table ) free ( compressed_table ) ;
2022-06-29 08:41:58 -04:00
return success ;
}
2022-07-02 06:09:49 -04:00
static bool bktrVerifyBucketInfo ( NcaBucketInfo * bucket , u64 node_size , u64 entry_size )
{
/* Verify bucket info properties. */
if ( ! ncaVerifyBucketInfo ( bucket ) ) return false ;
/* Validate table size. */
u64 node_storage_size = bktrQueryNodeStorageSize ( node_size , entry_size , bucket - > header . entry_count ) ;
u64 entry_storage_size = bktrQueryEntryStorageSize ( node_size , entry_size , bucket - > header . entry_count ) ;
u64 calc_table_size = ( node_storage_size + entry_storage_size ) ;
return ( bucket - > size > = calc_table_size ) ;
}
static bool bktrValidateTableOffsetNode ( const BucketTreeTable * table , u64 node_size , u64 entry_size , u32 entry_count , u64 * out_start_offset , u64 * out_end_offset )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
const BucketTreeOffsetNode * offset_node = & ( table - > offset_node ) ;
const BucketTreeNodeHeader * node_header = & ( offset_node - > header ) ;
/* Verify offset node header. */
if ( ! bktrVerifyNodeHeader ( node_header , 0 , node_size , sizeof ( u64 ) ) )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Bucket Tree Offset Node header verification failed! " ) ;
2020-04-30 04:25:03 -04:00
return false ;
}
2022-07-02 06:09:49 -04:00
/* Validate offsets. */
u32 offset_count = bktrGetOffsetCount ( node_size ) ;
u32 entry_set_count = bktrGetEntrySetCount ( node_size , entry_size , entry_count ) ;
const u64 start_offset = ( ( offset_count < entry_set_count & & node_header - > count < offset_count ) ? * bktrGetOffsetNodeEnd ( offset_node ) : * bktrGetOffsetNodeBegin ( offset_node ) ) ;
u64 end_offset = node_header - > offset ;
if ( start_offset > * bktrGetOffsetNodeBegin ( offset_node ) | | start_offset > = end_offset | | node_header - > count ! = entry_set_count )
{
LOG_MSG ( " Invalid Bucket Tree Offset Node! " ) ;
return false ;
}
/* Update output offsets. */
if ( out_start_offset ) * out_start_offset = start_offset ;
if ( out_end_offset ) * out_end_offset = end_offset ;
return true ;
}
NX_INLINE bool bktrVerifyNodeHeader ( const BucketTreeNodeHeader * node_header , u32 node_index , u64 node_size , u64 entry_size )
{
return ( node_header & & node_header - > index = = node_index & & entry_size > 0 & & node_size > = ( entry_size + BKTR_NODE_HEADER_SIZE ) & & \
node_header - > count > 0 & & node_header - > count < = ( ( node_size - BKTR_NODE_HEADER_SIZE ) / entry_size ) ) ;
}
static u64 bktrQueryNodeStorageSize ( u64 node_size , u64 entry_size , u32 entry_count )
{
if ( entry_size < sizeof ( u64 ) | | node_size < ( entry_size + BKTR_NODE_HEADER_SIZE ) | | node_size < BKTR_NODE_SIZE_MIN | | node_size > BKTR_NODE_SIZE_MAX | | \
! IS_POWER_OF_TWO ( node_size ) | | ! entry_count ) return 0 ;
return ( ( 1 + bktrGetNodeL2Count ( node_size , entry_size , entry_count ) ) * node_size ) ;
}
static u64 bktrQueryEntryStorageSize ( u64 node_size , u64 entry_size , u32 entry_count )
{
if ( entry_size < sizeof ( u64 ) | | node_size < ( entry_size + BKTR_NODE_HEADER_SIZE ) | | node_size < BKTR_NODE_SIZE_MIN | | node_size > BKTR_NODE_SIZE_MAX | | \
! IS_POWER_OF_TWO ( node_size ) | | ! entry_count ) return 0 ;
return ( ( u64 ) bktrGetEntrySetCount ( node_size , entry_size , entry_count ) * node_size ) ;
}
NX_INLINE u32 bktrGetEntryCount ( u64 node_size , u64 entry_size )
{
return ( u32 ) ( ( node_size - BKTR_NODE_HEADER_SIZE ) / entry_size ) ;
}
NX_INLINE u32 bktrGetOffsetCount ( u64 node_size )
{
return ( u32 ) ( ( node_size - BKTR_NODE_HEADER_SIZE ) / sizeof ( u64 ) ) ;
}
NX_INLINE u32 bktrGetEntrySetCount ( u64 node_size , u64 entry_size , u32 entry_count )
{
u32 entry_count_per_node = bktrGetEntryCount ( node_size , entry_size ) ;
return DIVIDE_UP ( entry_count , entry_count_per_node ) ;
}
NX_INLINE u32 bktrGetNodeL2Count ( u64 node_size , u64 entry_size , u32 entry_count )
{
u32 offset_count_per_node = bktrGetOffsetCount ( node_size ) ;
u32 entry_set_count = bktrGetEntrySetCount ( node_size , entry_size , node_count ) ;
if ( entry_set_count < = offset_count_per_node ) return 0 ;
u32 node_l2_count = DIVIDE_UP ( entry_set_count , offset_count_per_node ) ;
if ( node_l2_count > offset_count_per_node ) return 0 ;
2020-04-30 04:25:03 -04:00
2022-07-02 06:09:49 -04:00
return DIVIDE_UP ( entry_set_count - ( offset_count_per_node - ( node_l2_count - 1 ) ) , offset_count_per_node ) ;
}
NX_INLINE const void * bktrGetNodeArray ( const BucketTreeNodeHeader * node_header )
{
return ( ( const u8 * ) node_header + BKTR_NODE_HEADER_SIZE ) ;
}
NX_INLINE const u64 * bktrGetOffsetNodeArray ( const BucketTreeOffsetNode * offset_node )
{
return ( const u64 * ) bktrGetNodeArray ( & ( offset_node - > header ) ) ;
}
NX_INLINE const u64 * bktrGetOffsetNodeBegin ( const BucketTreeOffsetNode * offset_node )
{
return bktrGetOffsetNodeArray ( offset_node ) ;
}
NX_INLINE const u64 * bktrGetOffsetNodeEnd ( const BucketTreeOffsetNode * offset_node )
{
return ( bktrGetOffsetNodeArray ( offset_node ) + offset_node - > header . count ) ;
}
2022-07-02 10:07:08 -04:00
static bool bktrFindStorageEntry ( BucketTreeContext * ctx , u64 virtual_offset , BucketTreeVisitor * out_visitor )
2022-07-02 06:09:49 -04:00
{
2022-07-02 10:07:08 -04:00
if ( ! ctx | | virtual_offset > = ctx - > storage_table - > offset_node . header . offset | | ! out_visitor )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Invalid parameters! " ) ;
2020-04-30 04:25:03 -04:00
return false ;
}
2022-07-02 06:09:49 -04:00
/* Get the node. */
const BucketTreeOffsetNode * offset_node = & ( ctx - > storage_table - > offset_node ) ;
2020-04-30 04:25:03 -04:00
2022-07-02 06:09:49 -04:00
/* Get the entry node index. */
u32 entry_set_index = 0 ;
const u64 * start = NULL , * end = NULL , * pos = NULL ;
2020-04-30 04:25:03 -04:00
bool success = false ;
2022-07-02 06:09:49 -04:00
if ( bktrIsExistOffsetL2OnL1 ( ctx ) & & virtual_offset < * bktrGetOffsetNodeBegin ( offset_node ) )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
start = bktrGetOffsetNodeEnd ( offset_node ) ;
end = ( bktrGetOffsetNodeBegin ( offset_node ) + ctx - > offset_count ) ;
if ( ! bktrGetTreeNodeEntryIndex ( start , end , virtual_offset , & entry_set_index ) )
{
LOG_MSG ( " Failed to retrieve Bucket Tree Node entry index for virtual offset 0x%lX! (#1). " , virtual_offset ) ;
goto end ;
}
} else {
start = bktrGetOffsetNodeBegin ( offset_node ) ;
end = bktrGetOffsetNodeEnd ( offset_node ) ;
if ( ! bktrGetTreeNodeEntryIndex ( start , end , virtual_offset , & entry_set_index ) )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Failed to retrieve Bucket Tree Node entry index for virtual offset 0x%lX! (#2). " , virtual_offset ) ;
goto end ;
}
if ( bktrIsExistL2 ( ctx ) )
2020-09-26 07:49:18 -03:00
{
2022-07-02 06:09:49 -04:00
u32 node_index = entry_set_index ;
if ( node_index > = ctx - > offset_count | | ! bktrFindEntrySet ( & entry_set_index , virtual_offset , node_index ) )
2022-06-29 08:41:58 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Invalid L2 Bucket Tree Node index! " ) ;
goto end ;
2022-06-29 08:41:58 -04:00
}
2020-04-30 04:25:03 -04:00
}
}
2022-07-02 06:09:49 -04:00
/* Validate the entry set index. */
if ( entry_set_index > = ctx - > entry_set_count )
{
LOG_MSG ( " Invalid Bucket Tree Node offset! " ) ;
goto end ;
}
/* Find the entry. */
2022-07-02 10:07:08 -04:00
success = bktrFindEntry ( ctx , out_visitor , virtual_offset , entry_set_index ) ;
if ( ! success ) LOG_MSG ( " Failed to retrieve storage entry! " ) ;
2022-07-02 06:09:49 -04:00
end :
2020-04-30 04:25:03 -04:00
return success ;
}
2022-07-02 06:09:49 -04:00
static bool bktrGetTreeNodeEntryIndex ( const u64 * start , const u64 * end , u64 virtual_offset , u32 * out_index )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
if ( ! start | | ! end | | start > = end | | ! out_index )
2020-04-30 04:25:03 -04:00
{
2021-03-07 20:22:49 -03:00
LOG_MSG ( " Invalid parameters! " ) ;
2020-04-30 04:25:03 -04:00
return false ;
}
2022-07-02 06:09:49 -04:00
u64 * pos = ( u64 * ) start ;
bool found = false ;
2020-04-30 07:24:08 -04:00
2022-07-02 06:09:49 -04:00
while ( pos < end )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
if ( start < pos & & * pos > virtual_offset )
{
* out_index = ( ( u32 ) ( pos - start ) - 1 ) ;
found = true ;
break ;
}
2020-04-30 04:25:03 -04:00
2022-07-02 06:09:49 -04:00
pos + + ;
2020-04-30 04:25:03 -04:00
}
2022-07-02 06:09:49 -04:00
return found ;
2020-04-30 04:25:03 -04:00
}
2020-04-29 08:59:28 -04:00
2022-07-02 06:09:49 -04:00
static bool bktrGetEntryNodeEntryIndex ( const BucketTreeNodeHeader * node_header , u64 node_offset , u64 entry_size , u64 virtual_offset , u32 * out_index )
2022-06-30 12:46:45 -04:00
{
2022-07-02 06:09:49 -04:00
if ( ! node_header | | ! out_index )
2022-06-30 12:46:45 -04:00
{
LOG_MSG ( " Invalid parameters! " ) ;
return false ;
}
2022-07-02 06:09:49 -04:00
/* Initialize storage node and find the index for our virtual offset. */
BucketTreeStorageNode storage_node = { 0 } ;
bktrInitializeStorageNode ( & storage_node , node_offset , entry_size , node_header - > count ) ;
bktrStorageNodeFind ( & storage_node , node_header , virtual_offset ) ;
2022-06-30 12:46:45 -04:00
2022-07-02 06:09:49 -04:00
/* Validate index. */
if ( storage_node . index = = UINT32_MAX )
2022-06-30 12:46:45 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Unable to find index for virtual offset 0x%lX! " , virtual_offset ) ;
2022-06-30 12:46:45 -04:00
return false ;
}
2022-07-02 06:09:49 -04:00
/* Update output index. */
* out_index = storage_node . index ;
2022-06-30 12:46:45 -04:00
2022-07-02 06:09:49 -04:00
return true ;
2020-04-30 04:25:03 -04:00
}
2022-07-02 06:09:49 -04:00
static bool bktrFindEntrySet ( BucketTreeContext * ctx , u32 * out_index , u64 virtual_offset , u32 node_index )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
/* Get offset node header. */
const BucketTreeNodeHeader * node_header = bktrGetTreeNodeHeader ( ctx , node_index ) ;
if ( ! node_header )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Failed to retrieve offset node header at index 0x%X! " , node_index ) ;
return false ;
2020-04-30 04:25:03 -04:00
}
2022-07-02 06:09:49 -04:00
/* Calculate offset node extents. */
u64 node_size = ctx - > node_size ;
u64 node_offset = ( ( node_index + 1 ) * node_size ) ;
2020-04-30 04:25:03 -04:00
2022-07-02 06:09:49 -04:00
/* Get offset node entry index. */
u32 offset_index = 0 ;
if ( ! bktrGetEntryNodeEntryIndex ( node_header , node_offset , sizeof ( u64 ) , virtual_offset , & offset_index ) )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Failed to get offset node entry index! " ) ;
return false ;
2020-04-30 04:25:03 -04:00
}
2022-07-02 06:09:49 -04:00
/* Update output index. */
* out_index = bktrGetEntrySetIndex ( ctx , node_header - > index , offset_index ) ;
return true ;
}
static const BucketTreeNodeHeader * bktrGetTreeNodeHeader ( BucketTreeContext * ctx , u32 node_index )
{
/* Calculate offset node extents. */
const u64 node_size = ctx - > node_size ;
const u64 node_offset = ( ( node_index + 1 ) * node_size ) ;
if ( ( node_offset + BKTR_NODE_HEADER_SIZE ) > ctx - > bucket . size )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Invalid Bucket Tree Offset Node offset! " ) ;
2020-04-30 04:25:03 -04:00
return NULL ;
}
2022-07-02 06:09:49 -04:00
/* Get offset node header. */
2022-07-02 10:07:08 -04:00
const BucketTreeNodeHeader * node_header = ( const BucketTreeNodeHeader * ) ( ( u8 * ) ctx - > storage_table + node_offset ) ;
2020-04-30 04:25:03 -04:00
2022-07-02 06:09:49 -04:00
/* Validate offset node header. */
if ( ! bktrVerifyNodeHeader ( node_header , node_index , node_size , sizeof ( u64 ) ) )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Bucket Tree Offset Node header verification failed! " ) ;
return NULL ;
2020-04-30 04:25:03 -04:00
}
2022-07-02 06:09:49 -04:00
return node_header ;
2020-04-30 04:25:03 -04:00
}
2020-04-29 08:59:28 -04:00
2022-07-02 06:09:49 -04:00
NX_INLINE u32 bktrGetEntrySetIndex ( BucketTreeContext * ctx , u32 node_index , u32 offset_index )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
return ( u32 ) ( ( ctx - > offset_count - ctx - > storage_table - > offset_node . header . count ) + ( ctx - > offset_count * node_index ) + offset_index ) ;
2020-04-30 04:25:03 -04:00
}
2020-04-29 08:59:28 -04:00
2022-07-02 10:07:08 -04:00
static bool bktrFindEntry ( BucketTreeContext * ctx , BucketTreeVisitor * out_visitor , u64 virtual_offset , u32 entry_set_index )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
/* Get entry node header. */
const BucketTreeNodeHeader * entry_set_header = bktrGetEntryNodeHeader ( ctx , entry_set_index ) ;
if ( ! entry_set_header )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Failed to retrieve entry node header at index 0x%X! " , entry_set_index ) ;
return false ;
2020-04-30 04:25:03 -04:00
}
2022-07-02 06:09:49 -04:00
/* Calculate entry node extents. */
const u64 entry_size = ctx - > entry_size ;
const u64 entry_set_size = ctx - > node_size ;
const u64 entry_set_offset = ( entry_set_index * entry_set_size ) ;
2020-04-30 04:25:03 -04:00
2022-07-02 06:09:49 -04:00
/* Get entry node entry index. */
u32 entry_index = 0 ;
if ( ! bktrGetEntryNodeEntryIndex ( entry_set_header , entry_set_offset , entry_size , virtual_offset , & entry_index ) )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Failed to get entry node entry index! " ) ;
return false ;
}
/* Get entry node entry offset and validate it. */
u64 entry_offset = bktrGetEntryNodeEntryOffset ( entry_set_offset , entry_size , entry_index ) ;
if ( ( entry_offset + entry_size ) > ctx - > bucket . size )
{
LOG_MSG ( " Invalid Bucket Tree Entry Node entry offset! " ) ;
return false ;
2020-04-30 04:25:03 -04:00
}
2022-07-02 10:07:08 -04:00
/* Update output visitor. */
memset ( out , 0 , sizeof ( BucketTreeVisitor ) ) ;
out_visitor - > bktr_ctx = ctx ;
memcpy ( & ( out_visitor - > entry_set ) , entry_set_header , BKTR_NODE_HEADER_SIZE ) ;
out_visitor - > entry_index = entry_index
out_visitor - > entry = ( ( u8 * ) ctx - > storage_table + entry_offset ) ;
2022-07-02 06:09:49 -04:00
return true ;
}
static const BucketTreeNodeHeader * bktrGetEntryNodeHeader ( BucketTreeContext * ctx , u32 entry_set_index )
{
/* Calculate entry node extents. */
const u64 entry_size = ctx - > entry_size ;
const u64 entry_set_size = ctx - > node_size ;
const u64 entry_set_offset = ( entry_set_index * entry_set_size ) ;
2020-04-30 04:25:03 -04:00
2022-07-02 06:09:49 -04:00
if ( ( entry_set_offset + BKTR_NODE_HEADER_SIZE ) > ctx - > bucket . size )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Invalid Bucket Tree Entry Node offset! " ) ;
return NULL ;
2020-04-30 04:25:03 -04:00
}
2022-07-02 06:09:49 -04:00
/* Get entry node header. */
2022-07-02 10:07:08 -04:00
const BucketTreeNodeHeader * entry_set_header = ( const BucketTreeNodeHeader * ) ( ( u8 * ) ctx - > storage_table + entry_set_offset ) ;
2022-07-02 06:09:49 -04:00
/* Validate entry node header. */
if ( ! bktrVerifyNodeHeader ( entry_set_header , entry_set_index , entry_set_size , entry_size ) )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
LOG_MSG ( " Bucket Tree Entry Node header verification failed! " ) ;
2020-04-30 04:25:03 -04:00
return NULL ;
}
2022-07-02 06:09:49 -04:00
return node_header ;
}
NX_INLINE u64 bktrGetEntryNodeEntryOffset ( u64 entry_set_offset , u64 entry_size , u32 entry_index )
{
return ( entry_set_offset + BKTR_NODE_HEADER_SIZE + ( entry_index * entry_size ) ) ;
}
NX_INLINE bool bktrIsExistL2 ( BucketTreeContext * ctx )
{
return ( ctx - > offset_count < ctx - > entry_set_count ) ;
}
NX_INLINE bool bktrIsExistOffsetL2OnL1 ( BucketTreeContext * ctx )
{
return ( bktrIsExistL2 ( ctx ) & & ctx - > storage_table - > offset_node . header . count < ctx - > offset_count ) ;
}
static void bktrInitializeStorageNode ( BucketTreeStorageNode * out , u64 node_offset , u64 entry_size , u32 entry_count )
{
out - > start . offset = ( BKTR_NODE_HEADER_SIZE + node_offset ) ;
out - > start . stride = ( u32 ) entry_size ;
out - > count = entry_count ;
out - > index = UINT32_MAX ;
}
static void bktrStorageNodeFind ( BucketTreeStorageNode * storage_node , const BucketTreeNodeHeader * node_header , u64 virtual_offset )
{
u32 end = storage_node - > count ;
BucketTreeStorageNodeOffset pos = storage_node - > start ;
2020-04-30 04:25:03 -04:00
2022-07-02 06:09:49 -04:00
while ( end > 0 )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
u32 half = ( end / 2 ) ;
BucketTreeStorageNodeOffset mid = bktrStorageNodeOffsetAdd ( & pos , half ) ;
2020-04-30 04:25:03 -04:00
2022-07-02 06:09:49 -04:00
const u64 offset = * ( ( const u64 * ) ( ( const u8 * ) node_header + mid . offset ) ) ;
if ( offset < = virtual_offset )
2020-04-30 04:25:03 -04:00
{
2022-07-02 06:09:49 -04:00
pos = bktrStorageNodeOffsetAdd ( & mid , 1 ) ;
end - = ( half + 1 ) ;
2020-04-30 04:25:03 -04:00
} else {
2022-07-02 06:09:49 -04:00
end = half ;
2020-04-30 04:25:03 -04:00
}
}
2022-07-02 06:09:49 -04:00
storage_node - > index = ( ( u32 ) bktrStorageNodeOffsetSubstract ( & pos , & ( storage_node - > start ) ) - 1 ) ;
}
NX_INLINE BucketTreeStorageNodeOffset bktrStorageNodeOffsetAdd ( BucketTreeStorageNodeOffset * ofs , u64 value )
{
BucketTreeStorageNodeOffset out = { ofs - > offset + ( value * ( u64 ) ofs - > stride ) , ofs - > stride } ;
return out ;
}
NX_INLINE u64 bktrStorageNodeOffsetSubstract ( BucketTreeStorageNodeOffset * ofs1 , BucketTreeStorageNodeOffset * ofs2 )
{
return ( u64 ) ( ( ofs1 - > offset - ofs2 - > offset ) / ofs1 - > stride ) ;
2020-04-30 04:25:03 -04:00
}