Blame src/logic/tpax_archive_append.c

5874a9
/**************************************************************/
5874a9
/*  tpax: a topological pax implementation                    */
5874a9
/*  Copyright (C) 2020--2021  SysDeer Technologies, LLC       */
5874a9
/*  Released under GPLv2 and GPLv3; see COPYING.TPAX.         */
5874a9
/**************************************************************/
3db888
3db888
#include <stdint.h>
3db888
#include <stdlib.h>
3db888
#include <string.h>
3db888
#include <unistd.h>
3db888
#include <fcntl.h>
3db888
#include <errno.h>
3db888
#include <grp.h>
3db888
#include <pwd.h>
022508
#include <sys/mman.h>
3db888
#include <sys/stat.h>
3db888
3db888
#include <tpax/tpax.h>
3db888
#include <tpax/tpax_specs.h>
3db888
#include "tpax_driver_impl.h"
022508
#include "tpax_getdents_impl.h"
3db888
#include "tpax_tmpfile_impl.h"
3db888
#include "tpax_errinfo_impl.h"
3db888
3db888
#ifndef ssizeof
3db888
#define ssizeof(x) (ssize_t)(sizeof(x))
3db888
#endif
3db888
3db888
static int tpax_archive_append_memory_data(
3db888
	int                             fdout,
3db888
	void *                          buf,
3db888
	ssize_t                         nbytes)
3db888
{
3db888
	ssize_t ret;
3db888
	char  * ch;
3db888
3db888
	for (ch=buf; nbytes; ch+=ret) {
3db888
		ret = write(fdout,ch,nbytes);
3db888
3db888
		while ((ret < 0) && (errno == EINTR))
3db888
			ret = write(fdout,ch,nbytes);
3db888
3db888
		if (ret < 0)
3db888
			return ret;
3db888
3db888
		nbytes -= ret;
3db888
	}
3db888
3db888
	return 0;
3db888
}
3db888
3db888
static int tpax_archive_append_pad(
409008
	const struct tpax_driver_ctx *  dctx,
409008
	int                             fdout,
409008
	const struct stat *             st)
3db888
{
409008
	int     ret;
409008
	off_t   cpos;
3db888
	ssize_t nbytes;
3db888
	char    buf[512];
3db888
3db888
	nbytes  = st->st_size;
3db888
	nbytes += 0x1ff;
3db888
	nbytes |= 0x1ff;
3db888
	nbytes ^= 0x1ff;
3db888
	nbytes -= st->st_size;
3db888
3db888
	memset(buf,0,nbytes);
3db888
409008
	cpos  = tpax_get_driver_cpos(dctx);
409008
	cpos += st->st_size + nbytes;
409008
409008
	if (!(ret = tpax_archive_append_memory_data(fdout,buf,nbytes)))
409008
		tpax_set_driver_cpos(dctx,cpos);
409008
409008
	return ret;
3db888
}
3db888
022508
static struct tpax_dirent_buffer * tpax_dirent_buf_first_alloc(
022508
	const struct tpax_driver_ctx * dctx)
022508
{
022508
	void * addr;
022508
	struct tpax_driver_ctx_impl * ictx;
022508
022508
	addr = (struct tpax_dirent_buffer *)mmap(
022508
		0,TPAX_DIRENT_BUFLEN,
022508
		PROT_READ|PROT_WRITE,
022508
		MAP_PRIVATE|MAP_ANONYMOUS,
022508
		-1,0);
022508
022508
	if (addr == MAP_FAILED)
022508
		return 0;
022508
022508
	ictx                  = tpax_get_driver_ictx(dctx);
022508
	ictx->dirents         = (struct tpax_dirent_buffer *)addr;
022508
	ictx->dirents->cdent  = ictx->dirents->dbuf;
022508
022508
	ictx->dirents->size   = TPAX_DIRENT_BUFLEN;
022508
	ictx->dirents->next   = 0;
022508
022508
	ictx->dirents->nfree  = TPAX_DIRENT_BUFLEN;
022508
	ictx->dirents->nfree -= offsetof(struct tpax_dirent_buffer,dbuf);
022508
022508
	return ictx->dirents;
022508
}
022508
022508
static struct tpax_dirent_buffer * tpax_dirent_buf_next_alloc(
022508
	struct tpax_dirent_buffer * current)
