| |
| |
| |
| |
| |
| |
| #define _DEFAULT_SOURCE 1 |
| |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <sys/mman.h> |
| |
| #define ARGV_DRIVER |
| |
| #include <tpax/tpax.h> |
| #include "tpax_version.h" |
| #include "tpax_driver_impl.h" |
| #include "argv/argv.h" |
| |
| #define TPAX_DRIVER_EXEC_MODE_MASK \ |
| (TPAX_DRIVER_EXEC_MODE_LIST \ |
| | TPAX_DRIVER_EXEC_MODE_READ \ |
| | TPAX_DRIVER_EXEC_MODE_WRITE \ |
| | TPAX_DRIVER_EXEC_MODE_COPY) |
| |
| #define TPAX_DRIVER_WRITE_FORMAT_MASK \ |
| (TPAX_DRIVER_WRITE_FORMAT_PAX \ |
| | TPAX_DRIVER_WRITE_FORMAT_CPIO \ |
| | TPAX_DRIVER_WRITE_FORMAT_USTAR \ |
| | TPAX_DRIVER_WRITE_FORMAT_RUSTAR) |
| |
| |
| static const struct tpax_source_version tpax_src_version = { |
| TPAX_TAG_VER_MAJOR, |
| TPAX_TAG_VER_MINOR, |
| TPAX_TAG_VER_PATCH, |
| TPAX_GIT_VERSION |
| }; |
| |
| |
| static const struct tpax_fd_ctx tpax_default_fdctx = { |
| .fdin = STDIN_FILENO, |
| .fdout = STDOUT_FILENO, |
| .fderr = STDERR_FILENO, |
| .fdcwd = AT_FDCWD, |
| .fddst = AT_FDCWD, |
| .fdlog = (-1), |
| }; |
| |
| struct tpax_driver_ctx_alloc { |
| struct argv_meta * meta; |
| struct tpax_driver_ctx_impl ctx; |
| uint64_t guard; |
| const char * units[]; |
| }; |
| |
| static uint32_t tpax_argv_flags(uint32_t flags) |
| { |
| uint32_t ret = ARGV_CLONE_VECTOR; |
| |
| if (flags & TPAX_DRIVER_VERBOSITY_NONE) |
| ret |= ARGV_VERBOSITY_NONE; |
| |
| if (flags & TPAX_DRIVER_VERBOSITY_ERRORS) |
| ret |= ARGV_VERBOSITY_ERRORS; |
| |
| if (flags & TPAX_DRIVER_VERBOSITY_STATUS) |
| ret |= ARGV_VERBOSITY_STATUS; |
| |
| return ret; |
| } |
| |
| static int tpax_driver_usage( |
| int fdout, |
| const char * program, |
| const char * arg, |
| const struct argv_option ** optv, |
| struct argv_meta * meta) |
| { |
| char header[512]; |
| |
| 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 -r -w [-d]\n\n" |
| "Options:\n", |
| program,program,program,program,program); |
| |
| argv_usage(fdout,header,optv,arg); |
| argv_free(meta); |
| |
| return TPAX_USAGE; |
| } |
| |
| static int tpax_driver_usage_exec_mode( |
| 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, |
| "\nWhen using explicit (long) mode options, " |
| "only one of --list, --read, --write, --copy " |
| "may be used.\n"); |
| |
| return TPAX_USAGE; |
| } |
| |
| static int tpax_driver_usage_copy_mode( |
| int fdout, |
| const char * program, |
| const char * arg, |
| const struct argv_option ** optv, |
| struct argv_meta * meta, |
| struct argv_entry * operand) |
| { |
| const char * errdesc; |
| |
| if (!operand) { |
| tpax_driver_usage( |
| fdout,program, |
| arg,optv,meta); |
| |
| tpax_dprintf( |
| fdout, |
| "\nThe copy mode requires a destination " |
| "directory argument.\n\n"); |
| |
| return TPAX_USAGE; |
| } |
| |
| switch (errno) { |
| case ENOENT: |
| tpax_dprintf( |
| fdout, |
| "%s: error: " |
| "the destination directory '%s' " |
| "does not exist.\n\n", |
| program,operand->arg); |
| break; |
| |
| case ENOTDIR: |
| tpax_dprintf( |
| fdout, |
| "%s: error: " |
| "'%s' is not a directory.\n\n", |
| program,operand->arg); |
| break; |
| |
| default: |
| if (!(errdesc = strerror(errno))) |
| errdesc = "<no error description>"; |
| |
| tpax_dprintf( |
| fdout, |
| "%s: error: while opening the " |
| "destination directory '%s': %s.\n\n", |
| program,operand->arg,errdesc); |
| break; |
| } |
| |
| argv_free(meta); |
| |
| return TPAX_USAGE; |
| } |
| |
| static int tpax_driver_usage_write_format( |
| 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, |
| "\nArchive format may only be specified " |
| "in write mode.\n"); |
| |
| return TPAX_USAGE; |
| } |
| |
| 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); |
| |
| return TPAX_USAGE; |
| } |
| |
| 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' is outside the specified range of 512 to 32256.\n", |
| arg); |
| |
| return TPAX_USAGE; |
| } |
| |
| static int tpax_driver_error_not_implemented( |
| int fdout, |
| const char * program, |
| const char * feature, |
| struct argv_meta * meta) |
| |
| { |
| tpax_dprintf( |
| fdout,"%s: error: %s is not yet implemented!\n\n", |
| program,feature); |
| |
| argv_free(meta); |
| |
| return TPAX_FATAL; |
| } |
| |
| static struct tpax_driver_ctx_impl * tpax_driver_ctx_alloc( |
| struct argv_meta * meta, |
| const struct tpax_fd_ctx * fdctx, |
| const struct tpax_common_ctx * cctx, |
| size_t nunits) |
| { |
| struct tpax_driver_ctx_alloc * ictx; |
| size_t size; |
| struct argv_entry * entry; |
| const char ** units; |
| int elements; |
| |
| size = sizeof(struct tpax_driver_ctx_alloc); |
| size += (nunits+1)*sizeof(const char *); |
| |
| if (!(ictx = calloc(1,size))) |
| return 0; |
| |
| memcpy(&ictx->ctx.fdctx,fdctx,sizeof(*fdctx)); |
| memcpy(&ictx->ctx.cctx,cctx,sizeof(*cctx)); |
| |
| elements = sizeof(ictx->ctx.erribuf) / sizeof(*ictx->ctx.erribuf); |
| |
| ictx->ctx.errinfp = &ictx->ctx.erriptr[0]; |
| ictx->ctx.erricap = &ictx->ctx.erriptr[--elements]; |
| |
| ictx->meta = meta; |
| |
| for (entry=meta->entries,units=ictx->units; entry->fopt || entry->arg; entry++) |
| if (!entry->fopt) |
| *units++ = entry->arg; |
| |
| if (cctx->drvflags & TPAX_DRIVER_EXEC_MODE_WRITE_COPY) { |
| ictx->ctx.bufsize = TPAX_FILEIO_BUFLEN; |
| ictx->ctx.bufaddr = mmap( |
| 0,ictx->ctx.bufsize, |
| PROT_READ|PROT_WRITE, |
| MAP_PRIVATE|MAP_ANONYMOUS, |
| -1,0); |
| |
| if (ictx->ctx.bufaddr == MAP_FAILED) { |
| 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); |
| |
| if (ictx->ctx.dirbuff == MAP_FAILED) { |
| munmap(ictx->ctx.bufaddr,ictx->ctx.bufsize); |
| free(ictx); |
| return 0; |
| } |
| } |
| |
| ictx->ctx.ctx.units = ictx->units; |
| ictx->ctx.ctx.errv = ictx->ctx.errinfp; |
| return &ictx->ctx; |
| } |
| |
| static int tpax_get_driver_ctx_fail(struct argv_meta * meta) |
| { |
| argv_free(meta); |
| return -1; |
| } |
| |
| int tpax_get_driver_ctx( |
| char ** argv, |
| char ** envp, |
| uint32_t flags, |
| const struct tpax_fd_ctx * fdctx, |
| struct tpax_driver_ctx ** pctx) |
| { |
| struct tpax_driver_ctx_impl * ctx; |
| struct tpax_common_ctx cctx; |
| const struct argv_option * optv[TPAX_OPTV_ELEMENTS]; |
| struct argv_meta * meta; |
| struct argv_entry * entry; |
| struct argv_entry * operand; |
| size_t nunits; |
| const char * program; |
| int fddst; |
| const char * ch; |
| |
| (void)envp; |
| |
| if (!fdctx) |
| fdctx = &tpax_default_fdctx; |
| |
| argv_optv_init(tpax_default_options,optv); |
| |
| if (!(meta = argv_get( |
| argv,optv, |
| tpax_argv_flags(flags), |
| fdctx->fderr))) |
| return -1; |
| |
| nunits = 0; |
| operand = 0; |
| program = argv_program_name(argv[0]); |
| memset(&cctx,0,sizeof(cctx)); |
| |
| cctx.drvflags = flags; |
| fddst = fdctx->fddst; |
| |
| |
| for (entry=meta->entries; entry->fopt || entry->arg; entry++) { |
| if (entry->fopt) { |
| switch (entry->tag) { |
| case TAG_HELP: |
| if (flags & TPAX_DRIVER_VERBOSITY_USAGE) |
| return tpax_driver_usage( |
| fdctx->fdout, |
| program,entry->arg, |
| optv,meta); |
| break; |
| |
| case TAG_VERSION: |
| cctx.drvflags |= TPAX_DRIVER_VERSION; |
| break; |
| |
| case TAG_LIST: |
| cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_LIST; |
| break; |
| |
| case TAG_READ: |
| cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_READ; |
| break; |
| |
| case TAG_WRITE: |
| cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_WRITE; |
| break; |
| |
| case TAG_COPY: |
| cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_COPY; |
| break; |
| |
| case TAG_FORMAT: |
| cctx.drvflags &= ~(uint64_t)(TPAX_DRIVER_WRITE_FORMAT_MASK); |
| |
| if (!strcmp(entry->arg,"pax")) |
| cctx.drvflags |= TPAX_DRIVER_WRITE_FORMAT_PAX; |
| else if (!strcmp(entry->arg,"cpio")) |
| cctx.drvflags |= TPAX_DRIVER_WRITE_FORMAT_CPIO; |
| else if (!strcmp(entry->arg,"ustar")) |
| cctx.drvflags |= TPAX_DRIVER_WRITE_FORMAT_USTAR; |
| else if (!strcmp(entry->arg,"rustar")) |
| cctx.drvflags |= TPAX_DRIVER_WRITE_FORMAT_RUSTAR; |
| |
| break; |
| |
| case TAG_BLKSIZE: |
| ch = (entry->arg[0] == '+') |
| ? &entry->arg[1] |
| : entry->arg; |
| |
| for (; *ch; ch++) |
| if ((*ch < '0') || (*ch > '9')) |
| return tpax_driver_usage_block_size( |
| fdctx->fdout, |
| program,entry->arg, |
| optv,meta); |
| |
| cctx.blksize = atoi(entry->arg); |
| |
| if ((cctx.blksize < 512) || (cctx.blksize > 32256)) |
| return tpax_driver_usage_block_size_range( |
| fdctx->fdout, |
| program,entry->arg, |
| optv,meta); |
| break; |
| |
| case TAG_RECURSE: |
| cctx.drvflags |= TPAX_DRIVER_DIR_MEMBER_RECURSE; |
| break; |
| |
| case TAG_NORECURSE: |
| cctx.drvflags &= ~(uintptr_t)TPAX_DRIVER_DIR_MEMBER_RECURSE; |
| break; |
| |
| case TAG_STRICT_PATH: |
| cctx.drvflags |= TPAX_DRIVER_STRICT_PATH_INPUT; |
| break; |
| |
| case TAG_PURE_PATH: |
| cctx.drvflags |= TPAX_DRIVER_PURE_PATH_OUTPUT; |
| break; |
| } |
| } else { |
| operand = entry; |
| nunits++; |
| } |
| } |
| |
| |
| switch (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_MASK) { |
| case 0: |
| if (!(cctx.drvflags & TPAX_DRIVER_VERSION)) |
| cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_LIST; |
| break; |
| |
| case TPAX_DRIVER_EXEC_MODE_READ|TPAX_DRIVER_EXEC_MODE_WRITE: |
| cctx.drvflags &= ~(uint64_t)(TPAX_DRIVER_EXEC_MODE_READ); |
| cctx.drvflags &= ~(uint64_t)(TPAX_DRIVER_EXEC_MODE_WRITE); |
| cctx.drvflags |= TPAX_DRIVER_EXEC_MODE_COPY; |
| break; |
| |
| case TPAX_DRIVER_EXEC_MODE_LIST: |
| case TPAX_DRIVER_EXEC_MODE_READ: |
| case TPAX_DRIVER_EXEC_MODE_WRITE: |
| case TPAX_DRIVER_EXEC_MODE_COPY: |
| break; |
| |
| default: |
| return tpax_driver_usage_exec_mode( |
| fdctx->fderr, |
| program,entry->arg, |
| optv,meta); |
| } |
| |
| |
| if (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_COPY) { |
| if (!operand) |
| return tpax_driver_usage_copy_mode( |
| fdctx->fderr, |
| program,entry->arg, |
| optv,meta,operand); |
| |
| fddst = openat( |
| fdctx->fdcwd, |
| operand->arg, |
| O_RDONLY|O_DIRECTORY|O_CLOEXEC,0); |
| |
| if (fddst < 0) |
| return tpax_driver_usage_copy_mode( |
| fdctx->fderr, |
| program,entry->arg, |
| optv,meta,operand); |
| } |
| |
| |
| switch (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_MASK) { |
| case TPAX_DRIVER_EXEC_MODE_LIST: |
| return tpax_driver_error_not_implemented( |
| fdctx->fderr,program,"list mode",meta); |
| |
| case TPAX_DRIVER_EXEC_MODE_READ: |
| return tpax_driver_error_not_implemented( |
| fdctx->fderr,program,"read mode",meta); |
| |
| case TPAX_DRIVER_EXEC_MODE_COPY: |
| close(fddst); |
| |
| return tpax_driver_error_not_implemented( |
| fdctx->fderr,program,"copy mode",meta); |
| |
| default: |
| break; |
| } |
| |
| |
| switch (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_MASK) { |
| case TPAX_DRIVER_EXEC_MODE_WRITE: |
| if (!(cctx.drvflags & TPAX_DRIVER_WRITE_FORMAT_MASK)) |
| cctx.drvflags |= TPAX_DRIVER_WRITE_FORMAT_USTAR; |
| break; |
| |
| default: |
| if (cctx.drvflags & TPAX_DRIVER_WRITE_FORMAT_MASK) |
| return tpax_driver_usage_write_format( |
| fdctx->fderr,program, |
| entry->arg,optv,meta); |
| } |
| |
| |
| switch (cctx.drvflags & TPAX_DRIVER_WRITE_FORMAT_MASK) { |
| case TPAX_DRIVER_WRITE_FORMAT_PAX: |
| return tpax_driver_error_not_implemented( |
| fdctx->fderr,program,"the pax format",meta); |
| |
| case TPAX_DRIVER_WRITE_FORMAT_CPIO: |
| return tpax_driver_error_not_implemented( |
| fdctx->fderr,program,"the cpio format",meta); |
| |
| default: |
| break; |
| } |
| |
| |
| if (!(ctx = tpax_driver_ctx_alloc(meta,fdctx,&cctx,nunits))) { |
| if (cctx.drvflags & TPAX_DRIVER_EXEC_MODE_COPY) |
| close(fddst); |
| |
| return tpax_get_driver_ctx_fail(meta); |
| } |
| |
| ctx->ctx.program = program; |
| ctx->ctx.cctx = &ctx->cctx; |
| |
| *pctx = &ctx->ctx; |
| return TPAX_OK; |
| } |
| |
| static void tpax_free_driver_ctx_impl(struct tpax_driver_ctx_alloc * ictx) |
| { |
| void * next; |
| size_t size; |
| |
| for (; ictx->ctx.dirents; ) { |
| next = ictx->ctx.dirents->next; |
| size = ictx->ctx.dirents->size; |
| |
| munmap(ictx->ctx.dirents,size); |
| ictx->ctx.dirents = (struct tpax_dirent_buffer *)next; |
| } |
| |
| if (ictx->ctx.bufaddr) |
| munmap(ictx->ctx.bufaddr,ictx->ctx.bufsize); |
| |
| if (ictx->ctx.dirbuff) |
| munmap(ictx->ctx.dirbuff,TPAX_DIRENT_BUFLEN); |
| |
| argv_free(ictx->meta); |
| free(ictx); |
| } |
| |
| void tpax_free_driver_ctx(struct tpax_driver_ctx * ctx) |
| { |
| struct tpax_driver_ctx_alloc * ictx; |
| uintptr_t addr; |
| |
| if (ctx) { |
| addr = (uintptr_t)ctx - offsetof(struct tpax_driver_ctx_impl,ctx); |
| addr = addr - offsetof(struct tpax_driver_ctx_alloc,ctx); |
| ictx = (struct tpax_driver_ctx_alloc *)addr; |
| tpax_free_driver_ctx_impl(ictx); |
| } |
| } |
| |
| const struct tpax_source_version * tpax_source_version(void) |
| { |
| return &tpax_src_version; |
| } |
| |
| int tpax_get_driver_fdctx( |
| const struct tpax_driver_ctx * dctx, |
| struct tpax_fd_ctx * fdctx) |
| { |
| struct tpax_driver_ctx_impl * ictx; |
| |
| ictx = tpax_get_driver_ictx(dctx); |
| |
| fdctx->fdin = ictx->fdctx.fdin; |
| fdctx->fdout = ictx->fdctx.fdout; |
| fdctx->fderr = ictx->fdctx.fderr; |
| fdctx->fdlog = ictx->fdctx.fdlog; |
| fdctx->fdcwd = ictx->fdctx.fdcwd; |
| fdctx->fddst = ictx->fdctx.fddst; |
| |
| return 0; |
| } |
| |
| int tpax_set_driver_fdctx( |
| struct tpax_driver_ctx * dctx, |
| const struct tpax_fd_ctx * fdctx) |
| { |
| struct tpax_driver_ctx_impl * ictx; |
| |
| ictx = tpax_get_driver_ictx(dctx); |
| |
| ictx->fdctx.fdin = fdctx->fdin; |
| ictx->fdctx.fdout = fdctx->fdout; |
| ictx->fdctx.fderr = fdctx->fderr; |
| ictx->fdctx.fdlog = fdctx->fdlog; |
| ictx->fdctx.fdcwd = fdctx->fdcwd; |
| ictx->fdctx.fddst = fdctx->fddst; |
| |
| return 0; |
| } |