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
f66f53
static const char tpax_cpio_trailer[11] = TPAX_CPIO_TRAILER;
f66f53
f66f53
static void tpax_cpio_octal_write(char * ch, ssize_t len, uint64_t val)
f66f53
{
f66f53
	for (; len; ) {
f66f53
		ch[--len] = val % 8 + '0';
f66f53
		val /= 8;
f66f53
	}
f66f53
}
f66f53
fb29e1
static int tpax_archive_append_memory_data(
fb29e1
	int                             fdout,
19fba4
	const void *                    buf,
fb29e1
	ssize_t                         nbytes)
fb29e1
{
19fba4
	ssize_t       ret;
19fba4
	const 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
f66f53
	if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO)
f66f53
		return 0;
f66f53
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(
e99de3
	int                             ret,
e99de3
	const struct tpax_driver_ctx *  dctx,
e99de3
	struct tpax_unit_ctx *          uctx)
5ce6ff
{
e99de3
	if (dctx->cctx->drvflags & TPAX_DRIVER_VERBOSE)
e99de3
		tpax_dprintf(tpax_driver_fderr(dctx),"\n",0);
e99de3
c9eeca
	tpax_lib_free_unit_ctx(uctx);
5ce6ff
	return ret;
5ce6ff
}
5ce6ff
5ea1d7
static int tpax_apply_string_replacement(
5ea1d7
	const struct tpax_driver_ctx *  dctx,
5ea1d7
	const char *                    path,
5ea1d7
	char *                          replbuf,
5ea1d7
	size_t                          buflen)
5ea1d7
{
5ea1d7
	int                            ret;
5ea1d7
	struct tpax_driver_ctx_impl *  ictx;
5ea1d7
	struct tpax_replstr *          replstrv;
5ea1d7
5ea1d7
	ictx = tpax_get_driver_ictx(dctx);
5ea1d7
5ea1d7
	if (!(replstrv = ictx->replstrv))
5ea1d7
		return 0;
5ea1d7
5ea1d7
	for (ret=0; !ret && replstrv->regexp; replstrv++) {
5ea1d7
		ret = tpax_util_path_replstr(
5ea1d7
			replbuf,path,
5ea1d7
			replstrv->replstr,
5ea1d7
			&replstrv->regex,
5ea1d7
			buflen,replstrv->flags);
5ea1d7
5ea1d7
		if ((ret > 0) && (replstrv->flags & TPAX_REPL_PRINT))
5ea1d7
			tpax_dprintf(tpax_driver_fderr(dctx),"%s >> %s\n",path,replbuf);
5ea1d7
	}
5ea1d7
5ea1d7
	return ret;
5ea1d7
}
5ea1d7
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
{
a749fd
	int                             ret;
5ce6ff
	struct tpax_unit_ctx *          uctx;
f66f53
	struct tpax_cpio_header         chdr;
5ce6ff
	struct tpax_ustar_header        uhdr;
b8874b
	const struct stat *             st;
37f513
	struct stat                     stbuf;
5ea1d7
	const  char *                   apath;
5ce6ff
	const  char *                   path;
b8874b
	const  char *                   slnk;
37f513
	const  char *                   mlnk;
5ce6ff
	off_t                           hpos;
5ce6ff
	off_t                           dpos;
5ce6ff
	int                             fdtmp;
5ea1d7
	int                             slen;
5ce6ff
	ssize_t                         nread;
5ce6ff
	ssize_t                         nbytes;
5ce6ff
	void *                          buf;
5ce6ff
	size_t                          buflen;
5ce6ff
	size_t                          cmplen;
5ce6ff
	void *				membuf;
37f513
	char *                          ch;
5ea1d7
	char                           	replbuf[PATH_MAX];
37f513
	char                           	pathbuf[PATH_MAX];
37f513
37f513
	/* followed symlink? */
37f513
	if (cdent->flags & TPAX_ITEM_NAMEREF)
37f513
		return 0;
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
5ea1d7
	/* regex matching and patter substitution */
5ea1d7
	if ((slen = tpax_apply_string_replacement(dctx,path,replbuf,PATH_MAX)) < 0)
5ea1d7
		return TPAX_CUSTOM_ERROR(
5ea1d7
			dctx,
5ea1d7
			TPAX_ERR_FLOW_ERROR);
5ea1d7
5ea1d7
	apath = slen ? replbuf : path;
5ea1d7
e99de3
	/* verbose mode */
e99de3
	if (dctx->cctx->drvflags & TPAX_DRIVER_VERBOSE)
5ea1d7
		tpax_dprintf(tpax_driver_fderr(dctx),"%s",apath);
e99de3
5ce6ff
	/* uctx */
c9eeca
	if (tpax_lib_get_unit_ctx(dctx,fdcwd,path,&uctx) < 0)
c1438f
		return tpax_archive_write_ret(
e99de3
			TPAX_NESTED_ERROR(dctx),
e99de3
			dctx,0);
5ce6ff
b8874b
	st   = uctx->st;
b8874b
	slnk = uctx->link[0];
37f513
	mlnk = 0;
37f513
37f513
	if (cdent->flags & TPAX_ITEM_SYMLINK) {
37f513
		st   = &stbuf;
37f513
		mlnk = slnk;
37f513
		slnk = 0;
37f513
		ch   = 0;
37f513
37f513
		if (mlnk[0] != '/') {
37f513
			if (strlen(path) >= PATH_MAX)
37f513
				return tpax_archive_write_ret(
37f513
					TPAX_CUSTOM_ERROR(
37f513
						dctx,
37f513
						TPAX_ERR_FLOW_ERROR),
e99de3
					dctx,uctx);
37f513
37f513
			strcpy(pathbuf,path);
37f513
37f513
			ch = strrchr(pathbuf,'/');
37f513
		}
37f513
37f513
		if (ch && (++ch - pathbuf >= PATH_MAX))
37f513
			return tpax_archive_write_ret(
37f513
				TPAX_CUSTOM_ERROR(
37f513
					dctx,
37f513
					TPAX_ERR_FLOW_ERROR),
e99de3
				dctx,uctx);
37f513
37f513
		if (ch) {
37f513
			strcpy(ch,mlnk);
37f513
			mlnk = pathbuf;
37f513
		}
37f513
37f513
		if (fstatat(fdcwd,mlnk,&stbuf,0) < 0)
37f513
			return tpax_archive_write_ret(
37f513
				TPAX_SYSTEM_ERROR(dctx),
e99de3
				dctx,uctx);
37f513
	}
b8874b
5ce6ff
	/* record errors */
5ce6ff
	tpax_driver_set_ectx(
5ce6ff
		dctx,0,path);
5ce6ff
f66f53
	/* header offset */
5ce6ff
	hpos = tpax_get_driver_cpos(dctx);
5ce6ff
5ce6ff
	/* header */
a749fd
	if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_RUSTAR) {
a749fd
		ret = tpax_meta_init_rustar_header(apath,st,slnk,&uhdr);
a749fd
a749fd
	} else if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_USTAR) {
a749fd
		ret = tpax_meta_init_ustar_header(apath,st,slnk,&uhdr);
f66f53
f66f53
	} else if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) {
f66f53
		ret = tpax_meta_init_cpio_header(
f66f53
			apath,st,slnk,
f66f53
			cdent->cpdev,
f66f53
			cdent->cpino,
f66f53
			cdent->nlink,
f66f53
			&chdr);
a749fd
	}
