c08ab094c7dae0b95541ce8a331a923c54f4d5c2
[tprouty/samba.git] / source / lib / tdb / tools / tdbbackup.c
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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22
23   This program is meant for backup/restore of tdb databases. Typical usage would be:
24      tdbbackup *.tdb
25   when Samba shuts down cleanly, which will make a backup of all the local databases
26   to *.bak files. Then on Samba startup you would use:
27      tdbbackup -v *.tdb
28   and this will check the databases for corruption and if corruption is detected then
29   the backup will be restored.
30
31   You may also like to do a backup on a regular basis while Samba is
32   running, perhaps using cron.
33
34   The reason this program is needed is to cope with power failures
35   while Samba is running. A power failure could lead to database
36   corruption and Samba will then not start correctly.
37
38   Note that many of the databases in Samba are transient and thus
39   don't need to be backed up, so you can optimise the above a little
40   by only running the backup on the critical databases.
41
42  */
43
44 #include "replace.h"
45 #include "system/locale.h"
46 #include "system/time.h"
47 #include "system/filesys.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 char *add_suffix(const char *name, const char *suffix)
57 {
58         char *ret;
59         int len = strlen(name) + strlen(suffix) + 1;
60         ret = (char *)malloc(len);
61         if (!ret) {
62                 fprintf(stderr,"Out of memory!\n");
63                 exit(1);
64         }
65         snprintf(ret, len, "%s%s", name, suffix);
66         return ret;
67 }
68
69 static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
70 {
71         TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
72
73         if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
74                 fprintf(stderr,"Failed to insert into %s\n", tdb_name(tdb));
75                 failed = 1;
76                 return 1;
77         }
78         return 0;
79 }
80
81
82 static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
83 {
84         return 0;
85 }
86
87 /*
88   carefully backup a tdb, validating the contents and
89   only doing the backup if its OK
90   this function is also used for restore
91 */
92 static int backup_tdb(const char *old_name, const char *new_name, int hash_size)
93 {
94         TDB_CONTEXT *tdb;
95         TDB_CONTEXT *tdb_new;
96         char *tmp_name;
97         struct stat st;
98         int count1, count2;
99
100         tmp_name = add_suffix(new_name, ".tmp");
101
102         /* stat the old tdb to find its permissions */
103         if (stat(old_name, &st) != 0) {
104                 perror(old_name);
105                 free(tmp_name);
106                 return 1;
107         }
108
109         /* open the old tdb */
110         tdb = tdb_open(old_name, 0, 0, O_RDWR, 0);
111         if (!tdb) {
112                 printf("Failed to open %s\n", old_name);
113                 free(tmp_name);
114                 return 1;
115         }
116
117         /* create the new tdb */
118         unlink(tmp_name);
119         tdb_new = tdb_open(tmp_name,
120                            hash_size ? hash_size : tdb_hash_size(tdb),
121                            TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL, 
122                            st.st_mode & 0777);
123         if (!tdb_new) {
124                 perror(tmp_name);
125                 free(tmp_name);
126                 return 1;
127         }
128
129         /* lock the old tdb */
130         if (tdb_lockall(tdb) != 0) {
131                 fprintf(stderr,"Failed to lock %s\n", old_name);
132                 tdb_close(tdb);
133                 tdb_close(tdb_new);
134                 unlink(tmp_name);
135                 free(tmp_name);
136                 return 1;
137         }
138
139         failed = 0;
140
141         /* traverse and copy */
142         count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
143         if (count1 < 0 || failed) {
144                 fprintf(stderr,"failed to copy %s\n", old_name);
145                 tdb_close(tdb);
146                 tdb_close(tdb_new);
147                 unlink(tmp_name);
148                 free(tmp_name);
149                 return 1;
150         }
151
152         /* close the old tdb */
153         tdb_close(tdb);
154
155         /* close the new tdb and re-open read-only */
156         tdb_close(tdb_new);
157         tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0);
158         if (!tdb_new) {
159                 fprintf(stderr,"failed to reopen %s\n", tmp_name);
160                 unlink(tmp_name);
161                 perror(tmp_name);
162                 free(tmp_name);
163                 return 1;
164         }
165         
166         /* traverse the new tdb to confirm */
167         count2 = tdb_traverse(tdb_new, test_fn, 0);
168         if (count2 != count1) {
169                 fprintf(stderr,"failed to copy %s\n", old_name);
170                 tdb_close(tdb_new);
171                 unlink(tmp_name);
172                 free(tmp_name);
173                 return 1;
174         }
175
176         /* make sure the new tdb has reached stable storage */
177         fsync(tdb_fd(tdb_new));
178
179         /* close the new tdb and rename it to .bak */
180         tdb_close(tdb_new);
181         unlink(new_name);
182         if (rename(tmp_name, new_name) != 0) {
183                 perror(new_name);
184                 free(tmp_name);
185                 return 1;
186         }
187
188         free(tmp_name);
189
190         return 0;
191 }
192
193 /*
194   verify a tdb and if it is corrupt then restore from *.bak
195 */
196 static int verify_tdb(const char *fname, const char *bak_name)
197 {
198         TDB_CONTEXT *tdb;
199         int count = -1;
200
201         /* open the tdb */
202         tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
203
204         /* traverse the tdb, then close it */
205         if (tdb) {
206                 count = tdb_traverse(tdb, test_fn, NULL);
207                 tdb_close(tdb);
208         }
209
210         /* count is < 0 means an error */
211         if (count < 0) {
212                 printf("restoring %s\n", fname);
213                 return backup_tdb(bak_name, fname, 0);
214         }
215
216         printf("%s : %d records\n", fname, count);
217
218         return 0;
219 }
220
221 /*
222   see if one file is newer than another
223 */
224 static int file_newer(const char *fname1, const char *fname2)
225 {
226         struct stat st1, st2;
227         if (stat(fname1, &st1) != 0) {
228                 return 0;
229         }
230         if (stat(fname2, &st2) != 0) {
231                 return 1;
232         }
233         return (st1.st_mtime > st2.st_mtime);
234 }
235
236 static void usage(void)
237 {
238         printf("Usage: tdbbackup [options] <fname...>\n\n");
239         printf("   -h            this help message\n");
240         printf("   -s suffix     set the backup suffix\n");
241         printf("   -v            verify mode (restore if corrupt)\n");
242         printf("   -n hashsize   set the new hash size for the backup\n");
243 }
244                 
245
246  int main(int argc, char *argv[])
247 {
248         int i;
249         int ret = 0;
250         int c;
251         int verify = 0;
252         int hashsize = 0;
253         const char *suffix = ".bak";
254
255         while ((c = getopt(argc, argv, "vhs:n:")) != -1) {
256                 switch (c) {
257                 case 'h':
258                         usage();
259                         exit(0);
260                 case 'v':
261                         verify = 1;
262                         break;
263                 case 's':
264                         suffix = optarg;
265                         break;
266                 case 'n':
267                         hashsize = atoi(optarg);
268                         break;
269                 }
270         }
271
272         argc -= optind;
273         argv += optind;
274
275         if (argc < 1) {
276                 usage();
277                 exit(1);
278         }
279
280         for (i=0; i<argc; i++) {
281                 const char *fname = argv[i];
282                 char *bak_name;
283
284                 bak_name = add_suffix(fname, suffix);
285
286                 if (verify) {
287                         if (verify_tdb(fname, bak_name) != 0) {
288                                 ret = 1;
289                         }
290                 } else {
291                         if (file_newer(fname, bak_name) &&
292                             backup_tdb(fname, bak_name, hashsize) != 0) {
293                                 ret = 1;
294                         }
295                 }
296
297                 free(bak_name);
298         }
299
300         return ret;
301 }