Blame src/logic/tpax_init_ustar_header.c

7a9a40
/******************************************************/
7a9a40
/*  tpax: a topological pax implementation            */
07e762
/*  Copyright (C) 2020--2021  Z. Gilboa               */
7a9a40
/*  Released under GPLv2 and GPLv3; see COPYING.TPAX. */
7a9a40
/******************************************************/
7a9a40
7a9a40
#include <stdint.h>
7a9a40
#include <stdlib.h>
7a9a40
#include <string.h>
7a9a40
#include <unistd.h>
7a9a40
#include <fcntl.h>
7a9a40
#include <errno.h>
7a9a40
#include <grp.h>
7a9a40
#include <pwd.h>
7a9a40
#include <sys/stat.h>
7a9a40
7a9a40
#include <tpax/tpax.h>
7a9a40
#include <tpax/tpax_specs.h>
7a9a40
#include "tpax_driver_impl.h"
7a9a40
7a9a40
#ifndef ssizeof
7a9a40
#define ssizeof(x) (ssize_t)(sizeof(x))
7a9a40
#endif
7a9a40
7a9a40
static void tpax_octal_write(char * ch, ssize_t len, uint64_t val)
7a9a40
{
7a9a40
	for (--len,--len; len>=0 ; len--) {
7a9a40
		ch[len] = val % 8 + '0';
7a9a40
		val /= 8;
7a9a40
	}
7a9a40
}
7a9a40
7a9a40
int tpax_init_ustar_header(
7a9a40
	const struct tpax_driver_ctx *  dctx,
7a9a40
	const char *                    path,
7a9a40
	const struct stat *             st,
7a9a40
	const char *                    linkname,
7a9a40
	struct tpax_ustar_header *      uhdr)
7a9a40
{
7a9a40
	size_t		len;
7a9a40
	const char *	cap;
7a9a40
	const char *	mark;
7a9a40
	unsigned char *	uch;
7a9a40
	unsigned char *	uchcap;
7a9a40
	size_t		lnklen;
7a9a40
	size_t		stsize;
7a9a40
	int64_t		stmtim;
7a9a40
	uint32_t	chksum;
7a9a40
	char		typeflag;
7a9a40
	struct group	grp;
7a9a40
	struct group *	grpres;
7a9a40
	struct passwd	pwd;
7a9a40
	struct passwd *	pwdres;
7a9a40
	char		pwdbuf[2048];
7a9a40
7a9a40
	/* size & mtime validation */
7a9a40
	stsize = S_ISREG(st->st_mode) ? st->st_size : 0;
7a9a40
	stmtim = st->st_mtim.tv_sec;
7a9a40
7a9a40
	if (stsize > 077777777777)
7a9a40
		return -1;
7a9a40
7a9a40
	if ((stmtim < 0) || (stmtim > 077777777777))
7a9a40
		return -1;
7a9a40
7a9a40
	/* linkname validation */
7a9a40
	if (S_ISLNK(st->st_mode) && !linkname)
7a9a40
		return -1;
7a9a40
7a9a40
	lnklen = S_ISLNK(st->st_mode)
7a9a40
		? strnlen(linkname,sizeof(uhdr->u_linkname) + 1)
7a9a40
		: 0;
7a9a40
7a9a40
	if (lnklen > sizeof(uhdr->u_linkname))
7a9a40
		return -1;
7a9a40
7a9a40
	/* typeflag validation */
7a9a40
	if (S_ISREG(st->st_mode))
7a9a40
		typeflag = TPAX_USTAR_TYPEFLAG_REGFILE;
7a9a40
	else if (S_ISLNK(st->st_mode))
7a9a40
		typeflag = TPAX_USTAR_TYPEFLAG_SYMLINK;
800ed3
	else if (S_ISDIR(st->st_mode))
800ed3
		typeflag = TPAX_USTAR_TYPEFLAG_DIRFILE;
7a9a40
	else if (S_ISCHR(st->st_mode))
7a9a40
		typeflag = TPAX_USTAR_TYPEFLAG_CHARDEV;
7a9a40
	else if (S_ISBLK(st->st_mode))
7a9a40
		typeflag = TPAX_USTAR_TYPEFLAG_BLKDEV;
7a9a40
	else if (S_ISFIFO(st->st_mode))
7a9a40
		typeflag = TPAX_USTAR_TYPEFLAG_FIFODEV;
7a9a40
	else
7a9a40
		return -1;
7a9a40
7a9a40
	/* cap (without the trailing slash) */
7a9a40
	mark = &path[strlen(path)];
7a9a40
	cap  = mark;
7a9a40
7a9a40
	for (--cap; (*cap == '/') && (cap > path); cap--)
7a9a40
		(void)0;
7a9a40
7a9a40
	cap = ((cap == path) && (path[0] == '/'))
7a9a40
		? mark : &cap[1];
7a9a40
7a9a40
	/* path solely consists of slash symbols? */
7a9a40
	if ((cap == mark) && (path[0] =='/')) {
7a9a40
		if ((cap - path) > (ssizeof(uhdr->u_prefix) + 1 + ssizeof(uhdr->u_name)))
7a9a40
			return -1;
7a9a40
7a9a40
		else if ((cap - path) <= ssizeof(uhdr->u_name))
7a9a40
			mark = 0;
7a9a40
7a9a40
		else
7a9a40
			mark -= sizeof(uhdr->u_name);
7a9a40
7a9a40
	/* path entirely fits in u_name? */
7a9a40
	} else if ((cap - path) <= ssizeof(uhdr->u_name)) {
7a9a40
		mark = 0;
7a9a40
7a9a40
	/* split between u_prefix and u_name as needed */
7a9a40
	} else {
7a9a40
		mark = cap;
7a9a40
7a9a40
		do {
7a9a40
			for (--mark; (mark > path) && (*mark != '/'); mark--)
7a9a40
				(void)0;
7a9a40
		} while ((mark - path) > ssizeof(uhdr->u_prefix));
7a9a40
	}
7a9a40
7a9a40
	/* one shot */
7a9a40
	memset(uhdr,0,sizeof(*uhdr));
7a9a40
7a9a40
	/* u_name, u_prefix */
7a9a40
	if (mark) {
7a9a40
		memcpy(uhdr->u_prefix,path,mark-path);
7a9a40
		memcpy(uhdr->u_name,&mark[1],cap - mark);
7a9a40
	} else {
7a9a40
		memcpy(uhdr->u_name,path,cap - path);
7a9a40
	}
7a9a40
7a9a40
	/* u_mode */
7a9a40
	tpax_octal_write(uhdr->u_mode,ssizeof(uhdr->u_mode),st->st_mode & TPAX_USTAR_MODE_MASK);
7a9a40
7a9a40
	/* u_uid, u_gid, u_uname, u_gname */
7a9a40
	if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_RUSTAR) {
7a9a40
		tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),0);
