/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- add_suffix
- copy_fn
- test_fn
- backup_tdb
- verify_tdb
- file_newer
- usage
- main
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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 if (tdb_transaction_start(tdb) != 0) {
130 printf("Failed to start transaction on old tdb\n");
131 tdb_close(tdb);
132 tdb_close(tdb_new);
133 unlink(tmp_name);
134 free(tmp_name);
135 return 1;
136 }
137
138 if (tdb_transaction_start(tdb_new) != 0) {
139 printf("Failed to start transaction on new tdb\n");
140 tdb_close(tdb);
141 tdb_close(tdb_new);
142 unlink(tmp_name);
143 free(tmp_name);
144 return 1;
145 }
146
147 failed = 0;
148
149 /* traverse and copy */
150 count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
151 if (count1 < 0 || failed) {
152 fprintf(stderr,"failed to copy %s\n", old_name);
153 tdb_close(tdb);
154 tdb_close(tdb_new);
155 unlink(tmp_name);
156 free(tmp_name);
157 return 1;
158 }
159
160 /* close the old tdb */
161 tdb_close(tdb);
162
163 if (tdb_transaction_commit(tdb_new) != 0) {
164 fprintf(stderr, "Failed to commit new tdb\n");
165 tdb_close(tdb_new);
166 unlink(tmp_name);
167 free(tmp_name);
168 return 1;
169 }
170
171 /* close the new tdb and re-open read-only */
172 tdb_close(tdb_new);
173 tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0);
174 if (!tdb_new) {
175 fprintf(stderr,"failed to reopen %s\n", tmp_name);
176 unlink(tmp_name);
177 perror(tmp_name);
178 free(tmp_name);
179 return 1;
180 }
181
182 /* traverse the new tdb to confirm */
183 count2 = tdb_traverse(tdb_new, test_fn, NULL);
184 if (count2 != count1) {
185 fprintf(stderr,"failed to copy %s\n", old_name);
186 tdb_close(tdb_new);
187 unlink(tmp_name);
188 free(tmp_name);
189 return 1;
190 }
191
192 /* close the new tdb and rename it to .bak */
193 tdb_close(tdb_new);
194 if (rename(tmp_name, new_name) != 0) {
195 perror(new_name);
196 free(tmp_name);
197 return 1;
198 }
199
200 free(tmp_name);
201
202 return 0;
203 }
204
205 /*
206 verify a tdb and if it is corrupt then restore from *.bak
207 */
208 static int verify_tdb(const char *fname, const char *bak_name)
/* [<][>][^][v][top][bottom][index][help] */
209 {
210 TDB_CONTEXT *tdb;
211 int count = -1;
212
213 /* open the tdb */
214 tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
215
216 /* traverse the tdb, then close it */
217 if (tdb) {
218 count = tdb_traverse(tdb, test_fn, NULL);
219 tdb_close(tdb);
220 }
221
222 /* count is < 0 means an error */
223 if (count < 0) {
224 printf("restoring %s\n", fname);
225 return backup_tdb(bak_name, fname, 0);
226 }
227
228 printf("%s : %d records\n", fname, count);
229
230 return 0;
231 }
232
233 /*
234 see if one file is newer than another
235 */
236 static int file_newer(const char *fname1, const char *fname2)
/* [<][>][^][v][top][bottom][index][help] */
237 {
238 struct stat st1, st2;
239 if (stat(fname1, &st1) != 0) {
240 return 0;
241 }
242 if (stat(fname2, &st2) != 0) {
243 return 1;
244 }
245 return (st1.st_mtime > st2.st_mtime);
246 }
247
248 static void usage(void)
/* [<][>][^][v][top][bottom][index][help] */
249 {
250 printf("Usage: tdbbackup [options] <fname...>\n\n");
251 printf(" -h this help message\n");
252 printf(" -s suffix set the backup suffix\n");
253 printf(" -v verify mode (restore if corrupt)\n");
254 printf(" -n hashsize set the new hash size for the backup\n");
255 }
256
257
258 int main(int argc, char *argv[])
/* [<][>][^][v][top][bottom][index][help] */
259 {
260 int i;
261 int ret = 0;
262 int c;
263 int verify = 0;
264 int hashsize = 0;
265 const char *suffix = ".bak";
266
267 while ((c = getopt(argc, argv, "vhs:n:")) != -1) {
268 switch (c) {
269 case 'h':
270 usage();
271 exit(0);
272 case 'v':
273 verify = 1;
274 break;
275 case 's':
276 suffix = optarg;
277 break;
278 case 'n':
279 hashsize = atoi(optarg);
280 break;
281 }
282 }
283
284 argc -= optind;
285 argv += optind;
286
287 if (argc < 1) {
288 usage();
289 exit(1);
290 }
291
292 for (i=0; i<argc; i++) {
293 const char *fname = argv[i];
294 char *bak_name;
295
296 bak_name = add_suffix(fname, suffix);
297
298 if (verify) {
299 if (verify_tdb(fname, bak_name) != 0) {
300 ret = 1;
301 }
302 } else {
303 if (file_newer(fname, bak_name) &&
304 backup_tdb(fname, bak_name, hashsize) != 0) {
305 ret = 1;
306 }
307 }
308
309 free(bak_name);
310 }
311
312 return ret;
313 }