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