1 /*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2011, 2013 Oracle and/or its affiliates. All rights reserved.
5 *
6 * $Id$
7 */
8
9 #include "db_config.h"
10 #include "db_int.h"
11 #include "dbinc/db_page.h"
12 #include "dbinc/db_am.h"
13 #include "dbinc/heap.h"
14 #include "dbinc/mp.h"
15 #include "dbinc/partition.h"
16
17 #ifdef HAVE_QUEUE
18 #include "dbinc/qam.h"
19 #endif
20
21 static void save_error __P((const DB_ENV *, const char *, const char *));
22 static int backup_read_log_dir __P((DB_ENV *, const char *, int *, u_int32_t));
23 static int backup_read_data_dir
24 __P((DB_ENV *, DB_THREAD_INFO *, const char *, const char *, u_int32_t));
25 static int backup_dir_clean
26 __P((DB_ENV *, const char *, const char *, int *, u_int32_t));
27 static int backup_data_copy
28 __P((DB_ENV *, const char *, const char *, const char *, int));
29 static int __db_backup
30 __P((DB_ENV *, const char *, DB_THREAD_INFO *, int, u_int32_t));
31
32 /*
33 * __db_dbbackup_pp --
34 * Copy a database file coordinated with mpool.
35 *
36 * PUBLIC: int __db_dbbackup_pp __P((DB_ENV *,
37 * PUBLIC: const char *, const char *, u_int32_t));
38 */
39 int
__db_dbbackup_pp(dbenv,dbfile,target,flags)40 __db_dbbackup_pp(dbenv, dbfile, target, flags)
41 DB_ENV *dbenv;
42 const char *dbfile, *target;
43 u_int32_t flags;
44 {
45 DB_THREAD_INFO *ip;
46 int ret;
47
48 if ((ret = __db_fchk(dbenv->env,
49 "DB_ENV->dbbackup", flags, DB_EXCL)) != 0)
50 return (ret);
51 ENV_ENTER(dbenv->env, ip);
52 REPLICATION_WRAP(dbenv->env,
53 (__db_dbbackup(dbenv, ip, dbfile, target, flags)), 0, ret);
54 ENV_LEAVE(dbenv->env, ip);
55 return (ret);
56 }
57
58 /*
59 * __db_dbbackup --
60 * Copy a database file coordinated with mpool.
61 *
62 * PUBLIC: int __db_dbbackup __P((DB_ENV *, DB_THREAD_INFO *,
63 * PUBLIC: const char *, const char *, u_int32_t));
64 */
65 int
__db_dbbackup(dbenv,ip,dbfile,target,flags)66 __db_dbbackup(dbenv, ip, dbfile, target, flags)
67 DB_ENV *dbenv;
68 DB_THREAD_INFO *ip;
69 const char *dbfile, *target;
70 u_int32_t flags;
71 {
72 DB *dbp;
73 DB_FH *fp;
74 void *handle;
75 int ret, retry_count, t_ret;
76
77 dbp = NULL;
78 retry_count = 0;
79
80 retry: if ((ret = __db_create_internal(&dbp, dbenv->env, 0)) == 0 &&
81 (ret = __db_open(dbp, ip, NULL, dbfile, NULL,
82 DB_UNKNOWN, DB_AUTO_COMMIT | DB_RDONLY, 0, PGNO_BASE_MD)) != 0) {
83 if (ret == DB_LOCK_DEADLOCK || ret == DB_LOCK_NOTGRANTED) {
84 (void)__db_close(dbp, NULL, DB_NOSYNC);
85 dbp = NULL;
86 if (++retry_count > 100)
87 return (ret);
88 __db_errx(dbenv->env, DB_STR_A("0702",
89 "Deadlock while opening %s, retrying", "%s"), dbfile);
90 __os_yield(dbenv->env, 1, 0);
91 goto retry;
92 }
93 }
94
95 if (ret == 0) {
96 if ((ret = __memp_backup_open(dbenv->env,
97 dbp->mpf, dbfile, target, flags, &fp, &handle)) == 0) {
98 if (dbp->type == DB_HEAP)
99 ret = __heap_backup(
100 dbenv, dbp, ip, fp, handle, flags);
101 else
102 ret = __memp_backup_mpf(
103 dbenv->env, dbp->mpf,
104 ip, 0, dbp->mpf->mfp->last_pgno,
105 fp, handle, flags);
106 }
107 if ((t_ret = __memp_backup_close(dbenv->env,
108 dbp->mpf, dbfile, fp, handle)) != 0 && ret == 0)
109 ret = t_ret;
110 }
111
112 #ifdef HAVE_QUEUE
113 /*
114 * For compatibility with the 5.2 and patch versions of db_copy
115 * dump the queue extents here.
116 */
117 if (ret == 0 && dbp->type == DB_QUEUE)
118 ret = __qam_backup_extents(dbp, ip, target, flags);
119 #endif
120
121 if (dbp != NULL &&
122 (t_ret = __db_close(dbp, NULL, DB_NOSYNC)) != 0 && ret == 0)
123 ret = t_ret;
124
125 if (ret != 0)
126 __db_err(dbenv->env, ret, "Backup Failed");
127 return (ret);
128 }
129
130 /*
131 * backup_dir_clean --
132 * Clean out the backup directory.
133 */
134 static int
backup_dir_clean(dbenv,backup_dir,log_dir,remove_maxp,flags)135 backup_dir_clean(dbenv, backup_dir, log_dir, remove_maxp, flags)
136 DB_ENV *dbenv;
137 const char *backup_dir, *log_dir;
138 int *remove_maxp;
139 u_int32_t flags;
140 {
141 ENV *env;
142 int cnt, fcnt, ret, v;
143 const char *dir;
144 char **names, buf[DB_MAXPATHLEN], path[DB_MAXPATHLEN];
145
146 env = dbenv->env;
147
148 /* We may be cleaning a log directory separate from the target. */
149 if (log_dir != NULL) {
150 if ((ret = __os_concat_path(buf,
151 sizeof(buf), backup_dir, log_dir)) != 0) {
152 buf[sizeof(buf) - 1] = '\0';
153 __db_errx(env, DB_STR_A("0717",
154 "%s: path too long", "%s"), buf);
155 return (EINVAL);
156 }
157 dir = buf;
158 } else
159 dir = backup_dir;
160
161 /* Get a list of file names. */
162 if ((ret = __os_dirlist(env, dir, 0, &names, &fcnt)) != 0) {
163 if (log_dir != NULL && !LF_ISSET(DB_BACKUP_UPDATE))
164 return (0);
165 __db_err(env,
166 ret, DB_STR_A("0718", "%s: directory read", "%s"), dir);
167 return (ret);
168 }
169 for (cnt = fcnt; --cnt >= 0;) {
170 /*
171 * Skip non-log files (if update was specified).
172 */
173 if (!IS_LOG_FILE(names[cnt])) {
174 if (LF_ISSET(DB_BACKUP_UPDATE))
175 continue;
176 } else {
177 /* Track the highest-numbered log file removed. */
178 v = atoi(names[cnt] + sizeof(LFPREFIX) - 1);
179 if (*remove_maxp < v)
180 *remove_maxp = v;
181 }
182 if ((ret = __os_concat_path(path,
183 sizeof(path), dir, names[cnt])) != 0) {
184 path[sizeof(path) - 1] = '\0';
185 __db_errx(env, DB_STR_A("0714",
186 "%s: path too long", "%s"), path);
187 return (EINVAL);
188 }
189 if (FLD_ISSET(dbenv->verbose, DB_VERB_BACKUP))
190 __db_msg(env, DB_STR_A("0715", "removing %s",
191 "%s"), path);
192 if ((ret = __os_unlink(env, path, 0)) != 0)
193 return (ret);
194 }
195
196 __os_dirfree(env, names, fcnt);
197
198 if (FLD_ISSET(dbenv->verbose, DB_VERB_BACKUP) && *remove_maxp != 0)
199 __db_msg(env, DB_STR_A("0719",
200 "highest numbered log file removed: %d", "%d"),
201 *remove_maxp);
202
203 return (0);
204 }
205
206 /*
207 * backup_data_copy --
208 * Copy a non-database file into the backup directory.
209 */
210 static int
backup_data_copy(dbenv,file,from_dir,to_dir,log)211 backup_data_copy(dbenv, file, from_dir, to_dir, log)
212 DB_ENV *dbenv;
213 const char *file, *from_dir, *to_dir;
214 int log;
215 {
216 DB_BACKUP *backup;
217 DB_FH *rfhp, *wfhp;
218 ENV *env;
219 u_int32_t gigs, off;
220 size_t nr, nw;
221 int ret, t_ret;
222 char *buf;
223 void *handle;
224 char from[DB_MAXPATHLEN], to[DB_MAXPATHLEN];
225
226 rfhp = wfhp = NULL;
227 handle = NULL;
228 buf = NULL;
229 env = dbenv->env;
230 backup = env->backup_handle;
231
232 if ((ret = __os_concat_path(from,
233 sizeof(from), from_dir, file)) != 0) {
234 from[sizeof(from) - 1] = '\0';
235 __db_errx(env, DB_STR_A("0728",
236 "%s: path too long", "%s"), from);
237 goto err;
238 }
239 if ((ret = __os_concat_path(to,
240 sizeof(to), to_dir, file)) != 0) {
241 to[sizeof(to) - 1] = '\0';
242 __db_errx(env, DB_STR_A("0729",
243 "%s: path too long", "%s"), to);
244 goto err;
245 }
246 if (FLD_ISSET(dbenv->verbose, DB_VERB_BACKUP))
247 __db_msg(env, DB_STR_A("0726",
248 "copying %s to %s", "%s %s"), from, to);
249
250 if ((ret = __os_malloc(env, MEGABYTE, &buf)) != 0) {
251 __db_err(env, ret, DB_STR_A("0727",
252 "%lu buffer allocation", "%lu"), (u_long)MEGABYTE);
253 return (ret);
254 }
255
256 /* Open the input file. */
257 if ((ret = __os_open(env, from, 0, DB_OSO_RDONLY, 0, &rfhp)) != 0) {
258 if (ret == ENOENT && !log) {
259 ret = 0;
260 if (FLD_ISSET(dbenv->verbose, DB_VERB_BACKUP))
261 __db_msg(env, DB_STR_A("0730",
262 "%s%c%s not present", "%s %c %s"),
263 from_dir, PATH_SEPARATOR[0], file);
264 goto done;
265 }
266 __db_err(env, ret, "%s", buf);
267 goto err;
268 }
269
270 /* Open the output file. */
271 if (backup != NULL && backup->open != NULL)
272 ret = backup->open(env->dbenv, file, to_dir, &handle);
273 else {
274 if ((ret = __os_open(env, to, 0,
275 DB_OSO_CREATE | DB_OSO_TRUNC, DB_MODE_600, &wfhp)) != 0) {
276 __db_err(env, ret, "%s", to);
277 goto err;
278 }
279 }
280
281 off = 0;
282 gigs = 0;
283 /* Copy the data. */
284 while ((ret = __os_read(env, rfhp, buf, MEGABYTE, &nr)) == 0 &&
285 nr > 0) {
286 if (backup != NULL && backup->write != NULL) {
287 if ((ret = backup->write(env->dbenv, gigs,
288 off, (u_int32_t)nr, (u_int8_t *)buf, handle)) != 0)
289 break;
290 } else {
291 if ((ret = __os_write(env, wfhp, buf, nr, &nw)) != 0)
292 break;
293 if (nr != nw) {
294 ret = EIO;
295 break;
296 }
297 }
298 off += (u_int32_t)nr;
299 if (off >= GIGABYTE) {
300 gigs++;
301 off -= GIGABYTE;
302 }
303 }
304 if (ret != 0)
305 __db_err(env, ret, DB_STR("0748", "Write failed."));
306
307 err:
308 done: if (buf != NULL)
309 __os_free(env, buf);
310
311 if (backup != NULL && backup->close != NULL &&
312 (t_ret = backup->close(env->dbenv, file, handle)) != 0 && ret != 0)
313 ret = t_ret;
314 if (rfhp != NULL &&
315 (t_ret = __os_closehandle(env, rfhp)) != 0 && ret == 0)
316 ret = t_ret;
317
318 /* We may be running on a remote filesystem; force the flush. */
319 if (ret == 0 && wfhp != NULL) {
320 ret = __os_fsync(env, wfhp);
321 if (ret != 0)
322 __db_err(env, ret, DB_STR("0731", "Sync failed"));
323 }
324 if (wfhp != NULL &&
325 (t_ret = __os_closehandle(env, wfhp)) != 0 && ret == 0)
326 ret = t_ret;
327 return (ret);
328 }
329
save_error(dbenv,prefix,errstr)330 static void save_error(dbenv, prefix, errstr)
331 const DB_ENV *dbenv;
332 const char *prefix;
333 const char *errstr;
334 {
335 COMPQUIET(prefix, NULL);
336 if (DB_GLOBAL(saved_errstr) != NULL)
337 __os_free(dbenv->env, DB_GLOBAL(saved_errstr));
338 (void)__os_strdup(dbenv->env, errstr, &DB_GLOBAL(saved_errstr));
339 }
340
341 /*
342 * backup_read_data_dir --
343 * Read a directory looking for databases to copy.
344 */
345 static int
backup_read_data_dir(dbenv,ip,dir,backup_dir,flags)346 backup_read_data_dir(dbenv, ip, dir, backup_dir, flags)
347 DB_ENV *dbenv;
348 DB_THREAD_INFO *ip;
349 const char *dir, *backup_dir;
350 u_int32_t flags;
351 {
352 DB_MSGBUF mb;
353 ENV *env;
354 FILE *savefile;
355 int fcnt, ret;
356 size_t cnt;
357 const char *bd;
358 char **names, buf[DB_MAXPATHLEN], bbuf[DB_MAXPATHLEN];
359 void (*savecall) (const DB_ENV *, const char *, const char *);
360
361 env = dbenv->env;
362 memset(bbuf, 0, sizeof(bbuf));
363
364 bd = backup_dir;
365 if (!LF_ISSET(DB_BACKUP_SINGLE_DIR) && dir != env->db_home) {
366 cnt = sizeof(bbuf);
367 /* Build a path name to the destination. */
368 if ((ret = __os_concat_path(bbuf, sizeof(bbuf),
369 backup_dir, dir)) != 0 ||
370 (((cnt = strlen(bbuf)) == sizeof(bbuf) ||
371 (cnt == sizeof(bbuf) - 1 &&
372 strchr(PATH_SEPARATOR, bbuf[cnt - 1]) == NULL)) &&
373 LF_ISSET(DB_CREATE))) {
374 bbuf[sizeof(bbuf) - 1] = '\0';
375 __db_errx(env, DB_STR_A("0720",
376 "%s: path too long", "%s"), bbuf);
377 return (1);
378 }
379
380 /* Create the path. */
381 if (LF_ISSET(DB_CREATE)) {
382 if (strchr(PATH_SEPARATOR, bbuf[cnt - 1]) == NULL)
383 bbuf[cnt] = PATH_SEPARATOR[0];
384
385 if ((ret = __db_mkpath(env, bbuf)) != 0) {
386 __db_err(env, ret, DB_STR_A("0721",
387 "%s: cannot create", "%s"), bbuf);
388 return (ret);
389 }
390 /* step on the trailing '/' */
391 bbuf[cnt] = '\0';
392 }
393 bd = bbuf;
394
395 }
396 if (!__os_abspath(dir) && dir != env->db_home) {
397 /* Build a path name to the source. */
398 if ((ret = __os_concat_path(buf,
399 sizeof(buf), env->db_home, dir)) != 0) {
400 buf[sizeof(buf) - 1] = '\0';
401 __db_errx(env, DB_STR_A("0722",
402 "%s: path too long", "%s"), buf);
403 return (EINVAL);
404 }
405 dir = buf;
406 }
407 /* Get a list of file names. */
408 if ((ret = __os_dirlist(env, dir, 0, &names, &fcnt)) != 0) {
409 __db_err(env, ret, DB_STR_A("0723", "%s: directory read",
410 "%s"), dir);
411 return (ret);
412 }
413 for (cnt = (size_t)fcnt; cnt-- > 0;) {
414 /*
415 * Skip files in DB's name space, except replication dbs.
416 */
417 if (IS_LOG_FILE(names[cnt]))
418 continue;
419 if (IS_DB_FILE(names[cnt]) && !IS_REP_FILE(names[cnt])
420 #ifdef HAVE_PARTITION
421 && !IS_PARTITION_DB_FILE(names[cnt])
422 #endif
423 )
424 continue;
425
426 /*
427 * Skip DB_CONFIG.
428 */
429 if (LF_ISSET(DB_BACKUP_SINGLE_DIR) &&
430 !strncmp(names[cnt], "DB_CONFIG", sizeof("DB_CONFIG")))
431 continue;
432
433 /*
434 * Copy the database.
435 */
436
437 DB_MSGBUF_INIT(&mb);
438 if (FLD_ISSET(dbenv->verbose, DB_VERB_BACKUP))
439 __db_msgadd(env, &mb, DB_STR_A("0724",
440 "copying database %s%c%s to %s%c%s",
441 "%s%c%s %s%c%s"),
442 dir, PATH_SEPARATOR[0], names[cnt],
443 bd, PATH_SEPARATOR[0], names[cnt]);
444
445 /*
446 * Suppress errors on non-db files.
447 */
448 savecall = dbenv->db_errcall;
449 dbenv->db_errcall = save_error;
450 savefile = dbenv->db_errfile;
451 dbenv->db_errfile = NULL;
452
453 ret = __db_dbbackup(dbenv, ip, names[cnt], bd, flags);
454
455 dbenv->db_errcall = savecall;
456 dbenv->db_errfile = savefile;
457
458 /* The file might not be a database. */
459 if (ret == ENOENT || ret == EINVAL) {
460 if (FLD_ISSET(dbenv->verbose, DB_VERB_BACKUP)) {
461 __db_msgadd(env, &mb, " -- Not a database");
462 DB_MSGBUF_FLUSH(env, &mb);
463 }
464 if (LF_ISSET(DB_BACKUP_FILES))
465 ret = backup_data_copy(
466 dbenv, names[cnt], dir, bd, 0);
467 else
468 ret = 0;
469 } else if (FLD_ISSET(dbenv->verbose, DB_VERB_BACKUP))
470 DB_MSGBUF_FLUSH(env, &mb);
471
472 if (ret != 0) {
473 if (DB_GLOBAL(saved_errstr) != NULL) {
474 __db_errx(env, "%s", DB_GLOBAL(saved_errstr));
475 __os_free(env, DB_GLOBAL(saved_errstr));
476 DB_GLOBAL(saved_errstr) = NULL;
477 }
478 break;
479 }
480 }
481
482 __os_dirfree(env, names, fcnt);
483
484 return (ret);
485 }
486
487 /*
488 * backup_read_log_dir --
489 * Read a directory looking for log files to copy.
490 */
491 static int
backup_read_log_dir(dbenv,backup_dir,copy_minp,flags)492 backup_read_log_dir(dbenv, backup_dir, copy_minp, flags)
493 DB_ENV *dbenv;
494 const char *backup_dir;
495 int *copy_minp;
496 u_int32_t flags;
497 {
498 ENV *env;
499 u_int32_t aflag;
500 size_t cnt;
501 int ret, update, v;
502 const char *backupd;
503 char **begin, **names, *logd;
504 char from[DB_MAXPATHLEN], to[DB_MAXPATHLEN];
505
506 env = dbenv->env;
507 ret = 0;
508 begin = NULL;
509 memset(to, 0, sizeof(to));
510
511 /*
512 * Figure out where the log files are and create the log
513 * destination directory if necessary.
514 */
515 backupd = backup_dir;
516 if ((logd = dbenv->db_log_dir) == NULL)
517 logd = env->db_home;
518 else {
519 if (!LF_ISSET(DB_BACKUP_SINGLE_DIR)) {
520 cnt = sizeof(to);
521 if ((ret = __os_concat_path(to,
522 sizeof(to), backup_dir, logd)) != 0 ||
523 (((cnt = strlen(to)) == sizeof(to) ||
524 (cnt == sizeof(to) - 1 &&
525 strchr(PATH_SEPARATOR, to[cnt - 1]) == NULL)) &&
526 LF_ISSET(DB_CREATE))) {
527 to[sizeof(to) - 1] = '\0';
528 __db_errx(env, DB_STR_A("0733",
529 "%s: path too long", "%s"), to);
530 goto err;
531 }
532 if (LF_ISSET(DB_CREATE)) {
533 if (strchr(PATH_SEPARATOR, to[cnt - 1]) == NULL)
534 to[cnt] = PATH_SEPARATOR[0];
535
536 if ((ret = __db_mkpath(env, to)) != 0) {
537 __db_err(env, ret, DB_STR_A("0734",
538 "%s: cannot create", "%s"), to);
539 goto err;
540 }
541 to[cnt] = '\0';
542 }
543 if ((ret = __os_strdup(env, to, (void*) &backupd)) != 0)
544 goto err;
545 }
546 if (!__os_abspath(logd)) {
547 if ((ret = __os_concat_path(from,
548 sizeof(from), env->db_home, logd)) != 0) {
549 from[sizeof(from) - 1] = '\0';
550 __db_errx(env, DB_STR_A("0732",
551 "%s: path too long", "%s"), from);
552 goto err;
553 }
554 if ((ret = __os_strdup(env, from, &logd)) != 0)
555 goto err;
556 }
557 }
558
559 update = LF_ISSET(DB_BACKUP_UPDATE);
560 again: aflag = DB_ARCH_LOG;
561
562 /*
563 * If this is an update and we are deleting files, first process
564 * those files that can be removed, then repeat with the rest.
565 */
566 if (update)
567 aflag = 0;
568
569 /* Flush the log to get latest info. */
570 if ((ret = __log_flush(env, NULL)) != 0) {
571 __db_err(env, ret, DB_STR("0735", "Can't flush log"));
572 goto err;
573 }
574
575 /* Get a list of file names to be copied. */
576 if ((ret = __log_archive(env, &names, aflag)) != 0) {
577 __db_err(env, ret, DB_STR("0736", "Can't get log file names"));
578 goto err;
579 }
580 if (names == NULL)
581 goto done;
582 begin = names;
583 for (; *names != NULL; names++) {
584 /* Track the lowest-numbered log file copied. */
585 v = atoi(*names + sizeof(LFPREFIX) - 1);
586 if (*copy_minp == 0 || *copy_minp > v)
587 *copy_minp = v;
588
589 if ((ret = __os_concat_path(from,
590 sizeof(from), logd, *names)) != 0) {
591 from[sizeof(from) - 1] = '\0';
592 __db_errx(env, DB_STR_A("0737",
593 "%s: path too long", "%s"), from);
594 goto err;
595 }
596
597 /*
598 * If we're going to remove the file, attempt to rename it
599 * instead of copying and then removing. The likely failure
600 * is EXDEV (source and destination are on different volumes).
601 * Fall back to a copy, regardless of the error. We don't
602 * worry about partial contents, the copy truncates the file
603 * on open.
604 */
605 if (update) {
606 if ((ret = __os_concat_path(to,
607 sizeof(to), backupd, *names)) != 0) {
608 to[sizeof(to) - 1] = '\0';
609 __db_errx(env, DB_STR_A("0738",
610 "%s: path too long", "%s"), to);
611 goto err;
612 }
613 if (__os_rename(env, from, to, 1) == 0) {
614 if (FLD_ISSET(dbenv->verbose, DB_VERB_BACKUP))
615 __db_msg(env, DB_STR_A("0739",
616 "moving %s to %s",
617 "%s %s"), from, to);
618 continue;
619 }
620 }
621
622 /* Copy the file. */
623 if (backup_data_copy(dbenv, *names, logd, backupd, 1) != 0) {
624 ret = 1;
625 goto err;
626 }
627
628 if (update) {
629 if (FLD_ISSET(dbenv->verbose, DB_VERB_BACKUP))
630 __db_msg(env, DB_STR_A("0740",
631 "removing %s", "%s"), from);
632 if ((ret = __os_unlink(env, from, 0)) != 0) {
633 __db_err(env, ret, DB_STR_A("0741",
634 "unlink of %s failed", "%s"), from);
635 goto err;
636 }
637 }
638
639 }
640
641 __os_ufree(env, begin);
642 begin = NULL;
643 done: if (update) {
644 update = 0;
645 goto again;
646 }
647
648 if (FLD_ISSET(dbenv->verbose, DB_VERB_BACKUP) && *copy_minp != 0)
649 __db_msg(env, DB_STR_A("0742",
650 "lowest numbered log file copied: %d", "%d"),
651 *copy_minp);
652 err: if (logd != dbenv->db_log_dir && logd != env->db_home)
653 __os_free(env, logd);
654 if (backupd != NULL && backupd != backup_dir)
655 __os_free(env, (void *)backupd);
656 if (begin != NULL)
657 __os_ufree(env, begin);
658
659 return (ret);
660 }
661
662 /*
663 * __db_backup --
664 * Backup databases in the enviornment.
665 *
666 * PUBLIC: int __db_backup_pp __P((DB_ENV *, const char *, u_int32_t));
667 */
668 int
__db_backup_pp(dbenv,target,flags)669 __db_backup_pp(dbenv, target, flags)
670 DB_ENV *dbenv;
671 const char *target;
672 u_int32_t flags;
673 {
674 DB_THREAD_INFO *ip;
675 ENV *env;
676 int remove_max, ret;
677
678 env = dbenv->env;
679 remove_max = 0;
680
681 #undef OKFLAGS
682 #define OKFLAGS \
683 (DB_CREATE | DB_EXCL | DB_BACKUP_FILES | DB_BACKUP_SINGLE_DIR | \
684 DB_BACKUP_UPDATE | DB_BACKUP_NO_LOGS | DB_BACKUP_CLEAN)
685
686 if ((ret = __db_fchk(env, "DB_ENV->backup", flags, OKFLAGS)) != 0)
687 return (ret);
688
689 if (target == NULL) {
690 __db_errx(env,
691 DB_STR("0716", "Target directory may not be null."));
692 return (EINVAL);
693 }
694
695 /*
696 * If the target directory for the backup does not exist, create it
697 * with mode read-write-execute for the owner. Ignore errors here,
698 * it's simpler and more portable to just always try the create. If
699 * there's a problem, we'll fail with reasonable errors later.
700 */
701 if (LF_ISSET(DB_CREATE))
702 (void)__os_mkdir(NULL, target, DB_MODE_700);
703
704 if (LF_ISSET(DB_BACKUP_CLEAN)) {
705 if (!LF_ISSET(DB_BACKUP_SINGLE_DIR) &&
706 dbenv->db_log_dir != NULL &&
707 (ret = backup_dir_clean(dbenv, target,
708 dbenv->db_log_dir, &remove_max, flags)) != 0)
709 return (ret);
710 if ((ret = backup_dir_clean(dbenv,
711 target, NULL, &remove_max, flags)) != 0)
712 return (ret);
713
714 }
715
716 ENV_ENTER(env, ip);
717 REPLICATION_WRAP(env,
718 (__db_backup(dbenv, target, ip, remove_max, flags)), 0, ret);
719 ENV_LEAVE(env, ip);
720 return (ret);
721 }
722
723 /*
724 * __db_backup --
725 * Backup databases in the enviornment.
726 */
727 static int
__db_backup(dbenv,target,ip,remove_max,flags)728 __db_backup(dbenv, target, ip, remove_max, flags)
729 DB_ENV *dbenv;
730 const char *target;
731 DB_THREAD_INFO *ip;
732 int remove_max;
733 u_int32_t flags;
734 {
735 ENV *env;
736 int copy_min, ret;
737 char **dir;
738
739 env = dbenv->env;
740 copy_min = 0;
741
742 /*
743 * If the UPDATE option was not specified, copy all database
744 * files found in the database environment home directory and
745 * data directories..
746 */
747 if ((ret = __env_set_backup(env, 1)) != 0)
748 goto end;
749 F_SET(dbenv, DB_ENV_HOTBACKUP);
750 if (!LF_ISSET(DB_BACKUP_UPDATE)) {
751 if ((ret = backup_read_data_dir(dbenv,
752 ip, env->db_home, target, flags)) != 0)
753 goto err;
754 for (dir = dbenv->db_data_dir;
755 dir != NULL && *dir != NULL; ++dir) {
756 /*
757 * Don't allow absolute path names taken from the
758 * enviroment -- running recovery with them would
759 * corrupt the source files.
760 */
761 if (!LF_ISSET(DB_BACKUP_SINGLE_DIR)
762 && __os_abspath(*dir)) {
763 __db_errx(env, DB_STR_A("0725",
764 "data directory '%s' is absolute path, not permitted unless backup is to a single directory",
765 "%s"), *dir);
766 ret = EINVAL;
767 goto err;
768 }
769 if ((ret = backup_read_data_dir(
770 dbenv, ip, *dir, target, flags)) != 0)
771 goto err;
772 }
773 }
774
775 /*
776 * Copy all log files found in the log directory.
777 * The log directory defaults to the home directory.
778 */
779 if ((ret = backup_read_log_dir(dbenv, target, ©_min, flags)) != 0)
780 goto err;
781 /*
782 * If we're updating a snapshot, the lowest-numbered log file copied
783 * into the backup directory should be less than, or equal to, the
784 * highest-numbered log file removed from the backup directory during
785 * cleanup.
786 */
787 if (LF_ISSET(DB_BACKUP_UPDATE) && remove_max < copy_min &&
788 !(remove_max == 0 && copy_min == 1)) {
789 __db_errx(env, DB_STR_A("0743",
790 "the largest log file removed (%d) must be greater than or equal the smallest log file copied (%d)",
791 "%d %d"), remove_max, copy_min);
792 ret = EINVAL;
793 }
794
795 err: F_CLR(dbenv, DB_ENV_HOTBACKUP);
796 (void)__env_set_backup(env, 0);
797 end: return (ret);
798 }
799