022508
{
022508
	void * addr;
022508
022508
	addr = (struct tpax_dirent_buffer *)mmap(
022508
		0,TPAX_DIRENT_BUFLEN,
022508
		PROT_READ|PROT_WRITE,
022508
		MAP_PRIVATE|MAP_ANONYMOUS,
022508
		-1,0);
022508
022508
	if (addr == MAP_FAILED)
022508
		return 0;
022508
022508
	current->next         = (struct tpax_dirent_buffer *)addr;
022508
	current->next->cdent  = current->next->dbuf;
022508
022508
	current->next->size   = TPAX_DIRENT_BUFLEN;
022508
	current->next->next   = 0;
022508
022508
	current->next->nfree  = TPAX_DIRENT_BUFLEN;
022508
	current->next->nfree -= offsetof(struct tpax_dirent_buffer,dbuf);
022508
022508
	return current->next;
022508
}
022508
022508
static int tpax_archive_append_ret(
022508
	int                     ret,
022508
	struct tpax_unit_ctx *  unit)
022508
{
022508
	if (unit)
022508
		tpax_free_unit_ctx(unit);
022508
022508
	return ret;
022508
}
022508
022508
static int tpax_archive_append_one(
3db888
	const struct tpax_driver_ctx *  dctx,
022508
	const struct tpax_unit_ctx *    uctx,
022508
	const struct dirent *           dent,
022508
	int                             fdat,
022508
	const char *                    prefix,
022508
	const struct tpax_dirent *      parent,
022508
	const char *                    pdir)