a749fd
a749fd
	if (ret < 0)
5ce6ff
		return tpax_archive_write_ret(
5ce6ff
			TPAX_NESTED_ERROR(dctx),
e99de3
			dctx,uctx);
5ce6ff
5ce6ff
	/* buffer  */
5ce6ff
	membuf = 0;
5ce6ff
	fdtmp  = -1;
5ce6ff
5ce6ff
	/* associated data? */
b8874b
	if S_ISREG(st->st_mode) {
5ce6ff
		buf = tpax_get_driver_anon_map_addr(
5ce6ff
			dctx,&buflen);
5ce6ff
b8874b
		if (buflen >= (cmplen = st->st_size))
5ce6ff
			membuf = buf;
5ce6ff
5ce6ff
		/* snapshot */
5ce6ff
		if (membuf) {
0ee4a8
			if (tpax_io_create_memory_snapshot(
37f513
					dctx,fdcwd,
37f513
					mlnk ? mlnk : path,
b8874b
					st,membuf) < 0)
5ce6ff
				return tpax_archive_write_ret(
5ce6ff
					TPAX_NESTED_ERROR(dctx),
e99de3
					dctx,uctx);
5ce6ff
		} else {
0ee4a8
			if ((fdtmp = tpax_io_create_tmpfs_snapshot(
37f513
					dctx,fdcwd,
37f513
					mlnk ? mlnk : path,
b8874b
					st)) < 0)
5ce6ff
				return tpax_archive_write_ret(
5ce6ff
					TPAX_NESTED_ERROR(dctx),
e99de3
					dctx,uctx);
5ce6ff
5ce6ff
			if (lseek(fdtmp,0,SEEK_SET) < 0)
5ce6ff
				return tpax_archive_write_ret(
5ce6ff
					TPAX_SYSTEM_ERROR(dctx),
e99de3
					dctx,uctx);
5ce6ff
		}
5ce6ff
	}
