torture-krb5: Provide a generic handler to catch and print unexpected KRB_ERROR packets
[kai/samba-autobuild/.git] / lib / ntdb / tools / ntdbbackup.c
1 /*
2    Unix SMB/CIFS implementation.
3    low level ntdb 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 ntdb databases. Typical usage would be:
23      tdbbackup *.ntdb
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 *.ntdb
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 "config.h"
44 #include "ntdb.h"
45 #include "private.h"
46
47 #ifdef HAVE_GETOPT_H
48 #include <getopt.h>
49 #endif
50
51 static int failed;
52
53 static void ntdb_log(struct ntdb_context *ntdb,
54                     enum ntdb_log_level level,
55                     enum NTDB_ERROR ecode,
56                     const char *message,
57                     void *data)
58 {
59         fprintf(stderr, "%s:%s\n", ntdb_errorstr(ecode), message);
60 }
61
62 static char *add_suffix(const char *name, const char *suffix)
63 {
64         char *ret;
65         int len = strlen(name) + strlen(suffix) + 1;
66         ret = (char *)malloc(len);
67         if (!ret) {
68                 fprintf(stderr,"Out of memory!\n");
69                 exit(1);
70         }
71         snprintf(ret, len, "%s%s", name, suffix);
72         return ret;
73 }
74
75 static int copy_fn(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
76 {
77         struct ntdb_context *ntdb_new = (struct ntdb_context *)state;
78         enum NTDB_ERROR err;
79
80         err = ntdb_store(ntdb_new, key, dbuf, NTDB_INSERT);
81         if (err) {
82                 fprintf(stderr,"Failed to insert into %s: %s\n",
83                         ntdb_name(ntdb_new), ntdb_errorstr(err));
84                 failed = 1;
85                 return 1;
86         }
87         return 0;
88 }
89
90
91 static int test_fn(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
92 {
93         return 0;
94 }
95
96 /*
97   carefully backup a ntdb, validating the contents and
98   only doing the backup if its OK
99   this function is also used for restore
100 */
101 static int backup_ntdb(const char *old_name, const char *new_name)
102 {
103         struct ntdb_context *ntdb;
104         struct ntdb_context *ntdb_new;
105         char *tmp_name;
106         struct stat st;
107         int count1, count2;
108         enum NTDB_ERROR err;
109         union ntdb_attribute log_attr;
110
111         tmp_name = add_suffix(new_name, ".tmp");
112
113         /* stat the old ntdb to find its permissions */
114         if (stat(old_name, &st) != 0) {
115                 perror(old_name);
116                 free(tmp_name);
117                 return 1;
118         }
119
120         log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
121         log_attr.base.next = NULL;
122         log_attr.log.fn = ntdb_log;
123
124         /* open the old ntdb */
125         ntdb = ntdb_open(old_name, NTDB_DEFAULT, O_RDWR, 0, &log_attr);
126         if (!ntdb) {
127                 printf("Failed to open %s\n", old_name);
128                 free(tmp_name);
129                 return 1;
130         }
131
132         unlink(tmp_name);
133         ntdb_new = ntdb_open(tmp_name, NTDB_DEFAULT,
134                            O_RDWR|O_CREAT|O_EXCL, st.st_mode & 0777,
135                            &log_attr);
136         if (!ntdb_new) {
137                 perror(tmp_name);
138                 free(tmp_name);
139                 return 1;
140         }
141
142         err = ntdb_transaction_start(ntdb);
143         if (err) {
144                 fprintf(stderr, "Failed to start transaction on old ntdb: %s\n",
145                         ntdb_errorstr(err));
146                 ntdb_close(ntdb);
147                 ntdb_close(ntdb_new);
148                 unlink(tmp_name);
149                 free(tmp_name);
150                 return 1;
151         }
152
153         /* lock the backup ntdb so that nobody else can change it */
154         err = ntdb_lockall(ntdb_new);
155         if (err) {
156                 fprintf(stderr, "Failed to lock backup ntdb: %s\n",
157                         ntdb_errorstr(err));
158                 ntdb_close(ntdb);
159                 ntdb_close(ntdb_new);
160                 unlink(tmp_name);
161                 free(tmp_name);
162                 return 1;
163         }
164
165         failed = 0;
166
167         /* traverse and copy */
168         count1 = ntdb_traverse(ntdb, copy_fn, (void *)ntdb_new);
169         if (count1 < 0 || failed) {
170                 fprintf(stderr,"failed to copy %s\n", old_name);
171                 ntdb_close(ntdb);
172                 ntdb_close(ntdb_new);
173                 unlink(tmp_name);
174                 free(tmp_name);
175                 return 1;
176         }
177
178         /* close the old ntdb */
179         ntdb_close(ntdb);
180
181         /* copy done, unlock the backup ntdb */
182         ntdb_unlockall(ntdb_new);
183
184 #ifdef HAVE_FDATASYNC
185         if (fdatasync(ntdb_fd(ntdb_new)) != 0) {
186 #else
187         if (fsync(ntdb_fd(ntdb_new)) != 0) {
188 #endif
189                 /* not fatal */
190                 fprintf(stderr, "failed to fsync backup file\n");
191         }
192
193         /* close the new ntdb and re-open read-only */
194         ntdb_close(ntdb_new);
195
196         /* we don't need the hash attr any more */
197         log_attr.base.next = NULL;
198
199         ntdb_new = ntdb_open(tmp_name, NTDB_DEFAULT, O_RDONLY, 0, &log_attr);
200         if (!ntdb_new) {
201                 fprintf(stderr,"failed to reopen %s\n", tmp_name);
202                 unlink(tmp_name);
203                 perror(tmp_name);
204                 free(tmp_name);
205                 return 1;
206         }
207
208         /* traverse the new ntdb to confirm */
209         count2 = ntdb_traverse(ntdb_new, test_fn, NULL);
210         if (count2 != count1) {
211                 fprintf(stderr,"failed to copy %s\n", old_name);
212                 ntdb_close(ntdb_new);
213                 unlink(tmp_name);
214                 free(tmp_name);
215                 return 1;
216         }
217
218         /* close the new ntdb and rename it to .bak */
219         ntdb_close(ntdb_new);
220         if (rename(tmp_name, new_name) != 0) {
221                 perror(new_name);
222                 free(tmp_name);
223                 return 1;
224         }
225
226         free(tmp_name);
227
228         return 0;
229 }
230
231 /*
232   verify a ntdb and if it is corrupt then restore from *.bak
233 */
234 static int verify_ntdb(const char *fname, const char *bak_name)
235 {
236         struct ntdb_context *ntdb;
237         int count = -1;
238         union ntdb_attribute log_attr;
239
240         log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
241         log_attr.base.next = NULL;
242         log_attr.log.fn = ntdb_log;
243
244         /* open the ntdb */
245         ntdb = ntdb_open(fname, NTDB_DEFAULT, O_RDONLY, 0, &log_attr);
246
247         /* traverse the ntdb, then close it */
248         if (ntdb) {
249                 count = ntdb_traverse(ntdb, test_fn, NULL);
250                 ntdb_close(ntdb);
251         }
252
253         /* count is < 0 means an error */
254         if (count < 0) {
255                 printf("restoring %s\n", fname);
256                 return backup_ntdb(bak_name, fname);
257         }
258
259         printf("%s : %d records\n", fname, count);
260
261         return 0;
262 }
263
264 /*
265   see if one file is newer than another
266 */
267 static int file_newer(const char *fname1, const char *fname2)
268 {
269         struct stat st1, st2;
270         if (stat(fname1, &st1) != 0) {
271                 return 0;
272         }
273         if (stat(fname2, &st2) != 0) {
274                 return 1;
275         }
276         return (st1.st_mtime > st2.st_mtime);
277 }
278
279 static void usage(void)
280 {
281         printf("Usage: ntdbbackup [options] <fname...>\n\n");
282         printf("   -h            this help message\n");
283         printf("   -v            verify mode (restore if corrupt)\n");
284         printf("   -s suffix     set the backup suffix\n");
285         printf("   -v            verify mode (restore if corrupt)\n");
286 }
287
288
289  int main(int argc, char *argv[])
290 {
291         int i;
292         int ret = 0;
293         int c;
294         int verify = 0;
295         const char *suffix = ".bak";
296
297         while ((c = getopt(argc, argv, "vhs:")) != -1) {
298                 switch (c) {
299                 case 'h':
300                         usage();
301                         exit(0);
302                 case 'v':
303                         verify = 1;
304                         break;
305                 case 's':
306                         suffix = optarg;
307                         break;
308                 }
309         }
310
311         argc -= optind;
312         argv += optind;
313
314         if (argc < 1) {
315                 usage();
316                 exit(1);
317         }
318
319         for (i=0; i<argc; i++) {
320                 const char *fname = argv[i];
321                 char *bak_name;
322
323                 bak_name = add_suffix(fname, suffix);
324
325                 if (verify) {
326                         if (verify_ntdb(fname, bak_name) != 0) {
327                                 ret = 1;
328                         }
329                 } else {
330                         if (file_newer(fname, bak_name) &&
331                             backup_ntdb(fname, bak_name) != 0) {
332                                 ret = 1;
333                         }
334                 }
335
336                 free(bak_name);
337         }
338
339         return ret;
340 }