3db888
{
022508
	struct tpax_unit_ctx *          unit;
3db888
	struct tpax_ustar_header        uhdr;
409008
	off_t                           hpos;
409008
	off_t                           dpos;
3db888
	int                             fdout;
3db888
	int                             fdtmp;
3db888
	ssize_t                         nread;
3db888
	ssize_t                         nbytes;
3db888
	void *                          buf;
3db888
	size_t                          buflen;
3db888
	size_t                          cmplen;
3db888
	void *				membuf;
022508
	size_t                          nlen;
022508
	const char *			path;
022508
	const char *                    src;
022508
	char *                          dst;
022508
	char				pbuf[1024];
3db888
022508
	/* fake uctx for recursion items */
022508
	unit = 0;
022508
022508
	if (dent && tpax_get_unit_ctx(
022508
			dctx,fdat,dent->d_name,
022508
			&unit) < 0)
022508
		return TPAX_NESTED_ERROR(dctx);
022508
022508
	uctx = dent ? unit : uctx;
022508
022508
	/* prefixed path */
022508
	if (!prefix && !parent && !pdir) {
022508
		path = *uctx->path;
022508
022508
	} else {
022508
		nlen  = strlen(*uctx->path);
022508
		nlen += prefix ? strlen(prefix) + 1 : 0;
022508
		nlen += parent ? strlen(parent->dirent.d_name) + 1 : 0;
022508
022508
		if (nlen >= sizeof(pbuf))
022508
			return TPAX_BUFFER_ERROR(dctx);
022508
022508
		dst = pbuf;
022508
022508
		if (prefix) {
022508
			src = prefix;
022508
022508
			for (; *src; )
022508
				*dst++ = *src++;
022508
022508
			if (dst[-1] != '/')
022508
				*dst++ = '/';
022508
		}
022508
022508
		if (parent) {
022508
			src = parent->dirent.d_name;
022508
022508
			for (; *src; )
022508
				*dst++ = *src++;
022508
022508
			*dst++ = '/';
022508
		}
022508
022508
		if (pdir) {
022508
			src = pdir;
022508
022508
			for (; *src; )
022508
				*dst++ = *src++;
022508
022508
			*dst++ = '/';
022508
		}
022508
022508
		src = *uctx->path;
022508
022508
		for (; *src; )
022508
			*dst++ = *src++;
022508
022508
		*dst = 0;
022508
		path = pbuf;
022508
	}
022508
3db888
	/* record errors */
3db888
	tpax_driver_set_ectx(
022508
		dctx,0,path);
3db888
3db888
	/* driver */
3db888
	fdout = tpax_driver_fdout(dctx);
3db888
409008
	/* header and data offsets: todo pax and cpio */
409008
	hpos = tpax_get_driver_cpos(dctx);
409008
	dpos = hpos + sizeof(uhdr);
409008
3db888
	/* header */
3db888
	if (tpax_init_ustar_header(
022508
			dctx,path,uctx->st,
3db888
			*uctx->link,&uhdr) < 0)
022508
		return tpax_archive_append_ret(
022508
			TPAX_NESTED_ERROR(dctx),
022508
			unit);
3db888
3db888
	/* buffer  */
3db888
	membuf = 0;
3db888
	fdtmp  = -1;
3db888
af4988
	/* associated data? */
af4988
	if S_ISREG(uctx->st->st_mode) {
d816c0
		buf = tpax_get_driver_anon_map_addr(
d816c0
			dctx,&buflen);
af4988
d816c0
		if (buflen >= (cmplen = uctx->st->st_size))
d816c0
			membuf = buf;
af4988
af4988
		/* snapshot */
af4988
		if (membuf) {
af4988
			if (tpax_file_create_memory_snapshot(
a2aed5
					dctx,fdat,*uctx->path,
af4988
					uctx->st,membuf) < 0)
022508
				return tpax_archive_append_ret(
022508
					TPAX_NESTED_ERROR(dctx),
022508
					unit);
af4988
		} else {
af4988
			if ((fdtmp = tpax_file_create_tmpfs_snapshot(
a2aed5
					dctx,fdat,*uctx->path,
af4988
					uctx->st)) < 0)
022508
				return tpax_archive_append_ret(
022508
					TPAX_NESTED_ERROR(dctx),
022508
					unit);
af4988
af4988
			if (lseek(fdtmp,0,SEEK_SET) < 0)
022508
				return tpax_archive_append_ret(
022508
					TPAX_SYSTEM_ERROR(dctx),
022508
					unit);
af4988
		}
3db888
	}
3db888
3db888
	/* append header */
3db888
	if (tpax_archive_append_memory_data(fdout,&uhdr,ssizeof(uhdr)) < 0) {
3db888
		if (fdtmp >= 0)
3db888
			close(fdtmp);
3db888
022508
		return tpax_archive_append_ret(
022508
			TPAX_SYSTEM_ERROR(dctx),
022508
			unit);
3db888
	}
3db888
409008
	tpax_set_driver_cpos(dctx,dpos);
409008
af4988
	/* all done? */
022508
	if (!(S_ISREG(uctx->st->st_mode))) {
022508
		tpax_archive_append_ret(0,unit);
af4988
		return 0;
022508
	}
af4988
3db888
	/* append data from snapshot */
3db888
	if (fdtmp >= 0) {
d816c0
		buf = tpax_get_driver_anon_map_addr(
d816c0
			dctx,&buflen);
3db888
3db888
		for (nread=0; nread<uctx->st->st_size; ) {
3db888
			nbytes = read(fdtmp,buf,buflen);
3db888
3db888
			while ((nbytes < 0) && (errno == EINTR))
3db888
				nbytes = read(fdtmp,buf,buflen);
3db888
3db888
			if (nbytes < 0) {
3db888
				close(fdtmp);
022508
				return tpax_archive_append_ret(
022508
					TPAX_SYSTEM_ERROR(dctx),
022508
					unit);
3db888
3db888
			} else if (nbytes == 0) {
3db888
				close(fdtmp);
022508
				return tpax_archive_append_ret(
022508
					TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR),
022508
					unit);
3db888
3db888
			} else {
3db888
				nread += nbytes;
3db888
			}
3db888
3db888
			if (tpax_archive_append_memory_data(fdout,buf,nbytes) < 0) {
3db888
				close(fdtmp);
022508
				return tpax_archive_append_ret(
022508
					TPAX_SYSTEM_ERROR(dctx),
022508
					unit);
3db888
			}
3db888
		}
3db888
3db888
		close(fdtmp);
3db888
	} else {
3db888
		if (tpax_archive_append_memory_data(
3db888
				fdout,membuf,
3db888
				uctx->st->st_size) < 0)
022508
			return tpax_archive_append_ret(
022508
				TPAX_SYSTEM_ERROR(dctx),
022508
				unit);
022508
	}
022508
022508
	return tpax_archive_append_ret(
022508
		tpax_archive_append_pad(dctx,fdout,uctx->st),
022508
		unit);