5ce6ff
f66f53
	/* append header (and cpio symbolic link data) */
f66f53
	if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) {
f66f53
		ret = tpax_archive_append_memory_data(
f66f53
			fdout,&chdr,
f66f53
			offsetof(struct tpax_cpio_header,c_namedata));
f66f53
f66f53
		if (ret == 0)
f66f53
			ret = tpax_archive_append_memory_data(
f66f53
				fdout,apath,
f66f53
				strlen(apath) + 1);
f66f53
f66f53
		if ((ret == 0) && slnk)
f66f53
			ret = tpax_archive_append_memory_data(
f66f53
				fdout,slnk,
f66f53
				strlen(slnk));
f66f53
	} else {
f66f53
		ret = tpax_archive_append_memory_data(
f66f53
			fdout,&uhdr,
f66f53
			ssizeof(uhdr));
f66f53
	}
f66f53
f66f53
	if (ret < 0) {
5ce6ff
		if (fdtmp >= 0)
5ce6ff
			close(fdtmp);
5ce6ff
5ce6ff
		return tpax_archive_write_ret(
5ce6ff
			TPAX_SYSTEM_ERROR(dctx),
e99de3
			dctx,uctx);
5ce6ff
	}
5ce6ff
f66f53
	/* data offset */
f66f53
	if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) {
f66f53
		dpos  = hpos;
f66f53
		dpos += offsetof(struct tpax_cpio_header,c_namedata);
f66f53
		dpos += strlen(apath) + 1;
f66f53
		dpos += slnk ? strlen(slnk) : 0;
f66f53
	} else {
f66f53
		dpos  = hpos + ssizeof(uhdr);
f66f53
	}
f66f53
5ce6ff
	tpax_set_driver_cpos(dctx,dpos);
5ce6ff
5ce6ff
	/* all done? */
e99de3
	if (!(S_ISREG(st->st_mode)))
e99de3
		return tpax_archive_write_ret(
e99de3
			0,dctx,uctx);
5ce6ff
5ce6ff
	/* append data from snapshot */
5ce6ff
	if (fdtmp >= 0) {
5ce6ff
		buf = tpax_get_driver_anon_map_addr(
5ce6ff
			dctx,&buflen);
5ce6ff
b8874b
		for (nread=0; nread<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),
e99de3
					dctx,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),
e99de3
					dctx,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),
e99de3
					dctx,uctx);
5ce6ff
			}
5ce6ff
		}
5ce6ff
5ce6ff
		close(fdtmp);
5ce6ff
	} else {
5ce6ff
		if (tpax_archive_append_memory_data(
5ce6ff
				fdout,membuf,
b8874b
				st->st_size) < 0)
5ce6ff
			return tpax_archive_write_ret(
5ce6ff
				TPAX_SYSTEM_ERROR(dctx),
e99de3
				dctx,uctx);
5ce6ff
	}
