Blame src/driver/tpax_driver_ctx.c

88751e
/******************************************************/
88751e
/*  tpax: a topological pax implementation            */
88751e
/*  Copyright (C) 2020  Z. Gilboa                     */
88751e
/*  Released under GPLv2 and GPLv3; see COPYING.TPAX. */
88751e
/******************************************************/
88751e
88751e
#include <stdint.h>
893ea2
#include <string.h>
88751e
#include <unistd.h>
88751e
#include <fcntl.h>
893ea2
#include <errno.h>
88751e
88751e
#define ARGV_DRIVER
88751e
88751e
#include <tpax/tpax.h>
88751e
#include "tpax_version.h"
88751e
#include "tpax_driver_impl.h"
88751e
#include "argv/argv.h"
88751e
893ea2
#define TPAX_DRIVER_EXEC_MODE_MASK \
893ea2
	(TPAX_DRIVER_EXEC_MODE_LIST \
893ea2
	| TPAX_DRIVER_EXEC_MODE_READ \
893ea2
	| TPAX_DRIVER_EXEC_MODE_WRITE \
893ea2
	| TPAX_DRIVER_EXEC_MODE_COPY)
893ea2
88751e
/* package info */
88751e
static const struct tpax_source_version tpax_src_version = {
88751e
	TPAX_TAG_VER_MAJOR,
88751e
	TPAX_TAG_VER_MINOR,
88751e
	TPAX_TAG_VER_PATCH,
88751e
	TPAX_GIT_VERSION
88751e
};
88751e
88751e
/* default fd context */
88751e
static const struct tpax_fd_ctx tpax_default_fdctx = {
88751e
	.fdin  = STDIN_FILENO,
88751e
	.fdout = STDOUT_FILENO,
88751e
	.fderr = STDERR_FILENO,
88751e
	.fdcwd = AT_FDCWD,
88751e
	.fddst = AT_FDCWD,
88751e
	.fdlog = (-1),
88751e
};
88751e
88751e
struct tpax_driver_ctx_alloc {
88751e
	struct argv_meta *		meta;
88751e
	struct tpax_driver_ctx_impl	ctx;
88751e
	uint64_t			guard;
88751e
	const char *			units[];
88751e
};
88751e
88751e
static uint32_t tpax_argv_flags(uint32_t flags)
88751e
{
88751e
	uint32_t ret = ARGV_CLONE_VECTOR;
88751e
88751e
	if (flags & TPAX_DRIVER_VERBOSITY_NONE)
88751e
		ret |= ARGV_VERBOSITY_NONE;
88751e
88751e
	if (flags & TPAX_DRIVER_VERBOSITY_ERRORS)
88751e
		ret |= ARGV_VERBOSITY_ERRORS;
88751e
88751e
	if (flags & TPAX_DRIVER_VERBOSITY_STATUS)
88751e
		ret |= ARGV_VERBOSITY_STATUS;
88751e
88751e
	return ret;
88751e
}
88751e
88751e
static int tpax_driver_usage(
88751e
	int				fdout,
88751e
	const char *			program,
88751e
	const char *			arg,
88751e
	const struct argv_option **	optv,
88751e
	struct argv_meta *		meta)
88751e
{
88751e
	char header[512];
88751e
88751e
	snprintf(header,sizeof(header),
893ea2
		"%s — topological pax implementation\n\n"
893ea2
		"Synopsis:\n"
893ea2
		"       %s\n"
893ea2
		"       %s -r\n"
893ea2
		"       %s -w\n"
893ea2
		"       %s -r -w\n\n"
893ea2
		"Options:\n",
893ea2
		program,program,program,program,program);
88751e
88751e
	argv_usage(fdout,header,optv,arg);
88751e
	argv_free(meta);
88751e
88751e
	return TPAX_USAGE;
88751e
}
88751e
893ea2
static int tpax_driver_usage_exec_mode(
893ea2
	int				fdout,
893ea2
	const char *			program,
893ea2
	const char *			arg,
893ea2
	const struct argv_option **	optv,
893ea2
	struct argv_meta *		meta)
893ea2
{
893ea2
	tpax_driver_usage(
893ea2
		fdout,program,
893ea2
		arg,optv,meta);
893ea2
893ea2
	tpax_dprintf(
893ea2
		fdout,
893ea2
		"\nWhen using explicit (long) mode options, "
893ea2
		"only one of --list, --read, --write, --copy "
893ea2
		"may be used.\n");
893ea2
893ea2
	return TPAX_USAGE;
893ea2
}
893ea2
893ea2
static int tpax_driver_usage_copy_mode(
893ea2
	int				fdout,
893ea2
	const char *			program,
893ea2
	const char *			arg,
893ea2
	const struct argv_option **	optv,
893ea2
	struct argv_meta *		meta,
893ea2
	struct argv_entry *		operand)
