summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore16
-rw-r--r--COPYING.TPAX2
-rw-r--r--include/tpax/tpax.h72
-rw-r--r--project/common.mk16
-rw-r--r--project/extras.mk2
-rw-r--r--project/headers.mk2
-rw-r--r--project/tree.mk4
-rw-r--r--src/driver/tpax_amain.c31
-rw-r--r--src/driver/tpax_driver_ctx.c327
-rw-r--r--src/driver/tpax_unit_ctx.c8
-rw-r--r--src/internal/argv/argv.h281
-rw-r--r--src/internal/tpax_dprintf_impl.c6
-rw-r--r--src/internal/tpax_driver_impl.h73
-rw-r--r--src/internal/tpax_errinfo_impl.c5
-rw-r--r--src/internal/tpax_errinfo_impl.h2
-rw-r--r--src/internal/tpax_ftime_impl.c48
-rw-r--r--src/internal/tpax_ftime_impl.h24
-rw-r--r--src/internal/tpax_getdents_impl.h2
-rw-r--r--src/internal/tpax_readlink_impl.h2
-rw-r--r--src/internal/tpax_tmpfile_impl.c6
-rw-r--r--src/internal/tpax_visibility_impl.h26
-rw-r--r--src/io/tpax_create_memory_snapshot.c (renamed from src/logic/tpax_file_create_memory_snapshot.c)16
-rw-r--r--src/io/tpax_create_tmpfs_snapshot.c (renamed from src/logic/tpax_file_create_tmpfs_snapshot.c)18
-rw-r--r--src/logic/tpax_archive_append.c709
-rw-r--r--src/logic/tpax_archive_enqueue.c582
-rw-r--r--src/logic/tpax_archive_reset.c59
-rw-r--r--src/logic/tpax_archive_write.c368
-rw-r--r--src/logic/tpax_queue_vector.c101
-rw-r--r--src/meta/tpax_init_ustar_header.c (renamed from src/logic/tpax_init_ustar_header.c)4
-rw-r--r--src/output/tpax_output_error.c2
-rw-r--r--src/skin/tpax_skin_default.c91
-rw-r--r--src/tpax.c2
-rw-r--r--src/util/tpax_path_copy.c (renamed from src/helper/tpax_path_copy.c)4
-rw-r--r--src/util/tpax_stat_compare.c (renamed from src/helper/tpax_stat_compare.c)4
34 files changed, 2015 insertions, 900 deletions
diff --git a/.gitignore b/.gitignore
index 796d153..5df402d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,23 @@
+Makefile
+app.tag
+dirs.tag
+host.tag
+tree.tag
+version.tag
+
+cfgdefs.mk
+usrdefs.mk
+ccenv/host.mk
+ccenv/native.mk
+
+config.log
+
*~
*.o
*.a
*.so
*.gch
*.exe
-*.lib.a
-Makefile
/bin
/build
/lib
diff --git a/COPYING.TPAX b/COPYING.TPAX
index d145097..8f58e04 100644
--- a/COPYING.TPAX
+++ b/COPYING.TPAX
@@ -2,7 +2,7 @@
/* */
/* tpax: a topological pax implementation */
/* */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
diff --git a/include/tpax/tpax.h b/include/tpax/tpax.h
index 85cb5cf..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;
@@ -135,49 +144,54 @@ struct tpax_unit_ctx {
};
/* driver api */
-tpax_api int tpax_get_driver_ctx (char ** argv, char ** envp, uint32_t flags,
- const struct tpax_fd_ctx *,
- struct tpax_driver_ctx **);
+tpax_api int tpax_lib_get_driver_ctx (char ** argv, char ** envp, uint32_t flags,
+ const struct tpax_fd_ctx *,
+ struct tpax_driver_ctx **);
+
+tpax_api void tpax_lib_free_driver_ctx (struct tpax_driver_ctx *);
-tpax_api void tpax_free_driver_ctx (struct tpax_driver_ctx *);
+tpax_api int tpax_lib_get_unit_ctx (const struct tpax_driver_ctx *, int, const char * path,
+ struct tpax_unit_ctx **);
-tpax_api int tpax_get_unit_ctx (const struct tpax_driver_ctx *, int, const char * path,
- struct tpax_unit_ctx **);
+tpax_api void tpax_lib_free_unit_ctx (struct tpax_unit_ctx *);
-tpax_api void tpax_free_unit_ctx (struct tpax_unit_ctx *);
+tpax_api int tpax_lib_get_driver_fdctx (const struct tpax_driver_ctx *, struct tpax_fd_ctx *);
-tpax_api int tpax_get_driver_fdctx (const struct tpax_driver_ctx *, struct tpax_fd_ctx *);
-tpax_api int tpax_set_driver_fdctx (struct tpax_driver_ctx *, const struct tpax_fd_ctx *);
+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 *,
- const char *);
+tpax_api int tpax_archive_enqueue (const struct tpax_driver_ctx *, const struct tpax_unit_ctx *);
-tpax_api int tpax_archive_seal (const struct tpax_driver_ctx *);
+tpax_api int tpax_archive_write (const struct tpax_driver_ctx *);
-/* helper api */
-tpax_api int tpax_path_copy (char *, const char *, size_t, uint32_t, size_t *);
-tpax_api int tpax_stat_compare (const struct stat *, const struct stat *);
+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 */
+tpax_api int tpax_util_path_copy (char *, const char *, size_t, uint32_t, size_t *);
+tpax_api int tpax_util_stat_compare (const struct stat *, const struct stat *);
/* utility api */
-tpax_api int tpax_main (char **, char **,
- const struct tpax_fd_ctx *);
-/* error trace api */
-tpax_api int tpax_output_error_record (const struct tpax_driver_ctx *, const struct tpax_error_info *);
-tpax_api int tpax_output_error_vector (const struct tpax_driver_ctx *);
+tpax_api int tpax_main (char **, char **,
+ const struct tpax_fd_ctx *);
+
+tpax_api int tpax_output_error_vector (const struct tpax_driver_ctx *);
+tpax_api int tpax_output_error_record (const struct tpax_driver_ctx *, const struct tpax_error_info *);
-/* low-level api */
-tpax_api int tpax_init_ustar_header (const struct tpax_driver_ctx *, const char *, const struct stat *,
- const char *, struct tpax_ustar_header *);
+/* meta interfaces */
+tpax_api int tpax_meta_init_ustar_header (const struct tpax_driver_ctx *, const char *, const struct stat *,
+ const char *, struct tpax_ustar_header *);
-tpax_api int tpax_file_create_memory_snapshot (const struct tpax_driver_ctx *, int, const char *,
- const struct stat *, void *);
+/* low-level interfaces */
+tpax_api int tpax_io_create_memory_snapshot(const struct tpax_driver_ctx *, int, const char *,
+ const struct stat *, void *);
-tpax_api int tpax_file_create_tmpfs_snapshot (const struct tpax_driver_ctx *, int, const char *,
- const struct stat *);
+tpax_api int tpax_io_create_tmpfs_snapshot (const struct tpax_driver_ctx *, int, const char *,
+ const struct stat *);
/* package info */
-tpax_api const struct tpax_source_version * tpax_source_version(void);
+tpax_api const struct tpax_source_version * tpax_api_source_version(void);
#ifdef __cplusplus
}
diff --git a/project/common.mk b/project/common.mk
index 378de0c..26e171c 100644
--- a/project/common.mk
+++ b/project/common.mk
@@ -2,18 +2,22 @@ API_SRCS = \
src/driver/tpax_amain.c \
src/driver/tpax_driver_ctx.c \
src/driver/tpax_unit_ctx.c \
- src/helper/tpax_path_copy.c \
- src/helper/tpax_stat_compare.c \
- src/logic/tpax_archive_append.c \
- src/logic/tpax_init_ustar_header.c \
- src/logic/tpax_file_create_memory_snapshot.c \
- src/logic/tpax_file_create_tmpfs_snapshot.c \
+ src/io/tpax_create_memory_snapshot.c \
+ src/io/tpax_create_tmpfs_snapshot.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 \
src/output/tpax_output_error.c \
src/skin/tpax_skin_default.c \
+ src/util/tpax_path_copy.c \
+ src/util/tpax_stat_compare.c \
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/extras.mk b/project/extras.mk
index f07c781..f8c9852 100644
--- a/project/extras.mk
+++ b/project/extras.mk
@@ -2,5 +2,7 @@ CFLAGS_SHARED_ATTR += -DTPAX_PRE_ALPHA -DTPAX_EXPORT
CFLAGS_STATIC_ATTR += -DTPAX_PRE_ALPHA -DTPAX_STATIC
CFLAGS_APP_ATTR += -DTPAX_APP
+CFLAGS_CONFIG += $(CFLAGS_ATTR_VISIBILITY_HIDDEN)
+
src/driver/tpax_driver_ctx.o: version.tag
src/driver/tpax_driver_ctx.lo: version.tag
diff --git a/project/headers.mk b/project/headers.mk
index b33a797..a5ee670 100644
--- a/project/headers.mk
+++ b/project/headers.mk
@@ -8,8 +8,10 @@ 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 \
+ $(SOURCE_DIR)/src/internal/tpax_visibility_impl.h \
ALL_HEADERS = $(API_HEADERS) $(INTERNAL_HEADERS)
diff --git a/project/tree.mk b/project/tree.mk
index 548db6f..affd4ba 100644
--- a/project/tree.mk
+++ b/project/tree.mk
@@ -1,10 +1,12 @@
TREE_DIRS = bin lib src \
src/driver \
- src/helper \
src/internal \
+ src/io \
src/logic \
+ src/meta \
src/output \
src/skin \
+ src/util \
tree.tag:
mkdir -p $(TREE_DIRS)
diff --git a/src/driver/tpax_amain.c b/src/driver/tpax_amain.c
index b11dd68..429633e 100644
--- a/src/driver/tpax_amain.c
+++ b/src/driver/tpax_amain.c
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
@@ -38,7 +38,7 @@ static ssize_t tpax_version(struct tpax_driver_ctx * dctx, int fdout)
const struct tpax_source_version * verinfo;
const char * const * verclr;
- verinfo = tpax_source_version();
+ verinfo = tpax_api_source_version();
verclr = isatty(fdout) ? tpax_ver_color : tpax_ver_plain;
return tpax_dprintf(
@@ -54,13 +54,27 @@ 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,0);
+ tpax_archive_enqueue(dctx,uctx);
+}
+
+static void tpax_archive_write_and_seal(
+ const struct tpax_driver_ctx * dctx)
+{
+ if (tpax_archive_write(dctx) == TPAX_OK)
+ tpax_archive_seal(dctx);
+}
+
+static void tpax_perform_archive_actions(
+ const struct tpax_driver_ctx * dctx)
+{
+ if (dctx->cctx->drvflags & TPAX_DRIVER_EXEC_MODE_WRITE)
+ tpax_archive_write_and_seal(dctx);
}
static int tpax_exit(struct tpax_driver_ctx * dctx, int ret)
{
tpax_output_error_vector(dctx);
- tpax_free_driver_ctx(dctx);
+ tpax_lib_free_driver_ctx(dctx);
return ret;
}
@@ -78,7 +92,7 @@ int tpax_main(char ** argv, char ** envp, const struct tpax_fd_ctx * fdctx)
fdout = fdctx ? fdctx->fdout : STDOUT_FILENO;
fdcwd = fdctx ? fdctx->fdcwd : AT_FDCWD;
- if ((ret = tpax_get_driver_ctx(argv,envp,flags,fdctx,&dctx)))
+ if ((ret = tpax_lib_get_driver_ctx(argv,envp,flags,fdctx,&dctx)))
return (ret == TPAX_USAGE)
? !argv || !argv[0] || !argv[1]
: TPAX_ERROR;
@@ -88,14 +102,13 @@ int tpax_main(char ** argv, char ** envp, const struct tpax_fd_ctx * fdctx)
return tpax_exit(dctx,TPAX_ERROR);
for (unit=dctx->units; *unit; unit++) {
- if (!(tpax_get_unit_ctx(dctx,fdcwd,*unit,&uctx))) {
+ if (!(tpax_lib_get_unit_ctx(dctx,fdcwd,*unit,&uctx))) {
tpax_perform_unit_actions(dctx,uctx);
- tpax_free_unit_ctx(uctx);
+ tpax_lib_free_unit_ctx(uctx);
}
}
- if ((dctx->cctx->drvflags & TPAX_DRIVER_EXEC_MODE_WRITE) && dctx->units[0])
- tpax_archive_seal(dctx);
+ tpax_perform_archive_actions(dctx);
return tpax_exit(dctx,dctx->errv[0] ? TPAX_ERROR : TPAX_OK);
}
diff --git a/src/driver/tpax_driver_ctx.c b/src/driver/tpax_driver_ctx.c
index 0a92537..dff6ef6 100644
--- a/src/driver/tpax_driver_ctx.c
+++ b/src/driver/tpax_driver_ctx.c
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
@@ -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 *);
@@ -276,12 +357,34 @@ static struct tpax_driver_ctx_impl * tpax_driver_ctx_alloc(
ictx->ctx.errinfp = &ictx->ctx.erriptr[0];
ictx->ctx.erricap = &ictx->ctx.erriptr[--elements];
+ elements = sizeof(ictx->ctx.prefptr) / sizeof(*ictx->ctx.prefptr);
+
+ ictx->ctx.prefixv = &ictx->ctx.prefptr[0];
+ ictx->ctx.prefixp = &ictx->ctx.prefptr[0];
+ ictx->ctx.prefcap = &ictx->ctx.prefptr[--elements];
+
ictx->meta = meta;
for (entry=meta->entries,units=ictx->units; entry->fopt || entry->arg; entry++)
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(
@@ -291,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;
@@ -320,7 +426,38 @@ static int tpax_get_driver_ctx_fail(struct argv_meta * meta)
return -1;
}
-int tpax_get_driver_ctx(
+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,
uint32_t flags,
@@ -332,7 +469,10 @@ int tpax_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;
@@ -352,6 +492,7 @@ int tpax_get_driver_ctx(
return -1;
nunits = 0;
+ archive = 0;
operand = 0;
program = argv_program_name(argv[0]);
memset(&cctx,0,sizeof(cctx));
@@ -375,6 +516,10 @@ int tpax_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;
@@ -391,6 +536,10 @@ int tpax_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);
@@ -413,17 +562,23 @@ int tpax_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:
@@ -434,6 +589,24 @@ int tpax_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;
@@ -476,11 +649,11 @@ int tpax_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,
@@ -491,7 +664,42 @@ int tpax_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) */
@@ -550,6 +758,16 @@ int tpax_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;
@@ -561,6 +779,7 @@ static void tpax_free_driver_ctx_impl(struct tpax_driver_ctx_alloc * ictx)
{
void * next;
size_t size;
+ char ** ppref;
for (; ictx->ctx.dirents; ) {
next = ictx->ctx.dirents->next;
@@ -570,17 +789,29 @@ 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);
if (ictx->ctx.dirbuff)
munmap(ictx->ctx.dirbuff,TPAX_DIRENT_BUFLEN);
+ for (ppref=ictx->ctx.prefixv; *ppref; ppref++)
+ free(*ppref);
+
+ if (ictx->ctx.prefixv != ictx->ctx.prefptr)
+ free(ictx->ctx.prefixv);
+
+ if (ictx->ctx.direntv)
+ free(ictx->ctx.direntv);
+
argv_free(ictx->meta);
free(ictx);
}
-void tpax_free_driver_ctx(struct tpax_driver_ctx * ctx)
+void tpax_lib_free_driver_ctx(struct tpax_driver_ctx * ctx)
{
struct tpax_driver_ctx_alloc * ictx;
uintptr_t addr;
@@ -593,12 +824,12 @@ void tpax_free_driver_ctx(struct tpax_driver_ctx * ctx)
}
}
-const struct tpax_source_version * tpax_source_version(void)
+const struct tpax_source_version * tpax_api_source_version(void)
{
return &tpax_src_version;
}
-int tpax_get_driver_fdctx(
+int tpax_lib_get_driver_fdctx(
const struct tpax_driver_ctx * dctx,
struct tpax_fd_ctx * fdctx)
{
@@ -616,7 +847,7 @@ int tpax_get_driver_fdctx(
return 0;
}
-int tpax_set_driver_fdctx(
+int tpax_lib_set_driver_fdctx(
struct tpax_driver_ctx * dctx,
const struct tpax_fd_ctx * fdctx)
{
diff --git a/src/driver/tpax_unit_ctx.c b/src/driver/tpax_unit_ctx.c
index b272257..5c4cf80 100644
--- a/src/driver/tpax_unit_ctx.c
+++ b/src/driver/tpax_unit_ctx.c
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
@@ -25,7 +25,7 @@ static int tpax_free_unit_ctx_impl(struct tpax_unit_ctx_impl * ctx, int ret)
return ret;
}
-int tpax_get_unit_ctx(
+int tpax_lib_get_unit_ctx(
const struct tpax_driver_ctx * dctx,
int fdat,
const char * path,
@@ -65,7 +65,7 @@ int tpax_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;
@@ -76,7 +76,7 @@ int tpax_get_unit_ctx(
return 0;
}
-void tpax_free_unit_ctx(struct tpax_unit_ctx * ctx)
+void tpax_lib_free_unit_ctx(struct tpax_unit_ctx * ctx)
{
struct tpax_unit_ctx_impl * ictx;
uintptr_t addr;
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_dprintf_impl.c b/src/internal/tpax_dprintf_impl.c
index b8d2b0b..2bc83a8 100644
--- a/src/internal/tpax_dprintf_impl.c
+++ b/src/internal/tpax_dprintf_impl.c
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
@@ -10,7 +10,9 @@
#include <unistd.h>
#include <errno.h>
-int tpax_dprintf(int fd, const char * fmt, ...)
+#include "tpax_visibility_impl.h"
+
+tpax_hidden int tpax_dprintf(int fd, const char * fmt, ...)
{
int ret;
int cnt;
diff --git a/src/internal/tpax_driver_impl.h b/src/internal/tpax_driver_impl.h
index 5fabb0a..d340748 100644
--- a/src/internal/tpax_driver_impl.h
+++ b/src/internal/tpax_driver_impl.h
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
@@ -26,27 +26,42 @@
(TPAX_DRIVER_EXEC_MODE_WRITE | \
TPAX_DRIVER_EXEC_MODE_COPY)
+#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;
struct dirent dirent;
};
@@ -60,19 +75,28 @@ 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];
struct tpax_error_info erribuf[64];
+ char ** prefixv;
+ char ** prefixp;
+ char ** prefcap;
+ char * prefptr[64];
+ struct tpax_dirent ** direntv;
struct tpax_dirent_buffer * dirents;
+ struct tpax_dirent * dirmark;
void * dirbuff;
void * bufaddr;
size_t bufsize;
+ size_t nqueued;
off_t cpos;
};
@@ -143,42 +167,42 @@ static inline void tpax_driver_set_ectx(
static inline int tpax_driver_fdin(const struct tpax_driver_ctx * dctx)
{
struct tpax_fd_ctx fdctx;
- tpax_get_driver_fdctx(dctx,&fdctx);
+ tpax_lib_get_driver_fdctx(dctx,&fdctx);
return fdctx.fdin;
}
static inline int tpax_driver_fdout(const struct tpax_driver_ctx * dctx)
{
struct tpax_fd_ctx fdctx;
- tpax_get_driver_fdctx(dctx,&fdctx);
+ tpax_lib_get_driver_fdctx(dctx,&fdctx);
return fdctx.fdout;
}
static inline int tpax_driver_fderr(const struct tpax_driver_ctx * dctx)
{
struct tpax_fd_ctx fdctx;
- tpax_get_driver_fdctx(dctx,&fdctx);
+ tpax_lib_get_driver_fdctx(dctx,&fdctx);
return fdctx.fderr;
}
static inline int tpax_driver_fdlog(const struct tpax_driver_ctx * dctx)
{
struct tpax_fd_ctx fdctx;
- tpax_get_driver_fdctx(dctx,&fdctx);
+ tpax_lib_get_driver_fdctx(dctx,&fdctx);
return fdctx.fdlog;
}
static inline int tpax_driver_fdcwd(const struct tpax_driver_ctx * dctx)
{
struct tpax_fd_ctx fdctx;
- tpax_get_driver_fdctx(dctx,&fdctx);
+ tpax_lib_get_driver_fdctx(dctx,&fdctx);
return fdctx.fdcwd;
}
static inline int tpax_driver_fddst(const struct tpax_driver_ctx * dctx)
{
struct tpax_fd_ctx fdctx;
- tpax_get_driver_fdctx(dctx,&fdctx);
+ tpax_lib_get_driver_fdctx(dctx,&fdctx);
return fdctx.fddst;
}
@@ -203,6 +227,21 @@ static inline struct tpax_dirent_buffer * tpax_get_driver_dirents(const struct t
return ictx->dirents;
}
+static inline struct tpax_dirent * tpax_get_driver_dirmark(const struct tpax_driver_ctx * dctx)
+{
+ struct tpax_driver_ctx_impl * ictx;
+ ictx = tpax_get_driver_ictx(dctx);
+ return ictx->dirmark;
+}
+
+static inline void tpax_set_driver_dirmark(const struct tpax_driver_ctx * dctx, struct tpax_dirent * dirent)
+{
+ struct tpax_driver_ctx_impl * ictx;
+ ictx = tpax_get_driver_ictx(dctx);
+ ictx->dirmark = dirent;
+ ictx->nqueued++;
+}
+
static inline off_t tpax_get_unit_hpos(const struct tpax_unit_ctx * uctx)
{
struct tpax_unit_ctx_impl * ictx;
@@ -231,22 +270,10 @@ 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;
+int tpax_update_queue_vector(const struct tpax_driver_ctx * dctx);
- else if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_RUSTAR)
- return TPAX_USTAR_BLOCK_SIZE;
-
- else
- return 0;
-}
+const char * tpax_queue_item_full_path(
+ const struct tpax_driver_ctx *,
+ const struct tpax_dirent *);
#endif
diff --git a/src/internal/tpax_errinfo_impl.c b/src/internal/tpax_errinfo_impl.c
index 5fd2247..50a99a5 100644
--- a/src/internal/tpax_errinfo_impl.c
+++ b/src/internal/tpax_errinfo_impl.c
@@ -1,14 +1,15 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
#include <tpax/tpax.h>
#include "tpax_driver_impl.h"
#include "tpax_errinfo_impl.h"
+#include "tpax_visibility_impl.h"
-int tpax_record_error(
+tpax_hidden int tpax_record_error(
const struct tpax_driver_ctx * dctx,
int esyscode,
int elibcode,
diff --git a/src/internal/tpax_errinfo_impl.h b/src/internal/tpax_errinfo_impl.h
index 99b416b..e8151c5 100644
--- a/src/internal/tpax_errinfo_impl.h
+++ b/src/internal/tpax_errinfo_impl.h
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
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/internal/tpax_getdents_impl.h b/src/internal/tpax_getdents_impl.h
index 42b9da0..2f46d63 100644
--- a/src/internal/tpax_getdents_impl.h
+++ b/src/internal/tpax_getdents_impl.h
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
diff --git a/src/internal/tpax_readlink_impl.h b/src/internal/tpax_readlink_impl.h
index d6c5580..e2febe1 100644
--- a/src/internal/tpax_readlink_impl.h
+++ b/src/internal/tpax_readlink_impl.h
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
diff --git a/src/internal/tpax_tmpfile_impl.c b/src/internal/tpax_tmpfile_impl.c
index 9dd9d40..aa78e40 100644
--- a/src/internal/tpax_tmpfile_impl.c
+++ b/src/internal/tpax_tmpfile_impl.c
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
@@ -14,6 +14,8 @@
#include <stdio.h>
#include <inttypes.h>
+#include "tpax_visibility_impl.h"
+
#define PPRIX64 "%"PRIx64
/* mkostemp might be guarded by non-standard macros */
@@ -55,7 +57,7 @@ static int tpax_mkostemp(char * tmplate)
return fd;
}
-int tpax_tmpfile(void)
+tpax_hidden int tpax_tmpfile(void)
{
int fd;
void * addr;
diff --git a/src/internal/tpax_visibility_impl.h b/src/internal/tpax_visibility_impl.h
new file mode 100644
index 0000000..6812494
--- /dev/null
+++ b/src/internal/tpax_visibility_impl.h
@@ -0,0 +1,26 @@
+#ifndef TPAX_VISIBILITY_IMPL_H
+#define TPAX_VISIBILITY_IMPL_H
+
+/**********************************************************************/
+/* PE targets: __dllexport suffices for the purpose of exporting only */
+/* the desired subset of global symbols; this makes the visibility */
+/* attribute not only redundant, but also tricky if not properly */
+/* supported by the toolchain. */
+/* */
+/* When targeting Midipix, __PE__, __dllexport and __dllimport are */
+/* always defined by the toolchain. Otherwise, the absnece of these */
+/* macros has been detected by sofort's ccenv.sh during ./configure, */
+/* and they have accordingly been added to CFLAGS_OS. */
+/**********************************************************************/
+
+#ifdef __PE__
+#define tpax_hidden
+#else
+#ifdef _ATTR_VISIBILITY_HIDDEN
+#define tpax_hidden _ATTR_VISIBILITY_HIDDEN
+#else
+#define tpax_hidden
+#endif
+#endif
+
+#endif
diff --git a/src/logic/tpax_file_create_memory_snapshot.c b/src/io/tpax_create_memory_snapshot.c
index 7c1f90e..6be087f 100644
--- a/src/logic/tpax_file_create_memory_snapshot.c
+++ b/src/io/tpax_create_memory_snapshot.c
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
@@ -18,12 +18,13 @@
#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))
#endif
-int tpax_file_create_memory_snapshot(
+int tpax_io_create_memory_snapshot(
const struct tpax_driver_ctx * dctx,
int fdat,
const char * path,
@@ -53,7 +54,7 @@ int tpax_file_create_memory_snapshot(
close(fd);
return TPAX_SYSTEM_ERROR(dctx);
- } else if (tpax_stat_compare(srcst,&dstst)) {
+ } else if (tpax_util_stat_compare(srcst,&dstst)) {
close(fd);
return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FILE_CHANGED);
}
@@ -69,11 +70,11 @@ int tpax_file_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,12 +82,15 @@ int tpax_file_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);
return TPAX_SYSTEM_ERROR(dctx);
- } else if (tpax_stat_compare(srcst,&dstst)) {
+ } else if (tpax_util_stat_compare(srcst,&dstst)) {
close(fd);
return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FILE_CHANGED);
}
diff --git a/src/logic/tpax_file_create_tmpfs_snapshot.c b/src/io/tpax_create_tmpfs_snapshot.c
index 5620238..99a1665 100644
--- a/src/logic/tpax_file_create_tmpfs_snapshot.c
+++ b/src/io/tpax_create_tmpfs_snapshot.c
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
@@ -19,12 +19,13 @@
#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))
#endif
-int tpax_file_create_tmpfs_snapshot(
+int tpax_io_create_tmpfs_snapshot(
const struct tpax_driver_ctx * dctx,
int fdat,
const char * path,
@@ -64,7 +65,7 @@ int tpax_file_create_tmpfs_snapshot(
close(fdtmp);
return TPAX_SYSTEM_ERROR(dctx);
- } else if (tpax_stat_compare(srcst,&dstst)) {
+ } else if (tpax_util_stat_compare(srcst,&dstst)) {
close(fdsrc);
close(fdtmp);
return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FILE_CHANGED);
@@ -78,12 +79,12 @@ int tpax_file_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_file_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,13 +109,16 @@ int tpax_file_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);
close(fdtmp);
return TPAX_SYSTEM_ERROR(dctx);
- } else if (tpax_stat_compare(srcst,&dstst)) {
+ } else if (tpax_util_stat_compare(srcst,&dstst)) {
close(fdsrc);
close(fdtmp);
return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FILE_CHANGED);
diff --git a/src/logic/tpax_archive_append.c b/src/logic/tpax_archive_append.c
deleted file mode 100644
index 76ac436..0000000
--- a/src/logic/tpax_archive_append.c
+++ /dev/null
@@ -1,709 +0,0 @@
-/**************************************************************/
-/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
-/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
-/**************************************************************/
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <grp.h>
-#include <pwd.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-
-#include <tpax/tpax.h>
-#include <tpax/tpax_specs.h>
-#include "tpax_driver_impl.h"
-#include "tpax_getdents_impl.h"
-#include "tpax_tmpfile_impl.h"
-#include "tpax_errinfo_impl.h"
-
-#ifndef ssizeof
-#define ssizeof(x) (ssize_t)(sizeof(x))
-#endif
-
-static int tpax_archive_append_memory_data(
- int fdout,
- void * buf,
- ssize_t nbytes)
-{
- ssize_t ret;
- char * ch;
-
- for (ch=buf; nbytes; ch+=ret) {
- ret = write(fdout,ch,nbytes);
-
- while ((ret < 0) && (errno == EINTR))
- ret = write(fdout,ch,nbytes);
-
- if (ret < 0)
- return ret;
-
- nbytes -= ret;
- }
-
- return 0;
-}
-
-static int tpax_archive_append_pad(
- const struct tpax_driver_ctx * dctx,
- int fdout,
- const struct stat * st)
-{
- int ret;
- off_t cpos;
- ssize_t nbytes;
- char buf[512];
-
- nbytes = st->st_size;
- nbytes += 0x1ff;
- nbytes |= 0x1ff;
- nbytes ^= 0x1ff;
- nbytes -= st->st_size;
-
- memset(buf,0,nbytes);
-
- cpos = tpax_get_driver_cpos(dctx);
- cpos += st->st_size + nbytes;
-
- if (!(ret = tpax_archive_append_memory_data(fdout,buf,nbytes)))
- tpax_set_driver_cpos(dctx,cpos);
-
- return ret;
-}
-
-static struct tpax_dirent_buffer * tpax_dirent_buf_first_alloc(
- const struct tpax_driver_ctx * dctx)
-{
- void * addr;
- struct tpax_driver_ctx_impl * ictx;
-
- addr = (struct tpax_dirent_buffer *)mmap(
- 0,TPAX_DIRENT_BUFLEN,
- PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_ANONYMOUS,
- -1,0);
-
- if (addr == MAP_FAILED)
- return 0;
-
- ictx = tpax_get_driver_ictx(dctx);
- ictx->dirents = (struct tpax_dirent_buffer *)addr;
- ictx->dirents->cdent = ictx->dirents->dbuf;
-
- ictx->dirents->size = TPAX_DIRENT_BUFLEN;
- ictx->dirents->next = 0;
-
- ictx->dirents->nfree = TPAX_DIRENT_BUFLEN;
- ictx->dirents->nfree -= offsetof(struct tpax_dirent_buffer,dbuf);
-
- return ictx->dirents;
-}
-
-static struct tpax_dirent_buffer * tpax_dirent_buf_next_alloc(
- struct tpax_dirent_buffer * current)
-{
- void * addr;
-
- addr = (struct tpax_dirent_buffer *)mmap(
- 0,TPAX_DIRENT_BUFLEN,
- PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_ANONYMOUS,
- -1,0);
-
- if (addr == MAP_FAILED)
- return 0;
-
- current->next = (struct tpax_dirent_buffer *)addr;
- current->next->cdent = current->next->dbuf;
-
- current->next->size = TPAX_DIRENT_BUFLEN;
- current->next->next = 0;
-
- current->next->nfree = TPAX_DIRENT_BUFLEN;
- current->next->nfree -= offsetof(struct tpax_dirent_buffer,dbuf);
-
- return current->next;
-}
-
-static int tpax_archive_append_ret(
- int ret,
- struct tpax_unit_ctx * unit)
-{
- if (unit)
- tpax_free_unit_ctx(unit);
-
- return ret;
-}
-
-static int tpax_archive_append_one(
- const struct tpax_driver_ctx * dctx,
- const struct tpax_unit_ctx * uctx,
- const struct dirent * dent,
- int fdat,
- const char * prefix,
- const struct tpax_dirent * parent,
- const char * pdir)
-{
- struct tpax_unit_ctx * unit;
- struct tpax_ustar_header uhdr;
- off_t hpos;
- off_t dpos;
- int fdout;
- int fdtmp;
- ssize_t nread;
- ssize_t nbytes;
- void * buf;
- size_t buflen;
- size_t cmplen;
- void * membuf;
- size_t nlen;
- const char * path;
- const char * src;
- char * dst;
- char pbuf[1024];
-
- /* fake uctx for recursion items */
- unit = 0;
-
- if (dent && tpax_get_unit_ctx(
- dctx,fdat,dent->d_name,
- &unit) < 0)
- return TPAX_NESTED_ERROR(dctx);
-
- uctx = dent ? unit : uctx;
-
- /* prefixed path */
- if (!prefix && !parent && !pdir) {
- path = *uctx->path;
-
- } else {
- nlen = strlen(*uctx->path);
- nlen += prefix ? strlen(prefix) + 1 : 0;
- nlen += parent ? strlen(parent->dirent.d_name) + 1 : 0;
-
- if (nlen >= sizeof(pbuf))
- return TPAX_BUFFER_ERROR(dctx);
-
- dst = pbuf;
-
- if (prefix) {
- src = prefix;
-
- for (; *src; )
- *dst++ = *src++;
-
- if (dst[-1] != '/')
- *dst++ = '/';
- }
-
- if (parent) {
- src = parent->dirent.d_name;
-
- for (; *src; )
- *dst++ = *src++;
-
- *dst++ = '/';
- }
-
- if (pdir) {
- src = pdir;
-
- for (; *src; )
- *dst++ = *src++;
-
- *dst++ = '/';
- }
-
- src = *uctx->path;
-
- for (; *src; )
- *dst++ = *src++;
-
- *dst = 0;
- path = pbuf;
- }
-
- /* record errors */
- tpax_driver_set_ectx(
- dctx,0,path);
-
- /* driver */
- fdout = tpax_driver_fdout(dctx);
-
- /* header and data offsets: todo pax and cpio */
- hpos = tpax_get_driver_cpos(dctx);
- dpos = hpos + sizeof(uhdr);
-
- /* header */
- if (tpax_init_ustar_header(
- dctx,path,uctx->st,
- *uctx->link,&uhdr) < 0)
- return tpax_archive_append_ret(
- TPAX_NESTED_ERROR(dctx),
- unit);
-
- /* buffer */
- membuf = 0;
- fdtmp = -1;
-
- /* associated data? */
- if S_ISREG(uctx->st->st_mode) {
- buf = tpax_get_driver_anon_map_addr(
- dctx,&buflen);
-
- if (buflen >= (cmplen = uctx->st->st_size))
- membuf = buf;
-
- /* snapshot */
- if (membuf) {
- if (tpax_file_create_memory_snapshot(
- dctx,fdat,*uctx->path,
- uctx->st,membuf) < 0)
- return tpax_archive_append_ret(
- TPAX_NESTED_ERROR(dctx),
- unit);
- } else {
- if ((fdtmp = tpax_file_create_tmpfs_snapshot(
- dctx,fdat,*uctx->path,
- uctx->st)) < 0)
- return tpax_archive_append_ret(
- TPAX_NESTED_ERROR(dctx),
- unit);
-
- if (lseek(fdtmp,0,SEEK_SET) < 0)
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
- }
- }
-
- /* append header */
- if (tpax_archive_append_memory_data(fdout,&uhdr,ssizeof(uhdr)) < 0) {
- if (fdtmp >= 0)
- close(fdtmp);
-
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
- }
-
- tpax_set_driver_cpos(dctx,dpos);
-
- /* all done? */
- if (!(S_ISREG(uctx->st->st_mode))) {
- tpax_archive_append_ret(0,unit);
- return 0;
- }
-
- /* append data from snapshot */
- if (fdtmp >= 0) {
- buf = tpax_get_driver_anon_map_addr(
- dctx,&buflen);
-
- for (nread=0; nread<uctx->st->st_size; ) {
- nbytes = read(fdtmp,buf,buflen);
-
- while ((nbytes < 0) && (errno == EINTR))
- nbytes = read(fdtmp,buf,buflen);
-
- if (nbytes < 0) {
- close(fdtmp);
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- } else if (nbytes == 0) {
- close(fdtmp);
- return tpax_archive_append_ret(
- TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR),
- unit);
-
- } else {
- nread += nbytes;
- }
-
- if (tpax_archive_append_memory_data(fdout,buf,nbytes) < 0) {
- close(fdtmp);
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
- }
- }
-
- close(fdtmp);
- } else {
- if (tpax_archive_append_memory_data(
- fdout,membuf,
- uctx->st->st_size) < 0)
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
- }
-
- return tpax_archive_append_ret(
- tpax_archive_append_pad(dctx,fdout,uctx->st),
- unit);
-}
-
-static int tpax_archive_append_dir(
- const struct tpax_driver_ctx * dctx,
- const struct tpax_unit_ctx * uctx,
- struct tpax_dirent * dent,
- int fdat,
- int depth,
- const char * prefix,
- const struct tpax_dirent * parent)
-{
- int fd;
- bool fkeep;
- long nbytes;
- size_t needed;
- struct dirent * dirent;
- struct dirent * dirents;
- struct tpax_dirent_buffer * dentbuf;
- struct tpax_dirent * cdent;
- struct tpax_unit_ctx * unit;
- struct stat st;
- uintptr_t addr;
- char * src;
- char * dst;
- char * cap;
-
- /* fake uctx for recursion items */
- unit = 0;
-
- if (dent && tpax_get_unit_ctx(
- dctx,dent->fdat,dent->dirent.d_name,
- &unit) < 0)
- return TPAX_NESTED_ERROR(dctx);
-
- uctx = dent ? unit : uctx;
-
- /* verify that recursion item is still a directory */
- if (unit && !S_ISDIR(unit->st->st_mode))
- return tpax_archive_append_ret(
- TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR),
- unit);
-
- /* append directory entry to archive */
- if (tpax_archive_append_one(
- dctx,uctx,0,
- tpax_driver_fdcwd(dctx),
- prefix,0,0) < 0)
- return tpax_archive_append_ret(
- TPAX_NESTED_ERROR(dctx),
- unit);
-
- /* obtain buffer for file-system directory entries */
- dirents = tpax_get_driver_getdents_buffer(dctx);
- dirent = dirents;
- fkeep = false;
- nbytes = 0;
- depth++;
-
- /* open directory and obtain first directory entries */
- if ((fd = openat(fdat,*uctx->path,O_RDONLY|O_DIRECTORY|O_CLOEXEC,0)) < 0)
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN);
-
- while ((nbytes == -EINTR) || ((nbytes < 0) && (errno == EINTR)))
- nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN);
-
- if (nbytes < 0)
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- /* iterate */
- for (; nbytes>0; ) {
- if (!strcmp(dirent->d_name,".")) {
- (void)0;
-
- } else if (!strcmp(dirent->d_name,"..")) {
- (void)0;
-
- } else {
- if (dirent->d_type == DT_UNKNOWN) {
- if (fstatat(fd,dirent->d_name,&st,AT_SYMLINK_NOFOLLOW))
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- if (S_ISDIR(st.st_mode)) {
- dirent->d_type = DT_DIR;
- }
- }
-
- if (dirent->d_type == DT_DIR) {
- if (!(dentbuf = tpax_get_driver_dirents(dctx)))
- if (!(dentbuf = tpax_dirent_buf_first_alloc(dctx)))
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- needed = dirent->d_reclen;
- needed += offsetof(struct tpax_dirent,dirent);
- needed += 0x7;
- needed |= 0x7;
- needed ^= 0x7;
-
- for (; dentbuf->next && (dentbuf->nfree < needed); )
- dentbuf = dentbuf->next;
-
- if (dentbuf->nfree < needed)
- if (!(dentbuf = tpax_dirent_buf_next_alloc(dentbuf)))
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- fkeep = true;
- cdent = dentbuf->cdent;
-
- cdent->fdat = fd;
- cdent->depth = depth;
- cdent->nsize = needed;
- cdent->parent = parent;
-
- memset(&cdent->dirent,0,offsetof(struct dirent,d_name));
-
- cdent->dirent.d_type = dirent->d_type;
- cdent->dirent.d_reclen = dirent->d_reclen;
-
- src = dirent->d_name;
- dst = cdent->dirent.d_name;
-
- cap = dst - offsetof(struct dirent,d_name);
- cap -= offsetof(struct tpax_dirent,dirent);
- cap += needed;
-
- for (; *src; )
- *dst++ = *src++;
-
- for (; dst<cap; )
- *dst++ = 0;
-
- dentbuf->cdent = (struct tpax_dirent *)cap;
- dentbuf->nfree -= needed;
- } else {
- if (tpax_archive_append_one(
- dctx,0,dirent,fd,prefix,
- 0,*uctx->path) < 0)
- return tpax_archive_append_ret(
- TPAX_NESTED_ERROR(dctx),
- unit);
- }
- }
-
- addr = (uintptr_t)dirent;
- addr += dirent->d_reclen;
- nbytes -= dirent->d_reclen;
- dirent = (struct dirent *)addr;
-
- if (nbytes == 0) {
- nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN);
-
- while ((nbytes == -EINTR) || ((nbytes < 0) && (errno == EINTR)))
- nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN);
-
- if (nbytes < 0)
- tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- dirent = dirents;
- }
- }
-
- /* all done */
- return tpax_archive_append_ret(
- fkeep ? 0 : close(fd),
- unit);
-}
-
-static int tpax_archive_append_impl(
- const struct tpax_driver_ctx * dctx,
- const struct tpax_unit_ctx * uctx,
- int depth,
- const char * prefix,
- struct tpax_dirent * parent)
-{
- if (S_ISDIR(uctx->st->st_mode))
- if (dctx->cctx->drvflags & TPAX_DRIVER_DIR_MEMBER_RECURSE)
- return tpax_archive_append_dir(
- dctx,uctx,0,
- tpax_driver_fdcwd(dctx),
- depth,prefix,parent);
-
- return tpax_archive_append_one(
- dctx,uctx,0,
- tpax_driver_fdcwd(dctx),
- prefix,0,0);
-}
-
-int tpax_archive_append(
- const struct tpax_driver_ctx * dctx,
- const struct tpax_unit_ctx * uctx,
- const char * prefix)
-{
- struct tpax_dirent_buffer * dentbuf;
- struct tpax_dirent * cdent;
- struct tpax_dirent * cnext;
- const struct tpax_dirent * parent;
- uintptr_t addr;
- const char * rdir;
- const char * src;
- char * dst;
- char * cap;
- char * mark;
- int idx;
- const char * dirv[256];
- char pbuf[2048];
-
- /* normalized prefix */
- if (prefix && (prefix[0] == '.') && (!prefix[1]))
- prefix = 0;
-
- if (prefix && (prefix[0] == '.') && (prefix[1] == '/'))
- for (++prefix; *prefix=='/'; prefix++)
- (void)0;
-
- /* append explicit item */
- tpax_archive_append_impl(dctx,uctx,0,prefix,0);
-
- /* iterate through queued items */
- dentbuf = tpax_get_driver_dirents(dctx);
- cdent = dentbuf ? dentbuf->dbuf : 0;
-
- if (cdent) {
- dst = pbuf;
- cap = &pbuf[sizeof(pbuf)];
-
- if (prefix && prefix[0]) {
- for (; *src && dst<cap; )
- *dst++ = *src++;
-
- if (dst == cap)
- return TPAX_BUFFER_ERROR(dctx);
-
- if (dst[-1] != '/')
- *dst++ = '/';
- }
-
- src = *uctx->path;
-
- for (; *src && dst<cap; )
- *dst++ = *src++;
-
- if (dst == cap)
- return TPAX_BUFFER_ERROR(dctx);
-
- if (dst[-1] != '/')
- *dst++ = '/';
-
- *dst = 0;
- mark = dst;
- rdir = pbuf;
-
- (void)mark;
- }
-
- for (; cdent; ) {
- switch (cdent->dirent.d_type) {
- case DT_DIR:
- if (tpax_archive_append_dir(
- dctx,0,cdent,cdent->fdat,
- cdent->depth,rdir,cdent) < 0)
- return TPAX_NESTED_ERROR(dctx);
-
- break;
-
- default:
- if (tpax_archive_append_one(
- dctx,0,&cdent->dirent,
- cdent->fdat,rdir,cdent,0) < 0)
- return TPAX_NESTED_ERROR(dctx);
- }
-
- addr = (uintptr_t)cdent;
- addr += cdent->nsize;
- cnext = (struct tpax_dirent *)addr;
-
- if (cnext == dentbuf->cdent) {
- dentbuf = dentbuf->next;
- cnext = dentbuf ? dentbuf->dbuf : 0;
- }
-
- if (cnext && (cnext->parent != cdent->parent)) {
- if (cnext->depth > 256)
- return TPAX_BUFFER_ERROR(dctx);
-
- for (parent=cnext->parent; parent; parent=parent->parent)
- dirv[parent->depth - 1] = parent->dirent.d_name;
-
- for (idx=0,dst=mark; idx<cnext->parent->depth; idx++) {
- src = dirv[idx];
-
- for (; *src; )
- *dst++ = *src++;
-
- *dst++ = '/';
- }
-
- *--dst = 0;
- }
-
- cdent = cnext;
- }
-
- return 0;
-}
-
-int tpax_archive_seal(const struct tpax_driver_ctx * dctx)
-{
- int fdout;
- off_t cpos;
- ssize_t nbytes;
- ssize_t nwritten;
- ssize_t blksize;
- char buf[512];
-
- blksize = tpax_get_archive_block_size(dctx);
- cpos = tpax_get_driver_cpos(dctx);
-
- if (cpos % 512)
- return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR);
-
- fdout = tpax_driver_fdout(dctx);
- memset(buf,0,sizeof(buf));
-
- switch (cpos % blksize) {
- case 0:
- nbytes = cpos + blksize;
- break;
-
- default:
- nbytes = cpos / blksize;
- nbytes *= blksize;
- nbytes += blksize;
-
- if (nbytes-cpos == 512)
- 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);
- }
-
- return 0;
-}
diff --git a/src/logic/tpax_archive_enqueue.c b/src/logic/tpax_archive_enqueue.c
new file mode 100644
index 0000000..8685cad
--- /dev/null
+++ b/src/logic/tpax_archive_enqueue.c
@@ -0,0 +1,582 @@
+/**************************************************************/
+/* tpax: a topological pax implementation */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
+/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
+/**************************************************************/
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <tpax/tpax.h>
+#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_add_prefix_item(
+ const struct tpax_driver_ctx * dctx,
+ const char * prefix)
+{
+ struct tpax_driver_ctx_impl * ictx;
+ char ** prefv;
+ char ** psrc;
+ char ** pdst;
+ off_t elements;
+ char * pitem;
+
+ ictx = tpax_get_driver_ictx(dctx);
+
+ for (psrc=ictx->prefixv; *psrc; psrc++)
+ if (!strcmp(*psrc,prefix))
+ return *psrc;
+
+ if (ictx->prefixp == ictx->prefcap) {
+ elements = ictx->prefcap - ictx->prefixv;
+ elements += 256;
+
+ if (!(prefv = calloc(elements,sizeof(char *))))
+ return 0;
+
+ for (psrc=ictx->prefixv,pdst=prefv; *psrc; psrc++,pdst++)
+ *pdst = *psrc;
+
+ if (ictx->prefixv != ictx->prefptr)
+ free(ictx->prefixv);
+
+ ictx->prefixv = prefv;
+ ictx->prefixp = pdst;
+ ictx->prefcap = &prefv[--elements];
+ }
+
+ if (!(pitem = strdup(prefix)))
+ return 0;
+
+ *ictx->prefixp++ = pitem;
+
+ return pitem;
+}
+
+static char * tpax_add_prefix_item_from_path(
+ const struct tpax_driver_ctx * dctx,
+ const char * path,
+ const char * mark)
+{
+ off_t nbytes;
+ char pathbuf[PATH_MAX];
+
+ if ((nbytes = (mark - path)) >= (PATH_MAX - 1))
+ return 0;
+
+ memcpy(pathbuf,path,nbytes);
+ pathbuf[nbytes++] = '/';
+ pathbuf[nbytes] = '\0';
+
+ 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(
+ const struct tpax_driver_ctx * dctx)
+{
+ void * addr;
+ struct tpax_driver_ctx_impl * ictx;
+
+ addr = (struct tpax_dirent_buffer *)mmap(
+ 0,TPAX_DIRENT_BUFLEN,
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS,
+ -1,0);
+
+ if (addr == MAP_FAILED)
+ return 0;
+
+ ictx = tpax_get_driver_ictx(dctx);
+ ictx->dirents = (struct tpax_dirent_buffer *)addr;
+ ictx->dirents->cdent = ictx->dirents->dbuf;
+
+ ictx->dirents->size = TPAX_DIRENT_BUFLEN;
+ ictx->dirents->next = 0;
+
+ ictx->dirents->nfree = TPAX_DIRENT_BUFLEN;
+ ictx->dirents->nfree -= offsetof(struct tpax_dirent_buffer,dbuf);
+
+ return ictx->dirents;
+}
+
+static struct tpax_dirent_buffer * tpax_dirent_buf_next_alloc(
+ struct tpax_dirent_buffer * current)
+{
+ void * addr;
+
+ addr = (struct tpax_dirent_buffer *)mmap(
+ 0,TPAX_DIRENT_BUFLEN,
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS,
+ -1,0);
+
+ if (addr == MAP_FAILED)
+ return 0;
+
+ current->next = (struct tpax_dirent_buffer *)addr;
+ current->next->cdent = current->next->dbuf;
+
+ current->next->size = TPAX_DIRENT_BUFLEN;
+ current->next->next = 0;
+
+ current->next->nfree = TPAX_DIRENT_BUFLEN;
+ current->next->nfree -= offsetof(struct tpax_dirent_buffer,dbuf);
+
+ return current->next;
+}
+
+static int tpax_archive_enqueue_ret(
+ int ret,
+ struct tpax_unit_ctx * unit)
+{
+ if (unit)
+ tpax_lib_free_unit_ctx(unit);
+
+ return ret;
+}
+
+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,
+ bool * fkeep)
+{
+ struct tpax_dirent_buffer * dentbuf;
+ struct tpax_dirent * cdent;
+ const char * src;
+ char * dst;
+ char * cap;
+ size_t needed;
+
+ if (!(dentbuf = tpax_get_driver_dirents(dctx)))
+ if (!(dentbuf = tpax_dirent_buf_first_alloc(dctx)))
+ return tpax_archive_enqueue_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ 0);
+
+ needed = dirent->d_reclen;
+ needed += offsetof(struct tpax_dirent,dirent);
+ needed += 0x7;
+ needed |= 0x7;
+ needed ^= 0x7;
+
+ for (; dentbuf->next && (dentbuf->nfree < needed); )
+ dentbuf = dentbuf->next;
+
+ if (dentbuf->nfree < needed)
+ if (!(dentbuf = tpax_dirent_buf_next_alloc(dentbuf)))
+ return tpax_archive_enqueue_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ 0);
+
+ *fkeep = true;
+ cdent = dentbuf->cdent;
+
+ cdent->fdat = fdat;
+ cdent->depth = depth;
+ cdent->flags = flags;
+ cdent->stdev = stdev;
+ cdent->nsize = needed;
+ cdent->parent = parent;
+ cdent->prefix = prefix;
+
+ memset(&cdent->dirent,0,offsetof(struct dirent,d_name));
+
+ cdent->dirent.d_ino = dirent->d_ino;
+ cdent->dirent.d_type = dirent->d_type;
+ cdent->dirent.d_reclen = dirent->d_reclen;
+
+ src = dirent->d_name;
+ dst = cdent->dirent.d_name;
+
+ cap = dst - offsetof(struct dirent,d_name);
+ cap -= offsetof(struct tpax_dirent,dirent);
+ cap += needed;
+
+ for (; *src; )
+ *dst++ = *src++;
+
+ for (; dst<cap; )
+ *dst++ = 0;
+
+ dentbuf->cdent = (struct tpax_dirent *)cap;
+ dentbuf->nfree -= needed;
+
+ tpax_set_driver_dirmark(dctx,cdent);
+
+ return 0;
+}
+
+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;
+ depth = dent->depth;
+
+ /* uctx on the fly */
+ if (tpax_lib_get_unit_ctx(
+ dctx,fdat,
+ dent->dirent.d_name,
+ &uctx) < 0)
+ return TPAX_NESTED_ERROR(dctx);
+
+ /* verify that recursion item is still a directory */
+ if (!S_ISDIR(uctx->st->st_mode))
+ 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;
+ fkeep = false;
+ nbytes = 0;
+ depth++;
+
+ /* 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_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_enqueue_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ uctx);
+
+ /* iterate */
+ for (; nbytes; ) {
+ if (!strcmp(dirent->d_name,".")) {
+ (void)0;
+
+ } else if (!strcmp(dirent->d_name,"..")) {
+ (void)0;
+
+ } else {
+ if (dirent->d_type == DT_UNKNOWN) {
+ if (fstatat(fd,dirent->d_name,&st,AT_SYMLINK_NOFOLLOW))
+ 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_add_queue_item(
+ dctx,dirent,dent,0,
+ st.st_dev,depth,
+ TPAX_ITEM_IMPLICIT,
+ fd,&fkeep) < 0)
+ 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;
+ addr += dirent->d_reclen;
+ nbytes -= dirent->d_reclen;
+ dirent = (struct dirent *)addr;
+
+ if (nbytes == 0) {
+ nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN);
+
+ while ((nbytes == -EINTR) || ((nbytes < 0) && (errno == EINTR)))
+ nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN);
+
+ if (nbytes < 0)
+ tpax_archive_enqueue_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ uctx);
+
+ dirent = dirents;
+ }
+ }
+
+ /* all done */
+ return tpax_archive_enqueue_ret(
+ fkeep ? 0 : close(fd),
+ uctx);
+}
+
+static const char * tpax_path_prefix_mark(const char * path)
+{
+ const char * src;
+ char * mark;
+ char pathbuf[PATH_MAX];
+
+ src = path;
+ mark = pathbuf;
+
+ for (; *src; )
+ *mark++ = *src++;
+
+ for (--mark; (*mark == '/'); mark--)
+ (void)0;
+
+ *++mark = '\0';
+
+ for (; (mark > pathbuf) && (mark[-1] != '/'); )
+ mark--;
+
+ return (mark <= pathbuf) ? 0 : &path[--mark-pathbuf];
+}
+
+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;
+ const char * prefix;
+ bool fkeep;
+ struct tpax_dirent_buffer * dentbuf;
+ 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_add_prefix_item_from_path(
+ dctx,*uctx->path,mark)))
+ return tpax_archive_enqueue_ret(
+ TPAX_BUFFER_ERROR(dctx),
+ 0);
+
+ name = mark ? ++mark : *uctx->path;
+
+ if (prefix)
+ if ((fdat = openat(fdat,prefix,O_RDONLY|O_DIRECTORY|O_CLOEXEC,0)) < 0)
+ return tpax_archive_enqueue_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ 0);
+
+ /* explicit item directory entry */
+ 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_add_queue_item(
+ dctx,dirent,0,prefix,
+ uctx->st->st_dev,0,
+ TPAX_ITEM_EXPLICIT,
+ fdat,&fkeep) < 0)
+ 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 (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;
+ cnext = (struct tpax_dirent *)addr;
+
+ if (cnext == dentbuf->cdent) {
+ dentbuf = dentbuf->next;
+ cnext = dentbuf ? dentbuf->dbuf : 0;
+ }
+
+ cdent = cnext;
+ }
+
+ return 0;
+}
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
new file mode 100644
index 0000000..7ce6cca
--- /dev/null
+++ b/src/logic/tpax_archive_write.c
@@ -0,0 +1,368 @@
+/**************************************************************/
+/* tpax: a topological pax implementation */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
+/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
+/**************************************************************/
+
+#include <tpax/tpax.h>
+#include "tpax_driver_impl.h"
+#include "tpax_errinfo_impl.h"
+#include "tpax_visibility_impl.h"
+
+#ifndef ssizeof
+#define ssizeof(x) (ssize_t)(sizeof(x))
+#endif
+
+static int tpax_archive_append_memory_data(
+ int fdout,
+ void * buf,
+ ssize_t nbytes)
+{
+ ssize_t ret;
+ char * ch;
+
+ for (ch=buf; nbytes; ch+=ret) {
+ ret = write(fdout,ch,nbytes);
+
+ while ((ret < 0) && (errno == EINTR))
+ ret = write(fdout,ch,nbytes);
+
+ if (ret < 0)
+ return ret;
+
+ nbytes -= ret;
+ }
+
+ return 0;
+}
+
+static int tpax_archive_append_pad(
+ const struct tpax_driver_ctx * dctx,
+ int fdout,
+ const struct stat * st)
+{
+ int ret;
+ off_t cpos;
+ ssize_t nbytes;
+ char buf[512];
+
+ nbytes = st->st_size;
+ nbytes += 0x1ff;
+ nbytes |= 0x1ff;
+ nbytes ^= 0x1ff;
+ nbytes -= st->st_size;
+
+ memset(buf,0,nbytes);
+
+ cpos = tpax_get_driver_cpos(dctx);
+ cpos += st->st_size + nbytes;
+
+ if (!(ret = tpax_archive_append_memory_data(fdout,buf,nbytes)))
+ tpax_set_driver_cpos(dctx,cpos);
+
+ return ret;
+}
+
+static int tpax_archive_write_ret(
+ 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;
+}
+
+static int tpax_archive_write_impl(
+ const struct tpax_driver_ctx * dctx,
+ const struct tpax_dirent * cdent,
+ int fdcwd,
+ int fdout)
+{
+ 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;
+ ssize_t nread;
+ ssize_t nbytes;
+ void * buf;
+ 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)))
+ return TPAX_CUSTOM_ERROR(
+ 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)
+ 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(
+ dctx,0,path);
+
+ /* header and data offsets: todo pax and cpio */
+ hpos = tpax_get_driver_cpos(dctx);
+ dpos = hpos + sizeof(uhdr);
+
+ /* header */
+ if (tpax_meta_init_ustar_header(
+ dctx,path,st,
+ slnk,&uhdr) < 0)
+ return tpax_archive_write_ret(
+ TPAX_NESTED_ERROR(dctx),
+ dctx,uctx);
+
+ /* buffer */
+ membuf = 0;
+ fdtmp = -1;
+
+ /* associated data? */
+ if S_ISREG(st->st_mode) {
+ buf = tpax_get_driver_anon_map_addr(
+ dctx,&buflen);
+
+ if (buflen >= (cmplen = st->st_size))
+ membuf = buf;
+
+ /* snapshot */
+ if (membuf) {
+ if (tpax_io_create_memory_snapshot(
+ dctx,fdcwd,
+ mlnk ? mlnk : path,
+ st,membuf) < 0)
+ return tpax_archive_write_ret(
+ TPAX_NESTED_ERROR(dctx),
+ dctx,uctx);
+ } else {
+ if ((fdtmp = tpax_io_create_tmpfs_snapshot(
+ dctx,fdcwd,
+ mlnk ? mlnk : path,
+ st)) < 0)
+ return tpax_archive_write_ret(
+ TPAX_NESTED_ERROR(dctx),
+ dctx,uctx);
+
+ if (lseek(fdtmp,0,SEEK_SET) < 0)
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+ }
+ }
+
+ /* append header */
+ if (tpax_archive_append_memory_data(fdout,&uhdr,ssizeof(uhdr)) < 0) {
+ if (fdtmp >= 0)
+ close(fdtmp);
+
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+ }
+
+ tpax_set_driver_cpos(dctx,dpos);
+
+ /* all done? */
+ 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<st->st_size; ) {
+ nbytes = read(fdtmp,buf,buflen);
+
+ while ((nbytes < 0) && (errno == EINTR))
+ nbytes = read(fdtmp,buf,buflen);
+
+ if (nbytes < 0) {
+ close(fdtmp);
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+
+ } else if (nbytes == 0) {
+ close(fdtmp);
+ return tpax_archive_write_ret(
+ TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR),
+ dctx,uctx);
+
+ } else {
+ nread += nbytes;
+ }
+
+ if (tpax_archive_append_memory_data(fdout,buf,nbytes) < 0) {
+ close(fdtmp);
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+ }
+ }
+
+ close(fdtmp);
+ } else {
+ if (tpax_archive_append_memory_data(
+ fdout,membuf,
+ st->st_size) < 0)
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+ }
+
+ return tpax_archive_write_ret(
+ tpax_archive_append_pad(dctx,fdout,st),
+ dctx,uctx);
+}
+
+int tpax_archive_write(const struct tpax_driver_ctx * dctx)
+{
+ struct tpax_driver_ctx_impl * ictx;
+ struct tpax_dirent ** direntv;
+ int fdcwd;
+ int fdout;
+
+ /* driver */
+ ictx = tpax_get_driver_ictx(dctx);
+ fdcwd = tpax_driver_fdcwd(dctx);
+ fdout = tpax_driver_fdout(dctx);
+
+ /* quote to archive */
+ if (tpax_update_queue_vector(dctx) < 0)
+ return TPAX_NESTED_ERROR(dctx);
+
+ for (direntv=ictx->direntv; *direntv; direntv++)
+ if (tpax_archive_write_impl(dctx,*direntv,fdcwd,fdout) < 0)
+ return TPAX_NESTED_ERROR(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;
+ off_t cpos;
+ ssize_t nbytes;
+ ssize_t nwritten;
+ ssize_t blksize;
+ char buf[1024];
+
+ /* 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));
+
+ if (tpax_archive_append_memory_data(fdout,buf,2*512) < 0)
+ return TPAX_SYSTEM_ERROR(dctx);
+
+ cpos += 2*512;
+
+ /* pax? */
+ if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_PAX) {
+ tpax_set_driver_cpos(dctx,cpos);
+ return 0;
+ }
+
+ /* already at block boundary? */
+ if ((cpos % blksize) == 0) {
+ tpax_set_driver_cpos(dctx,cpos);
+ return 0;
+ }
+
+ /* zero-fill the remainder of the block */
+ nbytes = cpos / blksize;
+ nbytes *= blksize;
+ nbytes += blksize;
+
+ for (nwritten=cpos; nwritten<nbytes; nwritten+=512)
+ if (tpax_archive_append_memory_data(fdout,buf,512) < 0)
+ return TPAX_SYSTEM_ERROR(dctx);
+
+ /* all done */
+ tpax_set_driver_cpos(dctx,nwritten);
+
+ return 0;
+}
diff --git a/src/logic/tpax_queue_vector.c b/src/logic/tpax_queue_vector.c
new file mode 100644
index 0000000..cae4da0
--- /dev/null
+++ b/src/logic/tpax_queue_vector.c
@@ -0,0 +1,101 @@
+/**************************************************************/
+/* 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 <tpax/tpax.h>
+#include "tpax_driver_impl.h"
+#include "tpax_errinfo_impl.h"
+#include "tpax_visibility_impl.h"
+
+#define TPAX_MAX_DEPTH 512
+
+tpax_hidden const char * tpax_queue_item_full_path(
+ const struct tpax_driver_ctx * dctx,
+ const struct tpax_dirent * cdent)
+{
+ char * ch;
+ char * pathbuf;
+ const struct tpax_dirent * pparent;
+ const struct tpax_dirent ** pdirent;
+ const struct tpax_dirent * dirstck[TPAX_MAX_DEPTH];
+
+ if (cdent->depth >= TPAX_MAX_DEPTH)
+ return 0;
+
+ ch = pathbuf = (tpax_get_driver_ictx(dctx))->dirbuff;
+
+ for (pparent=cdent,pdirent=dirstck; pparent; pparent=pparent->parent)
+ *pdirent++ = pparent;
+
+ *pdirent-- = 0;
+
+ if (pdirent[0]->prefix)
+ ch += sprintf(ch,"%s",pdirent[0]->prefix);
+
+ for (; pdirent > dirstck; ) {
+ if (!(pdirent[0]->flags & TPAX_ITEM_SYMLINK))
+ ch += sprintf(ch,"%s/",pdirent[0]->dirent.d_name);
+
+ pdirent--;
+ }
+
+ if (pdirent[0]->flags & TPAX_ITEM_SYMLINK) {
+ *--ch = '\0';
+ } else {
+ sprintf(ch,"%s",pdirent[0]->dirent.d_name);
+ }
+
+ return pathbuf;
+}
+
+tpax_hidden int tpax_update_queue_vector(const struct tpax_driver_ctx * dctx)
+{
+ uintptr_t addr;
+ struct tpax_driver_ctx_impl * ictx;
+ struct tpax_dirent_buffer * dentbuf;
+ struct tpax_dirent ** direntv;
+ struct tpax_dirent * cdent;
+ struct tpax_dirent * cnext;
+ size_t arrsize;
+
+ /* driver */
+ ictx = tpax_get_driver_ictx(dctx);
+
+ /* vector alloc */
+ if (ictx->direntv)
+ free(ictx->direntv);
+
+ arrsize = ictx->nqueued + 1;
+
+ if (!(ictx->direntv = calloc(arrsize,sizeof(struct tpax_dirent *))))
+ return TPAX_SYSTEM_ERROR(dctx);
+
+ if (ictx->nqueued == 0)
+ return 0;
+
+ /* queue vector */
+ dentbuf = tpax_get_driver_dirents(dctx);
+ cdent = dentbuf->dbuf;
+
+ for (direntv=ictx->direntv; cdent; direntv++) {
+ *direntv = cdent;
+
+ addr = (uintptr_t)cdent;
+ addr += cdent->nsize;
+ cnext = (struct tpax_dirent *)addr;
+
+ if (cnext == dentbuf->cdent) {
+ dentbuf = dentbuf->next;
+ cnext = dentbuf ? dentbuf->dbuf : 0;
+ }
+
+ cdent = cnext;
+ }
+
+ return 0;
+}
diff --git a/src/logic/tpax_init_ustar_header.c b/src/meta/tpax_init_ustar_header.c
index fb83ba4..2479e28 100644
--- a/src/logic/tpax_init_ustar_header.c
+++ b/src/meta/tpax_init_ustar_header.c
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
@@ -30,7 +30,7 @@ static void tpax_octal_write(char * ch, ssize_t len, uint64_t val)
}
}
-int tpax_init_ustar_header(
+int tpax_meta_init_ustar_header(
const struct tpax_driver_ctx * dctx,
const char * path,
const struct stat * st,
diff --git a/src/output/tpax_output_error.c b/src/output/tpax_output_error.c
index 6ecc7ce..bc724cd 100644
--- a/src/output/tpax_output_error.c
+++ b/src/output/tpax_output_error.c
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
diff --git a/src/skin/tpax_skin_default.c b/src/skin/tpax_skin_default.c
index 4de94b4..a618645 100644
--- a/src/skin/tpax_skin_default.c
+++ b/src/skin/tpax_skin_default.c
@@ -1,32 +1,52 @@
#include "tpax_driver_impl.h"
+#include "tpax_visibility_impl.h"
#include "argv/argv.h"
-const struct argv_option tpax_default_options[] = {
- {"version", 0,TAG_VERSION,ARGV_OPTARG_NONE,0,0,0,
+const tpax_hidden struct argv_option tpax_default_options[] = {
+ {"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 "
@@ -34,20 +54,59 @@ const 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 "
diff --git a/src/tpax.c b/src/tpax.c
index be9d920..7cbbbfa 100644
--- a/src/tpax.c
+++ b/src/tpax.c
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
diff --git a/src/helper/tpax_path_copy.c b/src/util/tpax_path_copy.c
index 454b6ea..046ddde 100644
--- a/src/helper/tpax_path_copy.c
+++ b/src/util/tpax_path_copy.c
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
@@ -13,7 +13,7 @@
#include <tpax/tpax.h>
#include "tpax_driver_impl.h"
-int tpax_path_copy(
+int tpax_util_path_copy(
char * dstpath,
const char * srcpath,
size_t bufsize,
diff --git a/src/helper/tpax_stat_compare.c b/src/util/tpax_stat_compare.c
index c9ab660..2de6922 100644
--- a/src/helper/tpax_stat_compare.c
+++ b/src/util/tpax_stat_compare.c
@@ -1,6 +1,6 @@
/**************************************************************/
/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
/**************************************************************/
@@ -18,7 +18,7 @@
return (src -> member > dst -> member) \
? (1) : (-1)
-int tpax_stat_compare(
+int tpax_util_stat_compare(
const struct stat * src,
const struct stat * dst)
{