2013-03-18 14:39:04 +01:00
/*
2002-03-07 18:58:33 +00:00
Unix SMB / CIFS implementation .
low level tdb backup and restore utility
Copyright ( C ) Andrew Tridgell 2002
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
2007-07-09 19:25:36 +00:00
the Free Software Foundation ; either version 3 of the License , or
2002-03-07 18:58:33 +00:00
( at your option ) any later version .
2013-03-18 14:39:04 +01:00
2002-03-07 18:58:33 +00:00
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 .
2013-03-18 14:39:04 +01:00
2002-03-07 18:58:33 +00:00
You should have received a copy of the GNU General Public License
2007-07-10 00:52:41 +00:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2002-03-07 18:58:33 +00:00
*/
2002-03-09 00:12:19 +00:00
/*
This program is meant for backup / restore of tdb databases . Typical usage would be :
2002-03-10 01:46:56 +00:00
tdbbackup * . tdb
2002-03-09 00:12:19 +00:00
when Samba shuts down cleanly , which will make a backup of all the local databases
to * . bak files . Then on Samba startup you would use :
2002-03-10 01:46:56 +00:00
tdbbackup - v * . tdb
2002-03-09 00:12:19 +00:00
and this will check the databases for corruption and if corruption is detected then
the backup will be restored .
You may also like to do a backup on a regular basis while Samba is
running , perhaps using cron .
The reason this program is needed is to cope with power failures
while Samba is running . A power failure could lead to database
corruption and Samba will then not start correctly .
Note that many of the databases in Samba are transient and thus
don ' t need to be backed up , so you can optimise the above a little
by only running the backup on the critical databases .
*/
2007-04-17 16:53:06 +00:00
# include "replace.h"
# include "system/locale.h"
# include "system/time.h"
# include "system/filesys.h"
2007-11-06 21:47:57 -08:00
# include "system/wait.h"
2007-04-17 16:53:06 +00:00
# include "tdb.h"
# ifdef HAVE_GETOPT_H
# include <getopt.h>
2003-10-02 18:22:51 +00:00
# endif
2007-04-17 16:53:06 +00:00
static int failed ;
2003-10-02 18:22:51 +00:00
2009-05-28 17:35:12 +10:00
static struct tdb_logging_context log_ctx ;
# ifdef PRINTF_ATTRIBUTE
static void tdb_log ( struct tdb_context * tdb , enum tdb_debug_level level , const char * format , . . . ) PRINTF_ATTRIBUTE ( 3 , 4 ) ;
# endif
static void tdb_log ( struct tdb_context * tdb , enum tdb_debug_level level , const char * format , . . . )
{
va_list ap ;
2013-03-18 14:39:04 +01:00
2009-05-28 17:35:12 +10:00
va_start ( ap , format ) ;
vfprintf ( stdout , format , ap ) ;
va_end ( ap ) ;
fflush ( stdout ) ;
}
2007-04-17 16:53:06 +00:00
static char * add_suffix ( const char * name , const char * suffix )
{
char * ret ;
int len = strlen ( name ) + strlen ( suffix ) + 1 ;
ret = ( char * ) malloc ( len ) ;
if ( ! ret ) {
fprintf ( stderr , " Out of memory! \n " ) ;
exit ( 1 ) ;
}
snprintf ( ret , len , " %s%s " , name , suffix ) ;
return ret ;
}
2003-10-02 18:22:51 +00:00
2007-04-17 16:53:06 +00:00
static int copy_fn ( TDB_CONTEXT * tdb , TDB_DATA key , TDB_DATA dbuf , void * state )
{
TDB_CONTEXT * tdb_new = ( TDB_CONTEXT * ) state ;
2003-10-02 18:22:51 +00:00
2007-04-17 16:53:06 +00:00
if ( tdb_store ( tdb_new , key , dbuf , TDB_INSERT ) ! = 0 ) {
2007-07-12 13:41:34 +00:00
fprintf ( stderr , " Failed to insert into %s \n " , tdb_name ( tdb_new ) ) ;
2007-04-17 16:53:06 +00:00
failed = 1 ;
return 1 ;
}
return 0 ;
}
2003-10-02 18:22:51 +00:00
2002-03-08 18:14:35 +00:00
2007-04-17 16:53:06 +00:00
static int test_fn ( TDB_CONTEXT * tdb , TDB_DATA key , TDB_DATA dbuf , void * state )
{
return 0 ;
}
/*
carefully backup a tdb , validating the contents and
only doing the backup if its OK
this function is also used for restore
*/
2013-02-21 16:34:32 +01:00
static int backup_tdb ( const char * old_name , const char * new_name ,
2018-05-01 11:10:43 +12:00
int hash_size , int nolock , bool readonly )
2007-04-17 16:53:06 +00:00
{
TDB_CONTEXT * tdb ;
TDB_CONTEXT * tdb_new ;
char * tmp_name ;
struct stat st ;
int count1 , count2 ;
tmp_name = add_suffix ( new_name , " .tmp " ) ;
/* stat the old tdb to find its permissions */
if ( stat ( old_name , & st ) ! = 0 ) {
perror ( old_name ) ;
free ( tmp_name ) ;
return 1 ;
}
/* open the old tdb */
2013-02-21 16:34:32 +01:00
tdb = tdb_open_ex ( old_name , 0 ,
TDB_DEFAULT | ( nolock ? TDB_NOLOCK : 0 ) ,
2009-05-28 17:35:12 +10:00
O_RDWR , 0 , & log_ctx , NULL ) ;
2007-04-17 16:53:06 +00:00
if ( ! tdb ) {
printf ( " Failed to open %s \n " , old_name ) ;
free ( tmp_name ) ;
return 1 ;
}
/* create the new tdb */
unlink ( tmp_name ) ;
2013-03-18 14:39:04 +01:00
tdb_new = tdb_open_ex ( tmp_name ,
hash_size ? hash_size : tdb_hash_size ( tdb ) ,
TDB_DEFAULT ,
O_RDWR | O_CREAT | O_EXCL , st . st_mode & 0777 ,
2009-05-28 17:35:12 +10:00
& log_ctx , NULL ) ;
2007-04-17 16:53:06 +00:00
if ( ! tdb_new ) {
perror ( tmp_name ) ;
free ( tmp_name ) ;
return 1 ;
}
2018-05-01 11:10:43 +12:00
if ( readonly ) {
if ( tdb_lockall_read ( tdb ) ! = 0 ) {
printf ( " Failed to obtain read only lock on old tdb \n " ) ;
tdb_close ( tdb ) ;
tdb_close ( tdb_new ) ;
unlink ( tmp_name ) ;
free ( tmp_name ) ;
return 1 ;
}
} else if ( tdb_transaction_start ( tdb ) ! = 0 ) {
printf ( " Failed to start transaction on db \n " ) ;
2008-12-16 14:36:56 +11:00
tdb_close ( tdb ) ;
tdb_close ( tdb_new ) ;
unlink ( tmp_name ) ;
free ( tmp_name ) ;
return 1 ;
}
2011-04-09 22:21:35 -04:00
/* lock the backup tdb so that nobody else can change it */
if ( tdb_lockall ( tdb_new ) ! = 0 ) {
printf ( " Failed to lock backup tdb \n " ) ;
2007-04-17 16:53:06 +00:00
tdb_close ( tdb ) ;
tdb_close ( tdb_new ) ;
unlink ( tmp_name ) ;
free ( tmp_name ) ;
return 1 ;
}
failed = 0 ;
/* traverse and copy */
2018-05-01 11:10:43 +12:00
if ( readonly ) {
count1 = tdb_traverse_read ( tdb ,
copy_fn ,
( void * ) tdb_new ) ;
} else {
count1 = tdb_traverse ( tdb ,
copy_fn ,
( void * ) tdb_new ) ;
}
2007-04-17 16:53:06 +00:00
if ( count1 < 0 | | failed ) {
fprintf ( stderr , " failed to copy %s \n " , old_name ) ;
tdb_close ( tdb ) ;
tdb_close ( tdb_new ) ;
unlink ( tmp_name ) ;
free ( tmp_name ) ;
return 1 ;
}
/* close the old tdb */
tdb_close ( tdb ) ;
2011-04-09 22:21:35 -04:00
/* copy done, unlock the backup tdb */
tdb_unlockall ( tdb_new ) ;
# ifdef HAVE_FDATASYNC
if ( fdatasync ( tdb_fd ( tdb_new ) ) ! = 0 ) {
# else
if ( fsync ( tdb_fd ( tdb_new ) ) ! = 0 ) {
# endif
/* not fatal */
fprintf ( stderr , " failed to fsync backup file \n " ) ;
2008-12-16 14:36:56 +11:00
}
2007-04-17 16:53:06 +00:00
/* close the new tdb and re-open read-only */
tdb_close ( tdb_new ) ;
2013-03-18 14:39:04 +01:00
tdb_new = tdb_open_ex ( tmp_name ,
2009-05-28 17:35:12 +10:00
0 ,
2013-03-18 14:39:04 +01:00
TDB_DEFAULT ,
2009-05-28 17:35:12 +10:00
O_RDONLY , 0 ,
& log_ctx , NULL ) ;
2007-04-17 16:53:06 +00:00
if ( ! tdb_new ) {
fprintf ( stderr , " failed to reopen %s \n " , tmp_name ) ;
unlink ( tmp_name ) ;
perror ( tmp_name ) ;
free ( tmp_name ) ;
return 1 ;
}
2013-03-18 14:39:04 +01:00
2007-04-17 16:53:06 +00:00
/* traverse the new tdb to confirm */
2007-07-17 10:30:13 +00:00
count2 = tdb_traverse ( tdb_new , test_fn , NULL ) ;
2007-04-17 16:53:06 +00:00
if ( count2 ! = count1 ) {
fprintf ( stderr , " failed to copy %s \n " , old_name ) ;
tdb_close ( tdb_new ) ;
unlink ( tmp_name ) ;
free ( tmp_name ) ;
return 1 ;
}
/* close the new tdb and rename it to .bak */
tdb_close ( tdb_new ) ;
if ( rename ( tmp_name , new_name ) ! = 0 ) {
perror ( new_name ) ;
free ( tmp_name ) ;
return 1 ;
}
free ( tmp_name ) ;
return 0 ;
}
/*
verify a tdb and if it is corrupt then restore from * . bak
*/
static int verify_tdb ( const char * fname , const char * bak_name )
{
TDB_CONTEXT * tdb ;
int count = - 1 ;
/* open the tdb */
2013-03-18 14:39:04 +01:00
tdb = tdb_open_ex ( fname , 0 , 0 ,
2009-05-28 17:35:12 +10:00
O_RDONLY , 0 , & log_ctx , NULL ) ;
2007-04-17 16:53:06 +00:00
/* traverse the tdb, then close it */
if ( tdb ) {
count = tdb_traverse ( tdb , test_fn , NULL ) ;
tdb_close ( tdb ) ;
}
/* count is < 0 means an error */
if ( count < 0 ) {
printf ( " restoring %s \n " , fname ) ;
2018-05-01 11:10:43 +12:00
return backup_tdb ( bak_name , fname , 0 , 0 , 0 ) ;
2007-04-17 16:53:06 +00:00
}
printf ( " %s : %d records \n " , fname , count ) ;
return 0 ;
}
2005-04-06 16:28:04 +00:00
2002-07-15 10:35:28 +00:00
/*
see if one file is newer than another
*/
static int file_newer ( const char * fname1 , const char * fname2 )
{
struct stat st1 , st2 ;
if ( stat ( fname1 , & st1 ) ! = 0 ) {
return 0 ;
}
if ( stat ( fname2 , & st2 ) ! = 0 ) {
return 1 ;
}
return ( st1 . st_mtime > st2 . st_mtime ) ;
}
2002-03-08 18:14:35 +00:00
static void usage ( void )
{
printf ( " Usage: tdbbackup [options] <fname...> \n \n " ) ;
printf ( " -h this help message \n " ) ;
printf ( " -s suffix set the backup suffix \n " ) ;
2003-06-12 07:32:44 +00:00
printf ( " -v verify mode (restore if corrupt) \n " ) ;
2006-11-13 09:34:55 +00:00
printf ( " -n hashsize set the new hash size for the backup \n " ) ;
2013-02-21 16:34:32 +01:00
printf ( " -l open without locking to back up mutex dbs \n " ) ;
2018-05-01 11:10:43 +12:00
printf ( " -r open with read only locking \n " ) ;
2002-03-08 18:14:35 +00:00
}
2002-03-07 18:58:33 +00:00
int main ( int argc , char * argv [ ] )
{
int i ;
int ret = 0 ;
2002-03-08 18:14:35 +00:00
int c ;
int verify = 0 ;
2006-11-13 09:34:55 +00:00
int hashsize = 0 ;
2013-02-21 16:34:32 +01:00
int nolock = 0 ;
2018-05-01 11:10:43 +12:00
bool readonly = false ;
2003-06-12 07:54:13 +00:00
const char * suffix = " .bak " ;
2002-03-08 18:14:35 +00:00
2009-05-28 17:35:12 +10:00
log_ctx . log_fn = tdb_log ;
2018-05-01 11:10:43 +12:00
while ( ( c = getopt ( argc , argv , " vhs:n:lr " ) ) ! = - 1 ) {
2002-03-08 18:14:35 +00:00
switch ( c ) {
case ' h ' :
usage ( ) ;
exit ( 0 ) ;
case ' v ' :
verify = 1 ;
break ;
case ' s ' :
suffix = optarg ;
break ;
2006-11-13 09:34:55 +00:00
case ' n ' :
hashsize = atoi ( optarg ) ;
break ;
2013-02-21 16:34:32 +01:00
case ' l ' :
nolock = 1 ;
break ;
2018-05-01 11:10:43 +12:00
case ' r ' :
readonly = true ;
2002-03-08 18:14:35 +00:00
}
}
2002-03-07 18:58:33 +00:00
2002-03-08 18:14:35 +00:00
argc - = optind ;
argv + = optind ;
if ( argc < 1 ) {
usage ( ) ;
2002-03-07 18:58:33 +00:00
exit ( 1 ) ;
}
2002-03-08 18:14:35 +00:00
for ( i = 0 ; i < argc ; i + + ) {
const char * fname = argv [ i ] ;
2002-03-10 08:30:15 +00:00
char * bak_name ;
2002-03-08 18:14:35 +00:00
2002-03-10 08:30:15 +00:00
bak_name = add_suffix ( fname , suffix ) ;
2002-03-08 18:14:35 +00:00
if ( verify ) {
if ( verify_tdb ( fname , bak_name ) ! = 0 ) {
ret = 1 ;
}
} else {
2002-07-15 10:35:28 +00:00
if ( file_newer ( fname , bak_name ) & &
2013-02-21 16:34:32 +01:00
backup_tdb ( fname , bak_name , hashsize ,
2018-05-01 11:10:43 +12:00
nolock , readonly ) ! = 0 ) {
2002-03-08 18:14:35 +00:00
ret = 1 ;
}
}
free ( bak_name ) ;
2002-03-07 18:58:33 +00:00
}
return ret ;
}