893ea2
{
893ea2
	const char * errdesc;
893ea2
893ea2
	if (!operand) {
893ea2
		tpax_driver_usage(
893ea2
			fdout,program,
893ea2
			arg,optv,meta);
893ea2
893ea2
		tpax_dprintf(
893ea2
			fdout,
893ea2
			"\nThe copy mode requires a destination "
893ea2
			"directory argument.\n\n");
893ea2
893ea2
		return TPAX_USAGE;
893ea2
	}
893ea2
893ea2
	switch (errno) {
893ea2
		case ENOENT:
893ea2
			tpax_dprintf(
893ea2
				fdout,
893ea2
				"%s: error: "
893ea2
				"the destination directory '%s' "
893ea2
				"does not exist.\n\n",
893ea2
				program,operand->arg);
893ea2
			break;
893ea2
893ea2
		case ENOTDIR:
893ea2
			tpax_dprintf(
893ea2
				fdout,
893ea2
				"%s: error: "
893ea2
				"'%s' is not a directory.\n\n",
893ea2
				program,operand->arg);
893ea2
			break;
893ea2
893ea2
		default:
893ea2
			if (!(errdesc = strerror(errno)))
893ea2
				errdesc = "<no error description>";
893ea2
893ea2
			tpax_dprintf(
893ea2
				fdout,
893ea2
				"%s: error: while opening the "
893ea2
				"destination directory '%s': %s.\n\n",
893ea2
				program,operand->arg,errdesc);
893ea2
			break;
893ea2
	}
893ea2
893ea2
	argv_free(meta);
893ea2
893ea2
	return TPAX_USAGE;
893ea2
}
893ea2
893ea2
static int tpax_driver_error_not_implemented(
893ea2
	int				fdout,
893ea2
	const char *			program,
893ea2
	const char *			feature,
893ea2
	struct argv_meta *		meta)
893ea2
893ea2
{
893ea2
	tpax_dprintf(
893ea2
		fdout,"%s: error: %s is not yet implemented!\n\n",
893ea2
		program,feature);
893ea2
893ea2
	argv_free(meta);
893ea2
893ea2
	return TPAX_FATAL;
893ea2
}
893ea2
88751e
static struct tpax_driver_ctx_impl * tpax_driver_ctx_alloc(
88751e
	struct argv_meta *		meta,
88751e
	const struct tpax_fd_ctx *	fdctx,
88751e
	const struct tpax_common_ctx *	cctx,
88751e
	size_t				nunits)
88751e
{
88751e
	struct tpax_driver_ctx_alloc *	ictx;
88751e
	size_t				size;
88751e
	struct argv_entry *		entry;
88751e
	const char **			units;
88751e
	int				elements;
88751e
88751e
	size =  sizeof(struct tpax_driver_ctx_alloc);
88751e
	size += (nunits+1)*sizeof(const char *);
88751e
88751e
	if (!(ictx = calloc(1,size)))
88751e
		return 0;
88751e
88751e
	memcpy(&ictx->ctx.fdctx,fdctx,sizeof(*fdctx));
88751e
	memcpy(&ictx->ctx.cctx,cctx,sizeof(*cctx));
88751e
88751e
	elements = sizeof(ictx->ctx.erribuf) / sizeof(*ictx->ctx.erribuf);
88751e
88751e
	ictx->ctx.errinfp  = &ictx->ctx.erriptr[0];
88751e
	ictx->ctx.erricap  = &ictx->ctx.erriptr[--elements];
88751e
88751e
	ictx->meta = meta;
88751e
88751e
	for (entry=meta->entries,units=ictx->units; entry->fopt || entry->arg; entry++)
88751e
		if (!entry->fopt)
88751e
			*units++ = entry->arg;
88751e
88751e
	ictx->ctx.ctx.units = ictx->units;
88751e
	ictx->ctx.ctx.errv  = ictx->ctx.errinfp;
88751e
	return &ictx->ctx;
88751e
}
88751e
88751e
static int tpax_get_driver_ctx_fail(struct argv_meta * meta)
88751e
{
88751e
	argv_free(meta);
88751e
	return -1;
88751e
}
88751e
88751e
int tpax_get_driver_ctx(
88751e
	char **				argv,
88751e
	char **				envp,
88751e
	uint32_t			flags,
88751e
	const struct tpax_fd_ctx *	fdctx,
88751e
	struct tpax_driver_ctx ** 	pctx)
