/*
**  %Z% %I% %W% %G% %U%
**
**  ZZ_Copyright_BEGIN
**
**
**  Licensed Materials - Property of IBM
**
**  IBM Linear Tape File System Single Drive Edition Version 2.2.0.2 for Linux and Mac OS X
**
**  Copyright IBM Corp. 2010, 2014
**
**  This file is part of the IBM Linear Tape File System Single Drive Edition for Linux and Mac OS X
**  (formally known as IBM Linear Tape File System)
**
**  The IBM Linear Tape File System Single Drive Edition for Linux and Mac OS X is free software;
**  you can redistribute it and/or modify it under the terms of the GNU Lesser
**  General Public License as published by the Free Software Foundation,
**  version 2.1 of the License.
**
**  The IBM Linear Tape File System Single Drive Edition for Linux and Mac OS X is distributed in the
**  hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
**  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**  See the GNU Lesser General Public License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**  or download the license from <http://www.gnu.org/licenses/>.
**
**
**  ZZ_Copyright_END
**
*************************************************************************************
**
** COMPONENT NAME:  IBM Linear Tape File System
**
** FILE NAME:       utils/mkltfs.c
**
** DESCRIPTION:     Formatter utility for LTFS volumes.
**
** AUTHORS:         Brian Biskeborn
**                  IBM Almaden Research Center
**                  bbiskebo@us.ibm.com
**
**                  Lucas C. Villa Real
**                  IBM Almaden Research Center
**                  lucasvr@us.ibm.com
**
*************************************************************************************
**
**  (C) Copyright 2015, 2016 Hewlett Packard Enterprise Development LP
**  07/06/10 Added new flag '-f, --force' and changed default behaviour to NOT allow
**            format if MAM attribute indicates tape contains an LTFS volume
**  10/29/12 Attempt to load and check write protect status earlier in the process
**
*************************************************************************************
**
** Copyright (C) 2012 OSR Open Systems Resources, Inc.
** 
************************************************************************************* 
*/

#ifdef mingw_PLATFORM
#include "libltfs/arch/win/win_util.h"
#else
#include <syslog.h>
#endif /* mingw_PLATFORM */

#include <getopt.h>
#include "libltfs/ltfs_fuse_version.h"
#include <fuse.h>

#include "libltfs/ltfs.h"
#include "ltfs_copyright.h"
#include "libltfs/index_criteria.h"
#include "libltfs/pathname.h"
#include "libltfs/plugin.h"
#include "libltfs/kmi.h"
#include "libltfs/tape.h"

volatile char *copyright = LTFS_COPYRIGHT_0"\n"LTFS_COPYRIGHT_1"\n"LTFS_COPYRIGHT_2"\n" \
	LTFS_COPYRIGHT_3"\n"LTFS_COPYRIGHT_4"\n"LTFS_COPYRIGHT_5"\n";

#ifdef __APPLE__
#include "libltfs/arch/osx/osx_string.h"
#endif

#define INDEX_PART_ID 'a'
#define DATA_PART_ID 'b'
#define INDEX_PART_NUM 0
#define DATA_PART_NUM 1

/* 
 * OSR
 * 
 * In our MinGW environment, we dynamically link to the package 
 * data. 
 *  
 */
#if defined(mingw_PLATFORM) && !defined(HPE_mingw_BUILD)
char *bin_mkltfs_dat;
#else
extern char bin_mkltfs_dat[];
#endif

struct other_format_opts {
	struct config_file *config; /**< Configuration data read from the global LTFS config file */
	char *devname;              /**< Device to format */
	char *backend_path;         /**< Path to tape backend shared library */
	char *kmi_backend_name;     /**< Name or path to the key manager interface backend library */
	char *volume_name;          /**< Human-readable volume name */
	char *filterrules;          /**< Rules for files that should go to the index partition */
	char *barcode;              /**< 6-character cartridge barcode number */
	unsigned long blocksize;    /**< Nominal tape block size */
	bool enable_compression;    /**< Use compression on the tape? */
	bool allow_update;          /**< Allow overriding index rules at mount time? */
	bool keep_capacity;         /**< Reset tape capacity? */
	bool unformat;              /**< Unformat medium? */
	bool force;                 /**< Force to format */
	bool quiet;                 /**< Quiet mode indicator */
	bool trace;                 /**< Debug mode indicator */
	bool syslogtrace;           /**< Generate debug output to stderr and syslog*/
	bool fulltrace;             /**< Full trace mode indicator */
	bool long_wipe;				/**< Clean up whole tape by long erase? */
	bool interactive;			/**< Interactive mode*/
};

