summaryrefslogtreecommitdiffhomepage
path: root/src/meta
diff options
context:
space:
mode:
Diffstat (limited to 'src/meta')
-rw-r--r--src/meta/tpax_init_ustar_header.c218
1 files changed, 218 insertions, 0 deletions
diff --git a/src/meta/tpax_init_ustar_header.c b/src/meta/tpax_init_ustar_header.c
new file mode 100644
index 0000000..2479e28
--- /dev/null
+++ b/src/meta/tpax_init_ustar_header.c
@@ -0,0 +1,218 @@
+/**************************************************************/
+/* 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/stat.h>
+
+#include <tpax/tpax.h>
+#include <tpax/tpax_specs.h>
+#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_meta_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_ISDIR(st->st_mode))
+ typeflag = TPAX_USTAR_TYPEFLAG_DIRFILE;
+ 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; uch<uchcap; uch++)
+ chksum += *uch;
+
+ chksum += sizeof(uhdr->u_chksum) * ' ';
+
+ tpax_octal_write(uhdr->u_chksum,ssizeof(uhdr->u_chksum),chksum);
+
+ /* all done; caller may now change REGFILE to HARDLINK */
+ return 0;
+}