r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[kai/samba.git] / source3 / 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, 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 "tdb.h"
48
49 #ifdef HAVE_GETOPT_H
50 #include <getopt.h>
51 #endif
52
53 static int failed;
54
55 static char *add_suffix(const char *name, const char *suffix)
56 {
57         char *ret;
58         int len = strlen(name) + strlen(suffix) + 1;
59         ret = (char *)malloc(len);
60         if (!ret) {
61                 fprintf(stderr,"Out of memory!\n");
62                 exit(1);
63         }
64         snprintf(ret, len, "%s%s", name, suffix);
65         return ret;
66 }
67
68 static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
69 {
70         TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
71
72         if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
73                 fprintf(stderr,"Failed to insert into %s\n", tdb_name(tdb));
74                 failed = 1;
75                 return 1;
76         }
77         return 0;
78 }
79
80
81 static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
82 {
83         return 0;
84 }
85
86 /*
87   carefully backup a tdb, validating the contents and
88   only doing the backup if its OK
89   this function is also used for restore
90 */
91 static int backup_tdb(const char *old_name, const char *new_name, int hash_size)
92 {
93         TDB_CONTEXT *tdb;
94         TDB_CONTEXT *tdb_new;
95         char *tmp_name;
96         struct stat st;
97         int count1, count2;
98
99         tmp_name = add_suffix(new_name, ".tmp");
100
101         /* stat the old tdb to find its permissions */
102         if (stat(old_name, &st) != 0) {
103                 perror(old_name);
104                 free(tmp_name);
105                 return 1;
106         }
107
108         /* open the old tdb */
109         tdb = tdb_open(old_name, 0, 0, O_RDWR, 0);
110         if (!tdb) {
111                 printf("Failed to open %s\n", old_name);
112                 free(tmp_name);
113                 return 1;
114         }
115
116         /* create the new tdb */
117         unlink(tmp_name);
118         tdb_new = tdb_open(tmp_name,
119                            hash_size ? hash_size : tdb_hash_size(tdb),
120                            TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL, 
121                            st.st_mode & 0777);
122         if (!tdb_new) {
123                 perror(tmp_name);
124                 free(tmp_name);
125                 return 1;
126         }
127
128         /* lock the old tdb */
129         if (tdb_lockall(tdb) != 0) {
130                 fprintf(stderr,"Failed to lock %s\n", old_name);
131                 tdb_close(tdb);
132                 tdb_close(tdb_new);
133                 unlink(tmp_name);
134                 free(tmp_name);
135                 return 1;
136         }
137
138         failed = 0;
139
140         /* traverse and copy */
141         count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
142         if (count1 < 0 || failed) {
143                 fprintf(stderr,"failed to copy %s\n", old_name);
144                 tdb_close(tdb);
145                 tdb_close(tdb_new);
146                 unlink(tmp_name);
147                 free(tmp_name);
148                 return 1;
149         }
150
151         /* close the old tdb */
152         tdb_close(tdb);
153
154         /* close the new tdb and re-open read-only */
155         tdb_close(tdb_new);
156         tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0);
157         if (!tdb_new) {
158                 fprintf(stderr,"failed to reopen %s\n", tmp_name);
159                 unlink(tmp_name);
160                 perror(tmp_name);
161                 free(tmp_name);
162                 return 1;
163         }
164         
165         /* traverse the new tdb to confirm */
166         count2 = tdb_traverse(tdb_new, test_fn, 0);
167         if (count2 != count1) {
168                 fprintf(stderr,"failed to copy %s\n", old_name);
169                 tdb_close(tdb_new);
170                 unlink(tmp_name);
171                 free(tmp_name);
172                 return 1;
173         }
174
175         /* make sure the new tdb has reached stable storage */
176         fsync(tdb_fd(tdb_new));
177
178         /* close the new tdb and rename it to .bak */
179         tdb_close(tdb_new);
180         unlink(new_name);
181         if (rename(tmp_name, new_name) != 0) {
182                 perror(new_name);
183                 free(tmp_name);
184                 return 1;
185         }
186
187         free(tmp_name);
188
189         return 0;
190 }
191
192 /*
193   verify a tdb and if it is corrupt then restore from *.bak
194 */
195 static int verify_tdb(const char *fname, const char *bak_name)
196 {
197         TDB_CONTEXT *tdb;
198         int count = -1;
199
200         /* open the tdb */
201         tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
202
203         /* traverse the tdb, then close it */
204         if (tdb) {
205                 count = tdb_traverse(tdb, test_fn, NULL);
206                 tdb_close(tdb);
207         }
208
209         /* count is < 0 means an error */
210         if (count < 0) {
211                 printf("restoring %s\n", fname);
212                 return backup_tdb(bak_name, fname, 0);
213         }
214
215         printf("%s : %d records\n", fname, count);
216
217         return 0;
218 }
219
220 /*
221   see if one file is newer than another
222 */
223 static int file_newer(const char *fname1, const char *fname2)
224 {
225         struct stat st1, st2;
226         if (stat(fname1, &st1) != 0) {
227                 return 0;
228         }
229         if (stat(fname2, &st2) != 0) {
230                 return 1;
231         }
232         return (st1.st_mtime > st2.st_mtime);
233 }
234
235 static void usage(void)
236 {
237         printf("Usage: tdbbackup [options] <fname...>\n\n");
238         printf("   -h            this help message\n");
239         printf("   -s suffix     set the backup suffix\n");
240         printf("   -v            verify mode (restore if corrupt)\n");
241         printf("   -n hashsize   set the new hash size for the backup\n");
242 }
243                 
244
245  int main(int argc, char *argv[])
246 {
247         int i;
248         int ret = 0;
249         int c;
250         int verify = 0;
251         int hashsize = 0;
252         const char *suffix = ".bak";
253
254         while ((c = getopt(argc, argv, "vhs:n:")) != -1) {
255                 switch (c) {
256                 case 'h':
257                         usage();
258                         exit(0);
259                 case 'v':
260                         verify = 1;
261                         break;
262                 case 's':
263                         suffix = optarg;
264                         break;
265                 case 'n':
266                         hashsize = atoi(optarg);
267                         break;
268                 }
269         }
270
271         argc -= optind;
272         argv += optind;
273
274         if (argc < 1) {
275                 usage();
276                 exit(1);
277         }
278
279         for (i=0; i<argc; i++) {
280                 const char *fname = argv[i];
281                 char *bak_name;
282
283                 bak_name = add_suffix(fname, suffix);
284
285                 if (verify) {
286                         if (verify_tdb(fname, bak_name) != 0) {
287                                 ret = 1;
288                         }
289                 } else {
290                         if (file_newer(fname, bak_name) &&
291                             backup_tdb(fname, bak_name, hashsize) != 0) {
292                                 ret = 1;
293                         }
294                 }
295
296                 free(bak_name);
297         }
298
299         return ret;
300 }