summaryrefslogtreecommitdiff
path: root/src/logic/tpax_archive_write.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/logic/tpax_archive_write.c')
-rw-r--r--src/logic/tpax_archive_write.c368
1 files changed, 368 insertions, 0 deletions
diff --git a/src/logic/tpax_archive_write.c b/src/logic/tpax_archive_write.c
new file mode 100644
index 0000000..7ce6cca
--- /dev/null
+++ b/src/logic/tpax_archive_write.c
@@ -0,0 +1,368 @@
+/**************************************************************/
+/* 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,
+ const struct tpax_driver_ctx * dctx,
+ struct tpax_unit_ctx * uctx)
+{
+ if (dctx->cctx->drvflags & TPAX_DRIVER_VERBOSE)
+ tpax_dprintf(tpax_driver_fderr(dctx),"\n",0);
+
+ 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 struct stat * st;
+ struct stat stbuf;
+ const char * path;
+ const char * slnk;
+ const char * mlnk;
+ off_t hpos;
+ off_t dpos;
+ int fdtmp;
+ ssize_t nread;
+ ssize_t nbytes;
+ void * buf;
+ size_t buflen;
+ size_t cmplen;
+ void * membuf;
+ char * ch;
+ char pathbuf[PATH_MAX];
+
+ /* followed symlink? */
+ if (cdent->flags & TPAX_ITEM_NAMEREF)
+ return 0;
+
+ /* full path */
+ if (!(path = tpax_queue_item_full_path(dctx,cdent)))
+ return TPAX_CUSTOM_ERROR(
+ dctx,
+ TPAX_ERR_FLOW_ERROR);
+
+ /* verbose mode */
+ if (dctx->cctx->drvflags & TPAX_DRIVER_VERBOSE)
+ tpax_dprintf(tpax_driver_fderr(dctx),"%s",path);
+
+ /* uctx */
+ if (tpax_lib_get_unit_ctx(dctx,fdcwd,path,&uctx) < 0)
+ tpax_archive_write_ret(
+ TPAX_NESTED_ERROR(dctx),
+ dctx,0);
+
+ st = uctx->st;
+ slnk = uctx->link[0];
+ mlnk = 0;
+
+ if (cdent->flags & TPAX_ITEM_SYMLINK) {
+ st = &stbuf;
+ mlnk = slnk;
+ slnk = 0;
+ ch = 0;
+
+ if (mlnk[0] != '/') {
+ if (strlen(path) >= PATH_MAX)
+ return tpax_archive_write_ret(
+ TPAX_CUSTOM_ERROR(
+ dctx,
+ TPAX_ERR_FLOW_ERROR),
+ dctx,uctx);
+
+ strcpy(pathbuf,path);
+
+ ch = strrchr(pathbuf,'/');
+ }
+
+ if (ch && (++ch - pathbuf >= PATH_MAX))
+ return tpax_archive_write_ret(
+ TPAX_CUSTOM_ERROR(
+ dctx,
+ TPAX_ERR_FLOW_ERROR),
+ dctx,uctx);
+
+ if (ch) {
+ strcpy(ch,mlnk);
+ mlnk = pathbuf;
+ }
+
+ if (fstatat(fdcwd,mlnk,&stbuf,0) < 0)
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+ }
+
+ /* 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,st,
+ slnk,&uhdr) < 0)
+ return tpax_archive_write_ret(
+ TPAX_NESTED_ERROR(dctx),
+ dctx,uctx);
+
+ /* buffer */
+ membuf = 0;
+ fdtmp = -1;
+
+ /* associated data? */
+ if S_ISREG(st->st_mode) {
+ buf = tpax_get_driver_anon_map_addr(
+ dctx,&buflen);
+
+ if (buflen >= (cmplen = st->st_size))
+ membuf = buf;
+
+ /* snapshot */
+ if (membuf) {
+ if (tpax_io_create_memory_snapshot(
+ dctx,fdcwd,
+ mlnk ? mlnk : path,
+ st,membuf) < 0)
+ return tpax_archive_write_ret(
+ TPAX_NESTED_ERROR(dctx),
+ dctx,uctx);
+ } else {
+ if ((fdtmp = tpax_io_create_tmpfs_snapshot(
+ dctx,fdcwd,
+ mlnk ? mlnk : path,
+ st)) < 0)
+ return tpax_archive_write_ret(
+ TPAX_NESTED_ERROR(dctx),
+ dctx,uctx);
+
+ if (lseek(fdtmp,0,SEEK_SET) < 0)
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ 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),
+ dctx,uctx);
+ }
+
+ tpax_set_driver_cpos(dctx,dpos);
+
+ /* all done? */
+ if (!(S_ISREG(st->st_mode)))
+ return tpax_archive_write_ret(
+ 0,dctx,uctx);
+
+ /* append data from snapshot */
+ if (fdtmp >= 0) {
+ buf = tpax_get_driver_anon_map_addr(
+ dctx,&buflen);
+
+ for (nread=0; nread<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),
+ dctx,uctx);
+
+ } else if (nbytes == 0) {
+ close(fdtmp);
+ return tpax_archive_write_ret(
+ TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR),
+ dctx,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),
+ dctx,uctx);
+ }
+ }
+
+ close(fdtmp);
+ } else {
+ if (tpax_archive_append_memory_data(
+ fdout,membuf,
+ st->st_size) < 0)
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+ }
+
+ return tpax_archive_write_ret(
+ tpax_archive_append_pad(dctx,fdout,st),
+ dctx,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;
+}
+
+static int tpax_archive_seal_cpio(const struct tpax_driver_ctx * dctx)
+{
+ (void)dctx;
+ return -1;
+}
+
+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[1024];
+
+ /* archive block size, current position */
+ blksize = dctx->cctx->blksize;
+ cpos = tpax_get_driver_cpos(dctx);
+
+ /* internal error? */
+ if (cpos % 512)
+ return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR);
+
+ /* cpio trailer? */
+ if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO)
+ return tpax_archive_seal_cpio(dctx);
+
+ /* ustar, pax */
+ fdout = tpax_driver_fdout(dctx);
+ memset(buf,0,sizeof(buf));
+
+ if (tpax_archive_append_memory_data(fdout,buf,2*512) < 0)
+ return TPAX_SYSTEM_ERROR(dctx);
+
+ cpos += 2*512;
+
+ /* pax? */
+ if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_PAX) {
+ tpax_set_driver_cpos(dctx,cpos);
+ return 0;
+ }
+
+ /* already at block boundary? */
+ if ((cpos % blksize) == 0) {
+ tpax_set_driver_cpos(dctx,cpos);
+ return 0;
+ }
+
+ /* zero-fill the remainder of the block */
+ nbytes = cpos / blksize;
+ nbytes *= blksize;
+ nbytes += blksize;
+
+ for (nwritten=cpos; nwritten<nbytes; nwritten+=512)
+ if (tpax_archive_append_memory_data(fdout,buf,512) < 0)
+ return TPAX_SYSTEM_ERROR(dctx);
+
+ /* all done */
+ tpax_set_driver_cpos(dctx,nwritten);
+
+ return 0;
+}