diff options
-rw-r--r-- | include/tpax/tpax.h | 15 | ||||
-rw-r--r-- | project/common.mk | 4 | ||||
-rw-r--r-- | project/headers.mk | 1 | ||||
-rw-r--r-- | src/driver/tpax_amain.c | 2 | ||||
-rw-r--r-- | src/driver/tpax_driver_ctx.c | 299 | ||||
-rw-r--r-- | src/driver/tpax_unit_ctx.c | 2 | ||||
-rw-r--r-- | src/internal/argv/argv.h | 281 | ||||
-rw-r--r-- | src/internal/tpax_driver_impl.h | 30 | ||||
-rw-r--r-- | src/internal/tpax_ftime_impl.c | 48 | ||||
-rw-r--r-- | src/internal/tpax_ftime_impl.h | 24 | ||||
-rw-r--r-- | src/io/tpax_create_memory_snapshot.c | 8 | ||||
-rw-r--r-- | src/io/tpax_create_tmpfs_snapshot.c | 10 | ||||
-rw-r--r-- | src/logic/tpax_archive_enqueue.c (renamed from src/logic/tpax_archive_append.c) | 246 | ||||
-rw-r--r-- | src/logic/tpax_archive_reset.c | 59 | ||||
-rw-r--r-- | src/logic/tpax_archive_write.c | 169 | ||||
-rw-r--r-- | src/logic/tpax_queue_vector.c | 10 | ||||
-rw-r--r-- | src/skin/tpax_skin_default.c | 88 |
17 files changed, 1075 insertions, 221 deletions
diff --git a/include/tpax/tpax.h b/include/tpax/tpax.h index 6a43415..00271cb 100644 --- a/include/tpax/tpax.h +++ b/include/tpax/tpax.h @@ -34,7 +34,8 @@ extern "C" { #define TPAX_DRIVER_CLONE_VECTOR 0x0008 #define TPAX_DRIVER_VERSION 0x0010 -#define TPAX_DRIVER_DRY_RUN 0x0020 +#define TPAX_DRIVER_VERBOSE 0x0020 +#define TPAX_DRIVER_DRY_RUN 0x0080 #define TPAX_DRIVER_EXEC_MODE_LIST 0x0100 #define TPAX_DRIVER_EXEC_MODE_READ 0x0200 @@ -54,6 +55,13 @@ extern "C" { #define TPAX_DRIVER_STRICT_PATH_INPUT 0x200000 #define TPAX_DRIVER_PURE_PATH_OUTPUT 0x400000 +#define TPAX_DRIVER_PRESERVE_ATIME 0x1000000 +#define TPAX_DRIVER_PRESERVE_MTIME 0x2000000 +#define TPAX_DRIVER_PAX_SYMLINK_ARGS 0x4000000 +#define TPAX_DRIVER_PAX_SYMLINK_ITEMS 0x8000000 + +#define TPAX_DRIVER_STRICT_DEVICE_ID 0X10000000 + /* error flags */ #define TPAX_ERROR_TOP_LEVEL 0x0001 #define TPAX_ERROR_NESTED 0x0002 @@ -117,6 +125,7 @@ struct tpax_driver_ctx { const char ** units; const char * program; const char * module; + const char * const * file; const struct tpax_common_ctx * cctx; struct tpax_error_info ** errv; const off_t * cpos; @@ -151,10 +160,12 @@ tpax_api int tpax_lib_get_driver_fdctx (const struct tpax_driver_ctx *, str tpax_api int tpax_lib_set_driver_fdctx (struct tpax_driver_ctx *, const struct tpax_fd_ctx *); /* core api */ -tpax_api int tpax_archive_append (const struct tpax_driver_ctx *, const struct tpax_unit_ctx *); +tpax_api int tpax_archive_enqueue (const struct tpax_driver_ctx *, const struct tpax_unit_ctx *); tpax_api int tpax_archive_write (const struct tpax_driver_ctx *); +tpax_api int tpax_archive_reset (const struct tpax_driver_ctx *); + tpax_api int tpax_archive_seal (const struct tpax_driver_ctx *); /* utility helper interfaces */ diff --git a/project/common.mk b/project/common.mk index a23c2ef..26e171c 100644 --- a/project/common.mk +++ b/project/common.mk @@ -4,7 +4,8 @@ API_SRCS = \ src/driver/tpax_unit_ctx.c \ src/io/tpax_create_memory_snapshot.c \ src/io/tpax_create_tmpfs_snapshot.c \ - src/logic/tpax_archive_append.c \ + src/logic/tpax_archive_enqueue.c \ + src/logic/tpax_archive_reset.c \ src/logic/tpax_archive_write.c \ src/logic/tpax_queue_vector.c \ src/meta/tpax_init_ustar_header.c \ @@ -16,6 +17,7 @@ API_SRCS = \ INTERNAL_SRCS = \ src/internal/$(PACKAGE)_dprintf_impl.c \ src/internal/$(PACKAGE)_errinfo_impl.c \ + src/internal/$(PACKAGE)_ftime_impl.c \ src/internal/$(PACKAGE)_tmpfile_impl.c \ APP_SRCS = \ diff --git a/project/headers.mk b/project/headers.mk index e495f1b..a5ee670 100644 --- a/project/headers.mk +++ b/project/headers.mk @@ -8,6 +8,7 @@ INTERNAL_HEADERS = \ $(SOURCE_DIR)/src/internal/tpax_dprintf_impl.h \ $(SOURCE_DIR)/src/internal/tpax_driver_impl.h \ $(SOURCE_DIR)/src/internal/tpax_errinfo_impl.h \ + $(SOURCE_DIR)/src/internal/tpax_ftime_impl.h \ $(SOURCE_DIR)/src/internal/tpax_getdents_impl.h \ $(SOURCE_DIR)/src/internal/tpax_readlink_impl.h \ $(SOURCE_DIR)/src/internal/tpax_tmpfile_impl.h \ diff --git a/src/driver/tpax_amain.c b/src/driver/tpax_amain.c index 61f53d1..429633e 100644 --- a/src/driver/tpax_amain.c +++ b/src/driver/tpax_amain.c @@ -54,7 +54,7 @@ static void tpax_perform_unit_actions( struct tpax_unit_ctx * uctx) { if (dctx->cctx->drvflags & TPAX_DRIVER_EXEC_MODE_WRITE) - tpax_archive_append(dctx,uctx); + tpax_archive_enqueue(dctx,uctx); } static void tpax_archive_write_and_seal( diff --git a/src/driver/tpax_driver_ctx.c b/src/driver/tpax_driver_ctx.c index 8ab684d..dff6ef6 100644 --- a/src/driver/tpax_driver_ctx.c +++ b/src/driver/tpax_driver_ctx.c @@ -86,9 +86,9 @@ static int tpax_driver_usage( snprintf(header,sizeof(header), "%s — topological pax implementation\n\n" "Synopsis:\n" - " %s [-d]\n" - " %s -r [-d]\n" - " %s -w [−x format] [-b blocksize] [-d]\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 -r -w [-d]\n\n" "Options:\n", program,program,program,program,program); @@ -112,9 +112,9 @@ static int tpax_driver_usage_exec_mode( tpax_dprintf( fdout, - "\nWhen using explicit (long) mode options, " + "\n%s: usage error: When using explicit (long) mode options, " "only one of --list, --read, --write, --copy " - "may be used.\n"); + "may be used.\n",program); return TPAX_USAGE; } @@ -125,19 +125,27 @@ static int tpax_driver_usage_copy_mode( const char * arg, const struct argv_option ** optv, struct argv_meta * meta, - struct argv_entry * operand) + struct argv_entry * operand, + struct argv_entry * archive) { const char * errdesc; - if (!operand) { + if (archive || !operand) { tpax_driver_usage( fdout,program, arg,optv,meta); - tpax_dprintf( - fdout, - "\nThe copy mode requires a destination " - "directory argument.\n\n"); + if (archive) { + tpax_dprintf( + fdout, + "\n%s: usage error: the __copy__ mode does not permit specifying " + "an archive path.\n\n",program); + } else { + tpax_dprintf( + fdout, + "\n%s: usage error: the __copy__ mode requires a destination " + "directory argument.\n\n",program); + } return TPAX_USAGE; } @@ -146,7 +154,7 @@ static int tpax_driver_usage_copy_mode( case ENOENT: tpax_dprintf( fdout, - "%s: error: " + "%s: file-system error: " "the destination directory '%s' " "does not exist.\n\n", program,operand->arg); @@ -155,7 +163,7 @@ static int tpax_driver_usage_copy_mode( case ENOTDIR: tpax_dprintf( fdout, - "%s: error: " + "%s: file-system error: " "'%s' is not a directory.\n\n", program,operand->arg); break; @@ -166,7 +174,7 @@ static int tpax_driver_usage_copy_mode( tpax_dprintf( fdout, - "%s: error: while opening the " + "%s: general error: while opening the " "destination directory '%s': %s.\n\n", program,operand->arg,errdesc); break; @@ -190,8 +198,8 @@ static int tpax_driver_usage_write_format( tpax_dprintf( fdout, - "\nArchive format may only be specified " - "in write mode.\n"); + "\n%s: usage error: Archive format may only be specified " + "in write mode.\n",program); return TPAX_USAGE; } @@ -200,17 +208,14 @@ static int tpax_driver_usage_block_size( int fdout, const char * program, const char * arg, - const struct argv_option ** optv, struct argv_meta * meta) { - tpax_driver_usage( - fdout,program, - arg,optv,meta); - tpax_dprintf( fdout, - "`%s' is not a valid positive decimal integer.\n", - arg); + "%s: usage error: the requested block size `%s' is not a positive decimal integer.\n", + program,arg); + + argv_free(meta); return TPAX_USAGE; } @@ -219,21 +224,70 @@ static int tpax_driver_usage_block_size_range( int fdout, const char * program, const char * arg, - const struct argv_option ** optv, struct argv_meta * meta) { - tpax_driver_usage( - fdout,program, - arg,optv,meta); + tpax_dprintf( + fdout, + "%s: usage error: `%s' is outside the specified range of 512 to 32256.\n", + program,arg); + + argv_free(meta); + return TPAX_USAGE; +} + +static int tpax_driver_usage_block_constraints( + int fdout, + const char * program, + const char * arg, + struct argv_meta * meta) +{ tpax_dprintf( fdout, - "`%s' is outside the specified range of 512 to 32256.\n", - arg); + "%s: usage error: the specified block size `%s' is not a multiple of 512 bytes.\n", + program,arg); + + argv_free(meta); return TPAX_USAGE; } +static int tpax_driver_error_archive_path( + int fdout, + const char * program, + const char * arg, + struct argv_meta * meta, + bool fwrite) +{ + int lerrno; + const char * errstr; + + lerrno = errno; + errno = 0; + + errstr = strerror(lerrno); + errstr = errno ? "" : errstr; + errno = lerrno; + + if (fwrite) { + tpax_dprintf( + fdout, + "%s: file-system error: archive file <%s> could not be created " + "or opened for writing (%s).\n", + program,arg,errstr); + } else { + tpax_dprintf( + fdout, + "%s: file-system error: archive file <%s> could not be opened " + "for reading (%s).\n", + program,arg,errstr); + } + + argv_free(meta); + + return TPAX_FATAL; +} + static int tpax_driver_error_not_implemented( int fdout, const char * program, @@ -250,6 +304,30 @@ static int tpax_driver_error_not_implemented( return TPAX_FATAL; } +static void tpax_set_archive_block_size(struct tpax_common_ctx * cctx) +{ + if (cctx->blksize) + (void)0; + + else if (cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_PAX) + cctx->blksize = TPAX_PAX_BLOCK_SIZE; + + else if (cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) + cctx->blksize = TPAX_CPIO_BLOCK_SIZE; + + else if (cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_USTAR) + cctx->blksize = TPAX_USTAR_BLOCK_SIZE; + + else if (cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_RUSTAR) + 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, @@ -261,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 *); @@ -288,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( @@ -297,24 +394,27 @@ 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; } - if (cctx->drvflags & TPAX_DRIVER_DIR_MEMBER_RECURSE) - ictx->ctx.dirbuff = mmap( - 0,TPAX_DIRENT_BUFLEN, - PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS, - -1,0); + ictx->ctx.dirbuff = mmap( + 0,TPAX_DIRENT_BUFLEN, + PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, + -1,0); if (ictx->ctx.dirbuff == MAP_FAILED) { munmap(ictx->ctx.bufaddr,ictx->ctx.bufsize); + free(ictx->ctx.keyvalv); free(ictx); return 0; } } + tpax_set_archive_block_size(&ictx->ctx.cctx); + ictx->ctx.ctx.units = ictx->units; ictx->ctx.ctx.errv = ictx->ctx.errinfp; return &ictx->ctx; @@ -326,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, @@ -338,7 +469,10 @@ int tpax_lib_get_driver_ctx( const struct argv_option * optv[TPAX_OPTV_ELEMENTS]; struct argv_meta * meta; 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; int fddst; @@ -358,6 +492,7 @@ int tpax_lib_get_driver_ctx( return -1; nunits = 0; + archive = 0; operand = 0; program = argv_program_name(argv[0]); memset(&cctx,0,sizeof(cctx)); @@ -381,6 +516,10 @@ int tpax_lib_get_driver_ctx( cctx.drvflags |= TPAX_DRIVER_VERSION; break; + case TAG_VERBOSE: + cctx.drvflags |= TPAX_DRIVER_VERBOSE; + break; + case TAG_LIST: cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_LIST; break; @@ -397,6 +536,10 @@ int tpax_lib_get_driver_ctx( cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_COPY; break; + case TAG_FILE: + archive = entry; + break; + case TAG_FORMAT: cctx.drvflags &= ~(uint64_t)(TPAX_DRIVER_WRITE_FORMAT_MASK); @@ -419,17 +562,23 @@ int tpax_lib_get_driver_ctx( for (; *ch; ch++) if ((*ch < '0') || (*ch > '9')) return tpax_driver_usage_block_size( - fdctx->fdout, + fdctx->fderr, program,entry->arg, - optv,meta); + meta); cctx.blksize = atoi(entry->arg); if ((cctx.blksize < 512) || (cctx.blksize > 32256)) return tpax_driver_usage_block_size_range( - fdctx->fdout, + fdctx->fderr, program,entry->arg, - optv,meta); + meta); + + if (cctx.blksize % 512) + return tpax_driver_usage_block_constraints( + fdctx->fderr, + program,entry->arg, + meta); break; case TAG_RECURSE: @@ -440,6 +589,24 @@ int tpax_lib_get_driver_ctx( cctx.drvflags &= ~(uintptr_t)TPAX_DRIVER_DIR_MEMBER_RECURSE; break; + case TAG_PRESERVE_ATIME: + cctx.drvflags |= TPAX_DRIVER_PRESERVE_ATIME; + break; + + case TAG_PAX_SYMLINK_ARGS: + cctx.drvflags |= TPAX_DRIVER_PAX_SYMLINK_ARGS; + cctx.drvflags &= ~(uint64_t)TPAX_DRIVER_PAX_SYMLINK_ITEMS; + break; + + case TAG_PAX_SYMLINK_ITEMS: + cctx.drvflags |= TPAX_DRIVER_PAX_SYMLINK_ARGS; + 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; @@ -482,11 +649,11 @@ int tpax_lib_get_driver_ctx( /* copy mode: destination directory */ if (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_COPY) { - if (!operand) + if (archive || !operand) return tpax_driver_usage_copy_mode( fdctx->fderr, program,entry->arg, - optv,meta,operand); + optv,meta,operand,archive); fddst = openat( fdctx->fdcwd, @@ -497,7 +664,42 @@ int tpax_lib_get_driver_ctx( return tpax_driver_usage_copy_mode( fdctx->fderr, program,entry->arg, - optv,meta,operand); + optv,meta,operand,archive); + } + + /* archive path */ + if (archive && (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_WRITE)) { + memcpy(&lfdctx,fdctx,sizeof(*fdctx)); + + lfdctx.fdout = openat( + fdctx->fdcwd, + archive->arg, + O_WRONLY|O_CREAT|O_TRUNC, + 0644); + + if (lfdctx.fdout < 0) + return tpax_driver_error_archive_path( + fdctx->fderr, + program,archive->arg, + meta,true); + + fdctx = &lfdctx; + + } else if (archive) { + memcpy(&lfdctx,fdctx,sizeof(*fdctx)); + + lfdctx.fdin = openat( + fdctx->fdcwd, + archive->arg, + O_RDONLY,0); + + if (lfdctx.fdin < 0) + return tpax_driver_error_archive_path( + fdctx->fderr, + program,archive->arg, + meta,false); + + fdctx = &lfdctx; } /* not implemented mode(s) */ @@ -556,6 +758,16 @@ 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; + } + ctx->ctx.program = program; ctx->ctx.cctx = &ctx->cctx; @@ -577,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/driver/tpax_unit_ctx.c b/src/driver/tpax_unit_ctx.c index 1fbc510..5c4cf80 100644 --- a/src/driver/tpax_unit_ctx.c +++ b/src/driver/tpax_unit_ctx.c @@ -65,7 +65,7 @@ int tpax_lib_get_unit_ctx( } ctx->path = path; - ctx->link = ctx->linkbuf; + ctx->link = ctx->linkbuf[0] ? ctx->linkbuf : 0; ctx->uctx.path = &ctx->path; ctx->uctx.link = &ctx->link; diff --git a/src/internal/argv/argv.h b/src/internal/argv/argv.h index 87c60d2..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; + } - if (!(imeta->meta.entries = calloc( - ctx->nentries+1, - sizeof(struct argv_entry)))) + 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); @@ -833,6 +1060,7 @@ static void argv_usage_impl( { const struct argv_option ** optv; const struct argv_option * option; + int nlong; bool fshort,flong,fboth; size_t len,optlen,desclen; char cache; @@ -865,7 +1093,7 @@ static void argv_usage_impl( if (header) argv_dprintf(fd,"%s",header); - for (optlen=0,optv=options; *optv; optv++) { + for (optlen=0,nlong=0,optv=options; *optv; optv++) { option = *optv; /* indent + comma */ @@ -884,6 +1112,11 @@ static void argv_usage_impl( /* optlen */ if (len > optlen) optlen = len; + + /* long (vs. hybrid-only) option? */ + if (option->long_name) + if (!(option->flags & ARGV_OPTION_HYBRID_ONLY)) + nlong++; } if (optlen >= optcap) { @@ -916,7 +1149,11 @@ static void argv_usage_impl( /* long/hybrid option prefix (-/--) */ prefix = option->flags & ARGV_OPTION_HYBRID_ONLY - ? " -" : "--"; + ? " -" : " --"; + + /* avoid extra <stace> when all long opts are hybrid-only */ + if (nlong == 0) + prefix++; /* option string */ if (fboth && option->short_name && option->long_name) diff --git a/src/internal/tpax_driver_impl.h b/src/internal/tpax_driver_impl.h index cd734b0..d340748 100644 --- a/src/internal/tpax_driver_impl.h +++ b/src/internal/tpax_driver_impl.h @@ -28,28 +28,38 @@ #define TPAX_ITEM_EXPLICIT 0X1 #define TPAX_ITEM_IMPLICIT 0X2 +#define TPAX_ITEM_SYMLINK 0X4 +#define TPAX_ITEM_NAMEREF 0x8 extern const struct argv_option tpax_default_options[]; enum app_tags { TAG_HELP, TAG_VERSION, + TAG_VERBOSE, TAG_LIST, TAG_READ, TAG_WRITE, TAG_COPY, + TAG_FILE, TAG_FORMAT, TAG_BLKSIZE, + TAG_OPTIONS, TAG_RECURSE, TAG_NORECURSE, TAG_STRICT_PATH, TAG_PURE_PATH, + TAG_PRESERVE_ATIME, + TAG_PAX_SYMLINK_ARGS, + TAG_PAX_SYMLINK_ITEMS, + TAG_STRICT_DEVICE_ID, }; struct tpax_dirent { int fdat; int depth; int flags; + dev_t stdev; size_t nsize; const char * prefix; const struct tpax_dirent * parent; @@ -65,11 +75,13 @@ struct tpax_dirent_buffer { }; struct tpax_driver_ctx_impl { + const char * file; struct tpax_common_ctx cctx; struct tpax_driver_ctx ctx; 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]; @@ -258,24 +270,6 @@ static inline void tpax_set_unit_dpos(const struct tpax_unit_ctx * uctx, off_t d ictx->dpos = dpos; } -static inline ssize_t tpax_get_archive_block_size(const struct tpax_driver_ctx * dctx) -{ - if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_PAX) - return TPAX_PAX_BLOCK_SIZE; - - else if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO) - return TPAX_CPIO_BLOCK_SIZE; - - else if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_USTAR) - return TPAX_USTAR_BLOCK_SIZE; - - else if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_RUSTAR) - return TPAX_USTAR_BLOCK_SIZE; - - else - return 0; -} - int tpax_update_queue_vector(const struct tpax_driver_ctx * dctx); const char * tpax_queue_item_full_path( diff --git a/src/internal/tpax_ftime_impl.c b/src/internal/tpax_ftime_impl.c new file mode 100644 index 0000000..19c6d7b --- /dev/null +++ b/src/internal/tpax_ftime_impl.c @@ -0,0 +1,48 @@ +/**************************************************************/ +/* tpax: a topological pax implementation */ +/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */ +/**************************************************************/ + +#include <time.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include "tpax_ftime_impl.h" +#include "tpax_driver_impl.h" +#include "tpax_visibility_impl.h" + +tpax_hidden void tpax_ftime_restore( + const struct tpax_driver_ctx * dctx, + int fd, + const struct stat * refst) +{ + struct timespec ts[2]; + + if (dctx->cctx->drvflags & TPAX_DRIVER_PRESERVE_ATIME) { + ts[0].tv_sec = refst->st_atim.tv_sec; + ts[0].tv_nsec = refst->st_atim.tv_nsec; + } else { + ts[0].tv_nsec = UTIME_OMIT; + } + + if (dctx->cctx->drvflags & TPAX_DRIVER_PRESERVE_MTIME) { + ts[1].tv_sec = refst->st_mtim.tv_sec; + ts[1].tv_nsec = refst->st_mtim.tv_nsec; + } else { + ts[1].tv_nsec = UTIME_OMIT; + } + + if ((ts[0].tv_nsec != UTIME_OMIT) || (ts[1].tv_nsec != UTIME_OMIT)) { + futimens(fd,ts); + } +} + +tpax_hidden void tpax_ftime_restore_and_close( + const struct tpax_driver_ctx * dctx, + int fd, + const struct stat * refst) +{ + tpax_ftime_restore(dctx,fd,refst); + close(fd); +} diff --git a/src/internal/tpax_ftime_impl.h b/src/internal/tpax_ftime_impl.h new file mode 100644 index 0000000..2d0e981 --- /dev/null +++ b/src/internal/tpax_ftime_impl.h @@ -0,0 +1,24 @@ +/**************************************************************/ +/* tpax: a topological pax implementation */ +/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */ +/**************************************************************/ + +#ifndef TPAX_FTIME_IMPL_H +#define TPAX_FTIME_IMPL_H + +#include <time.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include "tpax_driver_impl.h" + +void tpax_ftime_restore( + const struct tpax_driver_ctx *, + int fd, const struct stat *); + +void tpax_ftime_restore_and_close( + const struct tpax_driver_ctx *, + int fd, const struct stat *); + +#endif diff --git a/src/io/tpax_create_memory_snapshot.c b/src/io/tpax_create_memory_snapshot.c index 7c3dd7e..6be087f 100644 --- a/src/io/tpax_create_memory_snapshot.c +++ b/src/io/tpax_create_memory_snapshot.c @@ -18,6 +18,7 @@ #include <tpax/tpax_specs.h> #include "tpax_driver_impl.h" #include "tpax_errinfo_impl.h" +#include "tpax_ftime_impl.h" #ifndef ssizeof #define ssizeof(x) (ssize_t)(sizeof(x)) @@ -69,11 +70,11 @@ int tpax_io_create_memory_snapshot( nread = read(fd,ch,cap-ch); if (nread < 0) { - close(fd); + tpax_ftime_restore_and_close(dctx,fd,&dstst); return TPAX_SYSTEM_ERROR(dctx); } else if (nread == 0) { - close(fd); + tpax_ftime_restore_and_close(dctx,fd,&dstst); return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR); } else { @@ -81,6 +82,9 @@ int tpax_io_create_memory_snapshot( } } + /* preserve last data access time as needed */ + tpax_ftime_restore(dctx,fd,&dstst); + /* stat compare */ if ((fstat(fd,&dstst)) < 0) { close(fd); diff --git a/src/io/tpax_create_tmpfs_snapshot.c b/src/io/tpax_create_tmpfs_snapshot.c index 9891e98..99a1665 100644 --- a/src/io/tpax_create_tmpfs_snapshot.c +++ b/src/io/tpax_create_tmpfs_snapshot.c @@ -19,6 +19,7 @@ #include "tpax_driver_impl.h" #include "tpax_tmpfile_impl.h" #include "tpax_errinfo_impl.h" +#include "tpax_ftime_impl.h" #ifndef ssizeof #define ssizeof(x) (ssize_t)(sizeof(x)) @@ -78,12 +79,12 @@ int tpax_io_create_tmpfs_snapshot( nbytes = read(fdsrc,buf,buflen); if (nbytes < 0) { - close(fdsrc); + tpax_ftime_restore_and_close(dctx,fdsrc,&dstst); close(fdtmp); return TPAX_SYSTEM_ERROR(dctx); } else if (nbytes == 0) { - close(fdsrc); + tpax_ftime_restore_and_close(dctx,fdsrc,&dstst); close(fdtmp); return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR); @@ -98,7 +99,7 @@ int tpax_io_create_tmpfs_snapshot( ret = write(fdtmp,ch,nbytes); if (ret < 0) { - close(fdsrc); + tpax_ftime_restore_and_close(dctx,fdsrc,&dstst); close(fdtmp); return TPAX_SYSTEM_ERROR(dctx); @@ -108,6 +109,9 @@ int tpax_io_create_tmpfs_snapshot( } } + /* preserve last data access time as needed */ + tpax_ftime_restore(dctx,fdsrc,&dstst); + /* stat compare */ if ((fstat(fdsrc,&dstst)) < 0) { close(fdsrc); diff --git a/src/logic/tpax_archive_append.c b/src/logic/tpax_archive_enqueue.c index 6ed414b..8685cad 100644 --- a/src/logic/tpax_archive_append.c +++ b/src/logic/tpax_archive_enqueue.c @@ -19,10 +19,11 @@ #include <tpax/tpax_specs.h> #include "tpax_driver_impl.h" #include "tpax_getdents_impl.h" +#include "tpax_readlink_impl.h" #include "tpax_tmpfile_impl.h" #include "tpax_errinfo_impl.h" -static char * tpax_append_prefix_item( +static char * tpax_add_prefix_item( const struct tpax_driver_ctx * dctx, const char * prefix) { @@ -65,7 +66,7 @@ static char * tpax_append_prefix_item( return pitem; } -static char * tpax_append_prefix_item_from_path( +static char * tpax_add_prefix_item_from_path( const struct tpax_driver_ctx * dctx, const char * path, const char * mark) @@ -80,7 +81,46 @@ static char * tpax_append_prefix_item_from_path( pathbuf[nbytes++] = '/'; pathbuf[nbytes] = '\0'; - return tpax_append_prefix_item(dctx,pathbuf); + return tpax_add_prefix_item(dctx,pathbuf); +} + +static int tpax_dirent_init_from_stat( + const struct stat * st, + const char * basename, + struct dirent * dirent) +{ + /* st_mode to d_type translation */ + if (S_ISREG(st->st_mode)) + dirent->d_type = DT_REG; + else if (S_ISLNK(st->st_mode)) + dirent->d_type = DT_LNK; + else if (S_ISDIR(st->st_mode)) + dirent->d_type = DT_DIR; + else if (S_ISCHR(st->st_mode)) + dirent->d_type = DT_CHR; + else if (S_ISBLK(st->st_mode)) + dirent->d_type = DT_CHR; + else if (S_ISFIFO(st->st_mode)) + dirent->d_type = DT_CHR; + else + return -1; + + /* d_off, d_ino */ + dirent->d_off = 0; + dirent->d_ino = st->st_ino; + + /* d_reclen */ + dirent->d_reclen = offsetof(struct dirent,d_name); + dirent->d_reclen += strlen(basename) + 1; + + dirent->d_reclen += 0x1; + dirent->d_reclen |= 0x1; + dirent->d_reclen ^= 0x1; + + /* d_name */ + strcpy(dirent->d_name,basename); + + return 0; } static struct tpax_dirent_buffer * tpax_dirent_buf_first_alloc( @@ -137,7 +177,7 @@ static struct tpax_dirent_buffer * tpax_dirent_buf_next_alloc( return current->next; } -static int tpax_archive_append_ret( +static int tpax_archive_enqueue_ret( int ret, struct tpax_unit_ctx * unit) { @@ -147,11 +187,12 @@ static int tpax_archive_append_ret( return ret; } -static int tpax_archive_append_queue_item( +static int tpax_archive_add_queue_item( const struct tpax_driver_ctx * dctx, const struct dirent * dirent, const struct tpax_dirent * parent, const char * prefix, + dev_t stdev, int depth, int flags, int fdat, @@ -166,7 +207,7 @@ static int tpax_archive_append_queue_item( if (!(dentbuf = tpax_get_driver_dirents(dctx))) if (!(dentbuf = tpax_dirent_buf_first_alloc(dctx))) - return tpax_archive_append_ret( + return tpax_archive_enqueue_ret( TPAX_SYSTEM_ERROR(dctx), 0); @@ -181,7 +222,7 @@ static int tpax_archive_append_queue_item( if (dentbuf->nfree < needed) if (!(dentbuf = tpax_dirent_buf_next_alloc(dentbuf))) - return tpax_archive_append_ret( + return tpax_archive_enqueue_ret( TPAX_SYSTEM_ERROR(dctx), 0); @@ -191,6 +232,7 @@ static int tpax_archive_append_queue_item( cdent->fdat = fdat; cdent->depth = depth; cdent->flags = flags; + cdent->stdev = stdev; cdent->nsize = needed; cdent->parent = parent; cdent->prefix = prefix; @@ -222,20 +264,28 @@ static int tpax_archive_append_queue_item( return 0; } -static int tpax_archive_append_dir_entries( +static int tpax_archive_enqueue_dir_entries( const struct tpax_driver_ctx * dctx, struct tpax_dirent * dent) { int fd; int fdat; + int fdlnk; int depth; bool fkeep; + bool flinks; + bool fstdev; long nbytes; + struct dirent * lnkent; struct dirent * dirent; struct dirent * dirents; + struct tpax_dirent * cdent; struct tpax_unit_ctx * uctx; struct stat st; + struct stat lnkst; uintptr_t addr; + char lnktgt[PATH_MAX]; + char lnkbuf[PATH_MAX + sizeof(struct dirent)]; /* init */ fdat = dent->fdat; @@ -250,10 +300,15 @@ static int tpax_archive_append_dir_entries( /* verify that recursion item is still a directory */ if (!S_ISDIR(uctx->st->st_mode)) - return tpax_archive_append_ret( + return tpax_archive_enqueue_ret( 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; @@ -263,22 +318,28 @@ static int tpax_archive_append_dir_entries( /* open directory and obtain first directory entries */ if ((fd = openat(fdat,dent->dirent.d_name,O_RDONLY|O_DIRECTORY|O_CLOEXEC,0)) < 0) - return tpax_archive_append_ret( + return tpax_archive_enqueue_ret( TPAX_SYSTEM_ERROR(dctx), uctx); + lnkent = (struct dirent *)lnkbuf; + flinks = (dctx->cctx->drvflags & TPAX_DRIVER_PAX_SYMLINK_ITEMS); + fstdev = (dctx->cctx->drvflags & TPAX_DRIVER_STRICT_DEVICE_ID); nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN); + /* debugging, struct stat initialization when fstdev is false */ + memset(&st,0,sizeof(st)); + while ((nbytes == -EINTR) || ((nbytes < 0) && (errno == EINTR))) nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN); if (nbytes < 0) - return tpax_archive_append_ret( + return tpax_archive_enqueue_ret( TPAX_SYSTEM_ERROR(dctx), uctx); /* iterate */ - for (; nbytes>0; ) { + for (; nbytes; ) { if (!strcmp(dirent->d_name,".")) { (void)0; @@ -288,23 +349,68 @@ static int tpax_archive_append_dir_entries( } else { if (dirent->d_type == DT_UNKNOWN) { if (fstatat(fd,dirent->d_name,&st,AT_SYMLINK_NOFOLLOW)) - return tpax_archive_append_ret( + return tpax_archive_enqueue_ret( TPAX_SYSTEM_ERROR(dctx), uctx); if (S_ISDIR(st.st_mode)) { dirent->d_type = DT_DIR; } + } else if (fstdev) { + if (fstatat(fd,dirent->d_name,&st,AT_SYMLINK_NOFOLLOW)) + return tpax_archive_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + uctx); } - if (tpax_archive_append_queue_item( - dctx,dirent, - dent,0,depth, + if (tpax_archive_add_queue_item( + dctx,dirent,dent,0, + st.st_dev,depth, TPAX_ITEM_IMPLICIT, fd,&fkeep) < 0) - return tpax_archive_append_ret( + return tpax_archive_enqueue_ret( TPAX_NESTED_ERROR(dctx), uctx); + + /* follow encountered symlink arguments as needed */ + fdlnk = (flinks && (dirent->d_type == DT_LNK)) + ? openat(fd,dirent->d_name,O_RDONLY|O_CLOEXEC) + : (-1); + + if (fdlnk >= 0) { + if (fstat(fdlnk,&lnkst) <0) { + close(fdlnk); + return tpax_archive_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + uctx); + } + + if (tpax_readlinkat(fd,dirent->d_name,lnktgt,PATH_MAX) < 0) + return tpax_archive_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + uctx); + + close(fdlnk); + + if (tpax_dirent_init_from_stat(&lnkst,lnktgt,lnkent) < 0) + return tpax_archive_enqueue_ret( + TPAX_CUSTOM_ERROR( + dctx, + TPAX_ERR_FLOW_ERROR), + 0); + + cdent = tpax_get_driver_dirmark(dctx); + cdent->flags |= TPAX_ITEM_NAMEREF; + + if (tpax_archive_add_queue_item( + dctx,lnkent,cdent,0, + lnkst.st_dev,depth+1, + TPAX_ITEM_IMPLICIT|TPAX_ITEM_SYMLINK, + fd,&fkeep) < 0) + return tpax_archive_enqueue_ret( + TPAX_NESTED_ERROR(dctx), + 0); + } } addr = (uintptr_t)dirent; @@ -319,7 +425,7 @@ static int tpax_archive_append_dir_entries( nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN); if (nbytes < 0) - tpax_archive_append_ret( + tpax_archive_enqueue_ret( TPAX_SYSTEM_ERROR(dctx), uctx); @@ -328,7 +434,7 @@ static int tpax_archive_append_dir_entries( } /* all done */ - return tpax_archive_append_ret( + return tpax_archive_enqueue_ret( fkeep ? 0 : close(fd), uctx); } @@ -356,50 +462,12 @@ static const char * tpax_path_prefix_mark(const char * path) return (mark <= pathbuf) ? 0 : &path[--mark-pathbuf]; } -static int tpax_dirent_init_from_uctx( - const struct stat * st, - const char * basename, - struct dirent * dirent) -{ - /* st_mode to d_type translation */ - if (S_ISREG(st->st_mode)) - dirent->d_type = DT_REG; - else if (S_ISLNK(st->st_mode)) - dirent->d_type = DT_LNK; - else if (S_ISDIR(st->st_mode)) - dirent->d_type = DT_DIR; - else if (S_ISCHR(st->st_mode)) - dirent->d_type = DT_CHR; - else if (S_ISBLK(st->st_mode)) - dirent->d_type = DT_CHR; - else if (S_ISFIFO(st->st_mode)) - dirent->d_type = DT_CHR; - else - return -1; - - /* d_off, d_ino */ - dirent->d_off = 0; - dirent->d_ino = st->st_ino; - - /* d_reclen */ - dirent->d_reclen = offsetof(struct dirent,d_name); - dirent->d_reclen += strlen(basename) + 1; - - dirent->d_reclen += 0x1; - dirent->d_reclen |= 0x1; - dirent->d_reclen ^= 0x1; - - /* d_name */ - strcpy(dirent->d_name,basename); - - return 0; -} - -int tpax_archive_append( +int tpax_archive_enqueue( const struct tpax_driver_ctx * dctx, const struct tpax_unit_ctx * uctx) { int fdat; + int fdlnk; uintptr_t addr; const char * name; const char * mark; @@ -409,18 +477,22 @@ int tpax_archive_append( struct tpax_dirent * cdent; struct tpax_dirent * cnext; struct dirent * dirent; + struct dirent * lnkent; + struct stat lnkst; char entbuf[PATH_MAX + sizeof(struct dirent)]; + char lnkbuf[PATH_MAX + sizeof(struct dirent)]; /* init */ fdat = tpax_driver_fdcwd(dctx); dirent = (struct dirent *)entbuf; + lnkent = (struct dirent *)lnkbuf; prefix = 0; /* split path to prefix + basename */ if ((mark = tpax_path_prefix_mark(*uctx->path))) - if (!(prefix = tpax_append_prefix_item_from_path( + if (!(prefix = tpax_add_prefix_item_from_path( dctx,*uctx->path,mark))) - return tpax_archive_append_ret( + return tpax_archive_enqueue_ret( TPAX_BUFFER_ERROR(dctx), 0); @@ -428,35 +500,71 @@ int tpax_archive_append( if (prefix) if ((fdat = openat(fdat,prefix,O_RDONLY|O_DIRECTORY|O_CLOEXEC,0)) < 0) - return tpax_archive_append_ret( + return tpax_archive_enqueue_ret( TPAX_SYSTEM_ERROR(dctx), 0); /* explicit item directory entry */ - if (tpax_dirent_init_from_uctx(uctx->st,name,dirent) < 0) - return tpax_archive_append_ret( + if (tpax_dirent_init_from_stat(uctx->st,name,dirent) < 0) + return tpax_archive_enqueue_ret( TPAX_CUSTOM_ERROR( dctx, TPAX_ERR_FLOW_ERROR), 0); /* add to queue */ - if (tpax_archive_append_queue_item( - dctx,dirent,0,prefix,0, + if (tpax_archive_add_queue_item( + dctx,dirent,0,prefix, + uctx->st->st_dev,0, TPAX_ITEM_EXPLICIT, fdat,&fkeep) < 0) - return tpax_archive_append_ret( + return tpax_archive_enqueue_ret( TPAX_NESTED_ERROR(dctx), 0); + /* follow command-line symlink arguments as needed */ + fdlnk = (uctx->link[0] && (dctx->cctx->drvflags & TPAX_DRIVER_PAX_SYMLINK_ARGS)) + ? openat(fdat,uctx->link[0],O_RDONLY|O_CLOEXEC) : (-1); + + if (fdlnk >= 0) { + if (fstat(fdlnk,&lnkst) <0) { + close(fdlnk); + return tpax_archive_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + 0); + } + + close(fdlnk); + + if (tpax_dirent_init_from_stat(&lnkst,uctx->link[0],lnkent) < 0) + return tpax_archive_enqueue_ret( + TPAX_CUSTOM_ERROR( + dctx, + TPAX_ERR_FLOW_ERROR), + 0); + + cdent = tpax_get_driver_dirmark(dctx); + cdent->flags |= TPAX_ITEM_NAMEREF; + + if (tpax_archive_add_queue_item( + dctx,lnkent,cdent,0, + lnkst.st_dev,1, + TPAX_ITEM_EXPLICIT|TPAX_ITEM_SYMLINK, + fdat,&fkeep) < 0) + return tpax_archive_enqueue_ret( + TPAX_NESTED_ERROR(dctx), + 0); + } + /* queue directory child items */ dentbuf = tpax_get_driver_dirents(dctx); cdent = tpax_get_driver_dirmark(dctx); for (; cdent; ) { if (cdent->dirent.d_type == DT_DIR) - if (tpax_archive_append_dir_entries(dctx,cdent) < 0) - return TPAX_NESTED_ERROR(dctx); + if (dctx->cctx->drvflags & TPAX_DRIVER_DIR_MEMBER_RECURSE) + if (tpax_archive_enqueue_dir_entries(dctx,cdent) < 0) + return TPAX_NESTED_ERROR(dctx); addr = (uintptr_t)cdent; addr += cdent->nsize; diff --git a/src/logic/tpax_archive_reset.c b/src/logic/tpax_archive_reset.c new file mode 100644 index 0000000..52a9008 --- /dev/null +++ b/src/logic/tpax_archive_reset.c @@ -0,0 +1,59 @@ +/**************************************************************/ +/* 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 <sys/mman.h> + +#include <tpax/tpax.h> +#include "tpax_driver_impl.h" + +/**********************************************/ +/* release and reset the following objects: */ +/* */ +/* - item queue */ +/* - queue vector */ +/* - cached prefixes */ +/* - prefix vector */ +/* */ +/**********************************************/ + +int tpax_archive_reset(const struct tpax_driver_ctx * dctx) +{ + struct tpax_driver_ctx_impl * ictx; + void * next; + size_t size; + char ** ppref; + + ictx = tpax_get_driver_ictx(dctx); + + for (; ictx->dirents; ) { + next = ictx->dirents->next; + size = ictx->dirents->size; + + munmap(ictx->dirents,size); + ictx->dirents = (struct tpax_dirent_buffer *)next; + } + + for (ppref=ictx->prefixv; *ppref; ppref++) + free(*ppref); + + for (ppref=ictx->prefptr; ppref<ictx->prefcap; ppref++) + *ppref = 0; + + if (ictx->prefixv != ictx->prefptr) + free(ictx->prefixv); + + if (ictx->direntv) + free(ictx->direntv); + + ictx->nqueued = 0; + ictx->dirents = 0; + ictx->direntv = 0; + ictx->prefixv = ictx->prefptr; + + return 0; +} diff --git a/src/logic/tpax_archive_write.c b/src/logic/tpax_archive_write.c index 5214787..7ce6cca 100644 --- a/src/logic/tpax_archive_write.c +++ b/src/logic/tpax_archive_write.c @@ -64,9 +64,13 @@ static int tpax_archive_append_pad( } static int tpax_archive_write_ret( - int ret, - struct tpax_unit_ctx * uctx) + int ret, + const struct tpax_driver_ctx * dctx, + struct tpax_unit_ctx * uctx) { + if (dctx->cctx->drvflags & TPAX_DRIVER_VERBOSE) + tpax_dprintf(tpax_driver_fderr(dctx),"\n",0); + tpax_lib_free_unit_ctx(uctx); return ret; } @@ -79,7 +83,11 @@ static int tpax_archive_write_impl( { struct tpax_unit_ctx * uctx; struct tpax_ustar_header uhdr; + const struct stat * st; + struct stat stbuf; const char * path; + const char * slnk; + const char * mlnk; off_t hpos; off_t dpos; int fdtmp; @@ -89,6 +97,12 @@ static int tpax_archive_write_impl( size_t buflen; size_t cmplen; void * membuf; + char * ch; + char pathbuf[PATH_MAX]; + + /* followed symlink? */ + if (cdent->flags & TPAX_ITEM_NAMEREF) + return 0; /* full path */ if (!(path = tpax_queue_item_full_path(dctx,cdent))) @@ -96,9 +110,56 @@ static int tpax_archive_write_impl( dctx, TPAX_ERR_FLOW_ERROR); + /* verbose mode */ + if (dctx->cctx->drvflags & TPAX_DRIVER_VERBOSE) + tpax_dprintf(tpax_driver_fderr(dctx),"%s",path); + /* uctx */ if (tpax_lib_get_unit_ctx(dctx,fdcwd,path,&uctx) < 0) - return TPAX_NESTED_ERROR(dctx); + tpax_archive_write_ret( + TPAX_NESTED_ERROR(dctx), + dctx,0); + + st = uctx->st; + slnk = uctx->link[0]; + mlnk = 0; + + if (cdent->flags & TPAX_ITEM_SYMLINK) { + st = &stbuf; + mlnk = slnk; + slnk = 0; + ch = 0; + + if (mlnk[0] != '/') { + if (strlen(path) >= PATH_MAX) + return tpax_archive_write_ret( + TPAX_CUSTOM_ERROR( + dctx, + TPAX_ERR_FLOW_ERROR), + dctx,uctx); + + strcpy(pathbuf,path); + + ch = strrchr(pathbuf,'/'); + } + + if (ch && (++ch - pathbuf >= PATH_MAX)) + return tpax_archive_write_ret( + TPAX_CUSTOM_ERROR( + dctx, + TPAX_ERR_FLOW_ERROR), + dctx,uctx); + + if (ch) { + strcpy(ch,mlnk); + mlnk = pathbuf; + } + + if (fstatat(fdcwd,mlnk,&stbuf,0) < 0) + return tpax_archive_write_ret( + TPAX_SYSTEM_ERROR(dctx), + dctx,uctx); + } /* record errors */ tpax_driver_set_ectx( @@ -110,44 +171,46 @@ static int tpax_archive_write_impl( /* header */ if (tpax_meta_init_ustar_header( - dctx,path,uctx->st, - *uctx->link,&uhdr) < 0) + dctx,path,st, + slnk,&uhdr) < 0) return tpax_archive_write_ret( TPAX_NESTED_ERROR(dctx), - uctx); + dctx,uctx); /* buffer */ membuf = 0; fdtmp = -1; /* associated data? */ - if S_ISREG(uctx->st->st_mode) { + if S_ISREG(st->st_mode) { buf = tpax_get_driver_anon_map_addr( dctx,&buflen); - if (buflen >= (cmplen = uctx->st->st_size)) + if (buflen >= (cmplen = st->st_size)) membuf = buf; /* snapshot */ if (membuf) { if (tpax_io_create_memory_snapshot( - dctx,fdcwd,path, - uctx->st,membuf) < 0) + dctx,fdcwd, + mlnk ? mlnk : path, + st,membuf) < 0) return tpax_archive_write_ret( TPAX_NESTED_ERROR(dctx), - uctx); + dctx,uctx); } else { if ((fdtmp = tpax_io_create_tmpfs_snapshot( - dctx,fdcwd,path, - uctx->st)) < 0) + dctx,fdcwd, + mlnk ? mlnk : path, + st)) < 0) return tpax_archive_write_ret( TPAX_NESTED_ERROR(dctx), - uctx); + dctx,uctx); if (lseek(fdtmp,0,SEEK_SET) < 0) return tpax_archive_write_ret( TPAX_SYSTEM_ERROR(dctx), - uctx); + dctx,uctx); } } @@ -158,23 +221,22 @@ static int tpax_archive_write_impl( return tpax_archive_write_ret( TPAX_SYSTEM_ERROR(dctx), - uctx); + dctx,uctx); } tpax_set_driver_cpos(dctx,dpos); /* all done? */ - if (!(S_ISREG(uctx->st->st_mode))) { - tpax_lib_free_unit_ctx(uctx); - return 0; - } + if (!(S_ISREG(st->st_mode))) + return tpax_archive_write_ret( + 0,dctx,uctx); /* append data from snapshot */ if (fdtmp >= 0) { buf = tpax_get_driver_anon_map_addr( dctx,&buflen); - for (nread=0; nread<uctx->st->st_size; ) { + for (nread=0; nread<st->st_size; ) { nbytes = read(fdtmp,buf,buflen); while ((nbytes < 0) && (errno == EINTR)) @@ -184,13 +246,13 @@ static int tpax_archive_write_impl( close(fdtmp); return tpax_archive_write_ret( TPAX_SYSTEM_ERROR(dctx), - uctx); + dctx,uctx); } else if (nbytes == 0) { close(fdtmp); return tpax_archive_write_ret( TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR), - uctx); + dctx,uctx); } else { nread += nbytes; @@ -200,7 +262,7 @@ static int tpax_archive_write_impl( close(fdtmp); return tpax_archive_write_ret( TPAX_SYSTEM_ERROR(dctx), - uctx); + dctx,uctx); } } @@ -208,15 +270,15 @@ static int tpax_archive_write_impl( } else { if (tpax_archive_append_memory_data( fdout,membuf, - uctx->st->st_size) < 0) + st->st_size) < 0) return tpax_archive_write_ret( TPAX_SYSTEM_ERROR(dctx), - uctx); + dctx,uctx); } return tpax_archive_write_ret( - tpax_archive_append_pad(dctx,fdout,uctx->st), - uctx); + tpax_archive_append_pad(dctx,fdout,st), + dctx,uctx); } int tpax_archive_write(const struct tpax_driver_ctx * dctx) @@ -242,6 +304,12 @@ int tpax_archive_write(const struct tpax_driver_ctx * dctx) return 0; } +static int tpax_archive_seal_cpio(const struct tpax_driver_ctx * dctx) +{ + (void)dctx; + return -1; +} + int tpax_archive_seal(const struct tpax_driver_ctx * dctx) { int fdout; @@ -249,37 +317,52 @@ int tpax_archive_seal(const struct tpax_driver_ctx * dctx) ssize_t nbytes; ssize_t nwritten; ssize_t blksize; - char buf[512]; + char buf[1024]; - blksize = tpax_get_archive_block_size(dctx); + /* archive block size, current position */ + blksize = dctx->cctx->blksize; cpos = tpax_get_driver_cpos(dctx); + /* internal error? */ 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)); - switch (cpos % blksize) { - case 0: - nbytes = cpos + blksize; - break; + if (tpax_archive_append_memory_data(fdout,buf,2*512) < 0) + return TPAX_SYSTEM_ERROR(dctx); - default: - nbytes = cpos / blksize; - nbytes *= blksize; - nbytes += blksize; + cpos += 2*512; - if (nbytes-cpos == 512) - nbytes += blksize; + /* pax? */ + if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_PAX) { + tpax_set_driver_cpos(dctx,cpos); + return 0; } - for (nwritten=cpos; nwritten<nbytes; nwritten+=512) { + /* 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); - tpax_set_driver_cpos(dctx,nwritten); - } + /* all done */ + tpax_set_driver_cpos(dctx,nwritten); return 0; } diff --git a/src/logic/tpax_queue_vector.c b/src/logic/tpax_queue_vector.c index 82930b1..cae4da0 100644 --- a/src/logic/tpax_queue_vector.c +++ b/src/logic/tpax_queue_vector.c @@ -38,11 +38,17 @@ tpax_hidden const char * tpax_queue_item_full_path( ch += sprintf(ch,"%s",pdirent[0]->prefix); for (; pdirent > dirstck; ) { - ch += sprintf(ch,"%s/",pdirent[0]->dirent.d_name); + if (!(pdirent[0]->flags & TPAX_ITEM_SYMLINK)) + ch += sprintf(ch,"%s/",pdirent[0]->dirent.d_name); + pdirent--; } - sprintf(ch,"%s",pdirent[0]->dirent.d_name); + if (pdirent[0]->flags & TPAX_ITEM_SYMLINK) { + *--ch = '\0'; + } else { + sprintf(ch,"%s",pdirent[0]->dirent.d_name); + } return pathbuf; } diff --git a/src/skin/tpax_skin_default.c b/src/skin/tpax_skin_default.c index 68491df..a618645 100644 --- a/src/skin/tpax_skin_default.c +++ b/src/skin/tpax_skin_default.c @@ -3,31 +3,50 @@ #include "argv/argv.h" const tpax_hidden struct argv_option tpax_default_options[] = { - {"version", 0,TAG_VERSION,ARGV_OPTARG_NONE,0,0,0, + {"Wversion", 0,TAG_VERSION,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, "show version information"}, - {"help", 0,TAG_HELP,ARGV_OPTARG_OPTIONAL,0,"short|long",0, - "show usage information [listing %s options only]"}, + {"Whelp", 0,TAG_HELP,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, + "show usage information"}, - {"list", 0,TAG_LIST,ARGV_OPTARG_NONE,0,0,0, + {"Wlist", 0,TAG_LIST,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, "list mode (output names of archive members)"}, - {"read", 'r',TAG_READ,ARGV_OPTARG_NONE,0,0,0, + {"Wread", 'r',TAG_READ,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, "read mode (extract matching archive members)"}, - {"write", 'w',TAG_WRITE,ARGV_OPTARG_NONE,0,0,0, + {"Write", 'w',TAG_WRITE,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, "write mode (add specified files to archive)"}, - {"copy", 0,TAG_COPY,ARGV_OPTARG_NONE,0,0,0, + {"Wcopy", 0,TAG_COPY,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, "copy mode (copy specified files " "to a specified destination directory)"}, + {"Wfile", 'f',TAG_FILE,ARGV_OPTARG_REQUIRED, + ARGV_OPTION_HYBRID_ONLY|ARGV_OPTION_HYBRID_EQUAL, + 0,"<ARCHIVE>", + "read from (in read or list modes), " + "or write to (in write mode) the specified %s " + "after (in write mode) creating it as necessary"}, - {"format", 'x',TAG_FORMAT,ARGV_OPTARG_REQUIRED,0, + {"Wformat", 'x',TAG_FORMAT,ARGV_OPTARG_REQUIRED, + ARGV_OPTION_HYBRID_ONLY|ARGV_OPTION_HYBRID_EQUAL, "pax|cpio|ustar|rustar",0, "archive format [%s]"}, - {"blksize", 'b',TAG_BLKSIZE,ARGV_OPTARG_REQUIRED,0,0,0, + {"Wverbose", 'v',TAG_VERBOSE,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, + "write pathnames to stderr in read, write, and copy modes; " + "produce verbose output in list mode."}, + + {"Wblksize", 'b',TAG_BLKSIZE,ARGV_OPTARG_REQUIRED, + ARGV_OPTION_HYBRID_ONLY|ARGV_OPTION_HYBRID_EQUAL,0,0, "(non-default) block-size; valid values are " "in the range of 512 to 32256; keeping " "the default format-specific block size " @@ -35,20 +54,59 @@ const tpax_hidden struct argv_option tpax_default_options[] = { " 10240 for the ustar format) " "is strongly recommended."}, - {"recurse", 0,TAG_RECURSE,ARGV_OPTARG_NONE,0,0,0, + {"Wrecurse", 0,TAG_RECURSE,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, "recurse into directory archive members " "(this is the tpax_main() default)"}, - {"no-recurse", 'd',TAG_NORECURSE,ARGV_OPTARG_NONE,0,0,0, + {"Wno-recurse",'d',TAG_NORECURSE,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, "do not recurse into directory archive members"}, - {"strict-path-input", - 0,TAG_STRICT_PATH,ARGV_OPTARG_NONE,0,0,0, + {"Wpreserve-atime", + 't',TAG_PRESERVE_ATIME,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, + "when user has the necessary permissions, " + "set the access time of each file to the access " + "time that was reported by fstatat(3) prior to the " + "first read operation performed by pax"}, + + {"Wpax-symlink-args", + 'H',TAG_PAX_SYMLINK_ARGS,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, + "when a command-line argument is a symbolic link, " + "add the underlying (referenced) file-system object " + "or directory to the archive using the name of the " + "symbolic link."}, + + {"Wpax-symlink-items", + 'L',TAG_PAX_SYMLINK_ITEMS,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, + "when a command-line argument is a symbolic link, or when " + "an item added by way of recursion is a symbolic link, " + "add the underlying (referenced) file-system object " + "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, "do not allow file arguments (in write and copy modes) " "to contain parent-directoy (dot dot) references"}, - {"pure-path-output", - 0,TAG_PURE_PATH,ARGV_OPTARG_NONE,0,0,0, + {"Wpure-path-output", + 0,TAG_PURE_PATH,ARGV_OPTARG_NONE, + ARGV_OPTION_HYBRID_ONLY,0,0, "output (in list mode) or store (in write mode) path " "names in pure form, specifically by liminating all " "this-dir (dot) elements from the listed/stored path " |