022508
}
022508
022508
static int tpax_archive_append_dir(
022508
	const struct tpax_driver_ctx *  dctx,
022508
	const struct tpax_unit_ctx *    uctx,
022508
	struct tpax_dirent *            dent,
022508
	int                             fdat,
022508
	int                             depth,
022508
	const char *                    prefix,
022508
	const struct tpax_dirent *      parent)
022508
{
022508
	int                             fd;
022508
	bool                            fkeep;
022508
	long                            nbytes;
022508
	size_t                          needed;
022508
	struct dirent *                 dirent;
022508
	struct dirent *                 dirents;
022508
	struct tpax_dirent_buffer *     dentbuf;
022508
	struct tpax_dirent *            cdent;
022508
	struct tpax_unit_ctx *          unit;
022508
	struct stat                     st;
022508
	uintptr_t                       addr;
022508
	char *                          src;
022508
	char *                          dst;
022508
	char *                          cap;
022508
022508
	/* fake uctx for recursion items */
022508
	unit = 0;
022508
022508
	if (dent && tpax_get_unit_ctx(
022508
			dctx,dent->fdat,dent->dirent.d_name,
022508
			&unit) < 0)
022508
		return TPAX_NESTED_ERROR(dctx);
022508
022508
	uctx = dent ? unit : uctx;
022508
022508
	/* verify that recursion item is still a directory */
022508
	if (unit && !S_ISDIR(unit->st->st_mode))
022508
		return tpax_archive_append_ret(
022508
			TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR),
022508
			unit);
022508
022508
	/* append directory entry to archive */
022508
	if (tpax_archive_append_one(
022508
			dctx,uctx,0,
022508
			tpax_driver_fdcwd(dctx),
022508
			prefix,0,0) < 0)
022508
		return tpax_archive_append_ret(
022508
			TPAX_NESTED_ERROR(dctx),
022508
			unit);
022508
022508
	/* obtain buffer for file-system directory entries */
022508
	dirents = tpax_get_driver_getdents_buffer(dctx);
022508
	dirent  = dirents;
022508
	fkeep   = false;
022508
	nbytes  = 0;
022508
	depth++;
022508
022508
	/* open directory and obtain first directory entries */
022508
	if ((fd = openat(fdat,*uctx->path,O_RDONLY|O_DIRECTORY|O_CLOEXEC,0)) < 0)
022508
		return tpax_archive_append_ret(
022508
			TPAX_SYSTEM_ERROR(dctx),
022508
			unit);
022508
022508
	nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN);
022508
022508
	while ((nbytes == -EINTR) || ((nbytes < 0) && (errno == EINTR)))
022508
		nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN);
022508
022508
	if (nbytes < 0)
022508
		return tpax_archive_append_ret(
022508
			TPAX_SYSTEM_ERROR(dctx),
022508
			unit);
022508
022508
	/* iterate */
