r23950: unlink before rename is superfluous.
[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, 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_new));
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, NULL);
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         if (rename(tmp_name, new_name) != 0) {
181                 perror(new_name);
182                 free(tmp_name);
183                 return 1;
184         }
185
186         free(tmp_name);
187
188         return 0;
189 }
190
191 /*
192   verify a tdb and if it is corrupt then restore from *.bak
193 */
194 static int verify_tdb(const char *fname, const char *bak_name)
195 {
196         TDB_CONTEXT *tdb;
197         int count = -1;
198
199         /* open the tdb */
200         tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
201
202         /* traverse the tdb, then close it */
203         if (tdb) {
204                 count = tdb_traverse(tdb, test_fn, NULL);
205                 tdb_close(tdb);
206         }
207
208         /* count is < 0 means an error */
209         if (count < 0) {
210                 printf("restoring %s\n", fname);
211                 return backup_tdb(bak_name, fname, 0);
212         }
213
214         printf("%s : %d records\n", fname, count);
215
216         return 0;
217 }
218
219 /*
220   see if one file is newer than another
221 */
222 static int file_newer(const char *fname1, const char *fname2)
223 {
224         struct stat st1, st2;
225         if (stat(fname1, &st1) != 0) {
226                 return 0;
227         }
228         if (stat(fname2, &st2) != 0) {
229                 return 1;
230         }
231         return (st1.st_mtime > st2.st_mtime);
232 }
233
234 static void usage(void)
235 {
236         printf("Usage: tdbbackup [options] <fname...>\n\n");
237         printf("   -h            this help message\n");
238         printf("   -s suffix     set the backup suffix\n");
239         printf("   -v            verify mode (restore if corrupt)\n");
240         printf("   -n hashsize   set the new hash size for the backup\n");
241 }
242                 
243
244  int main(int argc, char *argv[])
245 {
246         int i;
247         int ret = 0;
248         int c;
249         int verify = 0;
250         int hashsize = 0;
251         const char *suffix = ".bak";
252
253         while ((c = getopt(argc, argv, "vhs:n:")) != -1) {
254                 switch (c) {
255                 case 'h':
256                         usage();
257                         exit(0);
258                 case 'v':
259                         verify = 1;
260                         break;
261                 case 's':
262                         suffix = optarg;
263                         break;
264                 case 'n':
265                         hashsize = atoi(optarg);
266                         break;
267                 }
268         }
269
270         argc -= optind;
271         argv += optind;
272
273         if (argc < 1) {
274                 usage();
275                 exit(1);
276         }
277
278         for (i=0; i<argc; i++) {
279                 const char *fname = argv[i];
280                 char *bak_name;
281
282                 bak_name = add_suffix(fname, suffix);
283
284                 if (verify) {
285                         if (verify_tdb(fname, bak_name) != 0) {
286                                 ret = 1;
287                         }
288                 } else {
289                         if (file_newer(fname, bak_name) &&
290                             backup_tdb(fname, bak_name, hashsize) != 0) {
291                                 ret = 1;
292                         }
293                 }
294
295                 free(bak_name);
296         }
297
298         return ret;
299 }