Blame src/driver/tpax_driver_ctx.c

5874a9
/**************************************************************/
5874a9
/*  tpax: a topological pax implementation                    */
bef28d
/*  Copyright (C) 2020--2024  SysDeer Technologies, LLC       */
5874a9
/*  Released under GPLv2 and GPLv3; see COPYING.TPAX.         */
5874a9
/**************************************************************/
88751e
76f8c5
#define _DEFAULT_SOURCE 1
76f8c5
5ea1d7
#include <regex.h>
88751e
#include <stdint.h>
54c29f
#include <stdlib.h>
893ea2
#include <string.h>
88751e
#include <unistd.h>
88751e
#include <fcntl.h>
893ea2
#include <errno.h>
76f8c5
#include <sys/mman.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
8aa3fc
#define TPAX_DRIVER_WRITE_FORMAT_MASK \
8aa3fc
	(TPAX_DRIVER_WRITE_FORMAT_PAX  \
8aa3fc
	| TPAX_DRIVER_WRITE_FORMAT_CPIO \
8aa3fc
	| TPAX_DRIVER_WRITE_FORMAT_USTAR \
8aa3fc
	| TPAX_DRIVER_WRITE_FORMAT_RUSTAR)
8aa3fc
57abba
#define TPAX_CACHE_MIN (1 << 12)
57abba
#define TPAX_CACHE_MAX (1 << 24)
57abba
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"
55da8f
		"       %s [-d] [-f archive]\n"
55da8f
		"       %s -r [-d] [-f archive]\n"
ffaddb
		"       %s -w [−x format] [-b blocksize] [-dtv] [-H|-L] [-f archive] [-s replstr]... \n"
23fa88
		"       %s -r -w [-d]\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,
31d5a4
		"\n%s: usage error: When using explicit (long) mode options, "
893ea2
		"only one of --list, --read, --write, --copy "
31d5a4
		"may be used.\n",program);
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,
156e23
	struct argv_entry *		operand,
156e23
	struct argv_entry *		archive)
