diff options
Diffstat (limited to 'src/logic/tpax_archive_write.c')
-rw-r--r-- | src/logic/tpax_archive_write.c | 368 |
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; +} |