Merge branch 'singlecompression' into single
[samba.git] / 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 "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 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_new));
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, NULL);
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         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 }