LCOV - code coverage report
Current view: top level - lib/tdb/tools - tdbbackup.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 93 182 51.1 %
Date: 2024-02-28 12:06:22 Functions: 7 9 77.8 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    low level tdb backup and restore utility
       4             :    Copyright (C) Andrew Tridgell              2002
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : /*
      21             : 
      22             :   This program is meant for backup/restore of tdb databases. Typical usage would be:
      23             :      tdbbackup *.tdb
      24             :   when Samba shuts down cleanly, which will make a backup of all the local databases
      25             :   to *.bak files. Then on Samba startup you would use:
      26             :      tdbbackup -v *.tdb
      27             :   and this will check the databases for corruption and if corruption is detected then
      28             :   the backup will be restored.
      29             : 
      30             :   You may also like to do a backup on a regular basis while Samba is
      31             :   running, perhaps using cron.
      32             : 
      33             :   The reason this program is needed is to cope with power failures
      34             :   while Samba is running. A power failure could lead to database
      35             :   corruption and Samba will then not start correctly.
      36             : 
      37             :   Note that many of the databases in Samba are transient and thus
      38             :   don't need to be backed up, so you can optimise the above a little
      39             :   by only running the backup on the critical databases.
      40             : 
      41             :  */
      42             : 
      43             : #include "replace.h"
      44             : #include "system/locale.h"
      45             : #include "system/time.h"
      46             : #include "system/filesys.h"
      47             : #include "system/wait.h"
      48             : #include "tdb.h"
      49             : 
      50             : #ifdef HAVE_GETOPT_H
      51             : #include <getopt.h>
      52             : #endif
      53             : 
      54             : static int failed;
      55             : 
      56             : static struct tdb_logging_context log_ctx;
      57             : 
      58             : #ifdef PRINTF_ATTRIBUTE
      59             : static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
      60             : #endif
      61           9 : static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
      62             : {
      63           0 :         va_list ap;
      64             : 
      65           9 :         va_start(ap, format);
      66           9 :         vfprintf(stdout, format, ap);
      67           9 :         va_end(ap);
      68           9 :         fflush(stdout);
      69           9 : }
      70             : 
      71        1010 : static char *add_suffix(const char *name, const char *suffix)
      72             : {
      73           4 :         char *ret;
      74        1010 :         int len = strlen(name) + strlen(suffix) + 1;
      75        1010 :         ret = (char *)malloc(len);
      76        1010 :         if (!ret) {
      77           0 :                 fprintf(stderr,"Out of memory!\n");
      78           0 :                 exit(1);
      79             :         }
      80        1010 :         snprintf(ret, len, "%s%s", name, suffix);
      81        1010 :         return ret;
      82             : }
      83             : 
      84      580079 : static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
      85             : {
      86      580079 :         TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
      87             : 
      88      580079 :         if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
      89           0 :                 fprintf(stderr,"Failed to insert into %s\n", tdb_name(tdb_new));
      90           0 :                 failed = 1;
      91           0 :                 return 1;
      92             :         }
      93      580067 :         return 0;
      94             : }
      95             : 
      96             : 
      97      580079 : static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
      98             : {
      99      580079 :         return 0;
     100             : }
     101             : 
     102             : /*
     103             :   carefully backup a tdb, validating the contents and
     104             :   only doing the backup if its OK
     105             :   this function is also used for restore
     106             : */
     107         505 : static int backup_tdb(const char *old_name, const char *new_name,
     108             :                       int hash_size, int nolock, bool readonly)
     109             : {
     110           2 :         TDB_CONTEXT *tdb;
     111           2 :         TDB_CONTEXT *tdb_new;
     112           2 :         char *tmp_name;
     113           2 :         struct stat st;
     114           2 :         int count1, count2;
     115             : 
     116         505 :         tmp_name = add_suffix(new_name, ".tmp");
     117             : 
     118             :         /* stat the old tdb to find its permissions */
     119         505 :         if (stat(old_name, &st) != 0) {
     120           0 :                 perror(old_name);
     121           0 :                 free(tmp_name);
     122           0 :                 return 1;
     123             :         }
     124             : 
     125             :         /* open the old tdb */
     126         507 :         tdb = tdb_open_ex(old_name, 0,
     127             :                           TDB_DEFAULT | (nolock ? TDB_NOLOCK : 0),
     128             :                           O_RDWR, 0, &log_ctx, NULL);
     129         505 :         if (!tdb) {
     130           9 :                 printf("Failed to open %s\n", old_name);
     131           9 :                 free(tmp_name);
     132           9 :                 return 1;
     133             :         }
     134             : 
     135             :         /* create the new tdb */
     136         496 :         unlink(tmp_name);
     137         496 :         tdb_new = tdb_open_ex(tmp_name,
     138         496 :                               hash_size ? hash_size : tdb_hash_size(tdb),
     139             :                               TDB_DEFAULT,
     140         496 :                               O_RDWR|O_CREAT|O_EXCL, st.st_mode & 0777,
     141             :                               &log_ctx, NULL);
     142         496 :         if (!tdb_new) {
     143           0 :                 perror(tmp_name);
     144           0 :                 free(tmp_name);
     145           0 :                 return 1;
     146             :         }
     147             : 
     148         496 :         if (readonly) {
     149         416 :                 if (tdb_lockall_read(tdb) != 0) {
     150           0 :                         printf("Failed to obtain read only lock on old tdb\n");
     151           0 :                         tdb_close(tdb);
     152           0 :                         tdb_close(tdb_new);
     153           0 :                         unlink(tmp_name);
     154           0 :                         free(tmp_name);
     155           0 :                         return 1;
     156             :                 }
     157          80 :         } else if (tdb_transaction_start(tdb) != 0) {
     158           0 :                 printf("Failed to start transaction on db\n");
     159           0 :                 tdb_close(tdb);
     160           0 :                 tdb_close(tdb_new);
     161           0 :                 unlink(tmp_name);
     162           0 :                 free(tmp_name);
     163           0 :                 return 1;
     164             :         }
     165             : 
     166             :         /* lock the backup tdb so that nobody else can change it */
     167         496 :         if (tdb_lockall(tdb_new) != 0) {
     168           0 :                 printf("Failed to lock backup tdb\n");
     169           0 :                 tdb_close(tdb);
     170           0 :                 tdb_close(tdb_new);
     171           0 :                 unlink(tmp_name);
     172           0 :                 free(tmp_name);
     173           0 :                 return 1;
     174             :         }
     175             : 
     176         496 :         failed = 0;
     177             : 
     178             :         /* traverse and copy */
     179         496 :         if (readonly) {
     180         416 :                 count1 = tdb_traverse_read(tdb,
     181             :                                            copy_fn,
     182             :                                            (void *)tdb_new);
     183             :         } else {
     184          80 :                 count1 = tdb_traverse(tdb,
     185             :                                       copy_fn,
     186             :                                       (void *)tdb_new);
     187             :         }
     188         496 :         if (count1 < 0 || failed) {
     189           0 :                 fprintf(stderr,"failed to copy %s\n", old_name);
     190           0 :                 tdb_close(tdb);
     191           0 :                 tdb_close(tdb_new);
     192           0 :                 unlink(tmp_name);
     193           0 :                 free(tmp_name);
     194           0 :                 return 1;
     195             :         }
     196             : 
     197             :         /* close the old tdb */
     198         496 :         tdb_close(tdb);
     199             : 
     200             :         /* copy done, unlock the backup tdb */
     201         496 :         tdb_unlockall(tdb_new);
     202             : 
     203             : #ifdef HAVE_FDATASYNC
     204         496 :         if (fdatasync(tdb_fd(tdb_new)) != 0) {
     205             : #else
     206             :         if (fsync(tdb_fd(tdb_new)) != 0) {
     207             : #endif
     208             :                 /* not fatal */
     209           0 :                 fprintf(stderr, "failed to fsync backup file\n");
     210             :         }
     211             : 
     212             :         /* close the new tdb and re-open read-only */
     213         496 :         tdb_close(tdb_new);
     214         496 :         tdb_new = tdb_open_ex(tmp_name,
     215             :                               0,
     216             :                               TDB_DEFAULT,
     217             :                               O_RDONLY, 0,
     218             :                               &log_ctx, NULL);
     219         496 :         if (!tdb_new) {
     220           0 :                 fprintf(stderr,"failed to reopen %s\n", tmp_name);
     221           0 :                 unlink(tmp_name);
     222           0 :                 perror(tmp_name);
     223           0 :                 free(tmp_name);
     224           0 :                 return 1;
     225             :         }
     226             : 
     227             :         /* traverse the new tdb to confirm */
     228         496 :         count2 = tdb_traverse(tdb_new, test_fn, NULL);
     229         496 :         if (count2 != count1) {
     230           0 :                 fprintf(stderr,"failed to copy %s\n", old_name);
     231           0 :                 tdb_close(tdb_new);
     232           0 :                 unlink(tmp_name);
     233           0 :                 free(tmp_name);
     234           0 :                 return 1;
     235             :         }
     236             : 
     237             :         /* close the new tdb and rename it to .bak */
     238         496 :         tdb_close(tdb_new);
     239         496 :         if (rename(tmp_name, new_name) != 0) {
     240           0 :                 perror(new_name);
     241           0 :                 free(tmp_name);
     242           0 :                 return 1;
     243             :         }
     244             : 
     245         496 :         free(tmp_name);
     246             : 
     247         496 :         return 0;
     248             : }
     249             : 
     250             : /*
     251             :   verify a tdb and if it is corrupt then restore from *.bak
     252             : */
     253           0 : static int verify_tdb(const char *fname, const char *bak_name)
     254             : {
     255           0 :         TDB_CONTEXT *tdb;
     256           0 :         int count = -1;
     257             : 
     258             :         /* open the tdb */
     259           0 :         tdb = tdb_open_ex(fname, 0, 0,
     260             :                           O_RDONLY, 0, &log_ctx, NULL);
     261             : 
     262             :         /* traverse the tdb, then close it */
     263           0 :         if (tdb) {
     264           0 :                 count = tdb_traverse(tdb, test_fn, NULL);
     265           0 :                 tdb_close(tdb);
     266             :         }
     267             : 
     268             :         /* count is < 0 means an error */
     269           0 :         if (count < 0) {
     270           0 :                 printf("restoring %s\n", fname);
     271           0 :                 return backup_tdb(bak_name, fname, 0, 0, 0);
     272             :         }
     273             : 
     274           0 :         printf("%s : %d records\n", fname, count);
     275             : 
     276           0 :         return 0;
     277             : }
     278             : 
     279             : /*
     280             :   see if one file is newer than another
     281             : */
     282         505 : static int file_newer(const char *fname1, const char *fname2)
     283             : {
     284           2 :         struct stat st1, st2;
     285         505 :         if (stat(fname1, &st1) != 0) {
     286           0 :                 return 0;
     287             :         }
     288         505 :         if (stat(fname2, &st2) != 0) {
     289         503 :                 return 1;
     290             :         }
     291           0 :         return (st1.st_mtime > st2.st_mtime);
     292             : }
     293             : 
     294           0 : static void usage(void)
     295             : {
     296           0 :         printf("Usage: tdbbackup [options] <fname...>\n\n");
     297           0 :         printf("   -h            this help message\n");
     298           0 :         printf("   -s suffix     set the backup suffix\n");
     299           0 :         printf("   -v            verify mode (restore if corrupt)\n");
     300           0 :         printf("   -n hashsize   set the new hash size for the backup\n");
     301           0 :         printf("   -l            open without locking to back up mutex dbs\n");
     302           0 :         printf("   -r            open with read only locking\n");
     303           0 : }
     304             : 
     305         505 :  int main(int argc, char *argv[])
     306             : {
     307           2 :         int i;
     308         505 :         int ret = 0;
     309           2 :         int c;
     310         505 :         int verify = 0;
     311         505 :         int hashsize = 0;
     312         505 :         int nolock = 0;
     313         505 :         bool readonly = false;
     314         505 :         const char *suffix = ".bak";
     315             : 
     316         505 :         log_ctx.log_fn = tdb_log;
     317             : 
     318        1435 :         while ((c = getopt(argc, argv, "vhs:n:lr")) != -1) {
     319         930 :                 switch (c) {
     320           0 :                 case 'h':
     321           0 :                         usage();
     322           0 :                         exit(0);
     323           0 :                 case 'v':
     324           0 :                         verify = 1;
     325           0 :                         break;
     326         505 :                 case 's':
     327         505 :                         suffix = optarg;
     328         505 :                         break;
     329           0 :                 case 'n':
     330           0 :                         hashsize = atoi(optarg);
     331           0 :                         break;
     332           0 :                 case 'l':
     333           0 :                         nolock = 1;
     334           0 :                         break;
     335         425 :                 case 'r':
     336         425 :                         readonly = true;
     337             :                 }
     338             :         }
     339             : 
     340         505 :         argc -= optind;
     341         505 :         argv += optind;
     342             : 
     343         505 :         if (argc < 1) {
     344           0 :                 usage();
     345           0 :                 exit(1);
     346             :         }
     347             : 
     348        1010 :         for (i=0; i<argc; i++) {
     349         505 :                 const char *fname = argv[i];
     350           2 :                 char *bak_name;
     351             : 
     352         505 :                 bak_name = add_suffix(fname, suffix);
     353             : 
     354         505 :                 if (verify) {
     355           0 :                         if (verify_tdb(fname, bak_name) != 0) {
     356           0 :                                 ret = 1;
     357             :                         }
     358             :                 } else {
     359        1010 :                         if (file_newer(fname, bak_name) &&
     360         505 :                             backup_tdb(fname, bak_name, hashsize,
     361             :                                        nolock, readonly) != 0) {
     362           9 :                                 ret = 1;
     363             :                         }
     364             :                 }
     365             : 
     366         505 :                 free(bak_name);
     367             :         }
     368             : 
     369         505 :         return ret;
     370             : }

Generated by: LCOV version 1.14