summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormidipix <writeonce@midipix.org>2024-07-17 05:45:36 +0000
committermidipix <writeonce@midipix.org>2024-07-17 05:45:36 +0000
commitf66f5352a25b77e4a930d39e3ba9c260efedf14b (patch)
tree5ee48a09d311982e223a3c43299293d0aa262c68
parent19fba4c00b514a0c25e6b3537a7dded39d2cc79a (diff)
downloadtpax-f66f5352a25b77e4a930d39e3ba9c260efedf14b.tar.bz2
tpax-f66f5352a25b77e4a930d39e3ba9c260efedf14b.tar.xz
write mode: implemented generation of the cpio archive format.
-rw-r--r--include/tpax/tpax.h4
-rw-r--r--project/common.mk1
-rw-r--r--src/driver/tpax_driver_ctx.c7
-rw-r--r--src/internal/tpax_driver_impl.h4
-rw-r--r--src/logic/tpax_archive_write.c142
-rw-r--r--src/logic/tpax_queue_vector.c130
-rw-r--r--src/meta/tpax_init_cpio_header.c145
7 files changed, 415 insertions, 18 deletions
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; nwritten<nbytes; nwritten+=512)
+ if (tpax_archive_append_memory_data(fdout,buf,512) < 0)
+ return TPAX_SYSTEM_ERROR(dctx);
+
+ /* all done */
+ tpax_set_driver_cpos(dctx,nwritten);
+
+ return 0;
}
int tpax_archive_seal(const struct tpax_driver_ctx * dctx)
@@ -365,6 +487,10 @@ int tpax_archive_seal(const struct tpax_driver_ctx * dctx)
ssize_t blksize;
char buf[1024];
+ /* cpio trailer? */
+ if (dctx->cctx->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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/stat.h>
+
+#include <tpax/tpax.h>
+#include <tpax/tpax_specs.h>
+#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;
+}