Blame src/logic/tpax_archive_write.c

fb29e1
/**************************************************************/
fb29e1
/*  tpax: a topological pax implementation                    */
fb29e1
/*  Copyright (C) 2020--2024  SysDeer Technologies, LLC       */
fb29e1
/*  Released under GPLv2 and GPLv3; see COPYING.TPAX.         */
fb29e1
/**************************************************************/
fb29e1
fb29e1
#include <tpax/tpax.h>
fb29e1
#include "tpax_driver_impl.h"
fb29e1
#include "tpax_errinfo_impl.h"
fb29e1
#include "tpax_visibility_impl.h"
fb29e1
5ce6ff
#ifndef ssizeof
5ce6ff
#define ssizeof(x) (ssize_t)(sizeof(x))
5ce6ff
#endif
5ce6ff
fb29e1
static int tpax_archive_append_memory_data(
fb29e1
	int                             fdout,
fb29e1
	void *                          buf,
fb29e1
	ssize_t                         nbytes)
fb29e1
{
fb29e1
	ssize_t ret;
fb29e1
	char  * ch;
fb29e1
fb29e1
	for (ch=buf; nbytes; ch+=ret) {
fb29e1
		ret = write(fdout,ch,nbytes);
fb29e1
fb29e1
		while ((ret < 0) && (errno == EINTR))
fb29e1
			ret = write(fdout,ch,nbytes);
fb29e1
fb29e1
		if (ret < 0)
fb29e1
			return ret;
fb29e1
fb29e1
		nbytes -= ret;
fb29e1
	}
fb29e1
fb29e1
	return 0;
fb29e1
}
fb29e1
fb29e1
static int tpax_archive_append_pad(
fb29e1
	const struct tpax_driver_ctx *  dctx,
fb29e1
	int                             fdout,
fb29e1
	const struct stat *             st)
fb29e1
{
fb29e1
	int     ret;
fb29e1
	off_t   cpos;
fb29e1
	ssize_t nbytes;
fb29e1
	char    buf[512];
fb29e1
fb29e1
	nbytes  = st->st_size;
fb29e1
	nbytes += 0x1ff;
fb29e1
	nbytes |= 0x1ff;
fb29e1
	nbytes ^= 0x1ff;
fb29e1
	nbytes -= st->st_size;
fb29e1
fb29e1
	memset(buf,0,nbytes);
fb29e1
fb29e1
	cpos  = tpax_get_driver_cpos(dctx);
fb29e1
	cpos += st->st_size + nbytes;
fb29e1
fb29e1
	if (!(ret = tpax_archive_append_memory_data(fdout,buf,nbytes)))
fb29e1
		tpax_set_driver_cpos(dctx,cpos);
fb29e1
fb29e1
	return ret;
fb29e1
}
fb29e1
5ce6ff
static int tpax_archive_write_ret(
5ce6ff
	int                     ret,
5ce6ff
	struct tpax_unit_ctx *  uctx)
5ce6ff
{
c9eeca
	tpax_lib_free_unit_ctx(uctx);
5ce6ff
	return ret;
5ce6ff
}
5ce6ff
5ce6ff
static int tpax_archive_write_impl(
5ce6ff
	const struct tpax_driver_ctx *  dctx,
5ce6ff
	const struct tpax_dirent *      cdent,
5ce6ff
	int                             fdcwd,
5ce6ff
	int                             fdout)