022508
	for (; nbytes>0; ) {
022508
		if (!strcmp(dirent->d_name,".")) {
022508
			(void)0;
022508
022508
		} else if (!strcmp(dirent->d_name,"..")) {
022508
			(void)0;
022508
022508
		} else {
022508
			if (dirent->d_type == DT_UNKNOWN) {
022508
				if (fstatat(fd,dirent->d_name,&st,AT_SYMLINK_NOFOLLOW))
022508
					return tpax_archive_append_ret(
022508
						TPAX_SYSTEM_ERROR(dctx),
022508
						unit);
022508
022508
				if (S_ISDIR(st.st_mode)) {
022508
					dirent->d_type = DT_DIR;
022508
				}
022508
			}
022508
022508
			if (dirent->d_type == DT_DIR) {
022508
				if (!(dentbuf = tpax_get_driver_dirents(dctx)))
022508
					if (!(dentbuf = tpax_dirent_buf_first_alloc(dctx)))
022508
						return tpax_archive_append_ret(
022508
							TPAX_SYSTEM_ERROR(dctx),
022508
							unit);
022508
022508
				needed  = dirent->d_reclen;
022508
				needed += offsetof(struct tpax_dirent,dirent);
022508
				needed += 0x7;
022508
				needed |= 0x7;
022508
				needed ^= 0x7;
022508
022508
				for (; dentbuf->next && (dentbuf->nfree < needed); )
022508
					dentbuf = dentbuf->next;
022508
022508
				if (dentbuf->nfree < needed)
022508
					if (!(dentbuf = tpax_dirent_buf_next_alloc(dentbuf)))
022508
						return tpax_archive_append_ret(
022508
							TPAX_SYSTEM_ERROR(dctx),
022508
							unit);
022508
022508
				fkeep = true;
022508
				cdent = dentbuf->cdent;
022508
022508
				cdent->fdat   = fd;
022508
				cdent->depth  = depth;
022508
				cdent->nsize  = needed;
022508
				cdent->parent = parent;
022508
022508
				memset(&cdent->dirent,0,offsetof(struct dirent,d_name));
022508
022508
				cdent->dirent.d_type   = dirent->d_type;
022508
				cdent->dirent.d_reclen = dirent->d_reclen;
022508
022508
				src  = dirent->d_name;
022508
				dst  = cdent->dirent.d_name;
022508
022508
				cap  = dst - offsetof(struct dirent,d_name);
022508
				cap -= offsetof(struct tpax_dirent,dirent);
022508
				cap += needed;
022508
022508
				for (; *src; )
022508
					*dst++ = *src++;
022508
022508
				for (; dst
022508
					*dst++ = 0;
022508
022508
				dentbuf->cdent  = (struct tpax_dirent *)cap;
022508
				dentbuf->nfree -= needed;
022508
			} else {
022508
				if (tpax_archive_append_one(
022508
						dctx,0,dirent,fd,prefix,
022508
						0,*uctx->path) < 0)
022508
					return tpax_archive_append_ret(
022508
						TPAX_NESTED_ERROR(dctx),
022508
						unit);
022508
			}
022508
		}
022508
022508
		addr    = (uintptr_t)dirent;
022508
		addr   += dirent->d_reclen;
022508
		nbytes -= dirent->d_reclen;
022508
		dirent  = (struct dirent *)addr;
022508
022508
		if (nbytes == 0) {
022508
			nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN);
022508
022508
			while ((nbytes == -EINTR) || ((nbytes < 0) && (errno == EINTR)))
022508
				nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN);
022508
022508
			if (nbytes < 0)
022508
				tpax_archive_append_ret(
022508
					TPAX_SYSTEM_ERROR(dctx),
022508
					unit);
022508
022508
			dirent = dirents;
022508
		}
3db888
	}
3db888
022508
	/* all done */
022508
	return tpax_archive_append_ret(
022508
		fkeep ? 0 : close(fd),
022508
		unit);
022508
}
022508
022508
static int tpax_archive_append_impl(
022508
	const struct tpax_driver_ctx *  dctx,
022508
	const struct tpax_unit_ctx *    uctx,
022508
	int                             depth,
022508
	const char *                    prefix,
022508
	struct tpax_dirent *            parent)
022508
{
022508
	if (S_ISDIR(uctx->st->st_mode))
022508
		if (dctx->cctx->drvflags & TPAX_DRIVER_DIR_MEMBER_RECURSE)
022508
			return tpax_archive_append_dir(
022508
				dctx,uctx,0,
022508
				tpax_driver_fdcwd(dctx),
022508
				depth,prefix,parent);
022508
022508
	return tpax_archive_append_one(
022508
			dctx,uctx,0,
022508
			tpax_driver_fdcwd(dctx),
022508
			prefix,0,0);
022508
}
022508
022508
int tpax_archive_append(
022508
	const struct tpax_driver_ctx *  dctx,
022508
	const struct tpax_unit_ctx *    uctx,
022508
	const char *                    prefix)