/* Forward declarations */
int format_tape(struct ltfs_volume *vol, struct other_format_opts *opt, void *args);
int unformat_tape(struct ltfs_volume *vol, struct other_format_opts *opt, void *args);
int _mkltfs_validate_options(char *prg_name, struct ltfs_volume *vol,
	struct other_format_opts *opt);

/* Command line options */
static const char *short_options = "i:e:d:b:s:n:r:cokwfqtxhpgV";
static struct option long_options[] = {
	{"config",          1, 0, 'i'},
	{"backend",         1, 0, 'e'},
	{"device",          1, 0, 'd'},
	{"blocksize",       1, 0, 'b'},
	{"tape-serial",     1, 0, 's'},
	{"volume-name",     1, 0, 'n'},
	{"rules",           1, 0, 'r'},
	{"kmi-backend",     1, 0, '-'},
	{"no-compression",  0, 0, 'c'},
	{"no-override",     0, 0, ' '},
	{"keep-capacity",   0, 0, 'k'},
	{"wipe",            0, 0, 'w'},
	{"long-wipe",       0, 0, '+'},
	{"force",           0, 0, 'f'},
	{"quiet",           0, 0, 'q'},
	{"trace",           0, 0, 't'},
	{"syslogtrace",     0, 0, '!'},
	{"fulltrace",       0, 0, 'x'},
	{"help",            0, 0, 'h'},
	{"advanced-help",   0, 0, 'p'},
	{"interactive",     0, 0, 'g'},
	{"version",			0, 0, 'V'},
	{0, 0, 0, 0}
};

void show_usage(char *appname, struct config_file *config, bool full)
{
	struct libltfs_plugin backend;
	const char *default_backend;
	char *devname = NULL;

	default_backend = config_file_get_default_plugin("driver", config);
	if (default_backend && plugin_load(&backend, "driver", default_backend, config) == 0) {
		devname = strdup(ltfs_default_device_name(backend.ops));
		plugin_unload(&backend);
	}

	if (! devname)
		devname = strdup("<devname>");

	fprintf(stderr, "\n");
	ltfsresult("15400I", appname);  /* Usage: %s <options> */
	fprintf(stderr, "\n");
	ltfsresult("15401I");           /* Available options are: */
	ltfsresult("15402I");           /* -d, --device=<name> */
	ltfsresult("15420I");           /* -f, --force */
	ltfsresult("15403I");           /* -s, --tape-serial=<id> */
	ltfsresult("15404I");           /* -n, --volume-name */
	ltfsresult("15405I");           /* -r, --rules=<rule[,rule]> */
	ltfsresult("15406I");           /*     --no-override */
	ltfsresult("15418I");           /* -w, --wipe */
	ltfsresult("15407I");           /* -q, --quiet */
	ltfsresult("15408I");           /* -t, --trace */
	ltfsresult("15422I");           /* --syslogtrace */
	ltfsresult("15423I");			/* -V, --version */
	ltfsresult("15409I");           /* -h, --help */
	ltfsresult("15412I");           /* -p, --advanced-help */
	ltfsresult("15496I");			/* -g, --interactive */
	if (full) {
		ltfsresult("15413I", LTFS_CONFIG_FILE);       /* -i, --config=<file> */
		ltfsresult("15414I", default_backend);        /* -e, --backend */
		/* We have disabled all messages related to the 'kmi' backend. */
		/*ltfsresult("15421I", config_file_get_default_plugin("kmi", config));*/ /*     --kmi-backend */
		ltfsresult("15415I", LTFS_DEFAULT_BLOCKSIZE); /* -b, --blocksize */
		ltfsresult("15416I");                         /* -c, --no-compression */
		ltfsresult("15419I");                         /* -k, --keep-capacity */
		ltfsresult("15417I");                         /* -x, --fulltrace */
		ltfsresult("15424I");                         /* --long-wipe */
		fprintf(stderr, "\n");
		plugin_usage(appname, "driver", config);
		/*plugin_usage("kmi", config);*/
	}
	fprintf(stderr, "\n");
	ltfsresult("15410I"); /* Usage example: */
	ltfsresult("15411I", appname, devname, "size=100K"); /* %s --device=%s --rules="%s" */
	ltfsresult("15411I", appname, devname, "size=1M/name=*.jpg");
	ltfsresult("15411I", appname, devname, "size=1M/name=*.jpg:*.png");
	fprintf(stderr, "\n");

	free(devname);
}


