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

#include "wt_internal.h"

static void __bm_method_set(WT_BM *, bool);

/*
 * __bm_readonly --
 *	General-purpose "writes not supported on this handle" function.
 */
static int
__bm_readonly(WT_BM *bm, WT_SESSION_IMPL *session)
{
	WT_RET_MSG(session, ENOTSUP,
	    "%s: write operation on read-only checkpoint handle",
	    bm->block->name);
}

/*
 * __bm_addr_invalid --
 *	Return an error code if an address cookie is invalid.
 */
static int
__bm_addr_invalid(WT_BM *bm,
    WT_SESSION_IMPL *session, const uint8_t *addr, size_t addr_size)
{
	return (__wt_block_addr_invalid(
	    session, bm->block, addr, addr_size, bm->is_live));
}

/*
 * __bm_addr_string --
 *	Return a printable string representation of an address cookie.
 */
static int
__bm_addr_string(WT_BM *bm, WT_SESSION_IMPL *session,
    WT_ITEM *buf, const uint8_t *addr, size_t addr_size)
{
	return (
	    __wt_block_addr_string(session, bm->block, buf, addr, addr_size));
}

/*
 * __bm_block_header --
 *	Return the size of the block header.
 */
static u_int
__bm_block_header(WT_BM *bm)
{
	return (__wt_block_header(bm->block));
}

/*
 * __bm_checkpoint --
 *	Write a buffer into a block, creating a checkpoint.
 */
static int
__bm_checkpoint(WT_BM *bm, WT_SESSION_IMPL *session,
    WT_ITEM *buf, WT_CKPT *ckptbase, bool data_checksum)
{
	return (__wt_block_checkpoint(
	    session, bm->block, buf, ckptbase, data_checksum));
}

/*
 * __bm_checkpoint_readonly --
 *	Write a buffer into a block, creating a checkpoint; readonly version.
 */
static int
__bm_checkpoint_readonly(WT_BM *bm, WT_SESSION_IMPL *session,
    WT_ITEM *buf, WT_CKPT *ckptbase, bool data_checksum)
{
	WT_UNUSED(buf);
	WT_UNUSED(ckptbase);
	WT_UNUSED(data_checksum);

	return (__bm_readonly(bm, session));
}

/*
 * __bm_checkpoint_load --
 *	Load a checkpoint.
 */
static int
__bm_checkpoint_load(WT_BM *bm, WT_SESSION_IMPL *session,
    const uint8_t *addr, size_t addr_size,
    uint8_t *root_addr, size_t *root_addr_sizep, bool checkpoint)
{
	/* If not opening a checkpoint, we're opening the live system. */
	bm->is_live = !checkpoint;
	WT_RET(__wt_block_checkpoint_load(session, bm->block,
	    addr, addr_size, root_addr, root_addr_sizep, checkpoint));

	if (checkpoint) {
		/*
		 * Read-only objects are optionally mapped into memory instead
		 * of being read into cache buffers.
		 */
		WT_RET(__wt_block_map(session,
		    bm->block, &bm->map, &bm->maplen, &bm->mapped_cookie));

		/*
		 * If this handle is for a checkpoint, that is, read-only, there
		 * isn't a lot you can do with it.  Although the btree layer
		 * prevents attempts to write a checkpoint reference, paranoia
		 * is healthy.
		 */
		__bm_method_set(bm, true);
	}

	return (0);
}

/*
 * __bm_checkpoint_resolve --
 *	Resolve the checkpoint.
 */
static int
__bm_checkpoint_resolve(WT_BM *bm, WT_SESSION_IMPL *session)
{
	return (__wt_block_checkpoint_resolve(session, bm->block));
}

/*
 * __bm_checkpoint_resolve_readonly --
 *	Resolve the checkpoint; readonly version.
 */
static int
__bm_checkpoint_resolve_readonly(WT_BM *bm, WT_SESSION_IMPL *session)
{
	return (__bm_readonly(bm, session));
}

/*
 * __bm_checkpoint_unload --
 *	Unload a checkpoint point.
 */
static int
__bm_checkpoint_unload(WT_BM *bm, WT_SESSION_IMPL *session)
{
	WT_DECL_RET;

	/* Unmap any mapped segment. */
	if (bm->map != NULL)
		WT_TRET(__wt_block_unmap(session,
		    bm->block, bm->map, bm->maplen, &bm->mapped_cookie));

	/* Unload the checkpoint. */
	WT_TRET(__wt_block_checkpoint_unload(session, bm->block, !bm->is_live));

	return (ret);
}

/*
 * __bm_close --
 *	Close a file.
 */