7a9a40
		tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),0);
7a9a40
	} else {
7a9a40
		if ((uint64_t)st->st_uid <= 07777777)
7a9a40
			tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),st->st_uid);
7a9a40
		else
7a9a40
			tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),0);
7a9a40
7a9a40
		if ((uint64_t)st->st_gid <= 07777777)
7a9a40
			tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),st->st_gid);
7a9a40
		else
7a9a40
			tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),0);
7a9a40
7a9a40
		pwdres = 0;
7a9a40
		grpres = 0;
7a9a40
7a9a40
		getpwuid_r(
7a9a40
			st->st_uid,&pwd,
7a9a40
			pwdbuf,sizeof(pwdbuf),
7a9a40
			&pwdres);
7a9a40
7a9a40
		if (pwdres && pwd.pw_name)
7a9a40
			if ((len = strlen(pwd.pw_name)) < sizeof(uhdr->u_uname))
7a9a40
				memcpy(uhdr->u_uname,pwd.pw_name,len);
7a9a40
7a9a40
		getgrgid_r(
7a9a40
			st->st_gid,&grp,
7a9a40
			pwdbuf,sizeof(pwdbuf),
7a9a40
			&grpres);
7a9a40
7a9a40
		if (grpres && grp.gr_name)
7a9a40
			if ((len = strlen(grp.gr_name)) < sizeof(uhdr->u_gname))
7a9a40
				memcpy(uhdr->u_gname,grp.gr_name,len);
7a9a40
	}
7a9a40
7a9a40
	/* u_size, u_mtime */
7a9a40
	tpax_octal_write(uhdr->u_size,ssizeof(uhdr->u_size),stsize);
7a9a40
	tpax_octal_write(uhdr->u_mtime,ssizeof(uhdr->u_mtime),stmtim);
7a9a40
7a9a40
	/* u_typeflag */
7a9a40
	uhdr->u_typeflag[0] = typeflag;
7a9a40
7a9a40
	/* u_linkname */
7a9a40
	if (lnklen)
7a9a40
		memcpy(uhdr->u_linkname,linkname,lnklen);
7a9a40
7a9a40
	/* u_magic */
7a9a40
	uhdr->u_magic[0] = 'u';
7a9a40
	uhdr->u_magic[1] = 's';
7a9a40
	uhdr->u_magic[2] = 't';
7a9a40
	uhdr->u_magic[3] = 'a';
7a9a40
	uhdr->u_magic[4] = 'r';
7a9a40
7a9a40
	/* u_version */
7a9a40
	uhdr->u_version[0] = '0';
7a9a40
	uhdr->u_version[1] = '0';
7a9a40
7a9a40
	/* u_devmajor, u_devminor */
7a9a40
	tpax_octal_write(uhdr->u_devmajor,ssizeof(uhdr->u_devmajor),0);
7a9a40
	tpax_octal_write(uhdr->u_devminor,ssizeof(uhdr->u_devminor),0);
7a9a40
7a9a40
	/* u_chksum */
7a9a40
	uch    = (unsigned char *)uhdr->u_name;
7a9a40
	uchcap = (unsigned char *)uhdr->u_pad;
7a9a40
7a9a40
	for (chksum=0; uch
7a9a40
		chksum += *uch;
7a9a40
7a9a40
	chksum += sizeof(uhdr->u_chksum) * ' ';
7a9a40
7a9a40
	tpax_octal_write(uhdr->u_chksum,ssizeof(uhdr->u_chksum),chksum);
7a9a40
7a9a40
	/* all done; caller may now change REGFILE to HARDLINK */
7a9a40
	return 0;
7a9a40
}