2007-08-30 23:48:31 +04:00
/*
Unix SMB / CIFS implementation .
Implement a stack of talloc contexts
Copyright ( C ) Volker Lendecke 2007
2009-04-14 23:23:22 +04:00
Copyright ( C ) Jeremy Allison 2009 - made thread safe .
2007-08-30 23:48:31 +04:00
This program 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 2 of the License , or
( at your option ) any later version .
This program 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 .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
/*
* Implement a stack of talloc frames .
*
* When a new talloc stackframe is allocated with talloc_stackframe ( ) , then
* the TALLOC_CTX returned with talloc_tos ( ) is reset to that new
* frame . Whenever that stack frame is TALLOC_FREE ( ) ' ed , then the reverse
* happens : The previous talloc_tos ( ) is restored .
*
* This API is designed to be robust in the sense that if someone forgets to
* TALLOC_FREE ( ) a stackframe , then the next outer one correctly cleans up and
* resets the talloc_tos ( ) .
*
* This robustness feature means that we can ' t rely on a linked list with
* talloc destructors because in a hierarchy of talloc destructors the parent
* destructor is called before its children destructors . The child destructor
* called after the parent would set the talloc_tos ( ) to the wrong value .
*/
# include "includes.h"
2009-04-14 23:23:22 +04:00
struct talloc_stackframe {
int talloc_stacksize ;
int talloc_stack_arraysize ;
TALLOC_CTX * * talloc_stack ;
} ;
/*
* In the single threaded case this is a pointer
* to the global talloc_stackframe . In the MT - case
* this is the pointer to the thread - specific key
* used to look up the per - thread talloc_stackframe
* pointer .
*/
static void * global_ts ;
2009-04-20 14:04:42 +04:00
/* Variable to ensure TLS value is only initialized once. */
static smb_thread_once_t ts_initialized = SMB_THREAD_ONCE_INIT ;
2009-05-13 17:49:59 +04:00
static void talloc_stackframe_init ( void * unused )
2009-04-20 14:04:42 +04:00
{
if ( SMB_THREAD_CREATE_TLS ( " talloc_stackframe " , global_ts ) ) {
smb_panic ( " talloc_stackframe_init create_tls failed " ) ;
}
}
static struct talloc_stackframe * talloc_stackframe_create ( void )
2009-04-14 23:23:22 +04:00
{
# if defined(PARANOID_MALLOC_CHECKER)
2010-03-21 17:16:27 +03:00
# ifdef calloc
# undef calloc
2009-04-14 23:23:22 +04:00
# endif
# endif
2010-03-21 17:16:27 +03:00
struct talloc_stackframe * ts = ( struct talloc_stackframe * ) calloc (
1 , sizeof ( struct talloc_stackframe ) ) ;
2009-04-14 23:23:22 +04:00
# if defined(PARANOID_MALLOC_CHECKER)
2010-03-21 17:16:27 +03:00
# define calloc(n, s) __ERROR_DONT_USE_MALLOC_DIRECTLY
2009-04-14 23:23:22 +04:00
# endif
if ( ! ts ) {
smb_panic ( " talloc_stackframe_init malloc failed " ) ;
}
2009-05-13 17:49:59 +04:00
SMB_THREAD_ONCE ( & ts_initialized , talloc_stackframe_init , NULL ) ;
2009-04-14 23:23:22 +04:00
if ( SMB_THREAD_SET_TLS ( global_ts , ts ) ) {
smb_panic ( " talloc_stackframe_init set_tls failed " ) ;
}
return ts ;
}
2007-08-30 23:48:31 +04:00
2008-01-06 19:25:20 +03:00
static int talloc_pop ( TALLOC_CTX * frame )
2007-08-30 23:48:31 +04:00
{
2009-04-14 23:23:22 +04:00
struct talloc_stackframe * ts =
( struct talloc_stackframe * ) SMB_THREAD_GET_TLS ( global_ts ) ;
2018-06-22 17:25:10 +03:00
size_t blocks ;
2007-08-30 23:48:31 +04:00
int i ;
2012-07-17 23:27:31 +04:00
/* Catch lazy frame-freeing. */
if ( ts - > talloc_stack [ ts - > talloc_stacksize - 1 ] ! = frame ) {
DEBUG ( 0 , ( " Freed frame %s, expected %s. \n " ,
talloc_get_name ( frame ) ,
talloc_get_name ( ts - > talloc_stack
[ ts - > talloc_stacksize - 1 ] ) ) ) ;
# ifdef DEVELOPER
smb_panic ( " Frame not freed in order. " ) ;
# endif
}
2018-06-22 17:25:10 +03:00
for ( i = 0 ; i < 10 ; i + + ) {
/*
* We have to free our children first , calling all
* destructors . If a destructor hanging deeply off
* " frame " uses talloc_tos ( ) itself while freeing the
* toplevel frame , we panic because that nested
* talloc_tos ( ) in the destructor does not find a
* stackframe anymore .
*
* Do it in a loop up to 10 times as the destructors
* might use more of talloc_tos ( ) .
*/
talloc_free_children ( frame ) ;
blocks = talloc_total_blocks ( frame ) ;
if ( blocks = = 1 ) {
break ;
}
}
if ( blocks ! = 1 ) {
DBG_WARNING ( " Left %zu blocks after %i "
" talloc_free_children(frame) calls \n " ,
blocks , i ) ;
}
2009-04-14 23:23:22 +04:00
for ( i = ts - > talloc_stacksize - 1 ; i > 0 ; i - - ) {
if ( frame = = ts - > talloc_stack [ i ] ) {
2008-01-06 19:25:20 +03:00
break ;
}
2010-03-21 17:55:04 +03:00
TALLOC_FREE ( ts - > talloc_stack [ i ] ) ;
2007-08-30 23:48:31 +04:00
}
2010-03-15 16:39:11 +03:00
ts - > talloc_stack [ i ] = NULL ;
2009-04-14 23:23:22 +04:00
ts - > talloc_stacksize = i ;
2007-08-30 23:48:31 +04:00
return 0 ;
}
/*
* Create a new talloc stack frame .
*
* When free ' d , it frees all stack frames that were created after this one and
* not explicitly freed .
*/
2012-07-17 23:26:31 +04:00
static TALLOC_CTX * talloc_stackframe_internal ( const char * location ,
size_t poolsize )
2007-08-30 23:48:31 +04:00
{
2012-07-17 23:25:31 +04:00
TALLOC_CTX * * tmp , * top ;
2009-04-14 23:23:22 +04:00
struct talloc_stackframe * ts =
( struct talloc_stackframe * ) SMB_THREAD_GET_TLS ( global_ts ) ;
2007-08-30 23:48:31 +04:00
2009-04-14 23:23:22 +04:00
if ( ts = = NULL ) {
2009-04-20 14:04:42 +04:00
ts = talloc_stackframe_create ( ) ;
2009-04-14 23:23:22 +04:00
}
if ( ts - > talloc_stack_arraysize < ts - > talloc_stacksize + 1 ) {
tmp = talloc_realloc ( NULL , ts - > talloc_stack , TALLOC_CTX * ,
ts - > talloc_stacksize + 1 ) ;
2008-01-09 00:12:35 +03:00
if ( tmp = = NULL ) {
goto fail ;
}
2009-04-14 23:23:22 +04:00
ts - > talloc_stack = tmp ;
ts - > talloc_stack_arraysize = ts - > talloc_stacksize + 1 ;
2008-01-09 00:12:35 +03:00
}
2007-08-30 23:48:31 +04:00
2008-01-10 04:07:58 +03:00
if ( poolsize ) {
2012-07-17 23:25:31 +04:00
top = talloc_pool ( ts - > talloc_stack , poolsize ) ;
2008-01-10 04:07:58 +03:00
} else {
2012-07-17 23:25:31 +04:00
TALLOC_CTX * parent ;
/* We chain parentage, so if one is a pool we draw from it. */
if ( ts - > talloc_stacksize = = 0 ) {
parent = ts - > talloc_stack ;
} else {
parent = ts - > talloc_stack [ ts - > talloc_stacksize - 1 ] ;
}
2008-01-10 11:53:51 +03:00
top = talloc_new ( parent ) ;
2008-01-10 04:07:58 +03:00
}
2007-08-30 23:48:31 +04:00
2008-01-09 00:12:35 +03:00
if ( top = = NULL ) {
2007-08-30 23:48:31 +04:00
goto fail ;
}
2012-07-17 23:26:31 +04:00
talloc_set_name_const ( top , location ) ;
2008-01-06 19:25:20 +03:00
talloc_set_destructor ( top , talloc_pop ) ;
2007-08-30 23:48:31 +04:00
2009-04-14 23:23:22 +04:00
ts - > talloc_stack [ ts - > talloc_stacksize + + ] = top ;
2007-08-30 23:48:31 +04:00
return top ;
fail :
smb_panic ( " talloc_stackframe failed " ) ;
return NULL ;
}
2012-07-17 23:26:31 +04:00
TALLOC_CTX * _talloc_stackframe ( const char * location )
2008-01-10 04:07:58 +03:00
{
2012-07-17 23:26:31 +04:00
return talloc_stackframe_internal ( location , 0 ) ;
2008-01-10 04:07:58 +03:00
}
2012-07-17 23:26:31 +04:00
TALLOC_CTX * _talloc_stackframe_pool ( const char * location , size_t poolsize )
2008-01-10 04:07:58 +03:00
{
2012-07-17 23:26:31 +04:00
return talloc_stackframe_internal ( location , poolsize ) ;
2008-01-10 04:07:58 +03:00
}
2007-08-30 23:48:31 +04:00
/*
* Get us the current top of the talloc stack .
*/
2012-07-18 09:37:27 +04:00
TALLOC_CTX * _talloc_tos ( const char * location )
2007-08-30 23:48:31 +04:00
{
2009-04-14 23:23:22 +04:00
struct talloc_stackframe * ts =
( struct talloc_stackframe * ) SMB_THREAD_GET_TLS ( global_ts ) ;
2010-03-15 16:32:52 +03:00
if ( ts = = NULL | | ts - > talloc_stacksize = = 0 ) {
2012-07-18 09:37:27 +04:00
_talloc_stackframe ( location ) ;
2009-04-14 23:23:22 +04:00
ts = ( struct talloc_stackframe * ) SMB_THREAD_GET_TLS ( global_ts ) ;
2012-07-18 09:37:27 +04:00
DEBUG ( 0 , ( " no talloc stackframe at %s, leaking memory \n " ,
location ) ) ;
# ifdef DEVELOPER
smb_panic ( " No talloc stackframe " ) ;
# endif
2007-08-30 23:48:31 +04:00
}
2009-04-14 23:23:22 +04:00
return ts - > talloc_stack [ ts - > talloc_stacksize - 1 ] ;
2007-08-30 23:48:31 +04:00
}
2011-06-17 08:22:28 +04:00
/*
* return true if a talloc stackframe exists
* this can be used to prevent memory leaks for code that can
* optionally use a talloc stackframe ( eg . nt_errstr ( ) )
*/
bool talloc_stackframe_exists ( void )
{
struct talloc_stackframe * ts =
( struct talloc_stackframe * ) SMB_THREAD_GET_TLS ( global_ts ) ;
if ( ts = = NULL | | ts - > talloc_stacksize = = 0 ) {
return false ;
}
return true ;
}