5ce6ff
{
5ce6ff
	struct tpax_unit_ctx *          uctx;
5ce6ff
	struct tpax_ustar_header        uhdr;
5ce6ff
	const  char *                   path;
5ce6ff
	off_t                           hpos;
5ce6ff
	off_t                           dpos;
5ce6ff
	int                             fdtmp;
5ce6ff
	ssize_t                         nread;
5ce6ff
	ssize_t                         nbytes;
5ce6ff
	void *                          buf;
5ce6ff
	size_t                          buflen;
5ce6ff
	size_t                          cmplen;
5ce6ff
	void *				membuf;
5ce6ff
5ce6ff
	/* full path */
5ce6ff
	if (!(path = tpax_queue_item_full_path(dctx,cdent)))
5ce6ff
		return TPAX_CUSTOM_ERROR(
5ce6ff
			dctx,
5ce6ff
			TPAX_ERR_FLOW_ERROR);
5ce6ff
5ce6ff
	/* uctx */
c9eeca
	if (tpax_lib_get_unit_ctx(dctx,fdcwd,path,&uctx) < 0)
5ce6ff
		return TPAX_NESTED_ERROR(dctx);
5ce6ff
5ce6ff
	/* record errors */
5ce6ff
	tpax_driver_set_ectx(
5ce6ff
		dctx,0,path);
5ce6ff
5ce6ff
	/* header and data offsets: todo pax and cpio */
5ce6ff
	hpos = tpax_get_driver_cpos(dctx);
5ce6ff
	dpos = hpos + sizeof(uhdr);
5ce6ff
5ce6ff
	/* header */
4cb6ae
	if (tpax_meta_init_ustar_header(
5ce6ff
			dctx,path,uctx->st,
5ce6ff
			*uctx->link,&uhdr) < 0)
5ce6ff
		return tpax_archive_write_ret(
5ce6ff
			TPAX_NESTED_ERROR(dctx),
5ce6ff
			uctx);
5ce6ff
5ce6ff
	/* buffer  */
5ce6ff
	membuf = 0;
5ce6ff
	fdtmp  = -1;
5ce6ff
5ce6ff
	/* associated data? */
5ce6ff
	if S_ISREG(uctx->st->st_mode) {
5ce6ff
		buf = tpax_get_driver_anon_map_addr(
5ce6ff
			dctx,&buflen);
5ce6ff
5ce6ff
		if (buflen >= (cmplen = uctx->st->st_size))
5ce6ff
			membuf = buf;
5ce6ff
5ce6ff
		/* snapshot */
5ce6ff
		if (membuf) {
0ee4a8
			if (tpax_io_create_memory_snapshot(
5ce6ff
					dctx,fdcwd,path,
5ce6ff
					uctx->st,membuf) < 0)
5ce6ff
				return tpax_archive_write_ret(
5ce6ff
					TPAX_NESTED_ERROR(dctx),
5ce6ff
					uctx);
5ce6ff
		} else {
0ee4a8
			if ((fdtmp = tpax_io_create_tmpfs_snapshot(
5ce6ff
					dctx,fdcwd,path,
5ce6ff
					uctx->st)) < 0)
5ce6ff
				return tpax_archive_write_ret(
5ce6ff
					TPAX_NESTED_ERROR(dctx),
5ce6ff
					uctx);
5ce6ff
5ce6ff
			if (lseek(fdtmp,0,SEEK_SET) < 0)
5ce6ff
				return tpax_archive_write_ret(
5ce6ff
					TPAX_SYSTEM_ERROR(dctx),
5ce6ff
					uctx);
5ce6ff
		}
5ce6ff
	}
5ce6ff
5ce6ff
	/* append header */
5ce6ff
	if (tpax_archive_append_memory_data(fdout,&uhdr,ssizeof(uhdr)) < 0) {
5ce6ff
		if (fdtmp >= 0)
5ce6ff
			close(fdtmp);
5ce6ff
5ce6ff
		return tpax_archive_write_ret(
5ce6ff
			TPAX_SYSTEM_ERROR(dctx),
5ce6ff
			uctx);
5ce6ff
	}
5ce6ff
5ce6ff
	tpax_set_driver_cpos(dctx,dpos);
5ce6ff
5ce6ff
	/* all done? */
5ce6ff
	if (!(S_ISREG(uctx->st->st_mode))) {
c9eeca
		tpax_lib_free_unit_ctx(uctx);
5ce6ff
		return 0;
5ce6ff
	}
5ce6ff
5ce6ff
	/* append data from snapshot */
5ce6ff
	if (fdtmp >= 0) {
5ce6ff
		buf = tpax_get_driver_anon_map_addr(
5ce6ff
			dctx,&buflen);
5ce6ff
5ce6ff
		for (nread=0; nread<uctx->st->st_size; ) {
5ce6ff
			nbytes = read(fdtmp,buf,buflen);
5ce6ff
5ce6ff
			while ((nbytes < 0) && (errno == EINTR))
5ce6ff
				nbytes = read(fdtmp,buf,buflen);
5ce6ff
5ce6ff
			if (nbytes < 0) {
5ce6ff
				close(fdtmp);
5ce6ff
				return tpax_archive_write_ret(
5ce6ff
					TPAX_SYSTEM_ERROR(dctx),
5ce6ff
					uctx);
5ce6ff
5ce6ff
			} else if (nbytes == 0) {
5ce6ff
				close(fdtmp);
5ce6ff
				return tpax_archive_write_ret(
5ce6ff
					TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR),
5ce6ff
					uctx);
5ce6ff
5ce6ff
			} else {
5ce6ff
				nread += nbytes;
5ce6ff
			}
5ce6ff
5ce6ff
			if (tpax_archive_append_memory_data(fdout,buf,nbytes) < 0) {
5ce6ff
				close(fdtmp);
5ce6ff
				return tpax_archive_write_ret(
5ce6ff
					TPAX_SYSTEM_ERROR(dctx),
5ce6ff
					uctx);
5ce6ff
			}
5ce6ff
		}
5ce6ff
5ce6ff
		close(fdtmp);
5ce6ff
	} else {
5ce6ff
		if (tpax_archive_append_memory_data(
5ce6ff
				fdout,membuf,
5ce6ff
				uctx->st->st_size) < 0)
5ce6ff
			return tpax_archive_write_ret(
5ce6ff
				TPAX_SYSTEM_ERROR(dctx),
5ce6ff
				uctx);
5ce6ff
	}