022508
{
022508
	struct tpax_dirent_buffer *     dentbuf;
022508
	struct tpax_dirent *            cdent;
022508
	struct tpax_dirent *            cnext;
022508
	const struct tpax_dirent *      parent;
022508
	uintptr_t                       addr;
022508
	const char *                    rdir;
022508
	const char *                    src;
022508
	char *                          dst;
022508
	char *                          cap;
022508
	char *                          mark;
022508
	int                             idx;
022508
	const char *                    dirv[256];
022508
	char                            pbuf[2048];
022508
022508
	/* normalized prefix */
022508
	if (prefix && (prefix[0] == '.') && (!prefix[1]))
022508
		prefix = 0;
022508
022508
	if (prefix && (prefix[0] == '.') && (prefix[1] == '/'))
022508
		for (++prefix; *prefix=='/'; prefix++)
022508
			(void)0;
022508
022508
	/* append explicit item */
022508
	tpax_archive_append_impl(dctx,uctx,0,prefix,0);
022508
022508
	/* iterate through queued items */
022508
	dentbuf = tpax_get_driver_dirents(dctx);
022508
	cdent   = dentbuf ? dentbuf->dbuf : 0;
022508
022508
	if (cdent) {
022508
		dst = pbuf;
022508
		cap = &pbuf[sizeof(pbuf)];
022508
022508
		if (prefix && prefix[0]) {
022508
			for (; *src && dst
022508
				*dst++ = *src++;
022508
022508
			if (dst == cap)
022508
				return TPAX_BUFFER_ERROR(dctx);
022508
022508
			if (dst[-1] != '/')
022508
				*dst++ = '/';
022508
		}
022508
022508
		src = *uctx->path;
022508
022508
		for (; *src && dst
022508
			*dst++ = *src++;
022508
022508
		if (dst == cap)
022508
			return TPAX_BUFFER_ERROR(dctx);
022508
022508
		if (dst[-1] != '/')
022508
			*dst++ = '/';
022508
022508
		*dst = 0;
022508
		mark = dst;
022508
		rdir = pbuf;
022508
022508
		(void)mark;
022508
	}
022508
022508
	for (; cdent; ) {
022508
		switch (cdent->dirent.d_type) {
022508
			case DT_DIR:
022508
				if (tpax_archive_append_dir(
022508
						dctx,0,cdent,cdent->fdat,
022508
						cdent->depth,rdir,cdent) < 0)
022508
					return TPAX_NESTED_ERROR(dctx);
022508
022508
				break;
022508
022508
			default:
022508
				if (tpax_archive_append_one(
022508
						dctx,0,&cdent->dirent,
022508
						cdent->fdat,rdir,cdent,0) < 0)
022508
					return TPAX_NESTED_ERROR(dctx);
022508
		}
022508
022508
		addr  = (uintptr_t)cdent;
022508
		addr += cdent->nsize;
022508
		cnext = (struct tpax_dirent *)addr;
022508
022508
		if (cnext == dentbuf->cdent) {
022508
			dentbuf = dentbuf->next;
022508
			cnext   = dentbuf ? dentbuf->dbuf : 0;
022508
		}
022508
022508
		if (cnext && (cnext->parent != cdent->parent)) {
022508
			if (cnext->depth > 256)
022508
				return TPAX_BUFFER_ERROR(dctx);
022508
022508
			for (parent=cnext->parent; parent; parent=parent->parent)
022508
				dirv[parent->depth - 1] = parent->dirent.d_name;
022508
022508
			for (idx=0,dst=mark; idx<cnext->parent->depth; idx++) {
022508
				src    = dirv[idx];
022508
022508
				for (; *src; )
022508
					*dst++ = *src++;
022508
022508
				*dst++ = '/';
022508
			}
022508
022508
			*--dst = 0;
022508
		}
022508
022508
		cdent = cnext;
022508
	}
022508
022508
	return 0;
409008
}
409008
409008
int tpax_archive_seal(const struct tpax_driver_ctx * dctx)
409008
{
409008
	int     fdout;
409008
	off_t   cpos;
409008
	ssize_t nbytes;
409008
	ssize_t nwritten;
409008
	ssize_t blksize;
409008
	char    buf[512];
409008
409008
	blksize = tpax_get_archive_block_size(dctx);
409008
	cpos    = tpax_get_driver_cpos(dctx);
409008
409008
	if (cpos % 512)
409008
		return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR);
409008
409008
	fdout = tpax_driver_fdout(dctx);
409008
	memset(buf,0,sizeof(buf));
409008
409008
	switch (cpos % blksize) {
409008
		case 0:
409008
			nbytes = cpos + blksize;
409008
			break;
409008
409008
		default:
409008
			nbytes  = cpos / blksize;
409008
			nbytes *= blksize;
409008
			nbytes += blksize;
409008
409008
			if (nbytes-cpos == 512)
409008
				nbytes += blksize;
409008
	}
409008
409008
	for (nwritten=cpos; nwritten
409008
		if (tpax_archive_append_memory_data(fdout,buf,512) < 0)
409008
			return TPAX_SYSTEM_ERROR(dctx);
409008
409008
		tpax_set_driver_cpos(dctx,nwritten);
409008
	}
409008
409008
	return 0;
3db888
}