Blob Blame History Raw
/******************************************************/
/*  tpax: a topological pax implementation            */
/*  Copyright (C) 2020  Z. Gilboa                     */
/*  Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/******************************************************/

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <sys/stat.h>

#include <tpax/tpax.h>
#include <tpax/tpax_specs.h>
#include "tpax_driver_impl.h"
#include "tpax_tmpfile_impl.h"
#include "tpax_errinfo_impl.h"

#ifndef ssizeof
#define ssizeof(x) (ssize_t)(sizeof(x))
#endif

static int tpax_archive_append_memory_data(
	int                             fdout,
	void *                          buf,
	ssize_t                         nbytes)
{
	ssize_t ret;
	char  * ch;

	for (ch=buf; nbytes; ch+=ret) {
		ret = write(fdout,ch,nbytes);

		while ((ret < 0) && (errno == EINTR))
			ret = write(fdout,ch,nbytes);

		if (ret < 0)
			return ret;

		nbytes -= ret;
	}

	return 0;
}

static int tpax_archive_append_pad(
	int                     fdout,
	const struct stat *     st)
{
	ssize_t nbytes;
	char    buf[512];

	nbytes  = st->st_size;
	nbytes += 0x1ff;
	nbytes |= 0x1ff;
	nbytes ^= 0x1ff;
	nbytes -= st->st_size;

	memset(buf,0,nbytes);

	return tpax_archive_append_memory_data(
		fdout,buf,nbytes);
}

int tpax_archive_append(
	const struct tpax_driver_ctx *  dctx,
	const struct tpax_unit_ctx *    uctx)
{
	struct tpax_ustar_header        uhdr;
	int                             fdout;
	int                             fdtmp;
	ssize_t                         nread;
	ssize_t                         nbytes;
	void *                          buf;
	size_t                          buflen;
	size_t                          cmplen;
	void *				membuf;
	char                            sbuf[2048];

	/* record errors */
	tpax_driver_set_ectx(
		dctx,0,
		*uctx->path);

	/* driver */
	fdout = tpax_driver_fdout(dctx);

	/* header */
	if (tpax_init_ustar_header(
			dctx,*uctx->path,uctx->st,
			*uctx->link,&uhdr) < 0)
		return TPAX_NESTED_ERROR(dctx);

	/* buffer  */
	membuf = 0;
	fdtmp  = -1;

	if ((buf = tpax_get_driver_anon_map_addr(dctx,&buflen)))
		if (buflen >= (cmplen = uctx->st->st_size))
			membuf = buf;

	if (ssizeof(sbuf) >= (uctx->st->st_size))
		membuf = sbuf;

	/* snapshot */
	if (membuf) {
		if (tpax_file_create_memory_snapshot(
				dctx,*uctx->path,
				uctx->st,membuf) < 0)
			return TPAX_NESTED_ERROR(dctx);
	} else {
		if ((fdtmp = tpax_file_create_tmpfs_snapshot(
				dctx,*uctx->path,
				uctx->st)) < 0)
			return TPAX_NESTED_ERROR(dctx);

		if (lseek(fdtmp,0,SEEK_SET) < 0)
			return TPAX_SYSTEM_ERROR(dctx);
	}

	/* append header */
	if (tpax_archive_append_memory_data(fdout,&uhdr,ssizeof(uhdr)) < 0) {
		if (fdtmp >= 0)
			close(fdtmp);

		return TPAX_SYSTEM_ERROR(dctx);
	}

	/* append data from snapshot */
	if (fdtmp >= 0) {
		if (!buf) {
			buf    = sbuf;
			buflen = sizeof(sbuf);
		}

		for (nread=0; nread<uctx->st->st_size; ) {
			nbytes = read(fdtmp,buf,buflen);

			while ((nbytes < 0) && (errno == EINTR))
				nbytes = read(fdtmp,buf,buflen);

			if (nbytes < 0) {
				close(fdtmp);
				return TPAX_SYSTEM_ERROR(dctx);

			} else if (nbytes == 0) {
				close(fdtmp);
				return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR);

			} else {
				nread += nbytes;
			}

			if (tpax_archive_append_memory_data(fdout,buf,nbytes) < 0) {
				close(fdtmp);
				return TPAX_SYSTEM_ERROR(dctx);
			}
		}

		close(fdtmp);
	} else {
		if (tpax_archive_append_memory_data(
				fdout,membuf,
				uctx->st->st_size) < 0)
			return TPAX_SYSTEM_ERROR(dctx);
	}

	return tpax_archive_append_pad(
		fdout,uctx->st);
}