static int
__bm_close(WT_BM *bm, WT_SESSION_IMPL *session)
{
	WT_DECL_RET;

	if (bm == NULL)				/* Safety check */
		return (0);

	ret = __wt_block_close(session, bm->block);

	__wt_overwrite_and_free(session, bm);
	return (ret);
}

/*
 * __bm_compact_end --
 *	End a block manager compaction.
 */
static int
__bm_compact_end(WT_BM *bm, WT_SESSION_IMPL *session)
{
	return (__wt_block_compact_end(session, bm->block));
}

/*
 * __bm_compact_end_readonly --
 *	End a block manager compaction; readonly version.
 */
static int
__bm_compact_end_readonly(WT_BM *bm, WT_SESSION_IMPL *session)
{
	return (__bm_readonly(bm, session));
}

/*
 * __bm_compact_page_skip --
 *	Return if a page is useful for compaction.
 */
static int
__bm_compact_page_skip(WT_BM *bm, WT_SESSION_IMPL *session,
    const uint8_t *addr, size_t addr_size, bool *skipp)
{
	return (__wt_block_compact_page_skip(
	    session, bm->block, addr, addr_size, skipp));
}

/*
 * __bm_compact_page_skip_readonly --
 *	Return if a page is useful for compaction; readonly version.
 */
static int
__bm_compact_page_skip_readonly(WT_BM *bm, WT_SESSION_IMPL *session,
    const uint8_t *addr, size_t addr_size, bool *skipp)
{
	WT_UNUSED(addr);
	WT_UNUSED(addr_size);
	WT_UNUSED(skipp);

	return (__bm_readonly(bm, session));
}

/*
 * __bm_compact_skip --
 *	Return if a file can be compacted.
 */
static int
__bm_compact_skip(WT_BM *bm, WT_SESSION_IMPL *session, bool *skipp)
{
	return (__wt_block_compact_skip(session, bm->block, skipp));
}

/*
 * __bm_compact_skip_readonly --
 *	Return if a file can be compacted; readonly version.
 */
static int
__bm_compact_skip_readonly(WT_BM *bm, WT_SESSION_IMPL *session, bool *skipp)
{
	WT_UNUSED(skipp);

	return (__bm_readonly(bm, session));
}

/*
 * __bm_compact_start --
 *	Start a block manager compaction.
 */
static int
__bm_compact_start(WT_BM *bm, WT_SESSION_IMPL *session)
{
	return (__wt_block_compact_start(session, bm->block));
}

/*
 * __bm_compact_start_readonly --
 *	Start a block manager compaction; readonly version.
 */
static int
__bm_compact_start_readonly(WT_BM *bm, WT_SESSION_IMPL *session)
{
	return (__bm_readonly(bm, session));
}

/*
 * __bm_free --
 *	Free a block of space to the underlying file.
 */
static int
__bm_free(WT_BM *bm,
    WT_SESSION_IMPL *session, const uint8_t *addr, size_t addr_size)
{
	return (__wt_block_free(session, bm->block, addr, addr_size));
}

/*
 * __bm_free_readonly --
 *	Free a block of space to the underlying file; readonly version.
 */
static int
__bm_free_readonly(WT_BM *bm,
    WT_SESSION_IMPL *session, const uint8_t *addr, size_t addr_size)
{
	WT_UNUSED(addr);
	WT_UNUSED(addr_size);

	return (__bm_readonly(bm, session));
}

/*
 * __bm_is_mapped --
 *	Return if the file is mapped into memory.
 */
static bool
__bm_is_mapped(WT_BM *bm, WT_SESSION_IMPL *session)
{
	WT_UNUSED(session);

	return (bm->map == NULL ? false : true);
}

/*
 * __bm_map_discard --
 *	Discard a mapped segment.
 */
static int
__bm_map_discard(WT_BM *bm, WT_SESSION_IMPL *session, void *map, size_t len)
{
	WT_FILE_HANDLE *handle;

	handle = bm->block->fh->handle;
	return (handle->fh_map_discard(
	    handle, (WT_SESSION *)session, map, len, bm->mapped_cookie));
}

/*
 * __bm_salvage_end --
 *	End a block manager salvage.
 */
static int
__bm_salvage_end(WT_BM *bm, WT_SESSION_IMPL *session)
{
	return (__wt_block_salvage_end(session, bm->block));
}

/*
 * __bm_salvage_end_readonly --
 *	End a block manager salvage; readonly version.
 */
static int
__bm_salvage_end_readonly(WT_BM *bm, WT_SESSION_IMPL *session)
{
	return (__bm_readonly(bm, session));
}

/*
 * __bm_salvage_next_readonly --
 *	Return the next block from the file; readonly version.
 */
