#ifndef lint
#if 0
static const char sccsid[] = "@(#)tar.c 8.2 (Berkeley) 4/18/94";
#else
static const char rcsid[] __attribute__((__unused__)) = "$OpenBSD: tar.c,v 1.34 2004/10/23 19:34:14 otto Exp $";
#endif
#endif
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "pax.h"
#include "extern.h"
#include "tar.h"
#include <fnmatch.h>
#include <regex.h>
#include "pat_rep.h"
#include <errno.h>
char pax_eh_datablk[4*1024];
int pax_read_or_list_mode = 0;
int want_a_m_time_headers = 0;
int want_linkdata = 0;
int pax_invalid_action = 0;
char * pax_invalid_action_write_path = NULL;
char * pax_invalid_action_write_cwd = NULL;
char
*path_g, *path_x, *path_g_current, *path_x_current,
*uname_g, *uname_x, *uname_g_current, *uname_x_current,
*gname_g, *gname_x, *gname_g_current, *gname_x_current,
*comment_g, *comment_x, *comment_g_current, *comment_x_current,
*charset_g, *charset_x, *charset_g_current, *charset_x_current,
*atime_g, *atime_x, *atime_g_current, *atime_x_current,
*gid_g, *gid_x, *gid_g_current, *gid_x_current,
*linkpath_g, *linkpath_x, *linkpath_g_current, *linkpath_x_current,
*mtime_g, *mtime_x, *mtime_g_current, *mtime_x_current,
*size_g, *size_x, *size_g_current, *size_x_current,
*uid_g, *uid_x, *uid_g_current, *uid_x_current;
char *header_name_g_requested = NULL,
*header_name_x_requested = NULL;
char *header_name_g = "/tmp/GlobalHead.%p.%n",
*header_name_x = "%d/PaxHeaders.%p/%f";
int nglobal_headers = 0;
char *pax_list_opt_format;
#define O_OPTION_ACTION_NOTIMPL 0
#define O_OPTION_ACTION_INVALID 1
#define O_OPTION_ACTION_DELETE 2
#define O_OPTION_ACTION_STORE_HEADER 3
#define O_OPTION_ACTION_TIMES 4
#define O_OPTION_ACTION_HEADER_NAME 5
#define O_OPTION_ACTION_LISTOPT 6
#define O_OPTION_ACTION_LINKDATA 7
#define O_OPTION_ACTION_IGNORE 8
#define O_OPTION_ACTION_ERROR 9
#define O_OPTION_ACTION_STORE_HEADER2 10
#define ATTRSRC_FROM_NOWHERE 0
#define ATTRSRC_FROM_X_O_OPTION 1
#define ATTRSRC_FROM_G_O_OPTION 2
#define ATTRSRC_FROM_X_HEADER 3
#define ATTRSRC_FROM_G_HEADER 4
#define KW_PATH_CASE 0
#define KW_SKIP_CASE -1
typedef struct {
char * name;
int len;
int active;
int cmdline_action;
int header_action;
char ** g_value;
char ** x_value;
char ** g_value_current;
char ** x_value_current;
int header_inx;
int header_len;
} O_OPTION_TYPE;
O_OPTION_TYPE o_option_table[] = {
{ "atime", 5, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER,
&atime_g, &atime_x, &atime_g_current, &atime_x_current, 0, KW_SKIP_CASE },
{ "charset", 7, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_IGNORE,
&charset_g, &charset_x, &charset_g_current, &charset_x_current, 0, KW_SKIP_CASE },
{ "comment", 7, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_IGNORE,
&comment_g, &comment_x, &comment_g_current, &comment_x_current, 0, KW_SKIP_CASE },
{ "gid", 3, 1, O_OPTION_ACTION_STORE_HEADER2, O_OPTION_ACTION_STORE_HEADER2,
&gid_g, &gid_x, &gid_g_current, &gid_x_current , 116, 8 },
{ "gname", 5, 1, O_OPTION_ACTION_STORE_HEADER2, O_OPTION_ACTION_STORE_HEADER2,
&gname_g, &gname_x, &gname_g_current, &gname_x_current, 297, 32 },
{ "linkpath", 8, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER,
&linkpath_g, &linkpath_x, &linkpath_g_current, &linkpath_x_current, 0, KW_SKIP_CASE },
{ "mtime", 5, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER,
&mtime_g, &mtime_x, &mtime_g_current, &mtime_x_current, 136, KW_SKIP_CASE },
{ "path", 4, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER,
&path_g, &path_x, &path_g_current, &path_x_current, 0, KW_PATH_CASE },
{ "size", 4, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER,
&size_g, &size_x, &size_g_current, &size_x_current, 124, KW_SKIP_CASE },
{ "uid", 3, 1, O_OPTION_ACTION_STORE_HEADER2, O_OPTION_ACTION_STORE_HEADER2,
&uid_g, &uid_x, &uid_g_current, &uid_x_current, 108, 8 },
{ "uname", 5, 1, O_OPTION_ACTION_STORE_HEADER2, O_OPTION_ACTION_STORE_HEADER2,
&uname_g, &uname_x, &uname_g_current, &uname_x_current, 265, 32 },
{ "exthdr.name", 11, 1, O_OPTION_ACTION_HEADER_NAME, O_OPTION_ACTION_ERROR,
&header_name_x, &header_name_x_requested, NULL, NULL, 0, KW_SKIP_CASE },
{ "globexthdr.name", 15, 1, O_OPTION_ACTION_HEADER_NAME, O_OPTION_ACTION_ERROR,
&header_name_g, &header_name_g_requested, NULL, NULL, 0, KW_SKIP_CASE },
{ "delete", 6, 1, O_OPTION_ACTION_DELETE, O_OPTION_ACTION_ERROR,
NULL, NULL, NULL, NULL, 0, KW_SKIP_CASE },
{ "invalid", 7, 1, O_OPTION_ACTION_INVALID, O_OPTION_ACTION_ERROR,
NULL, NULL, NULL, NULL, 0, KW_SKIP_CASE },
{ "linkdata", 8, 1, O_OPTION_ACTION_LINKDATA, O_OPTION_ACTION_ERROR,
NULL, NULL, NULL, NULL, 0, KW_SKIP_CASE },
{ "listopt", 7, 1, O_OPTION_ACTION_LISTOPT, O_OPTION_ACTION_ERROR,
&pax_list_opt_format, NULL, NULL, NULL, 0, KW_SKIP_CASE },
{ "times", 5, 1, O_OPTION_ACTION_TIMES, O_OPTION_ACTION_ERROR,
NULL, NULL, NULL, NULL, 0, KW_SKIP_CASE },
};
int ext_header_inx,
global_ext_header_inx;
int ext_header_entry [4*sizeof(o_option_table)/sizeof(O_OPTION_TYPE)],
global_ext_header_entry[4*sizeof(o_option_table)/sizeof(O_OPTION_TYPE)];
static size_t expandname(char *, size_t, char **, const char *, size_t);
static u_long pax_chksm(char *, int);
static char *name_split(char *, int);
static int ul_oct(u_long, char *, int, int);
#ifndef LONG_OFF_T
static int uqd_oct(u_quad_t, char *, int, int);
#endif
static uid_t uid_nobody;
static uid_t uid_warn;
static gid_t gid_nobody;
static gid_t gid_warn;
static int
ul_oct(u_long val, char *str, int len, int term)
{
char *pt;
pt = str + len - 1;
switch (term) {
case 3:
*pt-- = '\0';
break;
case 2:
*pt-- = ' ';
*pt-- = '\0';
break;
case 1:
*pt-- = ' ';
break;
case 0:
default:
*pt-- = '\0';
*pt-- = ' ';
break;
}
while (pt >= str) {
*pt-- = '0' + (char)(val & 0x7);
if ((val = val >> 3) == (u_long)0)
break;
}
while (pt >= str)
*pt-- = '0';
if (val != (u_long)0)
return(-1);
return(0);
}
#ifndef LONG_OFF_T
static int
uqd_oct(u_quad_t val, char *str, int len, int term)
{
char *pt;
pt = str + len - 1;
switch (term) {
case 3:
*pt-- = '\0';
break;
case 2:
*pt-- = ' ';
*pt-- = '\0';
break;
case 1:
*pt-- = ' ';
break;
case 0:
default:
*pt-- = '\0';
*pt-- = ' ';
break;
}
while (pt >= str) {
*pt-- = '0' + (char)(val & 0x7);
if ((val = val >> 3) == 0)
break;
}
while (pt >= str)
*pt-- = '0';
if (val != (u_quad_t)0)
return(-1);
return(0);
}
#endif
static u_long
pax_chksm(char *blk, int len)
{
char *stop;
char *pt;
u_long chksm = BLNKSUM;
pt = blk;
stop = blk + CHK_OFFSET;
while (pt < stop)
chksm += (u_long)(*pt++ & 0xff);
pt += CHK_LEN;
stop = blk + len;
while (pt < stop)
chksm += (u_long)(*pt++ & 0xff);
return(chksm);
}
void
pax_format_list_output(ARCHD *arcn, time_t now, FILE *fp, int term)
{
char *nextpercent, *nextchar;
char buf[4*1024];
int pos, cpylen;
char *fname;
nextpercent = strchr(pax_list_opt_format,'%');
if (nextpercent==NULL) {
safe_print(pax_list_opt_format, fp);
(void)putc(term, fp);
(void)fflush(fp);
return;
}
pos = nextpercent-pax_list_opt_format;
memcpy(buf,pax_list_opt_format, pos);
while (nextpercent++) {
switch (*nextpercent) {
case 'F':
fname = arcn->name;
cpylen = strlen(fname);
memcpy(&buf[pos],fname,cpylen);
pos+= cpylen;
break;
case 'D':
case 'T':
case 'M':
case 'L':
default:
paxwarn(1, "Unimplemented listopt format: %c",*nextpercent);
break;
}
nextpercent++;
if (*nextpercent=='\0') {
break;
}
nextchar = nextpercent;
nextpercent = strchr(nextpercent,'%');
if (nextpercent==NULL) {
cpylen = strlen(nextchar);
} else {
cpylen = nextpercent - nextchar;
}
memcpy(&buf[pos],nextchar, cpylen);
pos += cpylen;
}
buf[pos]='\0';
safe_print(&buf[0], fp);
(void)putc(term, fp);
(void)fflush(fp);
return;
}
void
cleanup_pax_invalid_action()
{
switch (pax_invalid_action) {
case PAX_INVALID_ACTION_BYPASS:
case PAX_INVALID_ACTION_RENAME:
break;
case PAX_INVALID_ACTION_WRITE:
pax_invalid_action_write_path = NULL;
if (pax_invalid_action_write_cwd) {
free(pax_invalid_action_write_cwd);
pax_invalid_action_write_cwd = NULL;
}
break;
case PAX_INVALID_ACTION_UTF8:
default:
paxwarn(1, "pax_invalid_action not implemented:%d", pax_invalid_action);
}
}
void
record_pax_invalid_action_results(ARCHD * arcn, char * fixed_path)
{
switch (pax_invalid_action) {
case PAX_INVALID_ACTION_BYPASS:
case PAX_INVALID_ACTION_RENAME:
break;
case PAX_INVALID_ACTION_WRITE:
pax_invalid_action_write_path = fixed_path;
pax_invalid_action_write_cwd = strdup(arcn->name);
pax_invalid_action_write_cwd[fixed_path-arcn->name-1] = '\0';
break;
case PAX_INVALID_ACTION_UTF8:
default:
paxwarn(1, "pax_invalid_action not implemented:%d", pax_invalid_action);
}
}
int
perform_pax_invalid_action(ARCHD * arcn, int err)
{
int rc = 0;
switch (pax_invalid_action) {
case PAX_INVALID_ACTION_BYPASS:
rc = -1;
break;
case PAX_INVALID_ACTION_RENAME:
rc = tty_rename(arcn);
break;
case PAX_INVALID_ACTION_WRITE:
pax_invalid_action_write_path = NULL;
pax_invalid_action_write_cwd = NULL;
rc = 2;
break;
case PAX_INVALID_ACTION_UTF8:
default:
paxwarn(1, "pax_invalid_action not implemented:%d", pax_invalid_action);
rc = -1;
}
return rc;
}
void
delete_keywords(char * pattern)
{
int i;
for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) {
if (fnmatch(pattern, o_option_table[i].name, 0) == 0) {
o_option_table[i].active = 0;
}
}
}
int
pax_opt(void)
{
OPLIST *opt;
int got_option = 0;
while ((opt = opt_next()) != NULL) {
int i;
got_option = -1;
pax_invalid_action = PAX_INVALID_ACTION_BYPASS;
for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) {
if (strncasecmp(opt->name, o_option_table[i].name, o_option_table[i].len) == 0) {
got_option = 1;
switch (o_option_table[i].cmdline_action) {
case O_OPTION_ACTION_INVALID:
if (opt->separator != SEP_EQ) {
paxwarn(1,"-o %s= option requires '=' separator: option ignored",
opt->name);
break;
}
if (opt->value) {
if (strncasecmp(opt->value,"bypass",6) == 0) {
pax_invalid_action = PAX_INVALID_ACTION_BYPASS;
} else if (strncasecmp(opt->value,"rename",6) == 0) {
pax_invalid_action = PAX_INVALID_ACTION_RENAME;
} else if (strncasecmp(opt->value,"UTF-8",5) == 0) {
pax_invalid_action = PAX_INVALID_ACTION_UTF8;
} else if (strncasecmp(opt->value,"write",5) == 0) {
pax_invalid_action = PAX_INVALID_ACTION_WRITE;
} else {
paxwarn(1,"Invalid action %s not recognized: option ignored",
opt->value);
}
} else {
paxwarn(1,"Invalid action RHS not specified: option ignored");
}
break;
case O_OPTION_ACTION_DELETE:
if (opt->separator != SEP_EQ) {
paxwarn(1,"-o %s= option requires '=' separator: option ignored",
opt->name);
break;
}
delete_keywords(opt->value);
break;
case O_OPTION_ACTION_STORE_HEADER2:
if(pax_read_or_list_mode) pids = 1;
case O_OPTION_ACTION_STORE_HEADER:
if (o_option_table[i].g_value == NULL ||
o_option_table[i].x_value == NULL ) {
paxwarn(1,"-o option not implemented: %s=%s",
opt->name, opt->value);
} else {
if (opt->separator == SEP_EQ) {
*(o_option_table[i].g_value) = opt->value;
global_ext_header_entry[global_ext_header_inx++] = i;
} else if (opt->separator == SEP_COLONEQ ) {
*(o_option_table[i].x_value) = opt->value;
ext_header_entry [ext_header_inx++] = i;
} else {
paxwarn(1,"-o %s option is missing value", opt->name);
}
}
break;
case O_OPTION_ACTION_TIMES:
if (opt->separator != SEP_NONE) {
paxwarn(1,"-o %s option takes no value: option ignored", opt->name);
break;
}
want_a_m_time_headers = 1;
break;
case O_OPTION_ACTION_LINKDATA:
if (opt->separator != SEP_NONE) {
paxwarn(1,"-o %s option takes no value: option ignored", opt->name);
break;
}
want_linkdata = 1;
break;
case O_OPTION_ACTION_HEADER_NAME:
if (opt->separator != SEP_EQ) {
paxwarn(1,"-o %s= option requires '=' separator: option ignored",
opt->name);
break;
}
*(o_option_table[i].g_value) = opt->value;
*(o_option_table[i].x_value) = "YES";
break;
case O_OPTION_ACTION_LISTOPT:
if (opt->separator != SEP_EQ) {
paxwarn(1,"-o %s= option requires '=' separator: option ignored",
opt->name);
break;
}
*(o_option_table[i].g_value) = opt->value;
break;
case O_OPTION_ACTION_NOTIMPL:
default:
paxwarn(1,"pax format -o option not yet implemented: %s=%s",
opt->name, opt->value);
break;
}
break;
}
}
if (got_option == -1) {
paxwarn(1,"pax format -o option not recognized: %s=%s",
opt->name, opt->value);
}
}
return(0);
}
int
expand_extended_headers(ARCHD *arcn, HD_USTAR *hd)
{
char mybuf[BLKMULT];
HD_USTAR *myhd;
char * current_value;
int path_replaced = 0;
int i, len;
myhd = hd;
while (myhd->typeflag == PAXGTYPE || myhd->typeflag == PAXXTYPE) {
char *name, *str;
int size, nbytes, inx;
size = asc_ul(myhd->size, sizeof(myhd->size), OCT);
if (size > sizeof(mybuf)) {
paxwarn(1,"extended header buffer overflow");
exit(1);
}
nbytes = rd_wrbuf(mybuf, size);
if (nbytes != size) {
paxwarn(1,"extended header data read failure: nbytes=%d, size=%d\n",
nbytes, size);
exit(1);
}
inx=0;
while (nbytes > 0) {
int got_option = -1;
int nentries = sscanf(&mybuf[inx],"%d ", &len);
if (nentries != 1) {
paxwarn(1,"Extended header failure: length");
exit(1);
}
if (len < 0 || (inx+len-1 >= sizeof(mybuf))) {
paxwarn(1, "Extended header failure: invalid length (%d)", len);
exit(1);
}
if (mybuf[inx+len-1] != '\n') {
paxwarn(1,"Extended header failure: missed newline");
exit(1);
} else
mybuf[inx+len-1] = '\0';
name = strchr(&mybuf[inx],' ');
if (name) name++;
else {
paxwarn(1,"Extended header failure: missing space");
exit(1);
}
str = strchr(name,'=');
if (str) {
*str++='\0';
} else {
paxwarn(1,"Extended header failure: missing RHS string");
exit(1);
}
for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) {
if (strncasecmp(name, o_option_table[i].name, o_option_table[i].len) == 0) {
got_option = i;
break;
}
}
if (got_option == -1) {
paxwarn(1,"Unrecognized header keyword: %s",name);
} else {
int found_value = ATTRSRC_FROM_NOWHERE;
current_value = NULL;
if (myhd->typeflag == PAXXTYPE) {
if (*o_option_table[got_option].x_value) {
current_value = *o_option_table[got_option].x_value;
found_value = ATTRSRC_FROM_X_O_OPTION;
} else {
current_value = str;
found_value = ATTRSRC_FROM_X_HEADER;
}
} else if (myhd->typeflag == PAXGTYPE) {
if (*o_option_table[got_option].g_value) {
current_value = *o_option_table[got_option].g_value;
found_value = ATTRSRC_FROM_G_O_OPTION;
} else {
current_value = str;
found_value = ATTRSRC_FROM_G_HEADER;
}
} else {
paxwarn(1,"Unsupported header type:%c",myhd->typeflag);
}
if (current_value) {
switch (o_option_table[got_option].header_action) {
case O_OPTION_ACTION_IGNORE:
paxwarn(1,"ignoring header keyword: %s",name);
break;
case O_OPTION_ACTION_STORE_HEADER2:
case O_OPTION_ACTION_STORE_HEADER:
switch (found_value) {
case ATTRSRC_FROM_NOWHERE:
paxwarn(1, "internal error: value from nowhere");
break;
case ATTRSRC_FROM_X_O_OPTION:
case ATTRSRC_FROM_G_O_OPTION:
break;
case ATTRSRC_FROM_X_HEADER:
current_value = strdup(current_value);
if(*o_option_table[got_option].x_value_current)
free(*o_option_table[got_option].x_value_current);
*o_option_table[got_option].x_value_current = current_value;
break;
case ATTRSRC_FROM_G_HEADER:
current_value = strdup(current_value);
if(*o_option_table[got_option].g_value_current)
free(*o_option_table[got_option].g_value_current);
*o_option_table[got_option].g_value_current = current_value;
break;
}
break;
case O_OPTION_ACTION_ERROR:
default:
paxwarn(1,"Unsupported extended header attribute: %s=%s",
name, str);
}
}
}
inx+=len;
nbytes -= len;
}
(void)rd_skip(TAR_PAD(size));
nbytes = rd_wrbuf(mybuf, frmt->hsz);
if (nbytes != frmt->hsz) {
paxwarn(1,"extended header read failure: nbytes=%d, size=%d\n",
nbytes, frmt->hsz);
}
myhd = ((HD_USTAR *)mybuf);
}
current_value = NULL;
for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) {
int header_len, free_it;
if (!o_option_table[i].active) continue;
header_len = o_option_table[i].header_len;
free_it = 0;
if (header_len >= 0) {
current_value = *o_option_table[i].x_value;
if (!current_value) {
current_value = *o_option_table[i].x_value_current;
if (current_value) {
*o_option_table[i].x_value_current = NULL;
free_it = 1;
} else {
current_value = *o_option_table[i].g_value;
if (!current_value)
current_value = *o_option_table[i].g_value_current;
}
}
if (current_value) {
len = strlen(current_value);
if (header_len == KW_PATH_CASE) {
path_replaced = 1;
arcn->nlen = len;
strlcpy(arcn->name,current_value,sizeof(arcn->name));
} else {
if (len > header_len) {
paxwarn(1," length of string from extended header bigger than header field:"
" THAT won't work!\n");
} else {
char * p = (char *) myhd;
memcpy(&p[o_option_table[i].header_inx],
current_value, len);
if (len != header_len) {
p[o_option_table[i].header_inx+len] = '\0';
}
}
}
}
if (free_it) free(current_value);
}
}
if (myhd==hd) return(path_replaced);
memcpy(hd, myhd, sizeof(HD_USTAR));
return(path_replaced);
}
int
pax_id(char *blk, int size)
{
HD_USTAR *hd;
if (size < BLKMULT)
return(-1);
hd = (HD_USTAR *)blk;
if (hd->name[0] == '\0')
return(-1);
if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0)
return(-1);
if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != pax_chksm(blk,BLKMULT))
return(-1);
if ((hd->typeflag != PAXXTYPE) && (hd->typeflag != PAXGTYPE)) {
if (act==LIST || act==EXTRACT) {
return(0);
}
return(-1);
}
pax_invalid_action = PAX_INVALID_ACTION_BYPASS;
return(0);
}
int
pax_rd(ARCHD *arcn, char *buf)
{
HD_USTAR *hd;
int cnt = 0;
int check_path;
dev_t devmajor;
dev_t devminor;
if (pax_id(buf, BLKMULT) < 0)
return(-1);
memset(arcn, 0, sizeof(*arcn));
arcn->org_name = arcn->name;
arcn->sb.st_nlink = 1;
hd = (HD_USTAR *)buf;
check_path = expand_extended_headers(arcn, hd);
if (check_path) {
if (arcn->nlen > sizeof(arcn->name)) {
paxwarn(1,"pathname from extended header info doesn't fit! (len=%d)\n",
arcn->nlen);
}
} else {
char *dest = arcn->name;
if (*(hd->prefix) != '\0') {
cnt = strlcpy(dest, hd->prefix, sizeof(arcn->name) - 1);
dest += cnt;
*dest++ = '/';
cnt++;
} else {
cnt = 0;
}
if (hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) {
arcn->nlen = cnt + expandname(dest, sizeof(arcn->name) - cnt,
&gnu_name_string, hd->name, sizeof(hd->name));
arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name),
&gnu_link_string, hd->linkname, sizeof(hd->linkname));
}
}
arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) &
0xfff);
#ifdef LONG_OFF_T
arcn->sb.st_size = (off_t)asc_ul(hd->size, sizeof(hd->size), OCT);
#else
arcn->sb.st_size = (off_t)asc_uqd(hd->size, sizeof(hd->size), OCT);
#endif
arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT);
arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
hd->gname[sizeof(hd->gname) - 1] = '\0';
if (gid_name(hd->gname, &(arcn->sb.st_gid)) < 0)
arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT);
hd->uname[sizeof(hd->uname) - 1] = '\0';
if (uid_name(hd->uname, &(arcn->sb.st_uid)) < 0)
arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT);
arcn->pad = 0;
arcn->skip = 0;
arcn->sb.st_rdev = (dev_t)0;
switch (hd->typeflag) {
case FIFOTYPE:
arcn->type = PAX_FIF;
arcn->sb.st_mode |= S_IFIFO;
break;
case DIRTYPE:
arcn->type = PAX_DIR;
arcn->sb.st_mode |= S_IFDIR;
arcn->sb.st_nlink = 2;
if (arcn->name[arcn->nlen - 1] == '/')
arcn->name[--arcn->nlen] = '\0';
break;
case BLKTYPE:
case CHRTYPE:
if (hd->typeflag == BLKTYPE) {
arcn->type = PAX_BLK;
arcn->sb.st_mode |= S_IFBLK;
} else {
arcn->type = PAX_CHR;
arcn->sb.st_mode |= S_IFCHR;
}
devmajor = (dev_t)asc_ul(hd->devmajor,sizeof(hd->devmajor),OCT);
devminor = (dev_t)asc_ul(hd->devminor,sizeof(hd->devminor),OCT);
arcn->sb.st_rdev = TODEV(devmajor, devminor);
break;
case SYMTYPE:
case LNKTYPE:
if (hd->typeflag == SYMTYPE) {
arcn->type = PAX_SLK;
arcn->sb.st_mode |= S_IFLNK;
} else {
arcn->type = PAX_HLK;
arcn->sb.st_mode |= S_IFREG;
arcn->sb.st_nlink = 2;
}
break;
case LONGLINKTYPE:
case LONGNAMETYPE:
arcn->type =
hd->typeflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF;
arcn->pad = TAR_PAD(arcn->sb.st_size);
arcn->skip = arcn->sb.st_size;
break;
case CONTTYPE:
case AREGTYPE:
case REGTYPE:
default:
arcn->type = PAX_REG;
arcn->pad = TAR_PAD(arcn->sb.st_size);
arcn->skip = arcn->sb.st_size;
arcn->sb.st_mode |= S_IFREG;
break;
}
return(0);
}
void
adjust_copy_for_pax_options(ARCHD * arcn)
{
int i;
if (global_ext_header_inx) {
for (i=0; i < global_ext_header_inx; i++) {
if (!o_option_table[global_ext_header_entry[i]].active) continue;
if (strcmp(o_option_table[global_ext_header_entry[i]].name, "path")==0) {
strlcpy(arcn->name,*(o_option_table[global_ext_header_entry[i]].g_value),
sizeof(arcn->name));
arcn->nlen = strlen(*(o_option_table[global_ext_header_entry[i]].g_value));
} else {
paxwarn(1, "adjust arcn for global extended header options not implemented:%d", i);
}
}
}
if (ext_header_inx) {
for (i=0; i < ext_header_inx; i++) {
if (!o_option_table[ext_header_entry[i]].active) continue;
if (strcmp(o_option_table[ext_header_entry[i]].name, "path")==0) {
strlcpy(arcn->name,*(o_option_table[ext_header_entry[i]].x_value),
sizeof(arcn->name));
arcn->nlen = strlen(*(o_option_table[ext_header_entry[i]].x_value));
} else {
paxwarn(1, "adjust arcn for extended header options not implemented:%d", i);
}
}
}
if (want_a_m_time_headers) {
}
}
static int
emit_extended_header_record(int len, int total_len, int head_type,
char * name, char * value)
{
if (total_len + len > sizeof(pax_eh_datablk)) {
paxwarn(1,"extended header buffer overflow for header type '%c': %d",
head_type, total_len+len);
} else {
sprintf(&pax_eh_datablk[total_len],"%d %s=%s\n", len, name, value);
total_len += len;
}
return (total_len);
}
static char *
substitute_percent(char * header, char * filename)
{
char *nextpercent, *nextchar;
char buf[4*1024];
int pos, cpylen;
char *dname, *fname;
nextpercent = strchr(header,'%');
if (nextpercent==NULL) return header;
pos = nextpercent-header;
memcpy(buf,header, pos);
while (nextpercent++) {
switch (*nextpercent) {
case '%':
buf[pos++]='%';
break;
case 'd':
dname = strrchr(filename,'/');
if (dname==NULL) {
cpylen = 1;
dname = ".";
} else {
cpylen = dname-filename;
dname = filename;
}
memcpy(&buf[pos],dname,cpylen);
pos+= cpylen;
break;
case 'f':
fname = strrchr(filename,'/');
if (fname==NULL) {
fname = filename;
} else {
fname++;
}
cpylen = strlen(fname);
memcpy(&buf[pos],fname,cpylen);
pos+= cpylen;
break;
case 'n':
pos += sprintf (&buf[pos],"%d",nglobal_headers);
break;
case 'p':
pos += sprintf (&buf[pos],"%d",getpid());
break;
default:
paxwarn(1,"header format substitution failed: '%c'", *nextpercent);
return (header);
}
nextpercent++;
if (*nextpercent=='\0') {
break;
}
nextchar = nextpercent;
nextpercent = strchr(nextpercent,'%');
if (nextpercent==NULL) {
cpylen = strlen(nextchar);
} else {
cpylen = nextpercent - nextchar;
}
memcpy(&buf[pos],nextchar, cpylen);
pos += cpylen;
}
buf[pos]='\0';
return (strdup(&buf[0]));
}
int
generate_pax_ext_header_and_data(ARCHD *arcn, int nfields, int *table,
char header_type, char * header_name, char * header_name_requested)
{
HD_USTAR *hd;
char hdblk[sizeof(HD_USTAR)];
u_long records_size;
int term_char, i, len, total_len;
char * str, *name;
if (nfields == 0 && (header_name_requested == NULL)) {
if (header_type==PAXXTYPE) {
if (!want_a_m_time_headers) return (0);
} else
return (0);
}
term_char = 1;
memset(hdblk, 0, sizeof(hdblk));
hd = (HD_USTAR *)hdblk;
memset(pax_eh_datablk, 0, sizeof(pax_eh_datablk));
hd->typeflag = header_type;
ul_oct((u_long)0444, hd->mode, sizeof(hd->mode), term_char);
strncpy(hd->magic, TMAGIC, TMAGLEN);
strncpy(hd->version, TVERSION, TVERSLEN);
ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),term_char);
total_len = 0;
for (i=0; i < nfields; i++) {
if (!o_option_table[table[i]].active) continue;
name = o_option_table[table[i]].name;
if (header_type == PAXXTYPE) {
str = *(o_option_table[table[i]].x_value);
} else {
str = *(o_option_table[table[i]].g_value);
}
if (str==NULL) {
paxwarn(1,"Missing option value for %s", name);
continue;
}
len = strlen(str) + o_option_table[table[i]].len + 3;
if (len < 9) len++;
else if (len < 98) len = len + 2;
else if (len < 997) len = len + 3;
else if (len < 9996) len = len + 4;
else {
paxwarn(1,"extended header data too long for header type '%c': %d",
header_type, len);
}
total_len = emit_extended_header_record(len, total_len,
header_type, name, str);
}
if ((header_type == PAXXTYPE) && want_a_m_time_headers) {
char time_buffer[12];
memset(time_buffer,0,sizeof(time_buffer));
sprintf(&time_buffer[0],"%d",(int)arcn->sb.st_atime);
len = 3 + 5 + strlen(&time_buffer[0]) + 2;
total_len = emit_extended_header_record(len, total_len,
header_type, "atime", &time_buffer[0]);
memset(time_buffer,0,sizeof(time_buffer));
sprintf(&time_buffer[0],"%d",(int)arcn->sb.st_mtime);
len = 3 + 5 + strlen(&time_buffer[0]) + 2;
total_len = emit_extended_header_record(len, total_len,
header_type, "mtime", &time_buffer[0]);
}
if ((total_len==0) && (header_name_requested == NULL)) return (0);
if (header_type == PAXGTYPE) nglobal_headers++;
header_name = substitute_percent(header_name, arcn->name);
if (strlen(header_name) == sizeof(hd->name)) {
strncpy(hd->name, header_name, sizeof(hd->name));
} else {
strlcpy(hd->name, header_name, sizeof(hd->name));
}
records_size = (u_long)total_len;
if (ul_oct(records_size, hd->size, sizeof(hd->size), term_char)) {
paxwarn(1,"extended header data too long for header type '%c'", header_type);
return(1);
}
if (ul_oct(pax_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum, sizeof(hd->chksum), term_char)) {
paxwarn(1,"extended header data checksum failed: header type '%c'", header_type);
return(1);
}
if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0)
return(-1);
if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0)
return(-1);
if (total_len > 0) {
if (wr_rdbuf(pax_eh_datablk, total_len) < 0)
return(-1);
if (wr_skip((off_t)(BLKMULT - total_len)) < 0)
return(-1);
}
return (0);
}
int
pax_wr(ARCHD *arcn)
{
HD_USTAR *hd;
char *pt;
char hdblk[sizeof(HD_USTAR)];
mode_t mode12only;
int term_char=3;
term_char=1;
if (arcn->type == PAX_SCK) {
paxwarn(1, "Pax cannot archive a socket %s", arcn->org_name);
return(1);
}
if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) ||
(arcn->type == PAX_HRG)) && (arcn->ln_nlen > sizeof(hd->linkname))){
paxwarn(1, "Link name too long for pax %s", arcn->ln_name);
return(1);
}
if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) {
paxwarn(1, "File name too long for pax %s", arcn->name);
return(1);
}
generate_pax_ext_header_and_data(arcn, global_ext_header_inx, &global_ext_header_entry[0],
PAXGTYPE, header_name_g, header_name_g_requested);
generate_pax_ext_header_and_data(arcn, ext_header_inx, &ext_header_entry[0],
PAXXTYPE, header_name_x, header_name_x_requested);
memset(hdblk, 0, sizeof(hdblk));
hd = (HD_USTAR *)hdblk;
arcn->pad = 0L;
ul_oct(0, hd->devmajor, sizeof(hd->devmajor), term_char);
ul_oct(0, hd->devminor, sizeof(hd->devminor), term_char);
if (pt != arcn->name) {
*pt = '\0';
strlcpy(hd->prefix, arcn->name, sizeof(hd->prefix));
*pt++ = '/';
}
if (strlen(pt) == sizeof(hd->name)) {
strncpy(hd->name, pt, sizeof(hd->name));
} else {
strlcpy(hd->name, pt, sizeof(hd->name));
}
switch (arcn->type) {
case PAX_DIR:
hd->typeflag = DIRTYPE;
if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char))
goto out;
break;
case PAX_CHR:
case PAX_BLK:
if (arcn->type == PAX_CHR)
hd->typeflag = CHRTYPE;
else
hd->typeflag = BLKTYPE;
if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor,
sizeof(hd->devmajor), term_char) ||
ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor,
sizeof(hd->devminor), term_char) ||
ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char))
goto out;
break;
case PAX_FIF:
hd->typeflag = FIFOTYPE;
if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char))
goto out;
break;
case PAX_SLK:
case PAX_HLK:
case PAX_HRG:
if (arcn->type == PAX_SLK)
hd->typeflag = SYMTYPE;
else
hd->typeflag = LNKTYPE;
if (strlen(arcn->ln_name) == sizeof(hd->linkname)) {
strncpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname));
} else {
strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname));
}
if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char))
goto out;
break;
case PAX_REG:
case PAX_CTG:
default:
if (arcn->type == PAX_CTG)
hd->typeflag = CONTTYPE;
else
hd->typeflag = REGTYPE;
arcn->pad = TAR_PAD(arcn->sb.st_size);
# ifdef LONG_OFF_T
if (ul_oct((u_long)arcn->sb.st_size, hd->size,
sizeof(hd->size), term_char)) {
# else
if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size,
sizeof(hd->size), term_char)) {
# endif
paxwarn(1,"File is too long for pax %s",arcn->org_name);
return(1);
}
break;
}
strncpy(hd->magic, TMAGIC, TMAGLEN);
strncpy(hd->version, TVERSION, TVERSLEN);
if (ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), term_char)) {
if (uid_nobody == 0) {
if (uid_name("nobody", &uid_nobody) == -1)
goto out;
}
if (uid_warn != arcn->sb.st_uid) {
uid_warn = arcn->sb.st_uid;
paxwarn(1,
"Pax header field is too small for uid %lu, "
"using nobody", (u_long)arcn->sb.st_uid);
}
if (ul_oct((u_long)uid_nobody, hd->uid, sizeof(hd->uid), term_char))
goto out;
}
if (ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), term_char)) {
if (gid_nobody == 0) {
if (gid_name("nobody", &gid_nobody) == -1)
goto out;
}
if (gid_warn != arcn->sb.st_gid) {
gid_warn = arcn->sb.st_gid;
paxwarn(1,
"Pax header field is too small for gid %lu, "
"using nobody", (u_long)arcn->sb.st_gid);
}
if (ul_oct((u_long)gid_nobody, hd->gid, sizeof(hd->gid), term_char))
goto out;
}
mode12only = ((u_long)arcn->sb.st_mode) & 0x00000fff;
if (ul_oct((u_long)mode12only, hd->mode, sizeof(hd->mode), term_char) ||
ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),term_char))
goto out;
strncpy(hd->uname, name_uid(arcn->sb.st_uid, 0), sizeof(hd->uname));
strncpy(hd->gname, name_gid(arcn->sb.st_gid, 0), sizeof(hd->gname));
if (ul_oct(pax_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum,
sizeof(hd->chksum), term_char))
goto out;
if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0)
return(-1);
if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0)
return(-1);
if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG))
return(0);
return(1);
out:
paxwarn(1, "Pax header field is too small for %s", arcn->org_name);
return(1);
}
static char *
name_split(char *name, int len)
{
char *start;
if (len <= TNMSZ)
return(name);
if (len > (TPFSZ + TNMSZ))
return(NULL);
start = name + len - TNMSZ -1;
if ((*start == '/') && (start == name))
++start;
while ((*start != '\0') && (*start != '/'))
++start;
if (*start == '\0')
return(NULL);
len = start - name;
if ((len >= TPFSZ) || (len == 0))
return(NULL);
return(start);
}
static size_t
expandname(char *buf, size_t len, char **gnu_name, const char *name, size_t name_len)
{
size_t nlen;
if (*gnu_name) {
if ((nlen = strlcpy(buf, *gnu_name, len)) >= len)
nlen = len - 1;
free(*gnu_name);
*gnu_name = NULL;
} else {
if (name_len < len) {
if ((nlen = strlcpy(buf, name, name_len+1)) >= name_len+1)
nlen = name_len;
} else {
if ((nlen = strlcpy(buf, name, len)) >= len)
nlen = len - 1;
}
}
return(nlen);
}