smbd: Use full_path_tos() where appropriate
[Samba/wip.git] / lib / ntdb / tools / ntdbbackup.c
blobc632f0ed84b01caeb1a87f4fb19bfe6d3bc8ec4d
1 /*
2 Unix SMB/CIFS implementation.
3 low level ntdb backup and restore utility
4 Copyright (C) Andrew Tridgell 2002
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.
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.
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/>.
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.
30 You may also like to do a backup on a regular basis while Samba is
31 running, perhaps using cron.
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.
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.
43 #include "config.h"
44 #include "ntdb.h"
45 #include "private.h"
47 #ifdef HAVE_GETOPT_H
48 #include <getopt.h>
49 #endif
51 static int failed;
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)
59 fprintf(stderr, "%s:%s\n", ntdb_errorstr(ecode), message);
62 static char *add_suffix(const char *name, const char *suffix)
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);
71 snprintf(ret, len, "%s%s", name, suffix);
72 return ret;
75 static int copy_fn(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
77 struct ntdb_context *ntdb_new = (struct ntdb_context *)state;
78 enum NTDB_ERROR err;
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;
87 return 0;
91 static int test_fn(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA dbuf, void *state)
93 return 0;
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
101 static int backup_ntdb(const char *old_name, const char *new_name)
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;
111 tmp_name = add_suffix(new_name, ".tmp");
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;
120 log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
121 log_attr.base.next = NULL;
122 log_attr.log.fn = ntdb_log;
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;
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;
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;
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;
165 failed = 0;
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;
178 /* close the old ntdb */
179 ntdb_close(ntdb);
181 /* copy done, unlock the backup ntdb */
182 ntdb_unlockall(ntdb_new);
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");
193 /* close the new ntdb and re-open read-only */
194 ntdb_close(ntdb_new);
196 /* we don't need the hash attr any more */
197 log_attr.base.next = NULL;
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;
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;
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;
226 free(tmp_name);
228 return 0;
232 verify a ntdb and if it is corrupt then restore from *.bak
234 static int verify_ntdb(const char *fname, const char *bak_name)
236 struct ntdb_context *ntdb;
237 int count = -1;
238 union ntdb_attribute log_attr;
240 log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
241 log_attr.base.next = NULL;
242 log_attr.log.fn = ntdb_log;
244 /* open the ntdb */
245 ntdb = ntdb_open(fname, NTDB_DEFAULT, O_RDONLY, 0, &log_attr);
247 /* traverse the ntdb, then close it */
248 if (ntdb) {
249 count = ntdb_traverse(ntdb, test_fn, NULL);
250 ntdb_close(ntdb);
253 /* count is < 0 means an error */
254 if (count < 0) {
255 printf("restoring %s\n", fname);
256 return backup_ntdb(bak_name, fname);
259 printf("%s : %d records\n", fname, count);
261 return 0;
265 see if one file is newer than another
267 static int file_newer(const char *fname1, const char *fname2)
269 struct stat st1, st2;
270 if (stat(fname1, &st1) != 0) {
271 return 0;
273 if (stat(fname2, &st2) != 0) {
274 return 1;
276 return (st1.st_mtime > st2.st_mtime);
279 static void usage(void)
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");
289 int main(int argc, char *argv[])
291 int i;
292 int ret = 0;
293 int c;
294 int verify = 0;
295 const char *suffix = ".bak";
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;
311 argc -= optind;
312 argv += optind;
314 if (argc < 1) {
315 usage();
316 exit(1);
319 for (i=0; i<argc; i++) {
320 const char *fname = argv[i];
321 char *bak_name;
323 bak_name = add_suffix(fname, suffix);
325 if (verify) {
326 if (verify_ntdb(fname, bak_name) != 0) {
327 ret = 1;
329 } else {
330 if (file_newer(fname, bak_name) &&
331 backup_ntdb(fname, bak_name) != 0) {
332 ret = 1;
336 free(bak_name);
339 return ret;