/* Operation */
int main(int argc, char **argv)
{
	struct ltfs_volume *newvol;
	struct other_format_opts opt;
	int ret, log_level, syslog_level, i, cmd_args_len;
	char *lang, *cmd_args;
	const char *config_file = NULL;
	void *message_handle;
	char cmd_input = 0;

	int fuse_argc = argc;
	char **fuse_argv = calloc(fuse_argc, sizeof(char *));
	if (! fuse_argv) {
		return MKLTFS_OPERATIONAL_ERROR;
	}
	for (i = 0; i < fuse_argc; ++i) {
		fuse_argv[i] = strdup(argv[i]);
		if (! fuse_argv[i]) {
			return MKLTFS_OPERATIONAL_ERROR;
		}
	}
	struct fuse_args args = FUSE_ARGS_INIT(fuse_argc, fuse_argv);

#ifdef HPE_mingw_BUILD
	(void) lang;
#endif /* HPE_mingw_BUILD */

#ifndef HPE_mingw_BUILD
	/* Check for LANG variable and set it to en_US.UTF-8 if it is unset. */
	lang = getenv("LANG");
	if (! lang) {
		fprintf(stderr, "LTFS9015W Setting the locale to 'en_US.UTF-8'. If this is wrong, please set the LANG environment variable before starting mkltfs.\n");
		ret = setenv("LANG", "en_US.UTF-8", 1);
		if (ret) {
			fprintf(stderr, "LTFS9016E Cannot set the LANG environment variable\n");
			return MKLTFS_OPERATIONAL_ERROR;
		}
	}
#endif /* HPE_mingw_BUILD */

	/* Start up libltfs with the default logging level. */
#ifndef mingw_PLATFORM
	openlog("mkltfs", LOG_PID, LOG_USER);
#endif
	ret = ltfs_init(LTFS_INFO, true, false);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "10000E", ret);
		return MKLTFS_OPERATIONAL_ERROR;
	}

	/*  Setup signal handler to terminate cleanly */
	ret = ltfs_set_signal_handlers();
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "10013E");
		return MKLTFS_OPERATIONAL_ERROR;
	}

	/* Register messages with libltfs */
	ret = ltfsprintf_load_plugin("bin_mkltfs", bin_mkltfs_dat, &message_handle);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "10012E", ret);
		return MKLTFS_OPERATIONAL_ERROR;
	}

	/* Set up empty format options and load the configuration file. */
	memset(&opt, 0, sizeof(struct other_format_opts));
	opt.enable_compression = true;
	opt.allow_update = true;
	opt.unformat = false;
	opt.force = false;
	opt.quiet = false;
	opt.blocksize = LTFS_DEFAULT_BLOCKSIZE;
	opt.long_wipe = false;
	opt.interactive = false;

	/* Check for a config file path given on the command line */
	while (true) {
		int option_index = 0;
		int c = getopt_long(argc, argv, short_options, long_options, &option_index);
		if (c == -1)
			break;
		if (c == 'i') {
			config_file = strdup(optarg);
			break;
		}
	}

	/* Load configuration file */
	ret = config_file_load(config_file, &opt.config);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "10008E", ret);
		return MKLTFS_OPERATIONAL_ERROR;
	}

	/* Parse all command line arguments */
	optind = 1;
	int num_of_o = 0;
	while (true) {
		int option_index = 0;
		int c = getopt_long(argc, argv, short_options, long_options, &option_index);
		if (c == -1)
			break;

		switch (c) {
			case 'i':
				break;
			case 'e':
				free(opt.backend_path);
				opt.backend_path = strdup(optarg);
				break;
			case 'd':
				opt.devname = strdup(optarg);
				break;
			case 'b':
				opt.blocksize = atoi(optarg);
				break;
			case 's':
				opt.barcode = strdup(optarg);
				break;
			case 'n':
				opt.volume_name = strdup(optarg);
				break;
			case 'r':
				opt.filterrules = strdup(optarg);
				break;
			case '-':
				opt.kmi_backend_name = strdup(optarg);
				break;
			case 'c':
				opt.enable_compression = false;
				break;
			case 'o':
				/* ignore -o here to parse them by fuse */
				++num_of_o;
				break;
			case ' ':
				opt.allow_update = false;
				break;
			case 'k':
				opt.keep_capacity = true;
				break;
			case 'w':
				opt.unformat = true;
				break;
			case 'f':
				opt.force = true;
				break;
			case '+':
				opt.unformat = true;
				opt.long_wipe = true;
				break;
			case 'q':
				opt.quiet = true;
				break;
			case 't':
				opt.trace = true;
				break;
			case '!':
				opt.syslogtrace = true;
				break;
			case 'x':
				opt.fulltrace = true;
				break;
			case 'g':
				opt.interactive = true;
				break;
			case 'h':
				show_usage(argv[0], opt.config, false);
				return 0;
			case 'p':
				show_usage(argv[0], opt.config, true);
				return 0;
			case 'V':
				ltfsresult("15059I", "mkltfs", PACKAGE_VERSION);
				ltfsresult("15059I", "LTFS Format Specification", LTFS_INDEX_VERSION_STR);
				return 0;
			case '?':
			default:
				show_usage(argv[0], opt.config, false);
				return MKLTFS_USAGE_SYNTAX_ERROR;
		}
	}

	if (optind + num_of_o < argc) {
		show_usage(argv[0], opt.config, false);
		return MKLTFS_USAGE_SYNTAX_ERROR;
	}

	/* Pick up default backend if one wasn't specified before */
	if (! opt.backend_path) {
		const char *default_backend = config_file_get_default_plugin("driver", opt.config);
		if (! default_backend) {
			ltfsmsg(LTFS_ERR, "10009E");
			return MKLTFS_OPERATIONAL_ERROR;
		}
		opt.backend_path = strdup(default_backend);
	}
	if (! opt.kmi_backend_name) {
		const char *default_backend = config_file_get_default_plugin("kmi", opt.config);
		if (default_backend)
			opt.kmi_backend_name = strdup(default_backend);
		else
			opt.kmi_backend_name = strdup("none");
	}
	if (opt.kmi_backend_name && strcmp(opt.kmi_backend_name, "none") == 0) {
		free(opt.kmi_backend_name);
		opt.kmi_backend_name = NULL;
	}

	/* Set the logging level */
	if (opt.quiet && (opt.trace || opt.fulltrace)) {
		ltfsmsg(LTFS_ERR, "9012E");
		show_usage(argv[0], opt.config, false);
		return 1;
	} else if (opt.quiet) {
		log_level = LTFS_WARN;
		syslog_level = LTFS_NONE;
	} else if (opt.trace) {
		log_level = LTFS_DEBUG;
		syslog_level = LTFS_NONE;
	} else if (opt.syslogtrace)
		log_level = syslog_level = LTFS_DEBUG;
	else if (opt.fulltrace) {
		log_level = LTFS_TRACE;
		syslog_level = LTFS_DEBUG;
	} else {
		log_level = LTFS_INFO;
		syslog_level = LTFS_NONE;
	}

	ltfs_set_log_level(log_level);
	ltfs_set_syslog_level(syslog_level);

	/* Starting mkltfs */
	ltfsmsg(LTFS_INFO, "15000I", LTFS_VENDOR_NAME SOFTWARE_PRODUCT_NAME, PACKAGE_VERSION, log_level);

	/* Show command line arguments */
	for (i = 0, cmd_args_len = 0 ; i < argc; i++) {
		cmd_args_len += strlen(argv[i]) + 1;
	}
	cmd_args = calloc(1, cmd_args_len + 1);
	if (!cmd_args) {
		/* Memory allocation failed */
		ltfsmsg(LTFS_ERR, "10001E", "mkltfs (arguments)");
		return MKLTFS_OPERATIONAL_ERROR;
	}
	strcat(cmd_args, argv[0]);
	for (i = 1; i < argc; i++) {
		strcat(cmd_args, " ");
		strcat(cmd_args, argv[i]);
	}
	ltfsmsg(LTFS_INFO, "15041I", cmd_args);
	free(cmd_args);

	/* Show build time information */
	ltfsmsg(LTFS_INFO, "15042I", BUILD_SYS_FOR);
	ltfsmsg(LTFS_INFO, "15043I", BUILD_SYS_GCC);

	/* Show run time information */
	show_runtime_system_info();

	ret = ltfs_fs_init();
	if (ret)
		return MKLTFS_OPERATIONAL_ERROR;

	/* Actually mkltfs logic starts here */
	ret = ltfs_volume_alloc("mkltfs", &newvol);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "15001E");
		return MKLTFS_OPERATIONAL_ERROR;
	}

	ret = ltfs_set_blocksize(opt.blocksize, newvol);
	if (ret < 0) {
		if (ret == -LTFS_SMALL_BLOCKSIZE)
			ltfsmsg(LTFS_ERR, "15028E", LTFS_MIN_BLOCKSIZE);
		show_usage(argv[0], opt.config, false);
		return MKLTFS_OPERATIONAL_ERROR;
	}
	ltfs_set_compression(opt.enable_compression, newvol);
	ret = ltfs_set_barcode(opt.barcode, newvol);
	if (ret < 0) {
		if (ret == -LTFS_BARCODE_LENGTH)
			ltfsmsg(LTFS_ERR, "15029E");
		else if (ret == -LTFS_BARCODE_INVALID)
			ltfsmsg(LTFS_ERR, "15030E");
		show_usage(argv[0], opt.config, false);
		return MKLTFS_USAGE_SYNTAX_ERROR;
	}

	if (_mkltfs_validate_options(argv[0], newvol, &opt)) {
		ltfsmsg(LTFS_ERR, "15002E");
		show_usage(argv[0], opt.config, false);
		return MKLTFS_USAGE_SYNTAX_ERROR;
	}

	ltfsmsg(LTFS_INFO, "15003I", opt.devname);
	ltfsmsg(LTFS_INFO, "15004I", opt.blocksize);
	ltfsmsg(LTFS_INFO, "15005I", opt.filterrules ? opt.filterrules : "None");
	if (! opt.quiet)
		fprintf(stderr, "\n");

	/* Statutory Warning before format/unformat */
	if (! opt.force && !opt.quiet && opt.interactive) {

		ltfsmsg(LTFS_INFO, "15492I");

		fflush(stdin);
		scanf("%c", &cmd_input);
		while (getchar() != '\n');

		switch (cmd_input) {
			case 'Y':
			case 'y':
				opt.force = true;
				break;
			default:
				ltfsmsg(LTFS_INFO, "15493I");
				ret = 1;
				goto out_close;
		}
	}

	if(opt.unformat)
		ret = unformat_tape(newvol, &opt, &args);
	else
		ret = format_tape(newvol, &opt, &args);

