| |
| |
| |
| |
| |
| |
| #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 const char tpax_cpio_trailer[11] = TPAX_CPIO_TRAILER; |
| |
| static void tpax_cpio_octal_write(char * ch, ssize_t len, uint64_t val) |
| { |
| for (; len; ) { |
| ch[--len] = val % 8 + '0'; |
| val /= 8; |
| } |
| } |
| |
| static int tpax_archive_append_memory_data( |
| int fdout, |
| const void * buf, |
| ssize_t nbytes) |
| { |
| ssize_t ret; |
| const 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]; |
| |
| if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) |
| return 0; |
| |
| 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_cpio_header chdr; |
| 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]; |
| |
| |
| if (cdent->flags & TPAX_ITEM_NAMEREF) |
| return 0; |
| |
| |
| if (!(path = tpax_queue_item_full_path(dctx,cdent))) |
| return TPAX_CUSTOM_ERROR( |
| dctx, |
| TPAX_ERR_FLOW_ERROR); |
| |
| |
| 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; |
| |
| |
| if (dctx->cctx->drvflags & TPAX_DRIVER_VERBOSE) |
| tpax_dprintf(tpax_driver_fderr(dctx),"%s",apath); |
| |
| |
| 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); |
| } |
| |
| |
| tpax_driver_set_ectx( |
| dctx,0,path); |
| |
| |
| hpos = tpax_get_driver_cpos(dctx); |
| |
| |
| 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); |
| |
| } else if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) { |
| ret = tpax_meta_init_cpio_header( |
| apath,st,slnk, |
| cdent->cpdev, |
| cdent->cpino, |
| cdent->nlink, |
| &chdr); |
| } |
| |
| if (ret < 0) |
| return tpax_archive_write_ret( |
| TPAX_NESTED_ERROR(dctx), |
| dctx,uctx); |
| |
| |
| membuf = 0; |
| fdtmp = -1; |
| |
| |
| if S_ISREG(st->st_mode) { |
| buf = tpax_get_driver_anon_map_addr( |
| dctx,&buflen); |
| |
| if (buflen >= (cmplen = st->st_size)) |
| membuf = buf; |
| |
| |
| 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); |
| } |
| } |
| |
| |
| if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) { |
| ret = tpax_archive_append_memory_data( |
| fdout,&chdr, |
| offsetof(struct tpax_cpio_header,c_namedata)); |
| |
| if (ret == 0) |
| ret = tpax_archive_append_memory_data( |
| fdout,apath, |
| strlen(apath) + 1); |
| |
| if ((ret == 0) && slnk) |
| ret = tpax_archive_append_memory_data( |
| fdout,slnk, |
| strlen(slnk)); |
| } else { |
| ret = tpax_archive_append_memory_data( |
| fdout,&uhdr, |
| ssizeof(uhdr)); |
| } |
| |
| if (ret < 0) { |
| if (fdtmp >= 0) |
| close(fdtmp); |
| |
| return tpax_archive_write_ret( |
| TPAX_SYSTEM_ERROR(dctx), |
| dctx,uctx); |
| } |
| |
| |
| if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) { |
| dpos = hpos; |
| dpos += offsetof(struct tpax_cpio_header,c_namedata); |
| dpos += strlen(apath) + 1; |
| dpos += slnk ? strlen(slnk) : 0; |
| } else { |
| dpos = hpos + ssizeof(uhdr); |
| } |
| |
| tpax_set_driver_cpos(dctx,dpos); |
| |
| |
| if (!(S_ISREG(st->st_mode))) |
| return tpax_archive_write_ret( |
| 0,dctx,uctx); |
| |
| |
| 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; |
| |
| |
| ictx = tpax_get_driver_ictx(dctx); |
| fdcwd = tpax_driver_fdcwd(dctx); |
| fdout = tpax_driver_fdout(dctx); |
| |
| |
| 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) |
| { |
| int fdout; |
| off_t cpos; |
| ssize_t nbytes; |
| ssize_t blksize; |
| ssize_t nwritten; |
| char buf[1024]; |
| struct tpax_cpio_header chdr; |
| |
| |
| memset(buf,0,sizeof(buf)); |
| memset(&chdr,0,sizeof(chdr)); |
| |
| tpax_cpio_octal_write(chdr.c_magic,sizeof(chdr.c_magic),0070707); |
| tpax_cpio_octal_write(chdr.c_dev,sizeof(chdr.c_dev),0); |
| tpax_cpio_octal_write(chdr.c_ino,sizeof(chdr.c_ino),0); |
| tpax_cpio_octal_write(chdr.c_mode,sizeof(chdr.c_mode),0); |
| tpax_cpio_octal_write(chdr.c_uid,sizeof(chdr.c_uid),0); |
| tpax_cpio_octal_write(chdr.c_gid,sizeof(chdr.c_gid),0); |
| tpax_cpio_octal_write(chdr.c_nlink,sizeof(chdr.c_nlink),1); |
| tpax_cpio_octal_write(chdr.c_rdev,sizeof(chdr.c_rdev),0); |
| tpax_cpio_octal_write(chdr.c_mtime,sizeof(chdr.c_mtime),0); |
| |
| tpax_cpio_octal_write(chdr.c_filesize,sizeof(chdr.c_filesize),0); |
| tpax_cpio_octal_write(chdr.c_namesize,sizeof(chdr.c_namesize),sizeof(tpax_cpio_trailer)); |
| |
| |
| fdout = tpax_driver_fdout(dctx); |
| blksize = dctx->cctx->blksize; |
| cpos = tpax_get_driver_cpos(dctx); |
| |
| |
| nbytes = offsetof(struct tpax_cpio_header,c_namedata); |
| |
| if (tpax_archive_append_memory_data(fdout,&chdr,nbytes) < 0) |
| return TPAX_SYSTEM_ERROR(dctx); |
| |
| cpos += nbytes; |
| |
| |
| nbytes = sizeof(tpax_cpio_trailer); |
| |
| if (tpax_archive_append_memory_data(fdout,tpax_cpio_trailer,nbytes) < 0) |
| return TPAX_SYSTEM_ERROR(dctx); |
| |
| cpos += nbytes; |
| |
| |
| nbytes = (cpos % 512) ? 512 - (cpos % 512) : 0; |
| |
| if (tpax_archive_append_memory_data(fdout,buf,nbytes) < 0) |
| return TPAX_SYSTEM_ERROR(dctx); |
| |
| cpos += nbytes; |
| |
| |
| if ((cpos % blksize) == 0) { |
| tpax_set_driver_cpos(dctx,cpos); |
| return 0; |
| } |
| |
| |
| 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); |
| |
| |
| tpax_set_driver_cpos(dctx,nwritten); |
| |
| 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[1024]; |
| |
| |
| if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) |
| return tpax_archive_seal_cpio(dctx); |
| |
| |
| blksize = dctx->cctx->blksize; |
| 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)); |
| |
| if (tpax_archive_append_memory_data(fdout,buf,2*512) < 0) |
| return TPAX_SYSTEM_ERROR(dctx); |
| |
| cpos += 2*512; |
| |
| |
| if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_PAX) { |
| tpax_set_driver_cpos(dctx,cpos); |
| return 0; |
| } |
| |
| |
| if ((cpos % blksize) == 0) { |
| tpax_set_driver_cpos(dctx,cpos); |
| return 0; |
| } |
| |
| |
| 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); |
| |
| |
| tpax_set_driver_cpos(dctx,nwritten); |
| |
| return 0; |
| } |