diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/driver/tpax_driver_ctx.c | 71 | ||||
-rw-r--r-- | src/internal/argv/argv.h | 267 | ||||
-rw-r--r-- | src/internal/tpax_driver_impl.h | 3 | ||||
-rw-r--r-- | src/logic/tpax_archive_enqueue.c | 5 | ||||
-rw-r--r-- | src/skin/tpax_skin_default.c | 10 |
5 files changed, 336 insertions, 20 deletions
diff --git a/src/driver/tpax_driver_ctx.c b/src/driver/tpax_driver_ctx.c index d2fec77..dff6ef6 100644 --- a/src/driver/tpax_driver_ctx.c +++ b/src/driver/tpax_driver_ctx.c @@ -322,6 +322,12 @@ static void tpax_set_archive_block_size(struct tpax_common_ctx * cctx) cctx->blksize = TPAX_USTAR_BLOCK_SIZE; } +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, @@ -333,6 +339,9 @@ static struct tpax_driver_ctx_impl * tpax_driver_ctx_alloc( 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,6 +369,22 @@ static struct tpax_driver_ctx_impl * tpax_driver_ctx_alloc( if (!entry->fopt) *units++ = entry->arg; + for (entry=meta->entries,nkeyval=0; entry->fopt || entry->arg; entry++) + if (entry->keyv) + for (keyval=entry->keyv; keyval->keyword; keyval++) + nkeyval++; + + if (nkeyval && !(ictx->ctx.keyvalv = calloc(nkeyval+1,sizeof(*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; + if (cctx->drvflags & TPAX_DRIVER_EXEC_MODE_WRITE_COPY) { ictx->ctx.bufsize = TPAX_FILEIO_BUFLEN; ictx->ctx.bufaddr = mmap( @@ -369,6 +394,7 @@ static struct tpax_driver_ctx_impl * tpax_driver_ctx_alloc( -1,0); if (ictx->ctx.bufaddr == MAP_FAILED) { + free(ictx->ctx.keyvalv); free(ictx); return 0; } @@ -381,6 +407,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 +426,37 @@ 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; +} + int tpax_lib_get_driver_ctx( char ** argv, char ** envp, @@ -413,6 +471,7 @@ 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; const char * program; @@ -544,6 +603,10 @@ int tpax_lib_get_driver_ctx( cctx.drvflags |= TPAX_DRIVER_PAX_SYMLINK_ITEMS; break; + case TAG_STRICT_DEVICE_ID: + cctx.drvflags |= TPAX_DRIVER_STRICT_DEVICE_ID; + break; + case TAG_STRICT_PATH: cctx.drvflags |= TPAX_DRIVER_STRICT_PATH_INPUT; break; @@ -695,6 +758,11 @@ int tpax_lib_get_driver_ctx( return tpax_get_driver_ctx_fail(meta); } + /* keyval validation */ + for (pkeyval=ctx->keyvalv; pkeyval && *pkeyval; pkeyval++) + if (!tpax_driver_is_valid_keyval(*pkeyval)) + return tpax_driver_keyval_error(ctx,*pkeyval,program); + if (archive) { ctx->file = archive->arg; ctx->ctx.file = &ctx->file; @@ -721,6 +789,9 @@ static void tpax_free_driver_ctx_impl(struct tpax_driver_ctx_alloc * ictx) ictx->ctx.dirents = (struct tpax_dirent_buffer *)next; } + if (ictx->ctx.keyvalv) + free(ictx->ctx.keyvalv); + if (ictx->ctx.bufaddr) munmap(ictx->ctx.bufaddr,ictx->ctx.bufsize); diff --git a/src/internal/argv/argv.h b/src/internal/argv/argv.h index e6051cf..8efedda 100644 --- a/src/internal/argv/argv.h +++ b/src/internal/argv/argv.h @@ -13,6 +13,7 @@ #include <string.h> #include <stdlib.h> #include <stdio.h> +#include <ctype.h> #include <unistd.h> #define ARGV_VERBOSITY_NONE 0x00 @@ -57,16 +58,27 @@ #define ARGV_OPTION_HYBRID_EQUAL 0x04 #define ARGV_OPTION_HYBRID_COMMA 0x08 #define ARGV_OPTION_HYBRID_JOINED 0x10 + +#define ARGV_OPTION_KEYVAL_PAIR 0X20 +#define ARGV_OPTION_KEYVAL_ARRAY 0X40 +#define ARGV_OPTION_KEYVAL_MASK (ARGV_OPTION_KEYVAL_PAIR \ + | ARGV_OPTION_KEYVAL_ARRAY) + #define ARGV_OPTION_HYBRID_CIRCUS (ARGV_OPTION_HYBRID_SPACE \ | ARGV_OPTION_HYBRID_JOINED) + #define ARGV_OPTION_HYBRID_DUAL (ARGV_OPTION_HYBRID_SPACE \ | ARGV_OPTION_HYBRID_EQUAL) + #define ARGV_OPTION_HYBRID_SWITCH (ARGV_OPTION_HYBRID_ONLY \ | ARGV_OPTION_HYBRID_SPACE \ | ARGV_OPTION_HYBRID_EQUAL \ | ARGV_OPTION_HYBRID_COMMA \ | ARGV_OPTION_HYBRID_JOINED) +#define ARGV_KEYVAL_ASSIGN 0x01 +#define ARGV_KEYVAL_OVERRIDE 0x02 + enum argv_optarg { ARGV_OPTARG_NONE, ARGV_OPTARG_REQUIRED, @@ -92,6 +104,9 @@ enum argv_error { ARGV_ERROR_HYBRID_SPACE, ARGV_ERROR_HYBRID_EQUAL, ARGV_ERROR_HYBRID_COMMA, + ARGV_ERROR_KEYVAL_KEY, + ARGV_ERROR_KEYVAL_VALUE, + ARGV_ERROR_KEYVAL_ALLOC, }; struct argv_option { @@ -105,13 +120,20 @@ struct argv_option { const char * description; }; +struct argv_keyval { + const char * keyword; + const char * value; + int flags; +}; + struct argv_entry { - const char * arg; - int tag; - bool fopt; - bool fval; - bool fnoscan; - enum argv_error errcode; + const char * arg; + struct argv_keyval * keyv; + int tag; + bool fopt; + bool fval; + bool fnoscan; + enum argv_error errcode; }; struct argv_meta { @@ -129,6 +151,7 @@ struct argv_ctx { const char * errch; const struct argv_option * erropt; const char * program; + size_t keyvlen; }; #ifdef ARGV_DRIVER @@ -136,6 +159,8 @@ struct argv_ctx { struct argv_meta_impl { char ** argv; char * strbuf; + char * keyvbuf; + char * keyvmark; struct argv_meta meta; }; @@ -305,12 +330,131 @@ static inline const struct argv_option * option_from_tag( return 0; } +static inline int argv_scan_keyval_array(struct argv_meta_impl * meta, struct argv_entry * entry) +{ + const char * ch; + char * dst; + int ncomma; + int cint; + struct argv_keyval * keyv; + + /* count key[val] elements, assume no comma after last element */ + for (ch=entry->arg,ncomma=1; *ch; ch++) { + if ((ch[0]=='\\') && (ch[1]==',')) { + ch++; + + } else if ((ch[0]==',')) { + ncomma++; + } + } + + /* keyval string buffer */ + dst = meta->keyvmark; + + /* allocate keyval array */ + if (!(entry->keyv = calloc(ncomma+1,sizeof(*entry->keyv)))) + return ARGV_ERROR_KEYVAL_ALLOC; + + /* create keyval array */ + entry->keyv->keyword = dst; + + for (ch=entry->arg,keyv=entry->keyv; *ch; ch++) { + if ((ch[0]=='\\') && (ch[1]==',')) { + *dst++ = ','; + ch++; + + } else if ((ch[0]==':') && (ch[1]=='=')) { + if (!keyv->keyword[0]) + return ARGV_ERROR_KEYVAL_KEY; + + keyv->flags = ARGV_KEYVAL_OVERRIDE; + keyv->value = ++dst; + ch++; + + } else if ((ch[0]=='=')) { + if (!keyv->keyword[0]) + return ARGV_ERROR_KEYVAL_KEY; + + keyv->flags = ARGV_KEYVAL_ASSIGN; + keyv->value = ++dst; + + } else if ((ch[0]==',')) { + for (; isblank(cint = ch[1]); ) + ch++; + + if (ch[1]) { + keyv++; + keyv->keyword = ++dst; + } + } else { + *dst++ = *ch; + } + } + + /* keyval string buffer */ + meta->keyvmark = ++dst; + + return ARGV_ERROR_OK; +} + +static inline int argv_scan_keyval_pair(struct argv_meta_impl * meta, struct argv_entry * entry) +{ + const char * ch; + char * dst; + struct argv_keyval * keyv; + + /* keyval string buffer */ + dst = meta->keyvmark; + + /* allocate keyval array */ + if (!(entry->keyv = calloc(2,sizeof(*entry->keyv)))) + return ARGV_ERROR_KEYVAL_ALLOC; + + /* create keyval array */ + entry->keyv->keyword = dst; + + for (ch=entry->arg,keyv=entry->keyv; *ch && !keyv->flags; ch++) { + if ((ch[0]=='\\') && (ch[1]==',')) { + *dst++ = ','; + ch++; + + } else if ((ch[0]==':') && (ch[1]=='=')) { + if (!keyv->keyword[0]) + return ARGV_ERROR_KEYVAL_KEY; + + keyv->flags = ARGV_KEYVAL_OVERRIDE; + keyv->value = ++dst; + ch++; + + } else if ((ch[0]=='=')) { + if (!keyv->keyword[0]) + return ARGV_ERROR_KEYVAL_KEY; + + keyv->flags = ARGV_KEYVAL_ASSIGN; + keyv->value = ++dst; + + } else { + *dst++ = *ch; + } + } + + for (; *ch; ) + *dst++ = *ch++; + + /* keyval string buffer */ + meta->keyvmark = ++dst; + + return ARGV_ERROR_OK; +} + static void argv_scan( char ** argv, const struct argv_option ** optv, struct argv_ctx * ctx, struct argv_meta * meta) { + struct argv_meta_impl * imeta; + uintptr_t addr; char ** parg; const char * ch; const char * val; @@ -324,6 +468,9 @@ static void argv_scan( bool fhybrid; bool fnoscan; + addr = (uintptr_t)meta - offsetof(struct argv_meta_impl,meta); + imeta = (struct argv_meta_impl *)addr; + parg = &argv[1]; ch = *parg; ferr = ARGV_ERROR_OK; @@ -495,13 +642,20 @@ static void argv_scan( if (!option && !ctx->unitidx) ctx->unitidx = parg - argv; + if (ferr == ARGV_ERROR_OK) + if (option && (option->flags & ARGV_OPTION_KEYVAL_MASK)) + if (ctx->mode == ARGV_MODE_SCAN) + ctx->keyvlen += strlen(ch) + 1; + if (ferr != ARGV_ERROR_OK) { ctx->errcode = ferr; ctx->errch = ctx->errch ? ctx->errch : ch; ctx->erropt = option; ctx->erridx = parg - argv; return; - } else if (ctx->mode == ARGV_MODE_SCAN) { + } + + if (ctx->mode == ARGV_MODE_SCAN) { if (!fnoscan) ctx->nentries++; else if (fval) @@ -511,12 +665,12 @@ static void argv_scan( parg++; ch = *parg; } + } else if (ctx->mode == ARGV_MODE_COPY) { if (fnoscan) { if (fval) { mentry->arg = ch; mentry->fnoscan = true; - mentry++; } parg++; @@ -526,7 +680,6 @@ static void argv_scan( mentry->tag = option->tag; mentry->fopt = true; mentry->fval = fval; - mentry++; if (fval) { parg++; @@ -534,11 +687,28 @@ static void argv_scan( } } else { mentry->arg = ch; - mentry++; parg++; ch = *parg; } } + + if (option && (option->flags & ARGV_OPTION_KEYVAL_PAIR)) + if (ctx->mode == ARGV_MODE_COPY) + ferr = argv_scan_keyval_pair(imeta,mentry); + + if (option && (option->flags & ARGV_OPTION_KEYVAL_ARRAY)) + if (ctx->mode == ARGV_MODE_COPY) + ferr = argv_scan_keyval_array(imeta,mentry); + + if (ferr != ARGV_ERROR_OK) { + ctx->errcode = ferr; + ctx->errch = ctx->errch ? ctx->errch : ch; + ctx->erropt = option; + ctx->erridx = parg - argv; + return; + } + + mentry++; } } @@ -663,6 +833,14 @@ static void argv_show_error(int fd, struct argv_ctx * ctx) break; + case ARGV_ERROR_KEYVAL_KEY: + argv_dprintf(fd,"illegal key detected in keyval argument\n"); + break; + + case ARGV_ERROR_KEYVAL_VALUE: + argv_dprintf(fd,"illegal value detected in keyval argument\n"); + break; + case ARGV_ERROR_INTERNAL: argv_dprintf(fd,"internal error"); break; @@ -678,8 +856,10 @@ static void argv_show_status( struct argv_ctx * ctx, struct argv_meta * meta) { + int i; int argc; char ** argv; + struct argv_keyval * keyv; struct argv_entry * entry; const struct argv_option * option; char short_name[2] = {0}; @@ -698,7 +878,7 @@ static void argv_show_status( argv_dprintf(fd,"argv[%d]: %s\n",argc,*argv); argv_dprintf(fd,"\n\nparsed entries:\n"); - for (entry=meta->entries; entry->arg || entry->fopt; entry++) + for (entry=meta->entries; entry->arg || entry->fopt; entry++) { if (entry->fopt) { option = option_from_tag(optv,entry->tag); short_name[0] = option->short_name; @@ -709,14 +889,48 @@ static void argv_show_status( else argv_dprintf(fd,"[-%s,--%s]\n", short_name,option->long_name); - } else + + if (entry->keyv) { + for (i=0,keyv=entry->keyv; keyv->keyword; i++,keyv++) { + switch (keyv->flags) { + case ARGV_KEYVAL_ASSIGN: + argv_dprintf(fd,"\tkeyval[%d]: <%s>=%s\n", + i,keyv->keyword,keyv->value); + break; + + case ARGV_KEYVAL_OVERRIDE: + argv_dprintf(fd,"\tkeyval[%d]: <%s>:=%s\n", + i,keyv->keyword,keyv->value); + break; + + default: + argv_dprintf(fd,"\tkeyval[%d]: <%s>\n", + i,keyv->keyword); + break; + } + } + } + } else { argv_dprintf(fd,"<program arg> := %s\n",entry->arg); + } + } argv_dprintf(fd,"\n\n"); } static struct argv_meta * argv_free_impl(struct argv_meta_impl * imeta) { + struct argv_entry * entry; + void * addr; + + if (imeta->keyvbuf) + for (entry=imeta->meta.entries; entry->fopt || entry->arg; entry++) + if (entry->keyv) + free((addr = entry->keyv)); + + if (imeta->keyvbuf) + free(imeta->keyvbuf); + if (imeta->argv) free(imeta->argv); @@ -750,7 +964,8 @@ static struct argv_meta * argv_alloc(char ** argv, struct argv_ctx * ctx) if (!(imeta->argv = calloc(argc+1,sizeof(char *)))) return argv_free_impl(imeta); - else if (!(imeta->strbuf = calloc(1,size+1))) + + if (!(imeta->strbuf = calloc(1,size+1))) return argv_free_impl(imeta); for (i=0,dst=imeta->strbuf; i<argc; i++) { @@ -760,15 +975,27 @@ static struct argv_meta * argv_alloc(char ** argv, struct argv_ctx * ctx) } imeta->meta.argv = imeta->argv; - } else + } else { imeta->meta.argv = argv; + } + + imeta->meta.entries = calloc( + ctx->nentries+1, + sizeof(struct argv_entry)); - if (!(imeta->meta.entries = calloc( - ctx->nentries+1, - sizeof(struct argv_entry)))) + if (!imeta->meta.entries) return argv_free_impl(imeta); - else - return &imeta->meta; + + if (ctx->keyvlen) { + imeta->keyvbuf = calloc( + ctx->keyvlen, + sizeof(char)); + + if (!(imeta->keyvmark = imeta->keyvbuf)) + return argv_free_impl(imeta); + } + + return &imeta->meta; } static struct argv_meta * argv_get( @@ -778,7 +1005,7 @@ static struct argv_meta * argv_get( int fd) { struct argv_meta * meta; - struct argv_ctx ctx = {flags,ARGV_MODE_SCAN,0,0,0,0,0,0,0}; + struct argv_ctx ctx = {flags,ARGV_MODE_SCAN,0,0,0,0,0,0,0,0}; argv_scan(argv,optv,&ctx,0); diff --git a/src/internal/tpax_driver_impl.h b/src/internal/tpax_driver_impl.h index 23e9e23..d340748 100644 --- a/src/internal/tpax_driver_impl.h +++ b/src/internal/tpax_driver_impl.h @@ -44,6 +44,7 @@ enum app_tags { TAG_FILE, TAG_FORMAT, TAG_BLKSIZE, + TAG_OPTIONS, TAG_RECURSE, TAG_NORECURSE, TAG_STRICT_PATH, @@ -51,6 +52,7 @@ enum app_tags { TAG_PRESERVE_ATIME, TAG_PAX_SYMLINK_ARGS, TAG_PAX_SYMLINK_ITEMS, + TAG_STRICT_DEVICE_ID, }; struct tpax_dirent { @@ -79,6 +81,7 @@ struct tpax_driver_ctx_impl { struct tpax_fd_ctx fdctx; const struct tpax_unit_ctx * euctx; const char * eunit; + struct argv_keyval ** keyvalv; struct tpax_error_info ** errinfp; struct tpax_error_info ** erricap; struct tpax_error_info * erriptr[64]; diff --git a/src/logic/tpax_archive_enqueue.c b/src/logic/tpax_archive_enqueue.c index 0d8c28f..8685cad 100644 --- a/src/logic/tpax_archive_enqueue.c +++ b/src/logic/tpax_archive_enqueue.c @@ -304,6 +304,11 @@ static int tpax_archive_enqueue_dir_entries( TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR), uctx); + /* ensure physical device identity as needed */ + if (dctx->cctx->drvflags & TPAX_DRIVER_STRICT_DEVICE_ID) + if (dent->parent && (uctx->st->st_dev != dent->parent->stdev)) + return 0; + /* obtain buffer for file-system directory entries */ dirents = tpax_get_driver_getdents_buffer(dctx); dirent = dirents; diff --git a/src/skin/tpax_skin_default.c b/src/skin/tpax_skin_default.c index ffbc2d2..a618645 100644 --- a/src/skin/tpax_skin_default.c +++ b/src/skin/tpax_skin_default.c @@ -88,6 +88,16 @@ 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], ...]"}, + + {"Wstrict-device-id", + 'X',TAG_STRICT_DEVICE_ID,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, + "do not recurse into directories across device boundaries"}, + {"Wstrict-path-input", 0,TAG_STRICT_PATH,ARGV_OPTARG_NONE, ARGV_OPTION_HYBRID_ONLY,0,0, |