diff options
Diffstat (limited to 'src')
27 files changed, 1410 insertions, 847 deletions
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..e8ecb9e 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] [-d] [-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,24 @@ 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 struct tpax_driver_ctx_impl * tpax_driver_ctx_alloc( struct argv_meta * meta, const struct tpax_fd_ctx * fdctx, @@ -276,6 +348,12 @@ 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++) @@ -295,12 +373,11 @@ static struct tpax_driver_ctx_impl * tpax_driver_ctx_alloc( 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); @@ -309,6 +386,8 @@ static struct tpax_driver_ctx_impl * tpax_driver_ctx_alloc( } } + 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 +399,7 @@ static int tpax_get_driver_ctx_fail(struct argv_meta * meta) return -1; } -int tpax_get_driver_ctx( +int tpax_lib_get_driver_ctx( char ** argv, char ** envp, uint32_t flags, @@ -332,7 +411,9 @@ 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 tpax_fd_ctx lfdctx; size_t nunits; const char * program; int fddst; @@ -352,6 +433,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)); @@ -391,6 +473,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 +499,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 +526,10 @@ 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_STRICT_PATH: cctx.drvflags |= TPAX_DRIVER_STRICT_PATH_INPUT; break; @@ -476,11 +572,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 +587,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 +681,11 @@ int tpax_get_driver_ctx( return tpax_get_driver_ctx_fail(meta); } + if (archive) { + ctx->file = archive->arg; + ctx->ctx.file = &ctx->file; + } + ctx->ctx.program = program; ctx->ctx.cctx = &ctx->cctx; @@ -561,6 +697,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; @@ -576,11 +713,20 @@ static void tpax_free_driver_ctx_impl(struct tpax_driver_ctx_alloc * ictx) 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 +739,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 +762,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 d66440c..e6051cf 100644 --- a/src/internal/argv/argv.h +++ b/src/internal/argv/argv.h @@ -833,6 +833,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 +866,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 +885,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) { @@ -904,7 +910,7 @@ static void argv_usage_impl( /* color */ if (fcolor) { color = (color == ccyan) ? cblue : ccyan; - argv_dprintf(fd,color); + argv_dprintf(fd,color,0); } /* description, using either paradigm or argname if applicable */ @@ -916,7 +922,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..1e898e9 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,6 +26,9 @@ (TPAX_DRIVER_EXEC_MODE_WRITE | \ TPAX_DRIVER_EXEC_MODE_COPY) +#define TPAX_ITEM_EXPLICIT 0X1 +#define TPAX_ITEM_IMPLICIT 0X2 + extern const struct argv_option tpax_default_options[]; enum app_tags { @@ -35,18 +38,22 @@ enum app_tags { TAG_READ, TAG_WRITE, TAG_COPY, + TAG_FILE, TAG_FORMAT, TAG_BLKSIZE, TAG_RECURSE, TAG_NORECURSE, TAG_STRICT_PATH, TAG_PURE_PATH, + TAG_PRESERVE_ATIME, }; struct tpax_dirent { int fdat; int depth; + int flags; size_t nsize; + const char * prefix; const struct tpax_dirent * parent; struct dirent dirent; }; @@ -60,6 +67,7 @@ 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; @@ -69,10 +77,17 @@ struct tpax_driver_ctx_impl { 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 +158,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 +218,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 +261,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 56d5c92..aa78e40 100644 --- a/src/internal/tpax_tmpfile_impl.c +++ b/src/internal/tpax_tmpfile_impl.c @@ -1,16 +1,22 @@ /**************************************************************/ /* 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. */ /**************************************************************/ #define _GNU_SOURCE +#include <time.h> #include <fcntl.h> #include <limits.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdio.h> +#include <inttypes.h> + +#include "tpax_visibility_impl.h" + +#define PPRIX64 "%"PRIx64 /* mkostemp might be guarded by non-standard macros */ /* unless HAVE_NO_MKOSTEMP, assume it is available */ @@ -51,11 +57,11 @@ static int tpax_mkostemp(char * tmplate) return fd; } -int tpax_tmpfile(void) +tpax_hidden int tpax_tmpfile(void) { int fd; - unsigned seed; - char tmplate[64]; + void * addr; + char tmplate[128]; /* try with __fs_tmpfile() */ if ((fd = tpax_tmpfile_by_framework()) >= 0) @@ -66,10 +72,18 @@ int tpax_tmpfile(void) return fd; /* fallback to mk{o}stemp */ - seed = getpid(); + addr = tmplate; memset(tmplate,0,sizeof(tmplate)); - snprintf(tmplate,sizeof(tmplate),"/tmp/tpax_%d_%d_%d_XXXXXXXXXXXX", - getppid(),getpid(),rand_r(&seed)); + snprintf(tmplate,sizeof(tmplate), + "/tmp/" + ".tpax.tmpfile" + ".time."PPRIX64 + ".salt.%p" + ".pid.%d" + ".XXXXXXXXXXXX", + time(0), + addr, + getpid()); return tpax_mkostemp(tmplate); } 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..222b0ad --- /dev/null +++ b/src/logic/tpax_archive_enqueue.c @@ -0,0 +1,475 @@ +/**************************************************************/ +/* 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_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 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, + 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->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 depth; + bool fkeep; + long nbytes; + struct dirent * dirent; + struct dirent * dirents; + struct tpax_unit_ctx * uctx; + struct stat st; + uintptr_t addr; + + /* 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); + + /* 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); + + 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_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + uctx); + + /* 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_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + uctx); + + if (S_ISDIR(st.st_mode)) { + dirent->d_type = DT_DIR; + } + } + + if (tpax_archive_add_queue_item( + dctx,dirent, + dent,0,depth, + TPAX_ITEM_IMPLICIT, + fd,&fkeep) < 0) + return tpax_archive_enqueue_ret( + TPAX_NESTED_ERROR(dctx), + uctx); + } + + 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]; +} + +static int tpax_dirent_init_from_uctx( + const struct stat * st, + const char * basename, + struct dirent * dirent) +{ + /* st_mode to d_type translation */ + if (S_ISREG(st->st_mode)) + dirent->d_type = DT_REG; + else if (S_ISLNK(st->st_mode)) + dirent->d_type = DT_LNK; + else if (S_ISDIR(st->st_mode)) + dirent->d_type = DT_DIR; + else if (S_ISCHR(st->st_mode)) + dirent->d_type = DT_CHR; + else if (S_ISBLK(st->st_mode)) + dirent->d_type = DT_CHR; + else if (S_ISFIFO(st->st_mode)) + dirent->d_type = DT_CHR; + else + return -1; + + /* d_off, d_ino */ + dirent->d_off = 0; + dirent->d_ino = st->st_ino; + + /* d_reclen */ + dirent->d_reclen = offsetof(struct dirent,d_name); + dirent->d_reclen += strlen(basename) + 1; + + dirent->d_reclen += 0x1; + dirent->d_reclen |= 0x1; + dirent->d_reclen ^= 0x1; + + /* d_name */ + strcpy(dirent->d_name,basename); + + return 0; +} + +int tpax_archive_enqueue( + const struct tpax_driver_ctx * dctx, + const struct tpax_unit_ctx * uctx) +{ + int fdat; + 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; + char entbuf[PATH_MAX + sizeof(struct dirent)]; + + /* init */ + fdat = tpax_driver_fdcwd(dctx); + dirent = (struct dirent *)entbuf; + 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_uctx(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,0, + TPAX_ITEM_EXPLICIT, + 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..2555d26 --- /dev/null +++ b/src/logic/tpax_archive_write.c @@ -0,0 +1,306 @@ +/**************************************************************/ +/* 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, + struct tpax_unit_ctx * uctx) +{ + 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 char * path; + off_t hpos; + off_t dpos; + int fdtmp; + ssize_t nread; + ssize_t nbytes; + void * buf; + size_t buflen; + size_t cmplen; + void * membuf; + + /* full path */ + if (!(path = tpax_queue_item_full_path(dctx,cdent))) + return TPAX_CUSTOM_ERROR( + dctx, + TPAX_ERR_FLOW_ERROR); + + /* uctx */ + if (tpax_lib_get_unit_ctx(dctx,fdcwd,path,&uctx) < 0) + return TPAX_NESTED_ERROR(dctx); + + /* 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,uctx->st, + *uctx->link,&uhdr) < 0) + return tpax_archive_write_ret( + TPAX_NESTED_ERROR(dctx), + uctx); + + /* 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_io_create_memory_snapshot( + dctx,fdcwd,path, + uctx->st,membuf) < 0) + return tpax_archive_write_ret( + TPAX_NESTED_ERROR(dctx), + uctx); + } else { + if ((fdtmp = tpax_io_create_tmpfs_snapshot( + dctx,fdcwd,path, + uctx->st)) < 0) + return tpax_archive_write_ret( + TPAX_NESTED_ERROR(dctx), + uctx); + + if (lseek(fdtmp,0,SEEK_SET) < 0) + return tpax_archive_write_ret( + TPAX_SYSTEM_ERROR(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), + uctx); + } + + tpax_set_driver_cpos(dctx,dpos); + + /* all done? */ + if (!(S_ISREG(uctx->st->st_mode))) { + tpax_lib_free_unit_ctx(uctx); + 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_write_ret( + TPAX_SYSTEM_ERROR(dctx), + uctx); + + } else if (nbytes == 0) { + close(fdtmp); + return tpax_archive_write_ret( + TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR), + 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), + uctx); + } + } + + close(fdtmp); + } else { + if (tpax_archive_append_memory_data( + fdout,membuf, + uctx->st->st_size) < 0) + return tpax_archive_write_ret( + TPAX_SYSTEM_ERROR(dctx), + uctx); + } + + return tpax_archive_write_ret( + tpax_archive_append_pad(dctx,fdout,uctx->st), + 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..82930b1 --- /dev/null +++ b/src/logic/tpax_queue_vector.c @@ -0,0 +1,95 @@ +/**************************************************************/ +/* 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; ) { + ch += sprintf(ch,"%s/",pdirent[0]->dirent.d_name); + pdirent--; + } + + 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..1684553 100644 --- a/src/skin/tpax_skin_default.c +++ b/src/skin/tpax_skin_default.c @@ -1,32 +1,47 @@ #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, + {"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 +49,32 @@ 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"}, + + {"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 " @@ -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) { |