Line data Source code
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 struct tdb_logging_context log_ctx;
57 :
58 : #ifdef PRINTF_ATTRIBUTE
59 : static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
60 : #endif
61 9 : static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
62 : {
63 0 : va_list ap;
64 :
65 9 : va_start(ap, format);
66 9 : vfprintf(stdout, format, ap);
67 9 : va_end(ap);
68 9 : fflush(stdout);
69 9 : }
70 :
71 1010 : static char *add_suffix(const char *name, const char *suffix)
72 : {
73 4 : char *ret;
74 1010 : int len = strlen(name) + strlen(suffix) + 1;
75 1010 : ret = (char *)malloc(len);
76 1010 : if (!ret) {
77 0 : fprintf(stderr,"Out of memory!\n");
78 0 : exit(1);
79 : }
80 1010 : snprintf(ret, len, "%s%s", name, suffix);
81 1010 : return ret;
82 : }
83 :
84 580563 : static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
85 : {
86 580563 : TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
87 :
88 580563 : if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
89 0 : fprintf(stderr,"Failed to insert into %s\n", tdb_name(tdb_new));
90 0 : failed = 1;
91 0 : return 1;
92 : }
93 580551 : return 0;
94 : }
95 :
96 :
97 580563 : static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
98 : {
99 580563 : return 0;
100 : }
101 :
102 : /*
103 : carefully backup a tdb, validating the contents and
104 : only doing the backup if its OK
105 : this function is also used for restore
106 : */
107 505 : static int backup_tdb(const char *old_name, const char *new_name,
108 : int hash_size, int nolock, bool readonly)
109 : {
110 2 : TDB_CONTEXT *tdb;
111 2 : TDB_CONTEXT *tdb_new;
112 2 : char *tmp_name;
113 2 : struct stat st;
114 2 : int count1, count2;
115 :
116 505 : tmp_name = add_suffix(new_name, ".tmp");
117 :
118 : /* stat the old tdb to find its permissions */
119 505 : if (stat(old_name, &st) != 0) {
120 0 : perror(old_name);
121 0 : free(tmp_name);
122 0 : return 1;
123 : }
124 :
125 : /* open the old tdb */
126 507 : tdb = tdb_open_ex(old_name, 0,
127 : TDB_DEFAULT | (nolock ? TDB_NOLOCK : 0),
128 : O_RDWR, 0, &log_ctx, NULL);
129 505 : if (!tdb) {
130 9 : printf("Failed to open %s\n", old_name);
131 9 : free(tmp_name);
132 9 : return 1;
133 : }
134 :
135 : /* create the new tdb */
136 496 : unlink(tmp_name);
137 496 : tdb_new = tdb_open_ex(tmp_name,
138 496 : hash_size ? hash_size : tdb_hash_size(tdb),
139 : TDB_DEFAULT,
140 496 : O_RDWR|O_CREAT|O_EXCL, st.st_mode & 0777,
141 : &log_ctx, NULL);
142 496 : if (!tdb_new) {
143 0 : perror(tmp_name);
144 0 : free(tmp_name);
145 0 : return 1;
146 : }
147 :
148 496 : if (readonly) {
149 416 : if (tdb_lockall_read(tdb) != 0) {
150 0 : printf("Failed to obtain read only lock on old tdb\n");
151 0 : tdb_close(tdb);
152 0 : tdb_close(tdb_new);
153 0 : unlink(tmp_name);
154 0 : free(tmp_name);
155 0 : return 1;
156 : }
157 80 : } else if (tdb_transaction_start(tdb) != 0) {
158 0 : printf("Failed to start transaction on db\n");
159 0 : tdb_close(tdb);
160 0 : tdb_close(tdb_new);
161 0 : unlink(tmp_name);
162 0 : free(tmp_name);
163 0 : return 1;
164 : }
165 :
166 : /* lock the backup tdb so that nobody else can change it */
167 496 : if (tdb_lockall(tdb_new) != 0) {
168 0 : printf("Failed to lock backup tdb\n");
169 0 : tdb_close(tdb);
170 0 : tdb_close(tdb_new);
171 0 : unlink(tmp_name);
172 0 : free(tmp_name);
173 0 : return 1;
174 : }
175 :
176 496 : failed = 0;
177 :
178 : /* traverse and copy */
179 496 : if (readonly) {
180 416 : count1 = tdb_traverse_read(tdb,
181 : copy_fn,
182 : (void *)tdb_new);
183 : } else {
184 80 : count1 = tdb_traverse(tdb,
185 : copy_fn,
186 : (void *)tdb_new);
187 : }
188 496 : if (count1 < 0 || failed) {
189 0 : fprintf(stderr,"failed to copy %s\n", old_name);
190 0 : tdb_close(tdb);
191 0 : tdb_close(tdb_new);
192 0 : unlink(tmp_name);
193 0 : free(tmp_name);
194 0 : return 1;
195 : }
196 :
197 : /* close the old tdb */
198 496 : tdb_close(tdb);
199 :
200 : /* copy done, unlock the backup tdb */
201 496 : tdb_unlockall(tdb_new);
202 :
203 : #ifdef HAVE_FDATASYNC
204 496 : if (fdatasync(tdb_fd(tdb_new)) != 0) {
205 : #else
206 : if (fsync(tdb_fd(tdb_new)) != 0) {
207 : #endif
208 : /* not fatal */
209 0 : fprintf(stderr, "failed to fsync backup file\n");
210 : }
211 :
212 : /* close the new tdb and re-open read-only */
213 496 : tdb_close(tdb_new);
214 496 : tdb_new = tdb_open_ex(tmp_name,
215 : 0,
216 : TDB_DEFAULT,
217 : O_RDONLY, 0,
218 : &log_ctx, NULL);
219 496 : if (!tdb_new) {
220 0 : fprintf(stderr,"failed to reopen %s\n", tmp_name);
221 0 : unlink(tmp_name);
222 0 : perror(tmp_name);
223 0 : free(tmp_name);
224 0 : return 1;
225 : }
226 :
227 : /* traverse the new tdb to confirm */
228 496 : count2 = tdb_traverse(tdb_new, test_fn, NULL);
229 496 : if (count2 != count1) {
230 0 : fprintf(stderr,"failed to copy %s\n", old_name);
231 0 : tdb_close(tdb_new);
232 0 : unlink(tmp_name);
233 0 : free(tmp_name);
234 0 : return 1;
235 : }
236 :
237 : /* close the new tdb and rename it to .bak */
238 496 : tdb_close(tdb_new);
239 496 : if (rename(tmp_name, new_name) != 0) {
240 0 : perror(new_name);
241 0 : free(tmp_name);
242 0 : return 1;
243 : }
244 :
245 496 : free(tmp_name);
246 :
247 496 : return 0;
248 : }
249 :
250 : /*
251 : verify a tdb and if it is corrupt then restore from *.bak
252 : */
253 0 : static int verify_tdb(const char *fname, const char *bak_name)
254 : {
255 0 : TDB_CONTEXT *tdb;
256 0 : int count = -1;
257 :
258 : /* open the tdb */
259 0 : tdb = tdb_open_ex(fname, 0, 0,
260 : O_RDONLY, 0, &log_ctx, NULL);
261 :
262 : /* traverse the tdb, then close it */
263 0 : if (tdb) {
264 0 : count = tdb_traverse(tdb, test_fn, NULL);
265 0 : tdb_close(tdb);
266 : }
267 :
268 : /* count is < 0 means an error */
269 0 : if (count < 0) {
270 0 : printf("restoring %s\n", fname);
271 0 : return backup_tdb(bak_name, fname, 0, 0, 0);
272 : }
273 :
274 0 : printf("%s : %d records\n", fname, count);
275 :
276 0 : return 0;
277 : }
278 :
279 : /*
280 : see if one file is newer than another
281 : */
282 505 : static int file_newer(const char *fname1, const char *fname2)
283 : {
284 2 : struct stat st1, st2;
285 505 : if (stat(fname1, &st1) != 0) {
286 0 : return 0;
287 : }
288 505 : if (stat(fname2, &st2) != 0) {
289 503 : return 1;
290 : }
291 0 : return (st1.st_mtime > st2.st_mtime);
292 : }
293 :
294 0 : static void usage(void)
295 : {
296 0 : printf("Usage: tdbbackup [options] <fname...>\n\n");
297 0 : printf(" -h this help message\n");
298 0 : printf(" -s suffix set the backup suffix\n");
299 0 : printf(" -v verify mode (restore if corrupt)\n");
300 0 : printf(" -n hashsize set the new hash size for the backup\n");
301 0 : printf(" -l open without locking to back up mutex dbs\n");
302 0 : printf(" -r open with read only locking\n");
303 0 : }
304 :
305 505 : int main(int argc, char *argv[])
306 : {
307 2 : int i;
308 505 : int ret = 0;
309 2 : int c;
310 505 : int verify = 0;
311 505 : int hashsize = 0;
312 505 : int nolock = 0;
313 505 : bool readonly = false;
314 505 : const char *suffix = ".bak";
315 :
316 505 : log_ctx.log_fn = tdb_log;
317 :
318 1435 : while ((c = getopt(argc, argv, "vhs:n:lr")) != -1) {
319 930 : switch (c) {
320 0 : case 'h':
321 0 : usage();
322 0 : exit(0);
323 0 : case 'v':
324 0 : verify = 1;
325 0 : break;
326 505 : case 's':
327 505 : suffix = optarg;
328 505 : break;
329 0 : case 'n':
330 0 : hashsize = atoi(optarg);
331 0 : break;
332 0 : case 'l':
333 0 : nolock = 1;
334 0 : break;
335 425 : case 'r':
336 425 : readonly = true;
337 : }
338 : }
339 :
340 505 : argc -= optind;
341 505 : argv += optind;
342 :
343 505 : if (argc < 1) {
344 0 : usage();
345 0 : exit(1);
346 : }
347 :
348 1010 : for (i=0; i<argc; i++) {
349 505 : const char *fname = argv[i];
350 2 : char *bak_name;
351 :
352 505 : bak_name = add_suffix(fname, suffix);
353 :
354 505 : if (verify) {
355 0 : if (verify_tdb(fname, bak_name) != 0) {
356 0 : ret = 1;
357 : }
358 : } else {
359 1010 : if (file_newer(fname, bak_name) &&
360 505 : backup_tdb(fname, bak_name, hashsize,
361 : nolock, readonly) != 0) {
362 9 : ret = 1;
363 : }
364 : }
365 :
366 505 : free(bak_name);
367 : }
368 :
369 505 : return ret;
370 : }
|