static int
__bm_salvage_next_readonly(WT_BM *bm,
    WT_SESSION_IMPL *session, uint8_t *addr, size_t *addr_sizep, bool *eofp)
{
	WT_UNUSED(addr);
	WT_UNUSED(addr_sizep);
	WT_UNUSED(eofp);

	return (__bm_readonly(bm, session));
}

/*
 * __bm_salvage_next --
 *	Return the next block from the file.
 */
static int
__bm_salvage_next(WT_BM *bm,
    WT_SESSION_IMPL *session, uint8_t *addr, size_t *addr_sizep, bool *eofp)
{
	return (__wt_block_salvage_next(
	    session, bm->block, addr, addr_sizep, eofp));
}

/*
 * __bm_salvage_start --
 *	Start a block manager salvage.
 */
static int
__bm_salvage_start(WT_BM *bm, WT_SESSION_IMPL *session)
{
	return (__wt_block_salvage_start(session, bm->block));
}

/*
 * __bm_salvage_start_readonly --
 *	Start a block manager salvage; readonly version.
 */
static int
__bm_salvage_start_readonly(WT_BM *bm, WT_SESSION_IMPL *session)
{
	return (__bm_readonly(bm, session));
}

/*
 * __bm_salvage_valid --
 *	Inform salvage a block is valid.
 */
static int
__bm_salvage_valid(WT_BM *bm,
    WT_SESSION_IMPL *session, uint8_t *addr, size_t addr_size, bool valid)
{
	return (__wt_block_salvage_valid(
	    session, bm->block, addr, addr_size, valid));
}

/*
 * __bm_salvage_valid_readonly --
 *	Inform salvage a block is valid; readonly version.
 */
static int
__bm_salvage_valid_readonly(WT_BM *bm,
    WT_SESSION_IMPL *session, uint8_t *addr, size_t addr_size, bool valid)
{
	WT_UNUSED(addr);
	WT_UNUSED(addr_size);
	WT_UNUSED(valid);

	return (__bm_readonly(bm, session));
}

/*
 * __bm_stat --
 *	Block-manager statistics.
 */
static int
__bm_stat(WT_BM *bm, WT_SESSION_IMPL *session, WT_DSRC_STATS *stats)
{
	__wt_block_stat(session, bm->block, stats);
	return (0);
}

/*
 * __bm_sync --
 *	Flush a file to disk.
 */
static int
__bm_sync(WT_BM *bm, WT_SESSION_IMPL *session, bool block)
{
	return (__wt_fsync(session, bm->block->fh, block));
}

/*
 * __bm_sync_readonly --
 *	Flush a file to disk; readonly version.
 */
static int
__bm_sync_readonly(WT_BM *bm, WT_SESSION_IMPL *session, bool async)
{
	WT_UNUSED(async);

	return (__bm_readonly(bm, session));
}

/*
 * __bm_verify_addr --
 *	Verify an address.
 */
static int
__bm_verify_addr(WT_BM *bm,
    WT_SESSION_IMPL *session, const uint8_t *addr, size_t addr_size)
{
	return (__wt_block_verify_addr(session, bm->block, addr, addr_size));
}

/*
 * __bm_verify_end --
 *	End a block manager verify.
 */
static int
__bm_verify_end(WT_BM *bm, WT_SESSION_IMPL *session)
{
	return (__wt_block_verify_end(session, bm->block));
}

/*
 * __bm_verify_start --
 *	Start a block manager verify.
 */
static int
__bm_verify_start(WT_BM *bm,
    WT_SESSION_IMPL *session, WT_CKPT *ckptbase, const char *cfg[])
{
	return (__wt_block_verify_start(session, bm->block, ckptbase, cfg));
}

/*
 * __bm_write --
 *	Write a buffer into a block, returning the block's address cookie.
 */
static int
__bm_write(WT_BM *bm, WT_SESSION_IMPL *session, WT_ITEM *buf,
    uint8_t *addr, size_t *addr_sizep, bool data_checksum, bool checkpoint_io)
{
	return (__wt_block_write(session,
	    bm->block, buf, addr, addr_sizep, data_checksum, checkpoint_io));
}

/*
 * __bm_write_readonly --
 *	Write a buffer into a block, returning the block's address cookie;
 * readonly version.
 */
static int
__bm_write_readonly(WT_BM *bm, WT_SESSION_IMPL *session, WT_ITEM *buf,
    uint8_t *addr, size_t *addr_sizep, bool data_checksum, bool checkpoint_io)
{
	WT_UNUSED(buf);
	WT_UNUSED(addr);
	WT_UNUSED(addr_sizep);
	WT_UNUSED(data_checksum);
	WT_UNUSED(checkpoint_io);

	return (__bm_readonly(bm, session));
}

