diff options
-rw-r--r-- | include/tpax/tpax.h | 27 | ||||
-rw-r--r-- | include/tpax/tpax_specs.h | 27 | ||||
-rw-r--r-- | project/common.mk | 2 | ||||
-rw-r--r-- | src/driver/tpax_driver_ctx.c | 376 | ||||
-rw-r--r-- | src/internal/tpax_driver_impl.h | 29 | ||||
-rw-r--r-- | src/logic/tpax_archive_enqueue.c | 29 | ||||
-rw-r--r-- | src/logic/tpax_archive_write.c | 204 | ||||
-rw-r--r-- | src/logic/tpax_queue_vector.c | 134 | ||||
-rw-r--r-- | src/meta/tpax_init_cpio_header.c | 145 | ||||
-rw-r--r-- | src/meta/tpax_init_ustar_header.c | 27 | ||||
-rw-r--r-- | src/skin/tpax_skin_default.c | 20 | ||||
-rw-r--r-- | src/util/tpax_path_replstr.c | 120 |
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; +} |