out_close:
	if (opt.backend_path)
		free(opt.backend_path);
	if (opt.kmi_backend_name)
		free(opt.kmi_backend_name);
	if (opt.devname)
		free(opt.devname);

	while (fuse_argc) {
		free(fuse_argv[--fuse_argc]);
	}
	free(fuse_argv);

	config_file_free(opt.config);
	ltfsprintf_unload_plugin(message_handle);
	ltfs_finish();

	return ret;
}

int format_tape(struct ltfs_volume *vol, struct other_format_opts *opt, void *args)
{
	int ret = MKLTFS_OPERATIONAL_ERROR;
	struct device_capacity cap;
	struct libltfs_plugin backend; /* tape driver backend */
	struct libltfs_plugin kmi; /* key manager interface backend */
	struct ltfs_volume *dummy_vol;
	bool is_worm;

	ret = ltfs_set_volume_name(opt->volume_name, vol);
	if (ret < 0)
		return MKLTFS_OPERATIONAL_ERROR;
	/* Volume lock state will be set to unlocked */
	ret = ltfs_set_volume_lockstate(vol, UNLOCKED_MAM, false);
	if (ret < 0)
		return MKLTFS_OPERATIONAL_ERROR;
	ret = ltfs_reset_capacity(!opt->keep_capacity, vol);
	if (ret < 0) {
		return MKLTFS_OPERATIONAL_ERROR;
	}

	/* load the backend, open the tape device, and load a tape */
	ltfsmsg(LTFS_DEBUG, "15006D");
	ret = plugin_load(&backend, "driver", opt->backend_path, opt->config);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "15008E", opt->backend_path);
		return MKLTFS_OPERATIONAL_ERROR;
	}
	if (opt->kmi_backend_name) {
		ret = plugin_load(&kmi, "kmi", opt->kmi_backend_name, opt->config);
		if (ret < 0) {
			ltfsmsg(LTFS_ERR, "15050E", opt->kmi_backend_name);
			return MKLTFS_OPERATIONAL_ERROR;
		}
	}
	ret = ltfs_device_open(opt->devname, backend.ops, vol);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "15009E", opt->devname, ret);
		ret = MKLTFS_OPERATIONAL_ERROR;
		goto out_unload_backend;
	}
	ret = ltfs_parse_tape_backend_opts(args, vol);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "15054E");
		goto out_unload_backend;
	}
	if (opt->kmi_backend_name) {
		ret = kmi_init(&kmi, vol);
		if (ret < 0) {
			ltfsmsg(LTFS_ERR, "15052E", opt->devname, ret);
			goto out_unload_backend;
		}

		ret = ltfs_parse_kmi_backend_opts(args, vol);
		if (ret < 0) {
			ltfsmsg(LTFS_ERR, "15053E");
			goto out_unload_backend;
		}

		ret = tape_clear_key(vol->device, vol->kmi_handle);
		if (ret < 0)
			goto out_unload_backend;
	}
	{
		int i = 0;
		struct fuse_args *a = args;

		for (i = 0; i < a->argc && a->argv[i]; ++i) {
			if (!strcmp(a->argv[i], "-o")) {
				ltfsmsg(LTFS_ERR, "15055E", a->argv[i], a->argv[i + 1] ? a->argv[i + 1] : "");
				ret = MKLTFS_USAGE_SYNTAX_ERROR;
				goto out_unload_backend;
			}
		}
	}
	vol->append_only_mode = false;
	vol->set_pew = false;
	ret = ltfs_setup_device(vol);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "15044E");
		ret = MKLTFS_OPERATIONAL_ERROR;
		goto out_close;
	}
	ltfsmsg(LTFS_DEBUG, "15007D");
	
	/*
	 * HPE
	 * Load and check write-protect status before proceeding
	 */

	/* Make sure there's a tape loaded: */
	ret = tape_load_tape(vol->device, vol->kmi_handle);
	if (ret < 0) {
		if (ret == -LTFS_UNSUPPORTED_MEDIUM)
			ltfsmsg(LTFS_ERR, "11299E");
		else
			ltfsmsg(LTFS_ERR, "11093E", ret);
		goto out_close;
	}

	/* Make sure it's not logically write protected i.e. incompatible medium*/
	ret = tape_logically_read_only(vol->device);
	if (ret == -LTFS_LOGICAL_WRITE_PROTECT) {
		ltfsmsg(LTFS_ERR, "11330E");
		goto out_close;
	}

	/* TODO: Make sure it's not Write-Protected: */
	ret = tape_read_only(vol->device, 0);
	if (ret == -LTFS_WRITE_PROTECT) {
		ltfsmsg(LTFS_ERR, "11095E");
		goto out_close;
	}

	ltfs_set_partition_map(DATA_PART_ID, INDEX_PART_ID, DATA_PART_NUM, INDEX_PART_NUM, vol);

	/* Get mam attributes to be used to get info about archive manager tape */
	ret = tape_get_MAM_AMVALattributes(vol->device,
								 ltfs_part_id2num(vol->label->partid_ip, vol),
								 opt->quiet,
								 &vol->mam_attr);

	/* Let us check if Archive Manager tape and return failure */
	ret = ltfs_get_archivemanager_media(vol);
	if (ret) {
		ltfsmsg(LTFS_ERR, "15488E");
		ret = MKLTFS_OPERATIONAL_ERROR;
		goto out_close;
	}

	/* Check target medium state */
	if (! opt->force) {
		ltfsmsg(LTFS_INFO, "15049I", "mount");
		ret = ltfs_volume_alloc("mkltfs", &dummy_vol);
		if (ret < 0) {
			ret = MKLTFS_OPERATIONAL_ERROR;
			goto out_close;
		}
		dummy_vol->device = vol->device;
		dummy_vol->kmi_handle = vol->kmi_handle;
		ret = ltfs_start_mount(true, dummy_vol);
		dummy_vol->device = NULL;
		dummy_vol->kmi_handle = NULL;
		if (ret != -LTFS_NOT_PARTITIONED && ret != -LTFS_LABEL_INVALID && ret != -LTFS_LABEL_MISMATCH) {
			if (ret == 0) {
				ltfsmsg(LTFS_ERR, "15047E", ret);
				ltfsmsg(LTFS_INFO, "15048I");
			}
			else if (ret == -EDEV_KEY_REQUIRED) {
				ltfsmsg(LTFS_ERR, "15056E");
				ltfsmsg(LTFS_INFO, "15057I");
			}
			ret = MKLTFS_USAGE_SYNTAX_ERROR;
			ltfs_volume_free(&dummy_vol);
			goto out_close;
		}
		ltfs_volume_free(&dummy_vol);
	}
	else {
		ltfsmsg(LTFS_INFO, "15049I", "load");
		ret = tape_load_tape(vol->device, vol->kmi_handle);
		if (ret < 0) {
			if (ret == -LTFS_UNSUPPORTED_MEDIUM)
				ltfsmsg(LTFS_ERR, "11298E");
			else
				ltfsmsg(LTFS_ERR, "11006E");

			ret = MKLTFS_OPERATIONAL_ERROR;
			goto out_close;
		}
	}

	if (tape_get_worm_status(vol->device, &is_worm) < 0) {
		ret = MKLTFS_OPERATIONAL_ERROR;
		goto out_close;
	}

	/* Set up index data: filter rules */
	ret = index_criteria_set_allow_update(is_worm? false : opt->allow_update, vol);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "15014E", ret);
		ret = MKLTFS_OPERATIONAL_ERROR;
		goto out_close;
	}

	if (opt->filterrules) {
		if (is_worm) {
			ltfsmsg(LTFS_ERR, "15060E");
			ret = MKLTFS_USAGE_SYNTAX_ERROR;
			goto out_close;
		}

		ret = ltfs_override_policy(opt->filterrules, true, vol);
		if (ret < 0) {
			ltfsmsg(LTFS_ERR, "15015E", ret);
			ret = MKLTFS_OPERATIONAL_ERROR;
			goto out_close;
		}
	}

	/* Create partitions and write labels and indices to the tape */
	ltfsmsg(LTFS_INFO, "15010I", DATA_PART_ID, DATA_PART_NUM);
	ltfsmsg(LTFS_INFO, "15011I", INDEX_PART_ID, INDEX_PART_NUM);
	ret = ltfs_format_tape(vol);
	if (ret < 0) {
		if (ret == -LTFS_INTERRUPTED) {
			ltfsmsg(LTFS_ERR, "15045E");
			ret = MKLTFS_CANCELED_BY_USER;
		}else if (ret == -EDEV_WRITE_PROTECTED_WORM) {
			ltfsmsg(LTFS_ERR, "15061E");
			ret = MKLTFS_USAGE_SYNTAX_ERROR;
		}else {
			ltfsmsg(LTFS_ERR, "15012E");
			if (ret == -LTFS_WRITE_PROTECT || ret == -LTFS_WRITE_ERROR)
				ret = MKLTFS_USAGE_SYNTAX_ERROR;
			else
				ret = MKLTFS_OPERATIONAL_ERROR;
		}
		goto out_close;
	}
	ltfsmsg(LTFS_INFO, "15013I", ltfs_get_volume_uuid(vol));
	if (! opt->quiet)
		fprintf(stderr, "\n");

	/* Print volume capacity as GB (10^9 Bytes - SI) */
	memset(&cap, 0, sizeof(cap));
	ltfs_capacity_data(&cap, vol);
	ltfsmsg(LTFS_INFO, "15019I", (unsigned long long)LTFS_CALC_VOLUME_SIZE(cap.total_dp,
													opt->blocksize, BYTE_MULTIPLIER));

