summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/tpax/tpax.h15
-rw-r--r--project/common.mk4
-rw-r--r--project/headers.mk1
-rw-r--r--src/driver/tpax_amain.c2
-rw-r--r--src/driver/tpax_driver_ctx.c299
-rw-r--r--src/driver/tpax_unit_ctx.c2
-rw-r--r--src/internal/argv/argv.h281
-rw-r--r--src/internal/tpax_driver_impl.h30
-rw-r--r--src/internal/tpax_ftime_impl.c48
-rw-r--r--src/internal/tpax_ftime_impl.h24
-rw-r--r--src/io/tpax_create_memory_snapshot.c8
-rw-r--r--src/io/tpax_create_tmpfs_snapshot.c10
-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.c59
-rw-r--r--src/logic/tpax_archive_write.c169
-rw-r--r--src/logic/tpax_queue_vector.c10
-rw-r--r--src/skin/tpax_skin_default.c88
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 "