summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/tpax/tpax.h27
-rw-r--r--include/tpax/tpax_specs.h27
-rw-r--r--project/common.mk2
-rw-r--r--src/driver/tpax_driver_ctx.c376
-rw-r--r--src/internal/tpax_driver_impl.h29
-rw-r--r--src/logic/tpax_archive_enqueue.c29
-rw-r--r--src/logic/tpax_archive_write.c204
-rw-r--r--src/logic/tpax_queue_vector.c134
-rw-r--r--src/meta/tpax_init_cpio_header.c145
-rw-r--r--src/meta/tpax_init_ustar_header.c27
-rw-r--r--src/skin/tpax_skin_default.c20
-rw-r--r--src/util/tpax_path_replstr.c120
12 files changed, 1079 insertions, 61 deletions
diff --git a/include/tpax/tpax.h b/include/tpax/tpax.h
index 00271cb..bb6aae7 100644
--- a/include/tpax/tpax.h
+++ b/include/tpax/tpax.h
@@ -1,6 +1,7 @@
#ifndef TPAX_H
#define TPAX_H
+#include <regex.h>
#include <stdint.h>
#include <stddef.h>
#include <sys/stat.h>
@@ -62,6 +63,17 @@ extern "C" {
#define TPAX_DRIVER_STRICT_DEVICE_ID 0X10000000
+/* source data flags */
+#define TPAX_SOURCE_DATA_NONE 0x0000
+#define TPAX_SOURCE_DATA_PENDING 0x0001
+#define TPAX_SOURCE_DATA_OPENED 0x0002
+#define TPAX_SOURCE_DATA_CLOSED 0x0004
+
+#define TPAX_SOURCE_DATA_FILEIO 0X0010
+#define TPAX_SOURCE_DATA_MAPPED 0x0020
+#define TPAX_SOURCE_DATA_CACHED 0x0040
+#define TPAX_SOURCE_DATA_ERROR 0x0080
+
/* error flags */
#define TPAX_ERROR_TOP_LEVEL 0x0001
#define TPAX_ERROR_NESTED 0x0002
@@ -118,6 +130,7 @@ struct tpax_common_ctx {
uint64_t drvflags;
uint64_t actflags;
uint64_t fmtflags;
+ uint64_t srcflags;
uint32_t blksize;
};
@@ -172,6 +185,9 @@ tpax_api int tpax_archive_seal (const struct tpax_driver_ctx *);
tpax_api int tpax_util_path_copy (char *, const char *, size_t, uint32_t, size_t *);
tpax_api int tpax_util_stat_compare (const struct stat *, const struct stat *);
+tpax_api int tpax_util_path_replstr (char * dstpath, const char * srcpath, const char * replstr,
+ const regex_t * regex, size_t buflen, int flags);
+
/* utility api */
tpax_api int tpax_main (char **, char **,
const struct tpax_fd_ctx *);
@@ -180,8 +196,15 @@ 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_ustar_header (const struct tpax_driver_ctx *, const char *, const struct stat *,
- const char *, struct tpax_ustar_header *);
+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 *);
+
+tpax_api int tpax_meta_init_rustar_header (const char * pathname, const struct stat *,
+ const char * linkname, struct tpax_ustar_header *);
/* low-level interfaces */
tpax_api int tpax_io_create_memory_snapshot(const struct tpax_driver_ctx *, int, const char *,
diff --git a/include/tpax/tpax_specs.h b/include/tpax/tpax_specs.h
index e30b36b..2ae13a9 100644
--- a/include/tpax/tpax_specs.h
+++ b/include/tpax/tpax_specs.h
@@ -28,6 +28,33 @@ extern "C" {
| S_IRGRP | S_IWGRP | S_IXGRP \
| S_IROTH | S_IWOTH | S_IXOTH )
+#define TPAX_CPIO_MAGIC {'0','7','0','7','0','7',0}
+#define TPAX_CPIO_TRAILER {'T','R','A','I','L','E','R','!','!','!',0}
+
+#define TPAX_CPIO_FILEMODE_IRUSR 0000400
+#define TPAX_CPIO_FILEMODE_IWUSR 0000200
+#define TPAX_CPIO_FILEMODE_IXUSR 0000100
+#define TPAX_CPIO_FILEMODE_IRGRP 0000040
+#define TPAX_CPIO_FILEMODE_IWGRP 0000020
+#define TPAX_CPIO_FILEMODE_IXGRP 0000010
+#define TPAX_CPIO_FILEMODE_IROTH 0000004
+#define TPAX_CPIO_FILEMODE_IWOTH 0000002
+#define TPAX_CPIO_FILEMODE_IXOTH 0000001
+
+#define TPAX_CPIO_FILEMODE_ISUID 0004000
+#define TPAX_CPIO_FILEMODE_ISGID 0002000
+#define TPAX_CPIO_FILEMODE_ISVTX 0001000
+
+#define TPAX_CPIO_FILEMODE_ISFIFO 0010000
+#define TPAX_CPIO_FILEMODE_ISCHR 0020000
+#define TPAX_CPIO_FILEMODE_ISDIR 0040000
+#define TPAX_CPIO_FILEMODE_ISBLK 0060000
+#define TPAX_CPIO_FILEMODE_ISREG 0100000
+
+#define TPAX_CPIO_FILEMODE_ISCTG 0110000
+#define TPAX_CPIO_FILEMODE_ISLNK 0120000
+#define TPAX_CPIO_FILEMODE_ISSOCK 0140000
+
struct tpax_ustar_header {
char u_name [100];
char u_mode [8];
diff --git a/project/common.mk b/project/common.mk
index 26e171c..d600ff2 100644
--- a/project/common.mk
+++ b/project/common.mk
@@ -8,10 +8,12 @@ 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 \
src/util/tpax_path_copy.c \
+ src/util/tpax_path_replstr.c \
src/util/tpax_stat_compare.c \
INTERNAL_SRCS = \
diff --git a/src/driver/tpax_driver_ctx.c b/src/driver/tpax_driver_ctx.c
index 79518f0..b7b949e 100644
--- a/src/driver/tpax_driver_ctx.c
+++ b/src/driver/tpax_driver_ctx.c
@@ -6,6 +6,7 @@
#define _DEFAULT_SOURCE 1
+#include <regex.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -33,6 +34,9 @@
| TPAX_DRIVER_WRITE_FORMAT_USTAR \
| TPAX_DRIVER_WRITE_FORMAT_RUSTAR)
+#define TPAX_CACHE_MIN (1 << 12)
+#define TPAX_CACHE_MAX (1 << 24)
+
/* package info */
static const struct tpax_source_version tpax_src_version = {
TPAX_TAG_VER_MAJOR,
@@ -51,6 +55,9 @@ static const struct tpax_fd_ctx tpax_default_fdctx = {
.fdlog = (-1),
};
+/* fallback error description */
+static const char tpax_null_errdesc[] = "<no error description>";
+
struct tpax_driver_ctx_alloc {
struct argv_meta * meta;
struct tpax_driver_ctx_impl ctx;
@@ -74,6 +81,21 @@ static uint32_t tpax_argv_flags(uint32_t flags)
return ret;
}
+static const char * tpax_driver_errdesc(void)
+{
+ int lerrno;
+ const char * errstr;
+
+ lerrno = errno;
+ errno = 0;
+
+ errstr = strerror(lerrno);
+ errstr = errno ? tpax_null_errdesc : errstr;
+ errno = lerrno;
+
+ return errstr;
+}
+
static int tpax_driver_usage(
int fdout,
const char * program,
@@ -88,7 +110,7 @@ static int tpax_driver_usage(
"Synopsis:\n"
" %s [-d] [-f archive]\n"
" %s -r [-d] [-f archive]\n"
- " %s -w [−x format] [-b blocksize] [-dtv] [-H|-L] [-f archive]\n"
+ " %s -w [−x format] [-b blocksize] [-dtv] [-H|-L] [-f archive] [-s replstr]... \n"
" %s -r -w [-d]\n\n"
"Options:\n",
program,program,program,program,program);
@@ -169,8 +191,7 @@ static int tpax_driver_usage_copy_mode(
break;
default:
- if (!(errdesc = strerror(errno)))
- errdesc = "<no error description>";
+ errdesc = tpax_driver_errdesc();
tpax_dprintf(
fdout,
@@ -259,15 +280,7 @@ static int tpax_driver_error_archive_path(
struct argv_meta * meta,
bool fwrite)
{
- int lerrno;
- const char * errstr;
-
- lerrno = errno;
- errno = 0;
-
- errstr = strerror(lerrno);
- errstr = errno ? "" : errstr;
- errno = lerrno;
+ const char * errstr = tpax_driver_errdesc();
if (fwrite) {
tpax_dprintf(
@@ -322,17 +335,131 @@ static void tpax_set_archive_block_size(struct tpax_common_ctx * cctx)
cctx->blksize = TPAX_USTAR_BLOCK_SIZE;
}
+static int tpax_add_replstr(
+ struct argv_entry * entry,
+ struct tpax_replstr * replstr,
+ char ** mark)
+{
+ const char * src;
+ char * dst;
+ char sep;
+ int nsep;
+
+ /* non-null separator character */
+ if (!(sep = entry->arg[0]))
+ return -1;
+
+ /* exactly three separator characters */
+ for (nsep=1,src=&entry->arg[1]; *src; src++) {
+ if ((src[0] == '\\') && (src[1] == sep)) {
+ src++;
+
+ } else if (src[0] == sep) {
+ nsep++;
+ }
+ }
+
+ if (nsep != 3)
+ return -1;
+
+ /* regexp */
+ for (src=&entry->arg[1],dst=*mark; (*src != sep); src++) {
+ if ((src[0] == '\\') && (src[1] == sep))
+ src++;
+
+ *dst++ = *src;
+ }
+
+ replstr->replarg = entry->arg;
+ replstr->replstr = ++dst;
+ replstr->regexp = *mark;
+
+ /* replstr */
+ for (++src; (*src != sep); src++) {
+ if ((src[0] == '\\') && (src[1] == sep))
+ src++;
+
+ *dst++ = *src;
+ }
+
+ src++;
+ dst++;
+
+ *mark = dst;
+
+ /* flags */
+ if (src[0] && src[1] && src[2])
+ return -1;
+
+ if (src[0] && (src[0] == src[1]))
+ return -1;
+
+ if (src[0] && (src[0] != 'g') && (src[0] != 'p'))
+ return -1;
+
+ if (src[0] && src[1] && (src[1] != 'g') && (src[1] != 'p'))
+ return -1;
+
+ if (src[0] && ((src[0] == 'g') || (src[1] == 'g')))
+ replstr->flags |= TPAX_REPL_GLOBAL;
+
+ if (src[0] && ((src[0] == 'p') || (src[1] == 'p')))
+ replstr->flags |= TPAX_REPL_PRINT;
+
+ /* regex */
+ if (regcomp(&replstr->regex,replstr->regexp,0)) {
+ replstr->regexp = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int tpax_init_replstr_vector(
+ struct tpax_driver_ctx_impl * ctx,
+ struct argv_meta * meta)
+{
+ struct argv_entry * entry;
+ struct tpax_replstr * replstr;
+ char * mark;
+
+ if (!(replstr = ctx->replstrv))
+ return 0;
+
+ for (entry=meta->entries,mark=ctx->replstrs; entry->fopt || entry->arg; entry++) {
+ if (entry->tag == TAG_REPLSTR) {
+ if (tpax_add_replstr(entry,replstr,&mark) < 0)
+ return -1;
+
+ replstr++;
+ }
+ }
+
+ return 0;
+}
+
+static int tpax_driver_is_valid_keyval(struct argv_keyval * keyval)
+{
+ (void)keyval;
+ return 0;
+}
+
static struct tpax_driver_ctx_impl * tpax_driver_ctx_alloc(
struct argv_meta * meta,
const struct tpax_fd_ctx * fdctx,
const struct tpax_common_ctx * cctx,
- size_t nunits)
+ size_t nunits,
+ size_t nreplstr,
+ size_t sreplstr)
{
struct tpax_driver_ctx_alloc * ictx;
size_t size;
struct argv_entry * entry;
const char ** units;
int elements;
+ int nkeyval;
+ struct argv_keyval ** pkeyval;
+ struct argv_keyval * keyval;
size = sizeof(struct tpax_driver_ctx_alloc);
size += (nunits+1)*sizeof(const char *);
@@ -360,19 +487,49 @@ static struct tpax_driver_ctx_impl * tpax_driver_ctx_alloc(
if (!entry->fopt)
*units++ = entry->arg;
- if (cctx->drvflags & TPAX_DRIVER_EXEC_MODE_WRITE_COPY) {
- ictx->ctx.bufsize = TPAX_FILEIO_BUFLEN;
- ictx->ctx.bufaddr = mmap(
- 0,ictx->ctx.bufsize,
- PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_ANONYMOUS,
- -1,0);
+ for (entry=meta->entries,nkeyval=0; entry->fopt || entry->arg; entry++)
+ if (entry->keyv)
+ for (keyval=entry->keyv; keyval->keyword; keyval++)
+ nkeyval++;
- if (ictx->ctx.bufaddr == MAP_FAILED) {
- free(ictx);
- return 0;
- }
+ if (nkeyval && !(ictx->ctx.keyvalv = calloc(nkeyval+1,sizeof(*ictx->ctx.keyvalv)))) {
+ free(ictx);
+ return 0;
+ }
+ if (nreplstr && !(ictx->ctx.replstrv = calloc(++nreplstr,sizeof(*ictx->ctx.replstrv)))) {
+ free(ictx->ctx.keyvalv);
+ free(ictx);
+ return 0;
+ }
+
+ if (sreplstr && !(ictx->ctx.replstrs = calloc(sreplstr,1))) {
+ free(ictx->ctx.replstrv);
+ free(ictx->ctx.keyvalv);
+ free(ictx);
+ return 0;
+ }
+
+ if ((pkeyval = ictx->ctx.keyvalv))
+ for (entry=meta->entries; entry->fopt || entry->arg; entry++)
+ if (entry->keyv)
+ for (keyval=entry->keyv; keyval->keyword; keyval++)
+ *pkeyval++ = keyval;
+
+ ictx->ctx.bufsize = TPAX_FILEIO_BUFLEN;
+ ictx->ctx.bufaddr = mmap(
+ 0,ictx->ctx.bufsize,
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS,
+ -1,0);
+
+ if (ictx->ctx.bufaddr == MAP_FAILED) {
+ free(ictx->ctx.keyvalv);
+ free(ictx);
+ return 0;
+ }
+
+ if (cctx->drvflags & TPAX_DRIVER_EXEC_MODE_WRITE_COPY) {
ictx->ctx.dirbuff = mmap(
0,TPAX_DIRENT_BUFLEN,
PROT_READ|PROT_WRITE,
@@ -381,6 +538,7 @@ static struct tpax_driver_ctx_impl * tpax_driver_ctx_alloc(
if (ictx->ctx.dirbuff == MAP_FAILED) {
munmap(ictx->ctx.bufaddr,ictx->ctx.bufsize);
+ free(ictx->ctx.keyvalv);
free(ictx);
return 0;
}
@@ -399,6 +557,77 @@ static int tpax_get_driver_ctx_fail(struct argv_meta * meta)
return -1;
}
+static int tpax_driver_keyval_error(
+ struct tpax_driver_ctx_impl * ctx,
+ struct argv_keyval * keyval,
+ const char * program)
+{
+ const char * equal;
+
+ switch (keyval->flags) {
+ case ARGV_KEYVAL_ASSIGN:
+ equal = "=";
+ break;
+
+ case ARGV_KEYVAL_OVERRIDE:
+ equal = ":=";
+ break;
+
+ default:
+ equal = "";
+ }
+
+ tpax_dprintf(
+ ctx->fdctx.fderr,
+ "%s: unsupported keyval argument (%s%s%s)\n",
+ program,keyval->keyword,equal,
+ keyval->value ? keyval->value : "");
+
+ tpax_lib_free_driver_ctx(&ctx->ctx);
+
+ return TPAX_ERROR;
+}
+
+static int tpax_driver_srcstat_error(
+ struct tpax_driver_ctx_impl * ctx,
+ const struct argv_entry * archive,
+ const char * program)
+{
+ const char * errstr = tpax_driver_errdesc();
+
+ if (archive) {
+ tpax_dprintf(
+ ctx->fdctx.fderr,
+ "%s: could not stat archive file '%s' (%s).\n",
+ program,archive->arg,errstr);
+ } else {
+ tpax_dprintf(
+ ctx->fdctx.fderr,
+ "%s: could not stat input source file <fd=%d> (%s).\n",
+ program,ctx->fdctx.fdin,errstr);
+ }
+
+ tpax_lib_free_driver_ctx(&ctx->ctx);
+
+ return TPAX_ERROR;
+}
+
+static int tpax_driver_cache_error(
+ struct tpax_driver_ctx_impl * ctx,
+ const char * program)
+{
+ const char * errstr = tpax_driver_errdesc();
+
+ tpax_dprintf(
+ ctx->fdctx.fderr,
+ "%s: failed to allocate source data cache (%s).\n",
+ program,errstr);
+
+ tpax_lib_free_driver_ctx(&ctx->ctx);
+
+ return TPAX_ERROR;
+}
+
int tpax_lib_get_driver_ctx(
char ** argv,
char ** envp,
@@ -413,8 +642,12 @@ int tpax_lib_get_driver_ctx(
struct argv_entry * entry;
struct argv_entry * archive;
struct argv_entry * operand;
+ struct argv_keyval ** pkeyval;
struct tpax_fd_ctx lfdctx;
size_t nunits;
+ size_t nreplstr;
+ size_t sreplstr;
+ size_t cachesize;
const char * program;
int fddst;
const char * ch;
@@ -438,6 +671,9 @@ int tpax_lib_get_driver_ctx(
program = argv_program_name(argv[0]);
memset(&cctx,0,sizeof(cctx));
+ nreplstr = 0;
+ sreplstr = 0;
+
cctx.drvflags = flags;
fddst = fdctx->fddst;
@@ -522,6 +758,12 @@ int tpax_lib_get_driver_ctx(
meta);
break;
+ case TAG_REPLSTR:
+ sreplstr += strlen(entry->arg);
+ sreplstr++;
+ nreplstr++;
+ break;
+
case TAG_RECURSE:
cctx.drvflags |= TPAX_DRIVER_DIR_MEMBER_RECURSE;
break;
@@ -629,6 +871,8 @@ int tpax_lib_get_driver_ctx(
} else if (archive) {
memcpy(&lfdctx,fdctx,sizeof(*fdctx));
+ cctx.srcflags = TPAX_SOURCE_DATA_PENDING;
+
lfdctx.fdin = openat(
fdctx->fdcwd,
archive->arg,
@@ -640,7 +884,15 @@ int tpax_lib_get_driver_ctx(
program,archive->arg,
meta,false);
+ cctx.srcflags = TPAX_SOURCE_DATA_OPENED;
+
fdctx = &lfdctx;
+
+ } else if (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_WRITE) {
+ cctx.srcflags = TPAX_SOURCE_DATA_NONE;
+
+ } else {
+ cctx.srcflags = TPAX_SOURCE_DATA_PENDING;
}
/* not implemented mode(s) */
@@ -683,22 +935,65 @@ 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;
}
/* driver ctx */
- if (!(ctx = tpax_driver_ctx_alloc(meta,fdctx,&cctx,nunits))) {
+ if (!(ctx = tpax_driver_ctx_alloc(meta,fdctx,&cctx,nunits,nreplstr,sreplstr))) {
if (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_COPY)
close(fddst);
return tpax_get_driver_ctx_fail(meta);
}
+ /* replstr validation and vector initialization */
+ if (tpax_init_replstr_vector(ctx,meta) < 0) {
+ tpax_lib_free_driver_ctx(&ctx->ctx);
+ return TPAX_ERROR;
+ }
+
+ /* keyval validation */
+ for (pkeyval=ctx->keyvalv; pkeyval && *pkeyval; pkeyval++)
+ if (!tpax_driver_is_valid_keyval(*pkeyval))
+ return tpax_driver_keyval_error(ctx,*pkeyval,program);
+
+ /* source data mapping (non-critical) */
+ if (cctx.srcflags) {
+ if (fstat(fdctx->fdin,&ctx->srcstat) < 0)
+ return tpax_driver_srcstat_error(ctx,archive,program);
+
+ ctx->mapsize = ctx->srcstat.st_size;
+ ctx->mapaddr = mmap(
+ 0,ctx->mapsize,
+ PROT_READ,MAP_PRIVATE,
+ fdctx->fdin,0);
+
+ if (ctx->mapaddr == MAP_FAILED) {
+ ctx->cctx.srcflags = TPAX_SOURCE_DATA_FILEIO;
+ ctx->mapaddr = 0;
+ ctx->mapsize = 0;
+ } else {
+ ctx->cctx.srcflags = TPAX_SOURCE_DATA_MAPPED;
+ }
+ }
+
+ /* allocate source data cache as needed */
+ if (ctx->cctx.srcflags == TPAX_SOURCE_DATA_FILEIO) {
+ cachesize = TPAX_CACHE_MAX;
+
+ for (; !ctx->cacheaddr && (cachesize >= TPAX_CACHE_MIN); )
+ if (!(ctx->cacheaddr = calloc(cachesize,1)))
+ cachesize >>= 1;
+
+ if (!ctx->cacheaddr)
+ return tpax_driver_cache_error(ctx,program);
+
+ ctx->cachemark = ctx->cacheaddr;
+ ctx->cachecap = &ctx->cacheaddr[cachesize];
+ }
+
+ /* all done */
if (archive) {
ctx->file = archive->arg;
ctx->ctx.file = &ctx->file;
@@ -717,6 +1012,8 @@ static void tpax_free_driver_ctx_impl(struct tpax_driver_ctx_alloc * ictx)
size_t size;
char ** ppref;
+ struct tpax_replstr * replstrv;
+
for (; ictx->ctx.dirents; ) {
next = ictx->ctx.dirents->next;
size = ictx->ctx.dirents->size;
@@ -725,6 +1022,24 @@ static void tpax_free_driver_ctx_impl(struct tpax_driver_ctx_alloc * ictx)
ictx->ctx.dirents = (struct tpax_dirent_buffer *)next;
}
+ for (replstrv=ictx->ctx.replstrv; replstrv && replstrv->regexp; replstrv++)
+ regfree(&replstrv->regex);
+
+ if (ictx->ctx.replstrv)
+ free(ictx->ctx.replstrv);
+
+ if (ictx->ctx.replstrs)
+ free(ictx->ctx.replstrs);
+
+ if (ictx->ctx.keyvalv)
+ free(ictx->ctx.keyvalv);
+
+ if (ictx->ctx.cacheaddr)
+ free(ictx->ctx.cacheaddr);
+
+ if (ictx->ctx.mapaddr)
+ munmap(ictx->ctx.mapaddr,ictx->ctx.mapsize);
+
if (ictx->ctx.bufaddr)
munmap(ictx->ctx.bufaddr,ictx->ctx.bufsize);
@@ -740,6 +1055,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 2bcb486..02e0d84 100644
--- a/src/internal/tpax_driver_impl.h
+++ b/src/internal/tpax_driver_impl.h
@@ -7,6 +7,7 @@
#ifndef TPAX_DRIVER_IMPL_H
#define TPAX_DRIVER_IMPL_H
+#include <regex.h>
#include <stdint.h>
#include <dirent.h>
#include <stdio.h>
@@ -31,6 +32,9 @@
#define TPAX_ITEM_SYMLINK 0X4
#define TPAX_ITEM_NAMEREF 0x8
+#define TPAX_REPL_GLOBAL 0x01
+#define TPAX_REPL_PRINT 0x02
+
extern const struct argv_option tpax_default_options[];
enum app_tags {
@@ -44,6 +48,8 @@ enum app_tags {
TAG_FILE,
TAG_FORMAT,
TAG_BLKSIZE,
+ TAG_OPTIONS,
+ TAG_REPLSTR,
TAG_RECURSE,
TAG_NORECURSE,
TAG_STRICT_PATH,
@@ -58,7 +64,12 @@ struct tpax_dirent {
int fdat;
int depth;
int flags;
+ int nlink;
+ int cpdev;
+ int cpino;
+ dev_t srdev;
dev_t stdev;
+ ino_t stino;
size_t nsize;
const char * prefix;
const struct tpax_dirent * parent;
@@ -73,6 +84,14 @@ struct tpax_dirent_buffer {
struct tpax_dirent dbuf[];
};
+struct tpax_replstr {
+ const char * replarg;
+ const char * replstr;
+ const char * regexp;
+ regex_t regex;
+ uint32_t flags;
+};
+
struct tpax_driver_ctx_impl {
const char * file;
struct tpax_common_ctx cctx;
@@ -80,6 +99,10 @@ struct tpax_driver_ctx_impl {
struct tpax_fd_ctx fdctx;
const struct tpax_unit_ctx * euctx;
const char * eunit;
+ struct stat srcstat;
+ struct argv_keyval ** keyvalv;
+ struct tpax_replstr * replstrv;
+ char * replstrs;
struct tpax_error_info ** errinfp;
struct tpax_error_info ** erricap;
struct tpax_error_info * erriptr[64];
@@ -88,12 +111,18 @@ 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;
void * dirbuff;
void * bufaddr;
size_t bufsize;
+ void * mapaddr;
+ size_t mapsize;
+ char * cacheaddr;
+ char * cachemark;
+ char * cachecap;
size_t nqueued;
off_t cpos;
};
diff --git a/src/logic/tpax_archive_enqueue.c b/src/logic/tpax_archive_enqueue.c
index 8685cad..7d8c278 100644
--- a/src/logic/tpax_archive_enqueue.c
+++ b/src/logic/tpax_archive_enqueue.c
@@ -192,7 +192,9 @@ static int tpax_archive_add_queue_item(
const struct dirent * dirent,
const struct tpax_dirent * parent,
const char * prefix,
+ dev_t srdev,
dev_t stdev,
+ ino_t stino,
int depth,
int flags,
int fdat,
@@ -233,10 +235,21 @@ static int tpax_archive_add_queue_item(
cdent->depth = depth;
cdent->flags = flags;
cdent->stdev = stdev;
+ cdent->stino = stino;
cdent->nsize = needed;
cdent->parent = parent;
cdent->prefix = prefix;
+ switch (dirent->d_type) {
+ case DT_CHR:
+ case DT_BLK:
+ cdent->srdev = srdev;
+ break;
+
+ default:
+ cdent->srdev = 0777777;
+ }
+
memset(&cdent->dirent,0,offsetof(struct dirent,d_name));
cdent->dirent.d_ino = dirent->d_ino;
@@ -365,7 +378,9 @@ static int tpax_archive_enqueue_dir_entries(
if (tpax_archive_add_queue_item(
dctx,dirent,dent,0,
- st.st_dev,depth,
+ st.st_rdev,
+ st.st_dev,st.st_ino,
+ depth,
TPAX_ITEM_IMPLICIT,
fd,&fkeep) < 0)
return tpax_archive_enqueue_ret(
@@ -404,7 +419,9 @@ static int tpax_archive_enqueue_dir_entries(
if (tpax_archive_add_queue_item(
dctx,lnkent,cdent,0,
- lnkst.st_dev,depth+1,
+ lnkst.st_rdev,
+ lnkst.st_dev,lnkst.st_ino,
+ depth+1,
TPAX_ITEM_IMPLICIT|TPAX_ITEM_SYMLINK,
fd,&fkeep) < 0)
return tpax_archive_enqueue_ret(
@@ -515,7 +532,9 @@ int tpax_archive_enqueue(
/* add to queue */
if (tpax_archive_add_queue_item(
dctx,dirent,0,prefix,
- uctx->st->st_dev,0,
+ uctx->st->st_rdev,
+ uctx->st->st_dev,
+ uctx->st->st_ino,0,
TPAX_ITEM_EXPLICIT,
fdat,&fkeep) < 0)
return tpax_archive_enqueue_ret(
@@ -548,7 +567,9 @@ int tpax_archive_enqueue(
if (tpax_archive_add_queue_item(
dctx,lnkent,cdent,0,
- lnkst.st_dev,1,
+ lnkst.st_rdev,
+ lnkst.st_dev,lnkst.st_ino,
+ 1,
TPAX_ITEM_EXPLICIT|TPAX_ITEM_SYMLINK,
fdat,&fkeep) < 0)
return tpax_archive_enqueue_ret(
diff --git a/src/logic/tpax_archive_write.c b/src/logic/tpax_archive_write.c
index 7ce6cca..492d3b1 100644
--- a/src/logic/tpax_archive_write.c
+++ b/src/logic/tpax_archive_write.c
@@ -13,13 +13,23 @@
#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,
- void * buf,
+ const void * buf,
ssize_t nbytes)
{
- ssize_t ret;
- char * ch;
+ ssize_t ret;
+ const char * ch;
for (ch=buf; nbytes; ch+=ret) {
ret = write(fdout,ch,nbytes);
@@ -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;
@@ -75,22 +88,55 @@ static int tpax_archive_write_ret(
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;
@@ -98,6 +144,7 @@ static int tpax_archive_write_impl(
size_t cmplen;
void * membuf;
char * ch;
+ char replbuf[PATH_MAX];
char pathbuf[PATH_MAX];
/* followed symlink? */
@@ -110,13 +157,21 @@ static int tpax_archive_write_impl(
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",path);
+ tpax_dprintf(tpax_driver_fderr(dctx),"%s",apath);
/* uctx */
if (tpax_lib_get_unit_ctx(dctx,fdcwd,path,&uctx) < 0)
- tpax_archive_write_ret(
+ return tpax_archive_write_ret(
TPAX_NESTED_ERROR(dctx),
dctx,0);
@@ -165,14 +220,26 @@ 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 (tpax_meta_init_ustar_header(
- dctx,path,st,
- slnk,&uhdr) < 0)
+ 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);
@@ -214,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);
@@ -224,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? */
@@ -306,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)
@@ -319,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);
@@ -327,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..a74cf1f 100644
--- a/src/logic/tpax_queue_vector.c
+++ b/src/logic/tpax_queue_vector.c
@@ -53,6 +53,122 @@ 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++;
+ }
+
+ if (nlink > 1)
+ for (idx=2; idx<=nlink; idx++)
+ cpiov[-idx]->nlink = nlink;
+
+ return 0;
+}
+
tpax_hidden int tpax_update_queue_vector(const struct tpax_driver_ctx * dctx)
{
uintptr_t addr;
@@ -67,20 +183,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 +219,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;
+}
diff --git a/src/meta/tpax_init_ustar_header.c b/src/meta/tpax_init_ustar_header.c
index 2479e28..5078344 100644
--- a/src/meta/tpax_init_ustar_header.c
+++ b/src/meta/tpax_init_ustar_header.c
@@ -10,6 +10,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <stdbool.h>
#include <grp.h>
#include <pwd.h>
#include <sys/stat.h>
@@ -30,12 +31,12 @@ static void tpax_octal_write(char * ch, ssize_t len, uint64_t val)
}
}
-int tpax_meta_init_ustar_header(
- const struct tpax_driver_ctx * dctx,
+static int tpax_meta_init_ustar_header_impl(
const char * path,
const struct stat * st,
const char * linkname,
- struct tpax_ustar_header * uhdr)
+ struct tpax_ustar_header * uhdr,
+ bool frustar)
{
size_t len;
const char * cap;
@@ -140,7 +141,7 @@ int tpax_meta_init_ustar_header(
tpax_octal_write(uhdr->u_mode,ssizeof(uhdr->u_mode),st->st_mode & TPAX_USTAR_MODE_MASK);
/* u_uid, u_gid, u_uname, u_gname */
- if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_RUSTAR) {
+ if (frustar) {
tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),0);
tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),0);
} else {
@@ -216,3 +217,21 @@ int tpax_meta_init_ustar_header(
/* all done; caller may now change REGFILE to HARDLINK */
return 0;
}
+
+int tpax_meta_init_ustar_header(
+ const char * path,
+ const struct stat * st,
+ const char * linkname,
+ struct tpax_ustar_header * uhdr)
+{
+ return tpax_meta_init_ustar_header_impl(path,st,linkname,uhdr,false);
+}
+
+int tpax_meta_init_rustar_header(
+ const char * path,
+ const struct stat * st,
+ const char * linkname,
+ struct tpax_ustar_header * uhdr)
+{
+ return tpax_meta_init_ustar_header_impl(path,st,linkname,uhdr,true);
+}
diff --git a/src/skin/tpax_skin_default.c b/src/skin/tpax_skin_default.c
index 33ab8b8..dd62b86 100644
--- a/src/skin/tpax_skin_default.c
+++ b/src/skin/tpax_skin_default.c
@@ -88,6 +88,26 @@ const tpax_hidden struct argv_option tpax_default_options[] = {
"or directory to the archive using the name of the "
"symbolic link."},
+ {"Woptions", 'o',TAG_OPTIONS,ARGV_OPTARG_REQUIRED,
+ ARGV_OPTION_HYBRID_ONLY|ARGV_OPTION_HYBRID_SPACE|ARGV_OPTION_KEYVAL_ARRAY,0,0,
+ "a user-provided, format-specific keyval array of the form "
+ "keyword[[:]=value][,keyword[[:]=value], ...]"},
+
+ {"Wreplstr", 's',TAG_REPLSTR,ARGV_OPTARG_REQUIRED,
+ ARGV_OPTION_HYBRID_ONLY|ARGV_OPTION_HYBRID_SPACE,0,0,
+ "rename files and archive members as they are being added to "
+ "or extracted from the archive according to the specified "
+ "ed(1) style replacement string, which should be in the format "
+ "<sep><regex><sep><replstr><sep>[gp]; as an example, "
+ "-s ',^/git/tpax/,tpax-1.2.3/,' uses <comma> as the separator "
+ "character, and instructs pax to prefix all files rooted in "
+ "'/git/tpax/' with 'tpax-1.2.3/' while leaving the names of files which "
+ "do not match the regex expression unchanged. "
+ "When this option is repeated, pax shall attempt to match each file or "
+ "member name against all of the provided repalcement-string arguments "
+ "in the order of appearnce on the command line until the first "
+ "successful match."},
+
{"Wstrict-device-id",
'X',TAG_STRICT_DEVICE_ID,ARGV_OPTARG_NONE,
ARGV_OPTION_HYBRID_ONLY,0,0,
diff --git a/src/util/tpax_path_replstr.c b/src/util/tpax_path_replstr.c
new file mode 100644
index 0000000..1935628
--- /dev/null
+++ b/src/util/tpax_path_replstr.c
@@ -0,0 +1,120 @@
+/**************************************************************/
+/* tpax: a topological pax implementation */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
+/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
+/**************************************************************/
+
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <tpax/tpax.h>
+#include "tpax_driver_impl.h"
+
+static int tpax_backref_idx(const char c)
+{
+ return ((c >= '1') && (c <= '9')) ? c - '0' : 0;
+}
+
+int tpax_util_path_replstr(
+ char * dstpath,
+ const char * srcpath,
+ const char * replstr,
+ const regex_t * regex,
+ size_t buflen,
+ int flags)
+{
+ int ret;
+ int idx;
+ regoff_t ro;
+ const char * ch;
+ char * dst;
+ size_t explen;
+ regmatch_t pmatch[11];
+
+ /* attempt to match */
+ switch (regexec(regex,srcpath,11,pmatch,0)) {
+ case 0:
+ break;
+
+ case REG_NOMATCH:
+ return 0;
+
+ default:
+ return -1;
+ }
+
+ /* copy bytes leading up to match */
+ if (buflen <= (explen = pmatch[0].rm_so)) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ for (ro=0,dst=dstpath; ro<pmatch[0].rm_so; ro++)
+ *dst++ = srcpath[ro];
+
+ buflen -= explen;
+
+ /* copy replacement string */
+ for (ch=replstr,ret=0; buflen && *ch; ch++) {
+ /* <ampersand> stands for the entire matched string */
+ if (ch[0] == '&') {
+ idx = 0;
+
+ /* back-reference semantics: a matched subexpression or an empty string */
+ } else if ((ch[0] == '\\') && (idx = tpax_backref_idx(ch[1]))) {
+ if (pmatch[idx].rm_so < 0)
+ idx = -1;
+
+ ch++;
+
+ /* all other escaped characters */
+ } else if (ch[0] == '\\') {
+ *dst++ = *++ch;
+ idx = -1;
+ buflen--;
+
+ /* all other characters */
+ } else {
+ *dst++ = *ch;
+ idx = -1;
+ buflen--;
+ }
+
+ /* copy matched string or matched subexpression, if any */
+ if (idx >= 0) {
+ if (buflen <= (explen = (pmatch[idx].rm_eo - pmatch[idx].rm_so))) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ for (ro=pmatch[idx].rm_so; ro<pmatch[idx].rm_eo; ro++)
+ *dst++ = srcpath[ro];
+
+ buflen -= explen;
+ }
+ }
+
+ /* replace further occurrences as needed */
+ if ((flags & TPAX_REPL_GLOBAL) && srcpath[pmatch[0].rm_eo])
+ ret = tpax_util_path_replstr(
+ dst,&srcpath[pmatch[0].rm_eo],replstr,
+ regex,buflen,flags);
+
+ if (ret < 0)
+ return -1;
+
+ /* copy remaining, non-matching bytes as needed */
+ if (ret == 0) {
+ for (ch=&srcpath[pmatch[0].rm_eo]; *ch; ch++)
+ *dst++ = *ch;
+
+ *dst = '\0';
+ }
+
+ /* all done */
+ ret += (dst - dstpath);
+
+ return ret;
+}