/* Separate implementation for MAM attributes exist. */													  
#if 0													  
	vol->t_attr = (struct tape_attr *) calloc(1, sizeof(struct tape_attr));
	if (! vol->t_attr) {
		ltfsmsg(LTFS_ERR, "10001E", "format_tape: vol->t_attr");
		goto out_close;
	}

	/* set Tape Attribute to vol->t_attr */
	set_tape_attribute(vol, vol->t_attr);

	ret = tape_format_attribute_to_cm(vol->device, vol->t_attr);
	if (ret < 0) {
		free(vol->t_attr);
		ltfsmsg(LTFS_ERR, "15058E", "format_tape");
	}
#endif /* 0 */
	ret = MKLTFS_NO_ERRORS;

	/* close the tape device and unload the backend */
	ltfsmsg(LTFS_DEBUG, "15020D");

out_close:
	ltfs_device_close(vol);
	ltfs_volume_free(&vol);
	ltfs_unset_signal_handlers();
	if (ret == MKLTFS_NO_ERRORS)
		ltfsmsg(LTFS_DEBUG, "15022D");
out_unload_backend:
	if (ret == MKLTFS_NO_ERRORS) {
		ret = plugin_unload(&backend);
		if (ret < 0)
			ltfsmsg(LTFS_WARN, "15021W");
		if (opt->kmi_backend_name) {
			ret = plugin_unload(&kmi);
			if (ret < 0)
				ltfsmsg(LTFS_WARN, "15051W");
		}
		ret = MKLTFS_NO_ERRORS;
	} else {
		plugin_unload(&backend);
		if (opt->kmi_backend_name)
			plugin_unload(&kmi);
	}

	if (ret == MKLTFS_NO_ERRORS)
		ltfsmsg(LTFS_INFO, "15024I");
	else
		ltfsmsg(LTFS_INFO, "15023I");

	return ret;
}

