text: /*-
   * Copyright (c) 2014-2015 MongoDB, Inc.
   * Copyright (c) 2008-2014 WiredTiger, Inc.
   *	All rights reserved.
   *
   * See the file LICENSE for redistribution information.
   */
  
  #include "util.h"
  
  static int copy(const char *, const char *);
  static int usage(void);
  
  #define	CBUF_LEN	(128 * 1024)		/* Copy buffer and size. */
  static char *cbuf;
  
  /*
   * append_target --
   *	Build a list of comma-separated targets.
   */
  static int
  append_target(const char *target, char **bufp)
  {
  	static int first = 1;
  	static size_t len = 0, remain = 0;
  	static char *buf = NULL;
  
  						/* 20 bytes of slop */
  	if (remain < strlen(target) + 20) {
  		len += strlen(target) + 512;
  		remain += strlen(target) + 512;
  		if ((buf = realloc(buf, len)) == NULL)
  			return (util_err(errno, NULL));
  		*bufp = buf;
  	}
  	if (first) {
  		first = 0;
  		strcpy(buf, "target=(");
  	} else
  		buf[strlen(buf) - 1] = ',';	/* overwrite previous ")" */
  	strcat(buf, "\"");
  	strcat(buf, target);
  	strcat(buf, "\")");
  	remain -= strlen(target) + 1;
  
  	return (0);
  }
  
  int
  util_backup(WT_SESSION *session, int argc, char *argv[])
  {
  	WT_CURSOR *cursor;
  	WT_DECL_RET;
  	int ch;
  	char *config;
  	const char *directory, *name;
  
  	config = NULL;
  	while ((ch = __wt_getopt(progname, argc, argv, "t:")) != EOF)
  		switch (ch) {
  		case 't':
  			if (append_target(__wt_optarg, &config))
  				return (1);
  			break;
  		case '?':
  		default:
  			return (usage());
  		}
  	argc -= __wt_optind;
  	argv += __wt_optind;
  
  	if (argc != 1) {
  		(void)usage();
  		goto err;
  	}
  	directory = *argv;
  
  	if ((ret = session->open_cursor(
  	    session, "backup:", NULL, config, &cursor)) != 0) {
  		fprintf(stderr, "%s: cursor open(backup:) failed: %s\n",
  		    progname, wiredtiger_strerror(ret));
  		goto err;
  	}
  
  	/* Copy the files. */
  	while (
  	    (ret = cursor->next(cursor)) == 0 &&
  	    (ret = cursor->get_key(cursor, &name)) == 0)
  		if ((ret = copy(name, directory)) != 0)
  			goto err;
  	if (ret == WT_NOTFOUND)
  		ret = 0;
  
  	if (ret != 0) {
  		fprintf(stderr, "%s: cursor next(backup:) failed: %s\n",
  		    progname, wiredtiger_strerror(ret));
  		goto err;
  	}
  
  err:	if (config != NULL)
  		free(config);
  	if (cbuf != NULL)
  		free(cbuf);
  
  	return (ret);
  }
  
  static int
  copy(const char *name, const char *directory)
  {
  	WT_DECL_RET;
  	ssize_t n;
  	int ifd, ofd;
  
  	ret = 1;
  	ifd = ofd = -1;
  
  	if (verbose &&
  	    printf("Backing up %s/%s to %s\n", home, name, directory) < 0) {
  		fprintf(stderr, "%s: %s\n", progname, strerror(errno));
  		return (1);
  	}
  
  	/* Allocate a large copy buffer (use it to build pathnames as well. */
  	if (cbuf == NULL && (cbuf = malloc(CBUF_LEN)) == NULL)
  		goto memerr;
  
  	/* Open the read file. */
  	if (snprintf(cbuf, CBUF_LEN, "%s/%s", home, name) >= CBUF_LEN)
  		goto memerr;
  	if ((ifd = open(cbuf, O_BINARY | O_RDONLY, 0)) < 0)
  		goto readerr;
  
  	/* Open the write file. */
  	if (snprintf(cbuf, CBUF_LEN, "%s/%s", directory, name) >= CBUF_LEN)
  		goto memerr;
  	if ((ofd = open(
  	    cbuf, O_BINARY | O_CREAT | O_WRONLY | O_TRUNC, 0666)) < 0)
  		goto writerr;
  
  	/* Copy the file. */
  	while ((n = read(ifd, cbuf, CBUF_LEN)) > 0)
  		if (write(ofd, cbuf, (size_t)n) != n)
  			goto writerr;
  	if (n != 0)
  		goto readerr;
  
  	/*
  	 * Close file descriptors (forcing a flush on the write side), and
  	 * check for any errors.
  	 */
  	ret = close(ifd);
  	ifd = -1;
  	if (ret != 0)
  		goto readerr;
  
  	/*
  	 * We need to know this file was successfully written, it's a backup.
  	 */
  #ifdef _WIN32
  	if (FlushFileBuffers((HANDLE)_get_osfhandle(ofd)) == 0) {
  		DWORD err = GetLastError();
  		ret = err;
  		goto writerr;
  	}
  #else
  	if (fsync(ofd))
  		goto writerr;
  #endif
  	ret = close(ofd);
  	ofd = -1;
  	if (ret != 0)
  		goto writerr;
  
  	/* Success. */
  	ret = 0;
  
  	if (0) {
  readerr:	fprintf(stderr,
  		    "%s: %s/%s: %s\n", progname, home, name, strerror(errno));
  	}
  	if (0) {
  writerr:	fprintf(stderr, "%s: %s/%s: %s\n",
  		    progname, directory, name, strerror(errno));
  	}
  	if (0) {
  memerr:		fprintf(stderr, "%s: %s\n", progname, strerror(errno));
  	}
  
  	if (ifd >= 0)
  		(void)close(ifd);
  	if (ofd >= 0)
  		(void)close(ofd);
  
  	return (ret);
  }
  
  static int
  usage(void)
  {
  	(void)fprintf(stderr,
  	    "usage: %s %s "
  	    "backup [-t uri] directory\n",
  	    progname, usage_prefix);
  	return (1);
  }
  