/**************************************************************/ /* tpax: a topological pax implementation */ /* Copyright (C) 2020--2024 SysDeer Technologies, LLC */ /* Released under GPLv2 and GPLv3; see COPYING.TPAX. */ /**************************************************************/ #include #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_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_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_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_file_create_memory_snapshot( dctx,fdcwd,path, uctx->st,membuf) < 0) return tpax_archive_write_ret( TPAX_NESTED_ERROR(dctx), uctx); } else { if ((fdtmp = tpax_file_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_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; nreadst->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