893ea2
{
893ea2
	const char * errdesc;
893ea2
156e23
	if (archive || !operand) {
893ea2
		tpax_driver_usage(
893ea2
			fdout,program,
893ea2
			arg,optv,meta);
893ea2
156e23
		if (archive) {
156e23
			tpax_dprintf(
156e23
				fdout,
31d5a4
				"\n%s: usage error: the __copy__ mode does not permit specifying "
156e23
				"an archive path.\n\n",program);
156e23
		} else {
156e23
			tpax_dprintf(
156e23
				fdout,
31d5a4
				"\n%s: usage error: the __copy__ mode requires a destination "
156e23
				"directory argument.\n\n",program);
156e23
		}
893ea2
893ea2
		return TPAX_USAGE;
893ea2
	}
893ea2
893ea2
	switch (errno) {
893ea2
		case ENOENT:
893ea2
			tpax_dprintf(
893ea2
				fdout,
31d5a4
				"%s: file-system 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,
31d5a4
				"%s: file-system 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,
31d5a4
				"%s: general 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
8aa3fc
static int tpax_driver_usage_write_format(
8aa3fc
	int				fdout,
8aa3fc
	const char *			program,
8aa3fc
	const char *			arg,
8aa3fc
	const struct argv_option **	optv,
8aa3fc
	struct argv_meta *		meta)
8aa3fc
{
8aa3fc
	tpax_driver_usage(
8aa3fc
		fdout,program,
8aa3fc
		arg,optv,meta);
8aa3fc
8aa3fc
	tpax_dprintf(
8aa3fc
		fdout,
31d5a4
		"\n%s: usage error: Archive format may only be specified "
31d5a4
		"in write mode.\n",program);
8aa3fc
8aa3fc
	return TPAX_USAGE;
8aa3fc
}
8aa3fc
54c29f
static int tpax_driver_usage_block_size(
54c29f
	int				fdout,
54c29f
	const char *			program,
54c29f
	const char *			arg,
54c29f
	struct argv_meta *		meta)
54c29f
{
54c29f
	tpax_dprintf(
54c29f
		fdout,
5cec44
		"%s: usage error: the requested block size `%s' is not a positive decimal integer.\n",
31d5a4
		program,arg);
54c29f
5cec44
	argv_free(meta);
5cec44
54c29f
	return TPAX_USAGE;
54c29f
}
54c29f
54c29f
static int tpax_driver_usage_block_size_range(
54c29f
	int				fdout,
54c29f
	const char *			program,
54c29f
	const char *			arg,
54c29f
	struct argv_meta *		meta)
54c29f
{
54c29f
	tpax_dprintf(
54c29f
		fdout,
31d5a4
		"%s: usage error: `%s' is outside the specified range of 512 to 32256.\n",
31d5a4
		program,arg);
54c29f
5ceeb1
	argv_free(meta);
5ceeb1
54c29f
	return TPAX_USAGE;
54c29f
}
54c29f
8c5859
static int tpax_driver_usage_block_constraints(
8c5859
	int				fdout,
8c5859
	const char *			program,
8c5859
	const char *			arg,
8c5859
	struct argv_meta *		meta)
8c5859
{
8c5859
	tpax_dprintf(
8c5859
		fdout,
8c5859
		"%s: usage error: the specified block size `%s' is not a multiple of 512 bytes.\n",
8c5859
		program,arg);
8c5859
8c5859
	argv_free(meta);
8c5859
8c5859
	return TPAX_USAGE;
8c5859
}
8c5859
156e23
static int tpax_driver_error_archive_path(
156e23
	int				fdout,
156e23
	const char *			program,
156e23
	const char *			arg,
156e23
	struct argv_meta *		meta,
156e23
	bool                            fwrite)
156e23
{
156e23
	int             lerrno;
156e23
	const char *    errstr;
156e23
156e23
	lerrno = errno;
156e23
	errno  = 0;
156e23
156e23
	errstr = strerror(lerrno);
156e23
	errstr = errno ? "" : errstr;
156e23
	errno  = lerrno;
156e23
156e23
	if (fwrite) {
156e23
		tpax_dprintf(
156e23
			fdout,
31d5a4
			"%s: file-system error: archive file <%s> could not be created "
156e23
			"or opened for writing (%s).\n",
156e23
			program,arg,errstr);
156e23
	} else {
156e23
		tpax_dprintf(
156e23
			fdout,
31d5a4
			"%s: file-system error: archive file <%s> could not be opened "
156e23
			"for reading (%s).\n",
156e23
			program,arg,errstr);
156e23
	}
156e23
156e23
	argv_free(meta);
156e23
156e23
	return TPAX_FATAL;
156e23
}
156e23
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
333924
static void tpax_set_archive_block_size(struct tpax_common_ctx * cctx)
333924
{
333924
	if (cctx->blksize)
333924
		(void)0;
333924
333924
	else if (cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_PAX)
333924
		cctx->blksize = TPAX_PAX_BLOCK_SIZE;
333924
333924
	else if (cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO)
333924
		cctx->blksize = TPAX_CPIO_BLOCK_SIZE;
333924
333924
	else if (cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_USTAR)
333924
		cctx->blksize = TPAX_USTAR_BLOCK_SIZE;
333924
333924
	else if (cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_RUSTAR)
333924
		cctx->blksize = TPAX_USTAR_BLOCK_SIZE;
333924
}
333924
5ea1d7
static int tpax_add_replstr(
5ea1d7
	struct argv_entry *             entry,
5ea1d7
	struct tpax_replstr *           replstr,
5ea1d7
	char **                         mark)
5ea1d7
{
5ea1d7
	const char *                    src;
5ea1d7
	char *                          dst;
5ea1d7
	char                            sep;
5ea1d7
	int                             nsep;
5ea1d7
5ea1d7
	/* non-null separator character */
5ea1d7
	if (!(sep = entry->arg[0]))
5ea1d7
		return -1;
5ea1d7
5ea1d7
	/* exactly three separator characters */
5ea1d7
	for (nsep=1,src=&entry->arg[1]; *src; src++) {
5ea1d7
		if ((src[0] == '\\') && (src[1] == sep)) {
5ea1d7
			src++;
5ea1d7
5ea1d7
		} else if (src[0] == sep) {
5ea1d7
			nsep++;
5ea1d7
		}
5ea1d7
	}
5ea1d7
5ea1d7
	if (nsep != 3)
5ea1d7
		return -1;
5ea1d7
5ea1d7
	/* regexp */
5ea1d7
	for (src=&entry->arg[1],dst=*mark; (*src != sep); src++) {
5ea1d7
		if ((src[0] == '\\') && (src[1] == sep))
5ea1d7
			src++;
5ea1d7
5ea1d7
		*dst++ = *src;
5ea1d7
	}
5ea1d7
5ea1d7
	replstr->replarg = entry->arg;
5ea1d7
	replstr->replstr = ++dst;
5ea1d7
	replstr->regexp  = *mark;
5ea1d7
5ea1d7
	/* replstr */
5ea1d7
	for (++src; (*src != sep); src++) {
5ea1d7
		if ((src[0] == '\\') && (src[1] == sep))
5ea1d7
			src++;
5ea1d7
5ea1d7
		*dst++ = *src;
5ea1d7
	}
5ea1d7
5ea1d7
	src++;
5ea1d7
	dst++;
5ea1d7
5ea1d7
	*mark = dst;
5ea1d7
5ea1d7
	/* flags */
5ea1d7
	if (src[0] && src[1] && src[2])
5ea1d7
		return -1;
5ea1d7
5ea1d7
	if (src[0] && (src[0] == src[1]))
5ea1d7
		return -1;
5ea1d7
5ea1d7
	if (src[0] && (src[0] != 'g') && (src[0] != 'p'))
5ea1d7
		return -1;
5ea1d7
5ea1d7
	if (src[0] && src[1] && (src[1] != 'g') && (src[1] != 'p'))
5ea1d7
		return -1;
5ea1d7
5ea1d7
	if (src[0] && ((src[0] == 'g') || (src[1] == 'g')))
5ea1d7
		replstr->flags |= TPAX_REPL_GLOBAL;
5ea1d7
5ea1d7
	if (src[0] && ((src[0] == 'p') || (src[1] == 'p')))
5ea1d7
		replstr->flags |= TPAX_REPL_PRINT;
5ea1d7
5ea1d7
	/* regex */
5ea1d7
	if (regcomp(&replstr->regex,replstr->regexp,0)) {
5ea1d7
		replstr->regexp = 0;
5ea1d7
		return -1;
5ea1d7
	}
5ea1d7
5ea1d7
	return 0;
5ea1d7
}
5ea1d7
5ea1d7
static int tpax_init_replstr_vector(
5ea1d7
	struct tpax_driver_ctx_impl *   ctx,
5ea1d7
	struct argv_meta *              meta)
5ea1d7
{
5ea1d7
	struct argv_entry *             entry;
5ea1d7
	struct tpax_replstr *           replstr;
5ea1d7
	char *                          mark;
5ea1d7
5ea1d7
	if (!(replstr = ctx->replstrv))
5ea1d7
		return 0;
5ea1d7
5ea1d7
	for (entry=meta->entries,mark=ctx->replstrs; entry->fopt || entry->arg; entry++) {
5ea1d7
		if (entry->tag == TAG_REPLSTR) {
5ea1d7
			if (tpax_add_replstr(entry,replstr,&mark) < 0)
5ea1d7
				return -1;
5ea1d7
5ea1d7
			replstr++;
5ea1d7
		}
5ea1d7
	}
5ea1d7
5ea1d7
	return 0;
5ea1d7
}
5ea1d7
722538
static int tpax_driver_is_valid_keyval(struct argv_keyval * keyval)
722538
{
722538
	(void)keyval;
722538
	return 0;
722538
}
722538
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,
5ea1d7
	size_t				nunits,
5ea1d7
	size_t                          nreplstr,
5ea1d7
	size_t                          sreplstr)
88751e
{
88751e
	struct tpax_driver_ctx_alloc *	ictx;
88751e
	size_t				size;
88751e
	struct argv_entry *		entry;
88751e
	const char **			units;
88751e
	int				elements;
722538
	int                             nkeyval;
722538
	struct argv_keyval **           pkeyval;
722538
	struct argv_keyval *            keyval;
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
9f55a1
	elements = sizeof(ictx->ctx.prefptr) / sizeof(*ictx->ctx.prefptr);
9f55a1
9f55a1
	ictx->ctx.prefixv  = &ictx->ctx.prefptr[0];
9f55a1
	ictx->ctx.prefixp  = &ictx->ctx.prefptr[0];
9f55a1
	ictx->ctx.prefcap  = &ictx->ctx.prefptr[--elements];
9f55a1
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
722538
	for (entry=meta->entries,nkeyval=0; entry->fopt || entry->arg; entry++)
722538
		if (entry->keyv)
722538
			for (keyval=entry->keyv; keyval->keyword; keyval++)
722538
				nkeyval++;
722538
722538
	if (nkeyval && !(ictx->ctx.keyvalv = calloc(nkeyval+1,sizeof(*ictx->ctx.keyvalv)))) {
722538
		free(ictx);
722538
		return 0;
722538
	}
722538
5ea1d7
	if (nreplstr && !(ictx->ctx.replstrv = calloc(++nreplstr,sizeof(*ictx->ctx.replstrv)))) {
5ea1d7
		free(ictx->ctx.keyvalv);
5ea1d7
		free(ictx);
5ea1d7
		return 0;
5ea1d7
	}
5ea1d7
5ea1d7
	if (sreplstr && !(ictx->ctx.replstrs = calloc(sreplstr,1))) {
5ea1d7
		free(ictx->ctx.replstrv);
5ea1d7
		free(ictx->ctx.keyvalv);
5ea1d7
		free(ictx);
5ea1d7
		return 0;
5ea1d7
	}
5ea1d7
722538
	if ((pkeyval = ictx->ctx.keyvalv))
722538
		for (entry=meta->entries; entry->fopt || entry->arg; entry++)
722538
			if (entry->keyv)
722538
				for (keyval=entry->keyv; keyval->keyword; keyval++)
722538
					*pkeyval++ = keyval;
722538
883919
	ictx->ctx.bufsize = TPAX_FILEIO_BUFLEN;
883919
	ictx->ctx.bufaddr = mmap(
883919
		0,ictx->ctx.bufsize,
883919
		PROT_READ|PROT_WRITE,
883919
		MAP_PRIVATE|MAP_ANONYMOUS,
883919
		-1,0);
883919
883919
	if (ictx->ctx.bufaddr == MAP_FAILED) {
883919
		free(ictx->ctx.keyvalv);
883919
		free(ictx);
883919
		return 0;
883919
	}
d7918a
883919
	if (cctx->drvflags & TPAX_DRIVER_EXEC_MODE_WRITE_COPY) {
5a35e1
		ictx->ctx.dirbuff = mmap(
5a35e1
			0,TPAX_DIRENT_BUFLEN,
5a35e1
			PROT_READ|PROT_WRITE,
5a35e1
			MAP_PRIVATE|MAP_ANONYMOUS,
5a35e1
			-1,0);
d7918a
d7918a
		if (ictx->ctx.dirbuff == MAP_FAILED) {
d7918a
			munmap(ictx->ctx.bufaddr,ictx->ctx.bufsize);
722538
			free(ictx->ctx.keyvalv);
d7918a
			free(ictx);
d7918a
			return 0;
d7918a
		}
76f8c5
	}
76f8c5
333924
	tpax_set_archive_block_size(&ictx->ctx.cctx);
333924
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
722538
static int tpax_driver_keyval_error(
722538
	struct tpax_driver_ctx_impl *   ctx,
722538
	struct argv_keyval *            keyval,
722538
	const char *                    program)
722538
{
722538
	const char * equal;
722538
722538
	switch (keyval->flags) {
722538
		case ARGV_KEYVAL_ASSIGN:
722538
			equal = "=";
722538
			break;
722538
722538
		case ARGV_KEYVAL_OVERRIDE:
722538
			equal = ":=";
722538
			break;
722538
722538
		default:
722538
			equal = "";
722538
	}
722538
722538
	tpax_dprintf(
722538
		ctx->fdctx.fderr,
722538
		"%s: unsupported keyval argument (%s%s%s)\n",
722538
		program,keyval->keyword,equal,
722538
		keyval->value ? keyval->value : "");
722538
722538
	tpax_lib_free_driver_ctx(&ctx->ctx);
722538
722538
	return TPAX_ERROR;
722538
}
722538
6a7e25
static int tpax_driver_srcstat_error(
6a7e25
	struct tpax_driver_ctx_impl *   ctx,
6a7e25
	const struct argv_entry *       archive,
6a7e25
	const char *                    program)
6a7e25
{
6a7e25
	int             lerrno;
6a7e25
	const char *    errstr;
6a7e25
6a7e25
	lerrno = errno;
6a7e25
	errno  = 0;
6a7e25
6a7e25
	errstr = strerror(lerrno);
6a7e25
	errstr = errno ? "" : errstr;
6a7e25
	errno  = lerrno;
6a7e25
6a7e25
	if (archive) {
6a7e25
		tpax_dprintf(
6a7e25
			ctx->fdctx.fderr,
6a7e25
			"%s: could not stat archive file '%s' (%s).\n",
6a7e25
			program,archive->arg,errstr);
6a7e25
	} else {
6a7e25
		tpax_dprintf(
6a7e25
			ctx->fdctx.fderr,
6a7e25
			"%s: could not stat input source file <fd=%d> (%s).\n",
6a7e25
			program,ctx->fdctx.fdin,errstr);
6a7e25
	}
6a7e25
6a7e25
	tpax_lib_free_driver_ctx(&ctx->ctx);
6a7e25
6a7e25
	return TPAX_ERROR;
6a7e25
}
6a7e25
57abba
static int tpax_driver_cache_error(
57abba
	struct tpax_driver_ctx_impl *   ctx,
57abba
	const char *                    program)
57abba
{
57abba
	int             lerrno;
57abba
	const char *    errstr;
57abba
57abba
	lerrno = errno;
57abba
	errno  = 0;
57abba
57abba
	errstr = strerror(lerrno);
57abba
	errstr = errno ? tpax_null_errdesc : errstr;
57abba
	errno  = lerrno;
57abba
57abba
	tpax_dprintf(
57abba
		ctx->fdctx.fderr,
57abba
		"%s: failed to allocate source data cache (%s).\n",
57abba
		program,errstr);
57abba
57abba
	tpax_lib_free_driver_ctx(&ctx->ctx);
57abba
57abba
	return TPAX_ERROR;
57abba
}
57abba
c9eeca
int tpax_lib_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;
156e23
	struct argv_entry *		archive;
893ea2
	struct argv_entry *		operand;
722538
	struct argv_keyval **           pkeyval;
156e23
	struct tpax_fd_ctx              lfdctx;
88751e
	size_t				nunits;
5ea1d7
	size_t                          nreplstr;
5ea1d7
	size_t                          sreplstr;
57abba
	size_t                          cachesize;
88751e
	const char *			program;
893ea2
	int				fddst;
54c29f
	const char *			ch;
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;
156e23
	archive = 0;
893ea2
	operand = 0;
88751e
	program = argv_program_name(argv[0]);
88751e
	memset(&cctx,0,sizeof(cctx));
88751e
5ea1d7
	nreplstr = 0;
5ea1d7
	sreplstr = 0;
5ea1d7
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
e99de3
				case TAG_VERBOSE:
e99de3
					cctx.drvflags |= TPAX_DRIVER_VERBOSE;
e99de3
					break;
e99de3
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;
8aa3fc
156e23
				case TAG_FILE:
156e23
					archive = entry;
156e23
					break;
156e23
8aa3fc
				case TAG_FORMAT:
8aa3fc
					cctx.drvflags &= ~(uint64_t)(TPAX_DRIVER_WRITE_FORMAT_MASK);
8aa3fc
8aa3fc
					if (!strcmp(entry->arg,"pax"))
8aa3fc
						cctx.drvflags |= TPAX_DRIVER_WRITE_FORMAT_PAX;
8aa3fc
					else if (!strcmp(entry->arg,"cpio"))
8aa3fc
						cctx.drvflags |= TPAX_DRIVER_WRITE_FORMAT_CPIO;
8aa3fc
					else if (!strcmp(entry->arg,"ustar"))
8aa3fc
						cctx.drvflags |= TPAX_DRIVER_WRITE_FORMAT_USTAR;
8aa3fc
					else if (!strcmp(entry->arg,"rustar"))
8aa3fc
						cctx.drvflags |= TPAX_DRIVER_WRITE_FORMAT_RUSTAR;
8aa3fc
8aa3fc
					break;
54c29f
54c29f
				case TAG_BLKSIZE:
54c29f
					ch = (entry->arg[0] == '+')
54c29f
						? &entry->arg[1]
54c29f
						: entry->arg;
54c29f
54c29f
					for (; *ch; ch++)
54c29f
						if ((*ch < '0') || (*ch > '9'))
54c29f
							return tpax_driver_usage_block_size(
1fd3f0
								fdctx->fderr,
54c29f
								program,entry->arg,
5cec44
								meta);
54c29f
54c29f
					cctx.blksize = atoi(entry->arg);
54c29f
54c29f
					if ((cctx.blksize < 512) || (cctx.blksize > 32256))
54c29f
						return tpax_driver_usage_block_size_range(
1fd3f0
							fdctx->fderr,
54c29f
							program,entry->arg,
5ceeb1
							meta);
8c5859
8c5859
					if (cctx.blksize % 512)
8c5859
						return tpax_driver_usage_block_constraints(
8c5859
							fdctx->fderr,
8c5859
							program,entry->arg,
8c5859
							meta);
54c29f
					break;
23fa88
5ea1d7
				case TAG_REPLSTR:
5ea1d7
					sreplstr += strlen(entry->arg);
5ea1d7
					sreplstr++;
5ea1d7
					nreplstr++;
5ea1d7
					break;
5ea1d7
23fa88
				case TAG_RECURSE:
23fa88
					cctx.drvflags |= TPAX_DRIVER_DIR_MEMBER_RECURSE;
23fa88
					break;
23fa88
23fa88
				case TAG_NORECURSE:
23fa88
					cctx.drvflags &= ~(uintptr_t)TPAX_DRIVER_DIR_MEMBER_RECURSE;
23fa88
					break;
efbaf8
ee80f8
				case TAG_PRESERVE_ATIME:
ee80f8
					cctx.drvflags |= TPAX_DRIVER_PRESERVE_ATIME;
ee80f8
					break;
ee80f8
37f513
				case TAG_PAX_SYMLINK_ARGS:
37f513
					cctx.drvflags |= TPAX_DRIVER_PAX_SYMLINK_ARGS;
063962
					cctx.drvflags &= ~(uint64_t)TPAX_DRIVER_PAX_SYMLINK_ITEMS;
37f513
					break;
37f513
e50240
				case TAG_PAX_SYMLINK_ITEMS:
e50240
					cctx.drvflags |= TPAX_DRIVER_PAX_SYMLINK_ARGS;
e50240
					cctx.drvflags |= TPAX_DRIVER_PAX_SYMLINK_ITEMS;
e50240
					break;
e50240
4275a9
				case TAG_STRICT_DEVICE_ID:
4275a9
					cctx.drvflags |= TPAX_DRIVER_STRICT_DEVICE_ID;
4275a9
					break;
4275a9
efbaf8
				case TAG_STRICT_PATH:
efbaf8
					cctx.drvflags |= TPAX_DRIVER_STRICT_PATH_INPUT;
efbaf8
					break;
efbaf8
efbaf8
				case TAG_PURE_PATH:
efbaf8
					cctx.drvflags |= TPAX_DRIVER_PURE_PATH_OUTPUT;
efbaf8
					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) {
156e23
		if (archive || !operand)
893ea2
			return tpax_driver_usage_copy_mode(
893ea2
				fdctx->fderr,
893ea2
				program,entry->arg,
156e23
				optv,meta,operand,archive);
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,
156e23
				optv,meta,operand,archive);
156e23
	}
156e23
156e23
	/* archive path */
156e23
	if (archive && (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_WRITE)) {
156e23
		memcpy(&lfdctx,fdctx,sizeof(*fdctx));
156e23
156e23
		lfdctx.fdout = openat(
156e23
			fdctx->fdcwd,
156e23
			archive->arg,
156e23
			O_WRONLY|O_CREAT|O_TRUNC,
156e23
			0644);
156e23
156e23
		if (lfdctx.fdout < 0)
156e23
			return tpax_driver_error_archive_path(
156e23
				fdctx->fderr,
156e23
				program,archive->arg,
156e23
				meta,true);
156e23
156e23
		fdctx = &lfdctx;
156e23
156e23
	} else if (archive) {
156e23
		memcpy(&lfdctx,fdctx,sizeof(*fdctx));
156e23
df2d16
		cctx.srcflags = TPAX_SOURCE_DATA_PENDING;
df2d16
156e23
		lfdctx.fdin = openat(
156e23
			fdctx->fdcwd,
156e23
			archive->arg,
156e23
			O_RDONLY,0);
156e23
156e23
		if (lfdctx.fdin < 0)
156e23
			return tpax_driver_error_archive_path(
156e23
				fdctx->fderr,
156e23
				program,archive->arg,
156e23
				meta,false);
156e23
df2d16
		cctx.srcflags = TPAX_SOURCE_DATA_OPENED;
df2d16
156e23
		fdctx = &lfdctx;
df2d16
df2d16
	} else if (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_WRITE) {
df2d16
		cctx.srcflags = TPAX_SOURCE_DATA_NONE;
df2d16
df2d16
	} else {
df2d16
		cctx.srcflags = TPAX_SOURCE_DATA_PENDING;
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_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
8aa3fc
	/* archive format vs. execution mode*/
8aa3fc
	switch (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_MASK) {
8aa3fc
		case TPAX_DRIVER_EXEC_MODE_WRITE:
8aa3fc
			if (!(cctx.drvflags & TPAX_DRIVER_WRITE_FORMAT_MASK))
8aa3fc
				cctx.drvflags |= TPAX_DRIVER_WRITE_FORMAT_USTAR;
8aa3fc
			break;
8aa3fc
8aa3fc
		default:
8aa3fc
			if (cctx.drvflags & TPAX_DRIVER_WRITE_FORMAT_MASK)
8aa3fc
				return tpax_driver_usage_write_format(
8aa3fc
					fdctx->fderr,program,
8aa3fc
					entry->arg,optv,meta);
8aa3fc
	}
8aa3fc
8aa3fc
	/* not implemented archive format(s) */
8aa3fc
	switch (cctx.drvflags & TPAX_DRIVER_WRITE_FORMAT_MASK) {
8aa3fc
		case TPAX_DRIVER_WRITE_FORMAT_PAX:
8aa3fc
			return tpax_driver_error_not_implemented(
8aa3fc
				fdctx->fderr,program,"the pax format",meta);
8aa3fc
8aa3fc
		default:
8aa3fc
			break;
8aa3fc
	}
8aa3fc
8aa3fc
	/* driver ctx */
5ea1d7
	if (!(ctx = tpax_driver_ctx_alloc(meta,fdctx,&cctx,nunits,nreplstr,sreplstr))) {
893ea2
		if (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_COPY)
893ea2
			close(fddst);
893ea2
88751e
		return tpax_get_driver_ctx_fail(meta);
893ea2
	}
88751e
5ea1d7
	/* replstr validation and vector initialization */
5ea1d7
	if (tpax_init_replstr_vector(ctx,meta) < 0) {
5ea1d7
		tpax_lib_free_driver_ctx(&ctx->ctx);
5ea1d7
		return TPAX_ERROR;
5ea1d7
	}
5ea1d7
722538
	/* keyval validation */
722538
	for (pkeyval=ctx->keyvalv; pkeyval && *pkeyval; pkeyval++)
722538
		if (!tpax_driver_is_valid_keyval(*pkeyval))
722538
			return tpax_driver_keyval_error(ctx,*pkeyval,program);
722538
6a7e25
	/* source data mapping (non-critical) */
6a7e25
	if (cctx.srcflags) {
6a7e25
		if (fstat(fdctx->fdin,&ctx->srcstat) < 0)
6a7e25
			return tpax_driver_srcstat_error(ctx,archive,program);
6a7e25
6a7e25
		ctx->mapsize = ctx->srcstat.st_size;
6a7e25
		ctx->mapaddr = mmap(
6a7e25
			0,ctx->mapsize,
6a7e25
			PROT_READ,MAP_PRIVATE,
6a7e25
			fdctx->fdin,0);
6a7e25
6a7e25
		if (ctx->mapaddr == MAP_FAILED) {
6a7e25
			ctx->cctx.srcflags = TPAX_SOURCE_DATA_FILEIO;
6a7e25
			ctx->mapaddr       = 0;
6a7e25
			ctx->mapsize       = 0;
6a7e25
		} else {
6a7e25
			ctx->cctx.srcflags = TPAX_SOURCE_DATA_MAPPED;
6a7e25
		}
6a7e25
	}
6a7e25
57abba
	/* allocate source data cache as needed */
57abba
	if (ctx->cctx.srcflags == TPAX_SOURCE_DATA_FILEIO) {
57abba
		cachesize = TPAX_CACHE_MAX;
57abba
57abba
		for (; !ctx->cacheaddr && (cachesize >= TPAX_CACHE_MIN); )
57abba
			if (!(ctx->cacheaddr = calloc(cachesize,1)))
57abba
				cachesize >>= 1;
57abba
57abba
		if (!ctx->cacheaddr)
57abba
			return tpax_driver_cache_error(ctx,program);
57abba
57abba
		ctx->cachemark = ctx->cacheaddr;
57abba
		ctx->cachecap  = &ctx->cacheaddr[cachesize];
57abba
	}
57abba
6a7e25
	/* all done */
156e23
	if (archive) {
156e23
		ctx->file     = archive->arg;
156e23
		ctx->ctx.file = &ctx->file;
156e23
	}
156e23
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
{
d7918a
	void *  next;
d7918a
	size_t  size;
9f55a1
	char ** ppref;
d7918a
5ea1d7
	struct tpax_replstr * replstrv;
5ea1d7
d7918a
	for (; ictx->ctx.dirents; ) {
d7918a
		next = ictx->ctx.dirents->next;
d7918a
		size = ictx->ctx.dirents->size;
d7918a
d7918a
		munmap(ictx->ctx.dirents,size);
d7918a
		ictx->ctx.dirents = (struct tpax_dirent_buffer *)next;
d7918a
	}
d7918a
5ea1d7
	for (replstrv=ictx->ctx.replstrv; replstrv && replstrv->regexp; replstrv++)
5ea1d7
		regfree(&replstrv->regex);
5ea1d7
5ea1d7
	if (ictx->ctx.replstrv)
5ea1d7
		free(ictx->ctx.replstrv);
5ea1d7
5ea1d7
	if (ictx->ctx.replstrs)
5ea1d7
		free(ictx->ctx.replstrs);
5ea1d7
722538
	if (ictx->ctx.keyvalv)
722538
		free(ictx->ctx.keyvalv);
722538
57abba
	if (ictx->ctx.cacheaddr)
57abba
		free(ictx->ctx.cacheaddr);
57abba
6a7e25
	if (ictx->ctx.mapaddr)
6a7e25
		munmap(ictx->ctx.mapaddr,ictx->ctx.mapsize);
6a7e25
76f8c5
	if (ictx->ctx.bufaddr)
76f8c5
		munmap(ictx->ctx.bufaddr,ictx->ctx.bufsize);
76f8c5
d7918a
	if (ictx->ctx.dirbuff)
d7918a
		munmap(ictx->ctx.dirbuff,TPAX_DIRENT_BUFLEN);
d7918a
9f55a1
	for (ppref=ictx->ctx.prefixv; *ppref; ppref++)
9f55a1
		free(*ppref);
9f55a1
9f55a1
	if (ictx->ctx.prefixv != ictx->ctx.prefptr)
9f55a1
		free(ictx->ctx.prefixv);
9f55a1
acc91b
	if (ictx->ctx.direntv)
acc91b
		free(ictx->ctx.direntv);
acc91b
f66f53
	if (ictx->ctx.cpiov)
f66f53
		free(ictx->ctx.cpiov);
f66f53
88751e
	argv_free(ictx->meta);
88751e
	free(ictx);
88751e
}
88751e
c9eeca
void tpax_lib_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
537964
const struct tpax_source_version * tpax_api_source_version(void)
88751e
{
88751e
	return &tpax_src_version;
88751e
}
88751e
c9eeca
int tpax_lib_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
c9eeca
int tpax_lib_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
}