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