/******************************************************/ /* tpax: a topological pax implementation */ /* Copyright (C) 2020 Z. Gilboa */ /* Released under GPLv2 and GPLv3; see COPYING.TPAX. */ /******************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "tpax_driver_impl.h" #include "tpax_tmpfile_impl.h" #include "tpax_errinfo_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; } int tpax_archive_append( const struct tpax_driver_ctx * dctx, const struct tpax_unit_ctx * uctx) { struct tpax_ustar_header uhdr; off_t hpos; off_t dpos; int fdout; int fdtmp; ssize_t nread; ssize_t nbytes; void * buf; size_t buflen; size_t cmplen; void * membuf; char sbuf[2048]; /* record errors */ tpax_driver_set_ectx( dctx,0, *uctx->path); /* driver */ fdout = tpax_driver_fdout(dctx); /* 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,*uctx->path,uctx->st, *uctx->link,&uhdr) < 0) return TPAX_NESTED_ERROR(dctx); /* buffer */ membuf = 0; fdtmp = -1; /* associated data? */ if S_ISREG(uctx->st->st_mode) { if ((buf = tpax_get_driver_anon_map_addr(dctx,&buflen))) if (buflen >= (cmplen = uctx->st->st_size)) membuf = buf; if (ssizeof(sbuf) >= (uctx->st->st_size)) membuf = sbuf; /* snapshot */ if (membuf) { if (tpax_file_create_memory_snapshot( dctx,*uctx->path, uctx->st,membuf) < 0) return TPAX_NESTED_ERROR(dctx); } else { if ((fdtmp = tpax_file_create_tmpfs_snapshot( dctx,*uctx->path, uctx->st)) < 0) return TPAX_NESTED_ERROR(dctx); if (lseek(fdtmp,0,SEEK_SET) < 0) return TPAX_SYSTEM_ERROR(dctx); } } /* append header */ if (tpax_archive_append_memory_data(fdout,&uhdr,ssizeof(uhdr)) < 0) { if (fdtmp >= 0) close(fdtmp); return TPAX_SYSTEM_ERROR(dctx); } tpax_set_driver_cpos(dctx,dpos); /* all done? */ if (!(S_ISREG(uctx->st->st_mode))) return 0; /* append data from snapshot */ if (fdtmp >= 0) { if (!buf) { buf = sbuf; buflen = sizeof(sbuf); } 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_SYSTEM_ERROR(dctx); } else if (nbytes == 0) { close(fdtmp); return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR); } else { nread += nbytes; } if (tpax_archive_append_memory_data(fdout,buf,nbytes) < 0) { close(fdtmp); return TPAX_SYSTEM_ERROR(dctx); } } close(fdtmp); } else { if (tpax_archive_append_memory_data( fdout,membuf, uctx->st->st_size) < 0) return TPAX_SYSTEM_ERROR(dctx); } return tpax_archive_append_pad( dctx,fdout,uctx->st); } 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