int unformat_tape(struct ltfs_volume *vol, struct other_format_opts *opt, void *args)
{
	int ret = MKLTFS_OPERATIONAL_ERROR;
	struct libltfs_plugin backend; /* tape driver backend */
	struct libltfs_plugin kmi; /* key manager interface backend */

	/* load the backend, open the tape device, and load a tape */
	ltfsmsg(LTFS_DEBUG, "15006D");
	ret = plugin_load(&backend, "driver", opt->backend_path, opt->config);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "15008E", opt->backend_path);
		return MKLTFS_OPERATIONAL_ERROR;
	}
	if (opt->kmi_backend_name) {
		ret = plugin_load(&kmi, "kmi", opt->kmi_backend_name, opt->config);
		if (ret < 0) {
			ltfsmsg(LTFS_ERR, "15050E", opt->kmi_backend_name);
			return MKLTFS_OPERATIONAL_ERROR;
		}
	}
	ret = ltfs_device_open(opt->devname, backend.ops, vol);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "15009E", opt->devname, ret);
		ret = MKLTFS_OPERATIONAL_ERROR;
		goto out_unload_backend;
	}
	if (opt->kmi_backend_name) {
		ret = kmi_init(&kmi, vol);
		if (ret < 0) {
			ltfsmsg(LTFS_ERR, "15052E", opt->devname, ret);
			goto out_unload_backend;
		}

		ret = ltfs_parse_kmi_backend_opts(args, vol);
		if (ret < 0) {
			ltfsmsg(LTFS_ERR, "15053E");
			goto out_unload_backend;
		}

		ret = tape_clear_key(vol->device, vol->kmi_handle);
		if (ret < 0)
			goto out_unload_backend;
	}
	{
		int i = 0;
		struct fuse_args *a = args;

		for (i = 0; i < a->argc && a->argv[i]; ++i) {
			if (!strcmp(a->argv[i], "-o")) {
				ltfsmsg(LTFS_ERR, "15055E", a->argv[i], a->argv[i + 1] ? a->argv[i + 1] : "");
				ret = MKLTFS_USAGE_SYNTAX_ERROR;
				goto out_unload_backend;
			}
		}
	}
	vol->append_only_mode = false;
	vol->set_pew = false;
	ret = ltfs_setup_device(vol);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "15044E");
		ret = MKLTFS_OPERATIONAL_ERROR;
		goto out_close;
	}
	ltfsmsg(LTFS_DEBUG, "15007D");

	/* Get mam attributes to be used to get info about archive manager tape */
	ret = tape_get_MAM_AMVALattributes(vol->device,
								 ltfs_part_id2num(vol->label->partid_ip, vol),
								 opt->quiet,
								 &vol->mam_attr);

	/* Let us check if Archive Manager tape and return failure */
	ret = ltfs_get_archivemanager_media(vol);
	if (ret) {
		ltfsmsg(LTFS_ERR, "15489E");
		ret = MKLTFS_OPERATIONAL_ERROR;
		goto out_close;
	}

	/* Donot unformat if tape is already unformated */
	ret = tape_check_unformat_ok(vol->device);
	if (ret < 0) {
		ltfsmsg(LTFS_ERR, "15494E");
		goto out_close;
	}

	/* Create 1 partition cartridge */
	ret = ltfs_unformat_tape(vol, opt->long_wipe);
	if (ret < 0) {
		if (ret == -LTFS_INTERRUPTED) {
			ltfsmsg(LTFS_ERR, "15046E");
			ret = MKLTFS_CANCELED_BY_USER;
		}else if (ret == -EDEV_WRITE_PROTECTED_WORM) {
			ltfsmsg(LTFS_ERR, "15062E");
			ret = MKLTFS_USAGE_SYNTAX_ERROR;
		}else {
			ltfsmsg(LTFS_ERR, "15038E");
			ret = MKLTFS_OPERATIONAL_ERROR;
		}
		goto out_close;
	}

	ret = MKLTFS_UNFORMATTED;

	/* close the tape device and unload the backend */
	ltfsmsg(LTFS_DEBUG, "15020D");