88751e
{
88751e
	struct tpax_driver_ctx_impl *	ctx;
88751e
	struct tpax_common_ctx		cctx;
88751e
	const struct argv_option *	optv[TPAX_OPTV_ELEMENTS];
88751e
	struct argv_meta *		meta;
88751e
	struct argv_entry *		entry;
893ea2
	struct argv_entry *		operand;
88751e
	size_t				nunits;
88751e
	const char *			program;
893ea2
	int				fddst;
88751e
88751e
	(void)envp;
88751e
88751e
	if (!fdctx)
88751e
		fdctx = &tpax_default_fdctx;
88751e
88751e
	argv_optv_init(tpax_default_options,optv);
88751e
88751e
	if (!(meta = argv_get(
88751e
			argv,optv,
88751e
			tpax_argv_flags(flags),
88751e
			fdctx->fderr)))
88751e
		return -1;
88751e
88751e
	nunits	= 0;
893ea2
	operand = 0;
88751e
	program = argv_program_name(argv[0]);
88751e
	memset(&cctx,0,sizeof(cctx));
88751e
893ea2
	cctx.drvflags = flags;
893ea2
	fddst         = fdctx->fddst;
88751e
88751e
	/* get options, count units */
88751e
	for (entry=meta->entries; entry->fopt || entry->arg; entry++) {
88751e
		if (entry->fopt) {
88751e
			switch (entry->tag) {
88751e
				case TAG_HELP:
88751e
					if (flags & TPAX_DRIVER_VERBOSITY_USAGE)
88751e
						return tpax_driver_usage(
88751e
							fdctx->fdout,
88751e
							program,entry->arg,
88751e
							optv,meta);
88751e
					break;
88751e
88751e
				case TAG_VERSION:
88751e
					cctx.drvflags |= TPAX_DRIVER_VERSION;
88751e
					break;
893ea2
893ea2
				case TAG_LIST:
893ea2
					cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_LIST;
893ea2
					break;
893ea2
893ea2
				case TAG_READ:
893ea2
					cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_READ;
893ea2
					break;
893ea2
893ea2
				case TAG_WRITE:
893ea2
					cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_WRITE;
893ea2
					break;
893ea2
893ea2
				case TAG_COPY:
893ea2
					cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_COPY;
893ea2
					break;
88751e
			}
893ea2
		} else {
893ea2
			operand = entry;
88751e
			nunits++;
893ea2
		}
893ea2
	}
893ea2
893ea2
	/* incompatible arguments? */
893ea2
	switch (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_MASK) {
893ea2
		case 0:
893ea2
			if (!(cctx.drvflags & TPAX_DRIVER_VERSION))
893ea2
				cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_LIST;
893ea2
			break;
893ea2
893ea2
		case TPAX_DRIVER_EXEC_MODE_READ|TPAX_DRIVER_EXEC_MODE_WRITE:
893ea2
			cctx.drvflags &= ~(uint64_t)(TPAX_DRIVER_EXEC_MODE_READ);
893ea2
			cctx.drvflags &= ~(uint64_t)(TPAX_DRIVER_EXEC_MODE_WRITE);
893ea2
			cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_COPY;
893ea2
			break;
893ea2
893ea2
		case TPAX_DRIVER_EXEC_MODE_LIST:
893ea2
		case TPAX_DRIVER_EXEC_MODE_READ:
893ea2
		case TPAX_DRIVER_EXEC_MODE_WRITE:
893ea2
		case TPAX_DRIVER_EXEC_MODE_COPY:
893ea2
			break;
893ea2
893ea2
		default:
893ea2
			return tpax_driver_usage_exec_mode(
893ea2
				fdctx->fderr,
893ea2
				program,entry->arg,
893ea2
				optv,meta);
88751e
	}
88751e
893ea2
	/* copy mode: destination directory */
893ea2
	if (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_COPY) {
893ea2
		if (!operand)
893ea2
			return tpax_driver_usage_copy_mode(
893ea2
				fdctx->fderr,
893ea2
				program,entry->arg,
893ea2
				optv,meta,operand);
893ea2
893ea2
		fddst = openat(
893ea2
			fdctx->fdcwd,
893ea2
			operand->arg,
893ea2
			O_RDONLY|O_DIRECTORY|O_CLOEXEC,0);
893ea2
893ea2
		if (fddst < 0)
893ea2
			return tpax_driver_usage_copy_mode(
893ea2
				fdctx->fderr,
893ea2
				program,entry->arg,
893ea2
				optv,meta,operand);
893ea2
	}
893ea2
893ea2
	/* not implemented mode(s) */
893ea2
	switch (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_MASK) {
893ea2
		case TPAX_DRIVER_EXEC_MODE_LIST:
893ea2
			return tpax_driver_error_not_implemented(
893ea2
				fdctx->fderr,program,"list mode",meta);
893ea2
893ea2
		case TPAX_DRIVER_EXEC_MODE_READ:
893ea2
			return tpax_driver_error_not_implemented(
893ea2
				fdctx->fderr,program,"read mode",meta);
893ea2
893ea2
		case TPAX_DRIVER_EXEC_MODE_WRITE:
893ea2
			return tpax_driver_error_not_implemented(
893ea2
				fdctx->fderr,program,"write mode",meta);
893ea2
893ea2
		case TPAX_DRIVER_EXEC_MODE_COPY:
893ea2
			close(fddst);
893ea2
893ea2
			return tpax_driver_error_not_implemented(
893ea2
				fdctx->fderr,program,"copy mode",meta);
893ea2
893ea2
		default:
893ea2
			break;
893ea2
	}
893ea2
893ea2
	if (!(ctx = tpax_driver_ctx_alloc(meta,fdctx,&cctx,nunits))) {
893ea2
		if (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_COPY)
893ea2
			close(fddst);
893ea2
88751e
		return tpax_get_driver_ctx_fail(meta);
893ea2
	}
88751e
88751e
	ctx->ctx.program	= program;
88751e
	ctx->ctx.cctx		= &ctx->cctx;
88751e
88751e
	*pctx = &ctx->ctx;
88751e
	return TPAX_OK;
88751e
}
88751e
88751e
static void tpax_free_driver_ctx_impl(struct tpax_driver_ctx_alloc * ictx)
88751e
{
88751e
	argv_free(ictx->meta);
88751e
	free(ictx);
88751e
}
88751e
88751e
void tpax_free_driver_ctx(struct tpax_driver_ctx * ctx)
88751e
{
88751e
	struct tpax_driver_ctx_alloc *	ictx;
88751e
	uintptr_t			addr;
88751e
88751e
	if (ctx) {
88751e
		addr = (uintptr_t)ctx - offsetof(struct tpax_driver_ctx_impl,ctx);
88751e
		addr = addr - offsetof(struct tpax_driver_ctx_alloc,ctx);
88751e
		ictx = (struct tpax_driver_ctx_alloc *)addr;
88751e
		tpax_free_driver_ctx_impl(ictx);
88751e
	}
88751e
}
88751e
88751e
const struct tpax_source_version * tpax_source_version(void)
88751e
{
88751e
	return &tpax_src_version;
88751e
}
88751e
88751e
int tpax_get_driver_fdctx(
88751e
	const struct tpax_driver_ctx *	dctx,
88751e
	struct tpax_fd_ctx *		fdctx)
