/*-
 * Copyright (c) 2014-2018 MongoDB, Inc.
 * Copyright (c) 2008-2014 WiredTiger, Inc.
 *	All rights reserved.
 *
 * See the file LICENSE for redistribution information.
 */

#include "wt_internal.h"

/*
 * __wt_filename --
 *	Build a file name in a scratch buffer, automatically calculate the
 *	length of the file name.
 */
int
__wt_filename(WT_SESSION_IMPL *session, const char *name, char **path)
{
	return (__wt_nfilename(session, name, strlen(name), path));
}

/*
 * __wt_nfilename --
 *	Build a file name in a scratch buffer.  If the name is already an
 *	absolute path duplicate it, otherwise generate a path relative to the
 *	connection home directory.
 */
int
__wt_nfilename(
    WT_SESSION_IMPL *session, const char *name, size_t namelen, char **path)
{
	WT_DECL_RET;
	size_t len;
	char *buf;

	*path = NULL;

	/*
	 * Needs to work with a NULL session handle - since this is called via
	 * the exists API which is used by the test utilities.
	 */
	if (session == NULL || __wt_absolute_path(name))
		return (__wt_strndup(session, name, namelen, path));

	len = strlen(S2C(session)->home) + 1 + namelen + 1;
	WT_RET(__wt_calloc(session, 1, len, &buf));
	WT_ERR(__wt_snprintf(buf, len, "%s%s%.*s",
	    S2C(session)->home, __wt_path_separator(), (int)namelen, name));
	*path = buf;
	return (0);

err:	__wt_free(session, buf);
	return (ret);
}

/*
 * __wt_filename_construct --
 *	Given unique identifiers, return a WT_ITEM of a generated file name of
 *	the given prefix type. Any identifier that is 0 will be skipped.
 */
int
__wt_filename_construct(WT_SESSION_IMPL *session, const char *path,
    const char *file_prefix, uintmax_t id_1, uint32_t id_2, WT_ITEM *buf)
{
	if (path != NULL && path[0] != '\0')
		WT_RET(__wt_buf_catfmt(
		    session, buf, "%s%s", path, __wt_path_separator()));
	WT_RET(__wt_buf_catfmt(session, buf, "%s", file_prefix));
	if (id_1 != UINTMAX_MAX)
		WT_RET(__wt_buf_catfmt(session, buf, ".%010" PRIuMAX, id_1));
	if (id_2 != UINT32_MAX)
		WT_RET(__wt_buf_catfmt(session, buf, ".%010" PRIu32, id_2));

	return (0);
}

/*
 * __wt_remove_if_exists --
 *	Remove a file if it exists.
 */
int
__wt_remove_if_exists(WT_SESSION_IMPL *session, const char *name, bool durable)
{
	bool exist;

	WT_RET(__wt_fs_exist(session, name, &exist));
	if (exist)
		WT_RET(__wt_fs_remove(session, name, durable));
	return (0);
}

/*
 * __wt_copy_and_sync --
 *	Copy a file safely; here to support the wt utility.
 */
int
__wt_copy_and_sync(WT_SESSION *wt_session, const char *from, const char *to)
    WT_GCC_FUNC_ATTRIBUTE((visibility("default")))
{
	WT_DECL_ITEM(tmp);
	WT_DECL_RET;
	WT_FH *ffh, *tfh;
	WT_SESSION_IMPL *session;
	wt_off_t n, offset, size;
	char *buf;

	session = (WT_SESSION_IMPL *)wt_session;
	ffh = tfh = NULL;
	buf = NULL;

	/*
	 * Remove the target file if it exists, then create a temporary file,
	 * copy the original into it and rename it into place. I don't think
	 * its necessary to remove the file, or create a copy and do a rename,
	 * it's likely safe to overwrite the backup file directly. I'm doing
	 * the remove and rename to insulate us from errors in other programs
	 * that might not detect a corrupted backup file; it's cheap insurance
	 * in a path where undetected failure is very bad.
	 */
	WT_ERR(__wt_scr_alloc(session, 0, &tmp));
	WT_ERR(__wt_buf_fmt(session, tmp, "%s.copy", to));

	WT_ERR(__wt_remove_if_exists(session, to, false));
	WT_ERR(__wt_remove_if_exists(session, tmp->data, false));

	/* Open the from and temporary file handles. */
	WT_ERR(__wt_open(session, from, WT_FS_OPEN_FILE_TYPE_REGULAR, 0, &ffh));
	WT_ERR(__wt_open(session, tmp->data, WT_FS_OPEN_FILE_TYPE_REGULAR,
	    WT_FS_OPEN_CREATE | WT_FS_OPEN_EXCLUSIVE, &tfh));

	/*
	 * Allocate a copy buffer. Don't use a scratch buffer, this thing is
	 * big, and we don't want it hanging around.
	 */
#define	WT_BACKUP_COPY_SIZE	(128 * 1024)
	WT_ERR(__wt_malloc(session, WT_BACKUP_COPY_SIZE, &buf));

	/* Get the file's size, then copy the bytes. */
	WT_ERR(__wt_filesize(session, ffh, &size));
	for (offset = 0; size > 0; size -= n, offset += n) {
		n = WT_MIN(size, WT_BACKUP_COPY_SIZE);
		WT_ERR(__wt_read(session, ffh, offset, (size_t)n, buf));
		WT_ERR(__wt_write(session, tfh, offset, (size_t)n, buf));
	}

	/* Close the from handle, then swap the temporary file into place. */
	WT_ERR(__wt_close(session, &ffh));
	WT_ERR(__wt_fsync(session, tfh, true));
	WT_ERR(__wt_close(session, &tfh));

	ret = __wt_fs_rename(session, tmp->data, to, true);

err:	WT_TRET(__wt_close(session, &ffh));
	WT_TRET(__wt_close(session, &tfh));

	__wt_free(session, buf);
	__wt_scr_free(session, &tmp);
	return (ret);
}
