diff --git a/include/tpax/tpax.h b/include/tpax/tpax.h index d9c73ad..8c45468 100644 --- a/include/tpax/tpax.h +++ b/include/tpax/tpax.h @@ -3,8 +3,10 @@ #include #include +#include #include "tpax_api.h" +#include "tpax_specs.h" #ifdef __cplusplus extern "C" { @@ -146,6 +148,10 @@ tpax_api int tpax_main (char **, char **, 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 *); +/* 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 *); + /* package info */ tpax_api const struct tpax_source_version * tpax_source_version(void); diff --git a/project/common.mk b/project/common.mk index 204f473..54f7d11 100644 --- a/project/common.mk +++ b/project/common.mk @@ -2,6 +2,7 @@ API_SRCS = \ src/driver/tpax_amain.c \ src/driver/tpax_driver_ctx.c \ src/driver/tpax_unit_ctx.c \ + src/logic/tpax_init_ustar_header.c \ src/output/tpax_output_error.c \ src/skin/tpax_skin_default.c \ diff --git a/project/tree.mk b/project/tree.mk index e78a86d..ab26f1e 100644 --- a/project/tree.mk +++ b/project/tree.mk @@ -1,6 +1,7 @@ TREE_DIRS = bin lib src \ src/driver \ src/internal \ + src/logic \ src/output \ src/skin \ diff --git a/src/logic/tpax_init_ustar_header.c b/src/logic/tpax_init_ustar_header.c new file mode 100644 index 0000000..a3ab120 --- /dev/null +++ b/src/logic/tpax_init_ustar_header.c @@ -0,0 +1,216 @@ +/******************************************************/ +/* tpax: a topological pax implementation */ +/* Copyright (C) 2020 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */ +/******************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "tpax_driver_impl.h" + +#ifndef ssizeof +#define ssizeof(x) (ssize_t)(sizeof(x)) +#endif + +static void tpax_octal_write(char * ch, ssize_t len, uint64_t val) +{ + for (--len,--len; len>=0 ; len--) { + ch[len] = val % 8 + '0'; + val /= 8; + } +} + +int tpax_init_ustar_header( + const struct tpax_driver_ctx * dctx, + const char * path, + const struct stat * st, + const char * linkname, + struct tpax_ustar_header * uhdr) +{ + size_t len; + const char * cap; + const char * mark; + unsigned char * uch; + unsigned char * uchcap; + size_t lnklen; + size_t stsize; + int64_t stmtim; + uint32_t chksum; + char typeflag; + struct group grp; + struct group * grpres; + struct passwd pwd; + struct passwd * pwdres; + char pwdbuf[2048]; + + /* size & mtime validation */ + stsize = S_ISREG(st->st_mode) ? st->st_size : 0; + stmtim = st->st_mtim.tv_sec; + + if (stsize > 077777777777) + return -1; + + if ((stmtim < 0) || (stmtim > 077777777777)) + return -1; + + /* linkname validation */ + if (S_ISLNK(st->st_mode) && !linkname) + return -1; + + lnklen = S_ISLNK(st->st_mode) + ? strnlen(linkname,sizeof(uhdr->u_linkname) + 1) + : 0; + + if (lnklen > sizeof(uhdr->u_linkname)) + return -1; + + /* typeflag validation */ + if (S_ISREG(st->st_mode)) + typeflag = TPAX_USTAR_TYPEFLAG_REGFILE; + else if (S_ISLNK(st->st_mode)) + typeflag = TPAX_USTAR_TYPEFLAG_SYMLINK; + else if (S_ISCHR(st->st_mode)) + typeflag = TPAX_USTAR_TYPEFLAG_CHARDEV; + else if (S_ISBLK(st->st_mode)) + typeflag = TPAX_USTAR_TYPEFLAG_BLKDEV; + else if (S_ISFIFO(st->st_mode)) + typeflag = TPAX_USTAR_TYPEFLAG_FIFODEV; + else + return -1; + + /* cap (without the trailing slash) */ + mark = &path[strlen(path)]; + cap = mark; + + for (--cap; (*cap == '/') && (cap > path); cap--) + (void)0; + + cap = ((cap == path) && (path[0] == '/')) + ? mark : &cap[1]; + + /* path solely consists of slash symbols? */ + if ((cap == mark) && (path[0] =='/')) { + if ((cap - path) > (ssizeof(uhdr->u_prefix) + 1 + ssizeof(uhdr->u_name))) + return -1; + + else if ((cap - path) <= ssizeof(uhdr->u_name)) + mark = 0; + + else + mark -= sizeof(uhdr->u_name); + + /* path entirely fits in u_name? */ + } else if ((cap - path) <= ssizeof(uhdr->u_name)) { + mark = 0; + + /* split between u_prefix and u_name as needed */ + } else { + mark = cap; + + do { + for (--mark; (mark > path) && (*mark != '/'); mark--) + (void)0; + } while ((mark - path) > ssizeof(uhdr->u_prefix)); + } + + /* one shot */ + memset(uhdr,0,sizeof(*uhdr)); + + /* u_name, u_prefix */ + if (mark) { + memcpy(uhdr->u_prefix,path,mark-path); + memcpy(uhdr->u_name,&mark[1],cap - mark); + } else { + memcpy(uhdr->u_name,path,cap - path); + } + + /* u_mode */ + tpax_octal_write(uhdr->u_mode,ssizeof(uhdr->u_mode),st->st_mode & TPAX_USTAR_MODE_MASK); + + /* u_uid, u_gid, u_uname, u_gname */ + if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_RUSTAR) { + tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),0); + tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),0); + } else { + if ((uint64_t)st->st_uid <= 07777777) + tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),st->st_uid); + else + tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),0); + + if ((uint64_t)st->st_gid <= 07777777) + tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),st->st_gid); + else + tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),0); + + pwdres = 0; + grpres = 0; + + getpwuid_r( + st->st_uid,&pwd, + pwdbuf,sizeof(pwdbuf), + &pwdres); + + if (pwdres && pwd.pw_name) + if ((len = strlen(pwd.pw_name)) < sizeof(uhdr->u_uname)) + memcpy(uhdr->u_uname,pwd.pw_name,len); + + getgrgid_r( + st->st_gid,&grp, + pwdbuf,sizeof(pwdbuf), + &grpres); + + if (grpres && grp.gr_name) + if ((len = strlen(grp.gr_name)) < sizeof(uhdr->u_gname)) + memcpy(uhdr->u_gname,grp.gr_name,len); + } + + /* u_size, u_mtime */ + tpax_octal_write(uhdr->u_size,ssizeof(uhdr->u_size),stsize); + tpax_octal_write(uhdr->u_mtime,ssizeof(uhdr->u_mtime),stmtim); + + /* u_typeflag */ + uhdr->u_typeflag[0] = typeflag; + + /* u_linkname */ + if (lnklen) + memcpy(uhdr->u_linkname,linkname,lnklen); + + /* u_magic */ + uhdr->u_magic[0] = 'u'; + uhdr->u_magic[1] = 's'; + uhdr->u_magic[2] = 't'; + uhdr->u_magic[3] = 'a'; + uhdr->u_magic[4] = 'r'; + + /* u_version */ + uhdr->u_version[0] = '0'; + uhdr->u_version[1] = '0'; + + /* u_devmajor, u_devminor */ + tpax_octal_write(uhdr->u_devmajor,ssizeof(uhdr->u_devmajor),0); + tpax_octal_write(uhdr->u_devminor,ssizeof(uhdr->u_devminor),0); + + /* u_chksum */ + uch = (unsigned char *)uhdr->u_name; + uchcap = (unsigned char *)uhdr->u_pad; + + for (chksum=0; uchu_chksum) * ' '; + + tpax_octal_write(uhdr->u_chksum,ssizeof(uhdr->u_chksum),chksum); + + /* all done; caller may now change REGFILE to HARDLINK */ + return 0; +}