out_close:
	ltfs_device_close(vol);
	ltfs_volume_free(&vol);
	ltfs_unset_signal_handlers();
	if (ret == MKLTFS_UNFORMATTED)
		ltfsmsg(LTFS_DEBUG, "15022D");
out_unload_backend:
	if (ret == MKLTFS_UNFORMATTED) {
		ret = plugin_unload(&backend);
		if (ret < 0)
			ltfsmsg(LTFS_WARN, "15021W");
		if (opt->kmi_backend_name) {
			ret = plugin_unload(&kmi);
			if (ret < 0)
				ltfsmsg(LTFS_WARN, "15051W");
		}
		ret = MKLTFS_UNFORMATTED;
	} else {
		plugin_unload(&backend);
		if (opt->kmi_backend_name)
			plugin_unload(&kmi);
	}

	if (ret == MKLTFS_UNFORMATTED)
		ltfsmsg(LTFS_INFO, "15040I");
	else
		ltfsmsg(LTFS_INFO, "15039I");

	return ret;
}

int _mkltfs_validate_options(char *prg_name, struct ltfs_volume *vol,
	struct other_format_opts *opt)
{
	int ret;
	char *tmp;

	ltfsmsg(LTFS_DEBUG, "15025D");

	if (!opt->devname) {
		ltfsmsg(LTFS_ERR, "15026E", "-d");
		return 1;
	}

	/* convert volume name to LTFS internal format, if specified */
	if (opt->volume_name) {
		ret = pathname_format(opt->volume_name, &tmp, true, false);
		if (ret < 0) {
			ltfsmsg(LTFS_ERR, "15031E");
			return 1;
		}
		free(opt->volume_name);
		opt->volume_name = tmp;
	}

	/* convert filter rules to UTF-8 NFC, fold the case, and parse them */
	if (opt->filterrules) {
		ret = pathname_format(opt->filterrules, &tmp, false, false);
		if (ret < 0) {
			ltfsmsg(LTFS_ERR, "15034E", ret);
			return 1;
		}
		free(opt->filterrules);
		opt->filterrules = tmp;
	}

	ltfsmsg(LTFS_DEBUG, "15037D");
	return 0;
}