88751e
{
88751e
	struct tpax_driver_ctx_impl *	ictx;
88751e
88751e
	ictx = tpax_get_driver_ictx(dctx);
88751e
88751e
	fdctx->fdin  = ictx->fdctx.fdin;
88751e
	fdctx->fdout = ictx->fdctx.fdout;
88751e
	fdctx->fderr = ictx->fdctx.fderr;
88751e
	fdctx->fdlog = ictx->fdctx.fdlog;
88751e
	fdctx->fdcwd = ictx->fdctx.fdcwd;
88751e
	fdctx->fddst = ictx->fdctx.fddst;
88751e
88751e
	return 0;
88751e
}
88751e
88751e
int tpax_set_driver_fdctx(
88751e
	struct tpax_driver_ctx *	dctx,
88751e
	const struct tpax_fd_ctx *	fdctx)
88751e
{
88751e
	struct tpax_driver_ctx_impl *	ictx;
88751e
88751e
	ictx = tpax_get_driver_ictx(dctx);
88751e
88751e
	ictx->fdctx.fdin  = fdctx->fdin;
88751e
	ictx->fdctx.fdout = fdctx->fdout;
88751e
	ictx->fdctx.fderr = fdctx->fderr;
88751e
	ictx->fdctx.fdlog = fdctx->fdlog;
88751e
	ictx->fdctx.fdcwd = fdctx->fdcwd;
88751e
	ictx->fdctx.fddst = fdctx->fddst;
88751e
88751e
	return 0;
88751e
}