/**************************************************************/ /* 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, 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_apply_string_replacement( const struct tpax_driver_ctx * dctx, const char * path, char * replbuf, size_t buflen) { int ret; struct tpax_driver_ctx_impl * ictx; struct tpax_replstr * replstrv; ictx = tpax_get_driver_ictx(dctx); if (!(replstrv = ictx->replstrv)) return 0; for (ret=0; !ret && replstrv->regexp; replstrv++) { ret = tpax_util_path_replstr( replbuf,path, replstrv->replstr, &replstrv->regex, buflen,replstrv->flags); if ((ret > 0) && (replstrv->flags & TPAX_REPL_PRINT)) tpax_dprintf(tpax_driver_fderr(dctx),"%s >> %s\n",path,replbuf); } return ret; } static int tpax_archive_write_impl( const struct tpax_driver_ctx * dctx, const struct tpax_dirent * cdent, int fdcwd, int fdout) { int ret; struct tpax_unit_ctx * uctx; struct tpax_ustar_header uhdr; const struct stat * st; struct stat stbuf; const char * apath; const char * path; const char * slnk; const char * mlnk; off_t hpos; off_t dpos; int fdtmp; int slen; ssize_t nread; ssize_t nbytes; void * buf; size_t buflen; size_t cmplen; void * membuf; char * ch; char replbuf[PATH_MAX]; 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); /* regex matching and patter substitution */ if ((slen = tpax_apply_string_replacement(dctx,path,replbuf,PATH_MAX)) < 0) return TPAX_CUSTOM_ERROR( dctx, TPAX_ERR_FLOW_ERROR); apath = slen ? replbuf : path; /* verbose mode */ if (dctx->cctx->drvflags & TPAX_DRIVER_VERBOSE) tpax_dprintf(tpax_driver_fderr(dctx),"%s",apath); /* uctx */ if (tpax_lib_get_unit_ctx(dctx,fdcwd,path,&uctx) < 0) return 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 (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_RUSTAR) { ret = tpax_meta_init_rustar_header(apath,st,slnk,&uhdr); } else if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_USTAR) { ret = tpax_meta_init_ustar_header(apath,st,slnk,&uhdr); } if (ret < 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; nreadst_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