5ce6ff
5ce6ff
	return tpax_archive_write_ret(
b8874b
		tpax_archive_append_pad(dctx,fdout,st),
e99de3
		dctx,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
fa0b03
static int tpax_archive_seal_cpio(const struct tpax_driver_ctx * dctx)
fa0b03
{
f66f53
	int                             fdout;
f66f53
	off_t                           cpos;
f66f53
	ssize_t                         nbytes;
f66f53
	ssize_t                         blksize;
f66f53
	ssize_t                         nwritten;
f66f53
	char                            buf[1024];
f66f53
	struct tpax_cpio_header         chdr;
f66f53
f66f53
	/* trailer initialization */
f66f53
	memset(buf,0,sizeof(buf));
f66f53
	memset(&chdr,0,sizeof(chdr));
f66f53
f66f53
	tpax_cpio_octal_write(chdr.c_magic,sizeof(chdr.c_magic),0070707);
f66f53
	tpax_cpio_octal_write(chdr.c_dev,sizeof(chdr.c_dev),0);
f66f53
	tpax_cpio_octal_write(chdr.c_ino,sizeof(chdr.c_ino),0);
f66f53
	tpax_cpio_octal_write(chdr.c_mode,sizeof(chdr.c_mode),0);
f66f53
	tpax_cpio_octal_write(chdr.c_uid,sizeof(chdr.c_uid),0);
f66f53
	tpax_cpio_octal_write(chdr.c_gid,sizeof(chdr.c_gid),0);
f66f53
	tpax_cpio_octal_write(chdr.c_nlink,sizeof(chdr.c_nlink),1);
f66f53
	tpax_cpio_octal_write(chdr.c_rdev,sizeof(chdr.c_rdev),0);
f66f53
	tpax_cpio_octal_write(chdr.c_mtime,sizeof(chdr.c_mtime),0);
f66f53
f66f53
	tpax_cpio_octal_write(chdr.c_filesize,sizeof(chdr.c_filesize),0);
f66f53
	tpax_cpio_octal_write(chdr.c_namesize,sizeof(chdr.c_namesize),sizeof(tpax_cpio_trailer));
f66f53
f66f53
	/* fdout, archive block size, current position */
f66f53
	fdout   = tpax_driver_fdout(dctx);
f66f53
	blksize = dctx->cctx->blksize;
f66f53
	cpos    = tpax_get_driver_cpos(dctx);
f66f53
f66f53
	/* trailer header */
f66f53
	nbytes = offsetof(struct tpax_cpio_header,c_namedata);
f66f53
f66f53
	if (tpax_archive_append_memory_data(fdout,&chdr,nbytes) < 0)
f66f53
		return TPAX_SYSTEM_ERROR(dctx);
f66f53
f66f53
	cpos += nbytes;
f66f53
f66f53
	/* trailer c_namedata[] */
f66f53
	nbytes = sizeof(tpax_cpio_trailer);
f66f53
f66f53
	if (tpax_archive_append_memory_data(fdout,tpax_cpio_trailer,nbytes) < 0)
f66f53
		return TPAX_SYSTEM_ERROR(dctx);
f66f53
f66f53
	cpos += nbytes;
f66f53
f66f53
	/* pad the current block to a 512 byte boundary */
f66f53
	nbytes = (cpos % 512) ? 512 - (cpos % 512) : 0;
f66f53
f66f53
	if (tpax_archive_append_memory_data(fdout,buf,nbytes) < 0)
f66f53
		return TPAX_SYSTEM_ERROR(dctx);
f66f53
f66f53
	cpos += nbytes;
f66f53
f66f53
	/* already at block boundary? */
f66f53
	if ((cpos % blksize) == 0) {
f66f53
		tpax_set_driver_cpos(dctx,cpos);
f66f53
		return 0;
f66f53
	}
f66f53
f66f53
	/* zero-fill the remainder of the block */
f66f53
	nbytes  = cpos / blksize;
f66f53
	nbytes *= blksize;
f66f53
	nbytes += blksize;
f66f53
f66f53
	for (nwritten=cpos; nwritten
f66f53
		if (tpax_archive_append_memory_data(fdout,buf,512) < 0)
f66f53
			return TPAX_SYSTEM_ERROR(dctx);
f66f53
f66f53
	/* all done */
f66f53
	tpax_set_driver_cpos(dctx,nwritten);
f66f53
f66f53
	return 0;
fa0b03
}
fa0b03
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;
fa0b03
	char    buf[1024];
fb29e1
f66f53
	/* cpio trailer? */
f66f53
	if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO)
f66f53
		return tpax_archive_seal_cpio(dctx);
f66f53
fa0b03
	/* archive block size, current position */
333924
	blksize = dctx->cctx->blksize;
fb29e1
	cpos    = tpax_get_driver_cpos(dctx);
fb29e1
fa0b03
	/* internal error? */
fb29e1
	if (cpos % 512)
fb29e1
		return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR);
fb29e1
fa0b03
	/* ustar, pax */
fb29e1
	fdout = tpax_driver_fdout(dctx);
fb29e1
	memset(buf,0,sizeof(buf));
fb29e1
fa0b03
	if (tpax_archive_append_memory_data(fdout,buf,2*512) < 0)
fa0b03
		return TPAX_SYSTEM_ERROR(dctx);
fb29e1
fa0b03
	cpos += 2*512;
fb29e1
fa0b03
	/* pax? */
fa0b03
	if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_PAX) {
fa0b03
		tpax_set_driver_cpos(dctx,cpos);
fa0b03
		return 0;
fb29e1
	}
fb29e1
fa0b03
	/* already at block boundary? */
fa0b03
	if ((cpos % blksize) == 0) {
fa0b03
		tpax_set_driver_cpos(dctx,cpos);
fa0b03
		return 0;
fa0b03
	}
fa0b03
fa0b03
	/* zero-fill the remainder of the block */
fa0b03
	nbytes  = cpos / blksize;
fa0b03
	nbytes *= blksize;
fa0b03
	nbytes += blksize;
fa0b03
fa0b03
	for (nwritten=cpos; nwritten
fb29e1
		if (tpax_archive_append_memory_data(fdout,buf,512) < 0)
fb29e1
			return TPAX_SYSTEM_ERROR(dctx);
fb29e1
fa0b03
	/* all done */
fa0b03
	tpax_set_driver_cpos(dctx,nwritten);
fb29e1
fb29e1
	return 0;
fb29e1
}