/*
 * __bm_write_size --
 *	Return the buffer size required to write a block.
 */
static int
__bm_write_size(WT_BM *bm, WT_SESSION_IMPL *session, size_t *sizep)
{
	return (__wt_block_write_size(session, bm->block, sizep));
}

/*
 * __bm_write_size_readonly --
 *	Return the buffer size required to write a block; readonly version.
 */
static int
__bm_write_size_readonly(WT_BM *bm, WT_SESSION_IMPL *session, size_t *sizep)
{
	WT_UNUSED(sizep);

	return (__bm_readonly(bm, session));
}

/*
 * __bm_method_set --
 *	Set up the legal methods.
 */
static void
__bm_method_set(WT_BM *bm, bool readonly)
{
	bm->addr_invalid = __bm_addr_invalid;
	bm->addr_string = __bm_addr_string;
	bm->block_header = __bm_block_header;
	bm->checkpoint = __bm_checkpoint;
	bm->checkpoint_load = __bm_checkpoint_load;
	bm->checkpoint_resolve = __bm_checkpoint_resolve;
	bm->checkpoint_unload = __bm_checkpoint_unload;
	bm->close = __bm_close;
	bm->compact_end = __bm_compact_end;
	bm->compact_page_skip = __bm_compact_page_skip;
	bm->compact_skip = __bm_compact_skip;
	bm->compact_start = __bm_compact_start;
	bm->free = __bm_free;
	bm->is_mapped = __bm_is_mapped;
	bm->map_discard = __bm_map_discard;
	bm->preload = __wt_bm_preload;
	bm->read = __wt_bm_read;
	bm->salvage_end = __bm_salvage_end;
	bm->salvage_next = __bm_salvage_next;
	bm->salvage_start = __bm_salvage_start;
	bm->salvage_valid = __bm_salvage_valid;
	bm->size = __wt_block_manager_size;
	bm->stat = __bm_stat;
	bm->sync = __bm_sync;
	bm->verify_addr = __bm_verify_addr;
	bm->verify_end = __bm_verify_end;
	bm->verify_start = __bm_verify_start;
	bm->write = __bm_write;
	bm->write_size = __bm_write_size;

	if (readonly) {
		bm->checkpoint = __bm_checkpoint_readonly;
		bm->checkpoint_resolve = __bm_checkpoint_resolve_readonly;
		bm->compact_end = __bm_compact_end_readonly;
		bm->compact_page_skip = __bm_compact_page_skip_readonly;
		bm->compact_skip = __bm_compact_skip_readonly;
		bm->compact_start = __bm_compact_start_readonly;
		bm->free = __bm_free_readonly;
		bm->salvage_end = __bm_salvage_end_readonly;
		bm->salvage_next = __bm_salvage_next_readonly;
		bm->salvage_start = __bm_salvage_start_readonly;
		bm->salvage_valid = __bm_salvage_valid_readonly;
		bm->sync = __bm_sync_readonly;
		bm->write = __bm_write_readonly;
		bm->write_size = __bm_write_size_readonly;
	}
}

/*
 * __wt_block_manager_open --
 *	Open a file.
 */
int
__wt_block_manager_open(WT_SESSION_IMPL *session,
    const char *filename, const char *cfg[],
    bool forced_salvage, bool readonly, uint32_t allocsize, WT_BM **bmp)
{
	WT_BM *bm;
	WT_DECL_RET;

	*bmp = NULL;

	WT_RET(__wt_calloc_one(session, &bm));
	__bm_method_set(bm, false);

	WT_ERR(__wt_block_open(session, filename, cfg,
	    forced_salvage, readonly, allocsize, &bm->block));

	*bmp = bm;
	return (0);

err:	WT_TRET(bm->close(bm, session));
	return (ret);
}

/*
 * __wt_block_panic --
 * 	Report an error, then panic the handle and the system.
 */
int
__wt_block_panic(WT_SESSION_IMPL *session, int error, const char *fmt, ...)
    WT_GCC_FUNC_ATTRIBUTE((cold))
    WT_GCC_FUNC_ATTRIBUTE((format (printf, 3, 4)))
{
	va_list ap;

	/*
	 * Ignore error returns from underlying event handlers, we already have
	 * an error value to return.
	 */
	va_start(ap, fmt);
	WT_IGNORE_RET(__wt_eventv(session, false, error, NULL, 0, fmt, ap));
	va_end(ap);

	/* Switch the handle into read-only mode. */
	__bm_method_set(S2BT(session)->bm, true);

	return (__wt_panic(session));
}