5ce6ff
5ce6ff
	return tpax_archive_write_ret(
5ce6ff
		tpax_archive_append_pad(dctx,fdout,uctx->st),
5ce6ff
		uctx);
5ce6ff
}
5ce6ff
5ce6ff
int tpax_archive_write(const struct tpax_driver_ctx * dctx)
5ce6ff
{
5ce6ff
	struct tpax_driver_ctx_impl *   ictx;
5ce6ff
	struct tpax_dirent **           direntv;
5ce6ff
	int                             fdcwd;
5ce6ff
	int                             fdout;
5ce6ff
5ce6ff
	/* driver */
5ce6ff
	ictx  = tpax_get_driver_ictx(dctx);
5ce6ff
	fdcwd = tpax_driver_fdcwd(dctx);
5ce6ff
	fdout = tpax_driver_fdout(dctx);
5ce6ff
5ce6ff
	/* quote to archive */
5ce6ff
	if (tpax_update_queue_vector(dctx) < 0)
5ce6ff
		return TPAX_NESTED_ERROR(dctx);
5ce6ff
5ce6ff
	for (direntv=ictx->direntv; *direntv; direntv++)
5ce6ff
		if (tpax_archive_write_impl(dctx,*direntv,fdcwd,fdout) < 0)
5ce6ff
			return TPAX_NESTED_ERROR(dctx);
5ce6ff
5ce6ff
	return 0;
5ce6ff
}
5ce6ff
fb29e1
int tpax_archive_seal(const struct tpax_driver_ctx * dctx)
fb29e1
{
fb29e1
	int     fdout;
fb29e1
	off_t   cpos;
fb29e1
	ssize_t nbytes;
fb29e1
	ssize_t nwritten;
fb29e1
	ssize_t blksize;
fb29e1
	char    buf[512];
fb29e1
fb29e1
	blksize = tpax_get_archive_block_size(dctx);
fb29e1
	cpos    = tpax_get_driver_cpos(dctx);
fb29e1
fb29e1
	if (cpos % 512)
fb29e1
		return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR);
fb29e1
fb29e1
	fdout = tpax_driver_fdout(dctx);
fb29e1
	memset(buf,0,sizeof(buf));
fb29e1
fb29e1
	switch (cpos % blksize) {
fb29e1
		case 0:
fb29e1
			nbytes = cpos + blksize;
fb29e1
			break;
fb29e1
fb29e1
		default:
fb29e1
			nbytes  = cpos / blksize;
fb29e1
			nbytes *= blksize;
fb29e1
			nbytes += blksize;
fb29e1
fb29e1
			if (nbytes-cpos == 512)
fb29e1
				nbytes += blksize;
fb29e1
	}
fb29e1
fb29e1
	for (nwritten=cpos; nwritten
fb29e1
		if (tpax_archive_append_memory_data(fdout,buf,512) < 0)
fb29e1
			return TPAX_SYSTEM_ERROR(dctx);
fb29e1
fb29e1
		tpax_set_driver_cpos(dctx,nwritten);
fb29e1
	}
fb29e1
fb29e1
	return 0;
fb29e1
}