From f66f5352a25b77e4a930d39e3ba9c260efedf14b Mon Sep 17 00:00:00 2001 From: midipix Date: Jul 17 2024 05:45:36 +0000 Subject: write mode: implemented generation of the cpio archive format. --- diff --git a/include/tpax/tpax.h b/include/tpax/tpax.h index b811987..1ea9887 100644 --- a/include/tpax/tpax.h +++ b/include/tpax/tpax.h @@ -184,6 +184,10 @@ tpax_api int tpax_output_error_vector (const struct tpax_driver_ctx *); tpax_api int tpax_output_error_record (const struct tpax_driver_ctx *, const struct tpax_error_info *); /* meta interfaces */ +tpax_api int tpax_meta_init_cpio_header (const char * pathname, const struct stat *, + const char * linkname, int c_dev, int c_ino, int c_nlink, + struct tpax_cpio_header *); + tpax_api int tpax_meta_init_ustar_header (const char * pathname, const struct stat *, const char * linkname, struct tpax_ustar_header *); diff --git a/project/common.mk b/project/common.mk index 03e35ee..d600ff2 100644 --- a/project/common.mk +++ b/project/common.mk @@ -8,6 +8,7 @@ API_SRCS = \ src/logic/tpax_archive_reset.c \ src/logic/tpax_archive_write.c \ src/logic/tpax_queue_vector.c \ + src/meta/tpax_init_cpio_header.c \ src/meta/tpax_init_ustar_header.c \ src/output/tpax_output_error.c \ src/skin/tpax_skin_default.c \ diff --git a/src/driver/tpax_driver_ctx.c b/src/driver/tpax_driver_ctx.c index 74db70d..6a5f882 100644 --- a/src/driver/tpax_driver_ctx.c +++ b/src/driver/tpax_driver_ctx.c @@ -872,10 +872,6 @@ int tpax_lib_get_driver_ctx( return tpax_driver_error_not_implemented( fdctx->fderr,program,"the pax format",meta); - case TPAX_DRIVER_WRITE_FORMAT_CPIO: - return tpax_driver_error_not_implemented( - fdctx->fderr,program,"the cpio format",meta); - default: break; } @@ -954,6 +950,9 @@ static void tpax_free_driver_ctx_impl(struct tpax_driver_ctx_alloc * ictx) if (ictx->ctx.direntv) free(ictx->ctx.direntv); + if (ictx->ctx.cpiov) + free(ictx->ctx.cpiov); + argv_free(ictx->meta); free(ictx); } diff --git a/src/internal/tpax_driver_impl.h b/src/internal/tpax_driver_impl.h index 7726e1f..a7350d7 100644 --- a/src/internal/tpax_driver_impl.h +++ b/src/internal/tpax_driver_impl.h @@ -64,6 +64,9 @@ struct tpax_dirent { int fdat; int depth; int flags; + int nlink; + int cpdev; + int cpino; dev_t srdev; dev_t stdev; ino_t stino; @@ -107,6 +110,7 @@ struct tpax_driver_ctx_impl { char ** prefixp; char ** prefcap; char * prefptr[64]; + struct tpax_dirent ** cpiov; struct tpax_dirent ** direntv; struct tpax_dirent_buffer * dirents; struct tpax_dirent * dirmark; diff --git a/src/logic/tpax_archive_write.c b/src/logic/tpax_archive_write.c index fbf46a7..492d3b1 100644 --- a/src/logic/tpax_archive_write.c +++ b/src/logic/tpax_archive_write.c @@ -13,6 +13,16 @@ #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, @@ -46,6 +56,9 @@ static int tpax_archive_append_pad( 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; @@ -112,6 +125,7 @@ static int tpax_archive_write_impl( { int ret; struct tpax_unit_ctx * uctx; + struct tpax_cpio_header chdr; struct tpax_ustar_header uhdr; const struct stat * st; struct stat stbuf; @@ -206,9 +220,8 @@ static int tpax_archive_write_impl( tpax_driver_set_ectx( dctx,0,path); - /* header and data offsets: todo pax and cpio */ + /* header offset */ hpos = tpax_get_driver_cpos(dctx); - dpos = hpos + sizeof(uhdr); /* header */ if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_RUSTAR) { @@ -216,6 +229,14 @@ static int tpax_archive_write_impl( } 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) @@ -260,8 +281,28 @@ static int tpax_archive_write_impl( } } - /* append header */ - if (tpax_archive_append_memory_data(fdout,&uhdr,ssizeof(uhdr)) < 0) { + /* append header (and cpio symbolic link data) */ + 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); @@ -270,6 +311,16 @@ static int tpax_archive_write_impl( dctx,uctx); } + /* data offset */ + 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); /* all done? */ @@ -352,8 +403,79 @@ int tpax_archive_write(const struct tpax_driver_ctx * dctx) static int tpax_archive_seal_cpio(const struct tpax_driver_ctx * dctx) { - (void)dctx; - return -1; + int fdout; + off_t cpos; + ssize_t nbytes; + ssize_t blksize; + ssize_t nwritten; + char buf[1024]; + struct tpax_cpio_header chdr; + + /* trailer initialization */ + 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, archive block size, current position */ + fdout = tpax_driver_fdout(dctx); + blksize = dctx->cctx->blksize; + cpos = tpax_get_driver_cpos(dctx); + + /* trailer header */ + 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; + + /* trailer c_namedata[] */ + nbytes = sizeof(tpax_cpio_trailer); + + if (tpax_archive_append_memory_data(fdout,tpax_cpio_trailer,nbytes) < 0) + return TPAX_SYSTEM_ERROR(dctx); + + cpos += nbytes; + + /* pad the current block to a 512 byte boundary */ + nbytes = (cpos % 512) ? 512 - (cpos % 512) : 0; + + if (tpax_archive_append_memory_data(fdout,buf,nbytes) < 0) + return TPAX_SYSTEM_ERROR(dctx); + + cpos += nbytes; + + /* 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; nwrittencctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) + return tpax_archive_seal_cpio(dctx); + /* archive block size, current position */ blksize = dctx->cctx->blksize; cpos = tpax_get_driver_cpos(dctx); @@ -373,10 +499,6 @@ int tpax_archive_seal(const struct tpax_driver_ctx * dctx) 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)); diff --git a/src/logic/tpax_queue_vector.c b/src/logic/tpax_queue_vector.c index cae4da0..1b10feb 100644 --- a/src/logic/tpax_queue_vector.c +++ b/src/logic/tpax_queue_vector.c @@ -53,6 +53,118 @@ tpax_hidden const char * tpax_queue_item_full_path( return pathbuf; } +static int tpax_cpio_dirent_compare(const void * a, const void * b) +{ + const struct tpax_dirent * direnta; + const struct tpax_dirent * direntb; + + direnta = *(const struct tpax_dirent **)a; + direntb = *(const struct tpax_dirent **)b; + + return (direnta->stdev == direntb->stdev) + ? direnta->stino - direntb->stino + : direnta->stdev - direntb->stdev; +} + +static int tpax_update_cpio_queue_vector(const struct tpax_driver_ctx * dctx) +{ + struct tpax_driver_ctx_impl * ictx; + struct tpax_dirent ** cpiov; + struct tpax_dirent ** direntv; + struct tpax_dirent * cdent; + dev_t stdev; + ino_t stino; + int cpdev; + int cpino; + int nlink; + int idx; + + /* driver */ + ictx = tpax_get_driver_ictx(dctx); + + /* not needed? */ + if (ictx->nqueued == 0) + return 0; + + /* vector copy */ + direntv = ictx->direntv; + cpiov = ictx->cpiov; + + for (; *direntv; ) + *cpiov++ = *direntv++; + + /* sort by real st_dev, st_ino */ + qsort(ictx->cpiov,ictx->nqueued,sizeof(*cpiov),tpax_cpio_dirent_compare); + + /* set the cpio internal c_dev, c_ino, and c_nlink fields (U+1F620) */ + cpiov = ictx->cpiov; + cdent = cpiov[0]; + cpiov++; + + cpdev = 1; + cpino = 1; + nlink = 1; + + stdev = cdent->stdev; + stino = cdent->stino; + + cdent->cpdev = cpdev; + cdent->cpino = cpino; + cdent->nlink = nlink; + + for (; *cpiov; ) { + cdent = *cpiov; + + if (cdent->srdev > 0777777) + return TPAX_CUSTOM_ERROR( + dctx, + TPAX_ERR_FLOW_ERROR); + + if ((cdent->stdev == stdev) && (cdent->stino == stino)) { + nlink++; + + } else if (cdent->stdev > stdev) { + if (nlink > 1) + for (idx=2; idx<=nlink; idx++) + cpiov[-idx]->nlink = nlink; + + cpdev++; + cpino = 1; + nlink = 1; + + stdev = cdent->stdev; + stino = cdent->stino; + + if (cpdev > 0777777) + return TPAX_CUSTOM_ERROR( + dctx, + TPAX_ERR_FLOW_ERROR); + + } else if (cdent->stino > stino) { + if (nlink > 1) + for (idx=2; idx<=nlink; idx++) + cpiov[-idx]->nlink = nlink; + + cpino++; + stino = cdent->stino; + nlink = 1; + + if (cpino > 0777777) + return TPAX_CUSTOM_ERROR( + dctx, + TPAX_ERR_FLOW_ERROR); + } + + cdent->cpdev = cpdev; + cdent->cpino = cpino; + cdent->nlink = nlink; + + cpiov++; + } + + return 0; +} + tpax_hidden int tpax_update_queue_vector(const struct tpax_driver_ctx * dctx) { uintptr_t addr; @@ -67,20 +179,26 @@ tpax_hidden int tpax_update_queue_vector(const struct tpax_driver_ctx * dctx) ictx = tpax_get_driver_ictx(dctx); /* vector alloc */ - if (ictx->direntv) + if (ictx->direntv) { free(ictx->direntv); + free(ictx->cpiov); + + ictx->direntv = 0; + ictx->cpiov = 0; + } arrsize = ictx->nqueued + 1; if (!(ictx->direntv = calloc(arrsize,sizeof(struct tpax_dirent *)))) return TPAX_SYSTEM_ERROR(dctx); - if (ictx->nqueued == 0) - return 0; + if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) + if (!(ictx->cpiov = calloc(arrsize,sizeof(struct tpax_dirent *)))) + return TPAX_SYSTEM_ERROR(dctx); /* queue vector */ dentbuf = tpax_get_driver_dirents(dctx); - cdent = dentbuf->dbuf; + cdent = ictx->nqueued ? dentbuf->dbuf : 0; for (direntv=ictx->direntv; cdent; direntv++) { *direntv = cdent; @@ -97,5 +215,9 @@ tpax_hidden int tpax_update_queue_vector(const struct tpax_driver_ctx * dctx) cdent = cnext; } + if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) + if (tpax_update_cpio_queue_vector(dctx) < 0) + return TPAX_NESTED_ERROR(dctx); + return 0; } diff --git a/src/meta/tpax_init_cpio_header.c b/src/meta/tpax_init_cpio_header.c new file mode 100644 index 0000000..3de22f6 --- /dev/null +++ b/src/meta/tpax_init_cpio_header.c @@ -0,0 +1,145 @@ +/**************************************************************/ +/* tpax: a topological pax implementation */ +/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */ +/**************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "tpax_driver_impl.h" + +#ifndef ssizeof +#define ssizeof(x) (ssize_t)(sizeof(x)) +#endif + +#define TPAX_CPIO_PERM_MASK \ + ( S_ISUID | S_ISGID \ + | S_IRUSR | S_IWUSR | S_IXUSR \ + | S_IRGRP | S_IWGRP | S_IXGRP \ + | S_IROTH | S_IWOTH | S_IXOTH ) + +static void tpax_octal_write(char * ch, ssize_t len, uint64_t val) +{ + for (; len; ) { + ch[--len] = val % 8 + '0'; + val /= 8; + } +} + +int tpax_meta_init_cpio_header( + const char * path, + const struct stat * st, + const char * linkname, + int cdev, + int cino, + int cnlink, + struct tpax_cpio_header * chdr) +{ + size_t fnsize; + size_t lnklen; + size_t stsize; + int64_t stmtim; + uint32_t typeflag; + uint32_t permbits; + uint32_t modebits; + + /* filename size */ + fnsize = strlen(path) + 1; + + /* size & mtime validation */ + stsize = S_ISREG(st->st_mode) ? st->st_size : 0; + stmtim = st->st_mtim.tv_sec; + + if (stsize > 077777777777) + return -1; + + if ((stmtim < 0) || (stmtim > 077777777777)) + return -1; + + /* linkname validation */ + if (S_ISLNK(st->st_mode) && !linkname) + return -1; + + lnklen = S_ISLNK(st->st_mode) + ? strlen(linkname) + : 0; + + /* typeflag validation */ + if (S_ISREG(st->st_mode)) + typeflag = TPAX_CPIO_FILEMODE_ISREG; + else if (S_ISLNK(st->st_mode)) + typeflag = TPAX_CPIO_FILEMODE_ISLNK; + else if (S_ISDIR(st->st_mode)) + typeflag = TPAX_CPIO_FILEMODE_ISDIR; + else if (S_ISCHR(st->st_mode)) + typeflag = TPAX_CPIO_FILEMODE_ISCHR; + else if (S_ISBLK(st->st_mode)) + typeflag = TPAX_CPIO_FILEMODE_ISBLK; + else if (S_ISFIFO(st->st_mode)) + typeflag = TPAX_CPIO_FILEMODE_ISFIFO; + else if (S_ISSOCK(st->st_mode)) + typeflag = TPAX_CPIO_FILEMODE_ISSOCK; + else + return -1; + + /* permbits, modeflag */ + permbits = st->st_mode & TPAX_CPIO_PERM_MASK; + modebits = permbits | typeflag; + + /* one shot */ + memset(chdr,0,sizeof(*chdr)); + + /* c_magic */ + chdr->c_magic[0] = '0'; + chdr->c_magic[1] = '7'; + chdr->c_magic[2] = '0'; + chdr->c_magic[3] = '7'; + chdr->c_magic[4] = '0'; + chdr->c_magic[5] = '7'; + + /* c_dev, c_ino */ + tpax_octal_write(chdr->c_dev,ssizeof(chdr->c_dev),cdev); + tpax_octal_write(chdr->c_ino,ssizeof(chdr->c_ino),cino); + + /* c_mode */ + tpax_octal_write(chdr->c_mode,ssizeof(chdr->c_mode),modebits); + + /* c_uid, c_gid */ + if ((uint64_t)st->st_uid <= 0777777) + tpax_octal_write(chdr->c_uid,ssizeof(chdr->c_uid),st->st_uid); + else + tpax_octal_write(chdr->c_uid,ssizeof(chdr->c_uid),0); + + if ((uint64_t)st->st_gid <= 0777777) + tpax_octal_write(chdr->c_gid,ssizeof(chdr->c_gid),st->st_gid); + else + tpax_octal_write(chdr->c_gid,ssizeof(chdr->c_gid),0); + + /* c_nlink */ + tpax_octal_write(chdr->c_nlink,ssizeof(chdr->c_nlink),cnlink); + + /* c_rdev */ + tpax_octal_write(chdr->c_rdev,ssizeof(chdr->c_rdev),st->st_rdev); + + /* c_mtime */ + tpax_octal_write(chdr->c_mtime,ssizeof(chdr->c_mtime),stmtim); + + /* c_namesize */ + tpax_octal_write(chdr->c_namesize,ssizeof(chdr->c_namesize),fnsize); + + /* c_filesize */ + tpax_octal_write(chdr->c_filesize,ssizeof(chdr->c_filesize),lnklen ? lnklen : stsize); + + /* all done; c_name to be written along with c_filedata */ + return 0; +}