From 5fd83771641d15c418f747bd343ba6738d3875f7 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Sun, 9 May 2021 14:20:58 -0400 Subject: Import macOS userland adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106 --- file_cmds/pax/pax_format.c | 1624 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1624 insertions(+) create mode 100644 file_cmds/pax/pax_format.c (limited to 'file_cmds/pax/pax_format.c') diff --git a/file_cmds/pax/pax_format.c b/file_cmds/pax/pax_format.c new file mode 100644 index 0000000..fbacdff --- /dev/null +++ b/file_cmds/pax/pax_format.c @@ -0,0 +1,1624 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#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 /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" +#include "tar.h" +#include +#include +#include "pat_rep.h" +#include + +/* + * This file implements the -x pax format support; it is incomplete. + * Known missing features include: + * many -o options for "copy" mode are not implemented (only path=) + * many format specifiers for -o listopt are not implemented + * -o listopt option should work for all archive formats, not just -x pax + * This file was originally derived from the file tar.c. You should + * 'diff' it to that file to see how much of the -x pax format has been implemented. + */ + +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 +#define KW_ATIME_CASE -2 + +typedef struct { + char * name; + int len; + int active; /* 1 means active, 0 means deleted via -o delete= */ + int cmdline_action; + int header_action; + /* next 2 entries only used by store_header actions */ + char ** g_value; /* -o keyword= value */ + char ** x_value; /* -o keyword:= value */ + char ** g_value_current; /* keyword= value found in Global extended header */ + char ** x_value_current; /* keyword= value found in extended header */ + int header_inx; /* starting index of header field this keyword represents */ + int header_len; /* length of header field this keyword represents */ + /* If negative, special cases line path= */ +} 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_ATIME_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 }, /* Test 241 */ + { "listopt", 7, 1, O_OPTION_ACTION_LISTOPT, O_OPTION_ACTION_ERROR, + &pax_list_opt_format, NULL, NULL, NULL, 0, KW_SKIP_CASE }, /* Test 242 */ + /* Note: listopt is supposed to apply for all formats, not just -x pax only */ + { "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; + +/* Make these tables big enough to handle lots of -o options, not just one per table entry */ +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)]; + +/* + * Routines for reading, writing and header identify of various versions of pax + */ + +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; + +/* + * Routines common to all versions of pax + */ + +/* + * ul_oct() + * convert an unsigned long to an octal string. many oddball field + * termination characters are used by the various versions of tar in the + * different fields. term selects which kind to use. str is '0' padded + * at the front to len. we are unable to use only one format as many old + * tar readers are very cranky about this. + * Return: + * 0 if the number fit into the string, -1 otherwise + */ + +static int +ul_oct(u_long val, char *str, int len, int term) +{ + char *pt; + + /* + * term selects the appropriate character(s) for the end of the string + */ + 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; + } + + /* + * convert and blank pad if there is space + */ + 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 +/* + * uqd_oct() + * convert an u_quad_t to an octal string. one of many oddball field + * termination characters are used by the various versions of tar in the + * different fields. term selects which kind to use. str is '0' padded + * at the front to len. we are unable to use only one format as many old + * tar readers are very cranky about this. + * Return: + * 0 if the number fit into the string, -1 otherwise + */ + +static int +uqd_oct(u_quad_t val, char *str, int len, int term) +{ + char *pt; + + /* + * term selects the appropriate character(s) for the end of the string + */ + 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; + } + + /* + * convert and blank pad if there is space + */ + 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 + +/* + * pax_chksm() + * calculate the checksum for a pax block counting the checksum field as + * all blanks (BLNKSUM is that value pre-calculated, the sum of 8 blanks). + * NOTE: we use len to short circuit summing 0's on write since we ALWAYS + * pad headers with 0. + * Return: + * unsigned long checksum + */ + +static u_long +pax_chksm(char *blk, int len) +{ + char *stop; + char *pt; + u_long chksm = BLNKSUM; /* initial value is checksum field sum */ + + /* + * add the part of the block before the checksum field + */ + pt = blk; + stop = blk + CHK_OFFSET; + while (pt < stop) + chksm += (u_long)(*pt++ & 0xff); + /* + * move past the checksum field and keep going, spec counts the + * checksum field as the sum of 8 blanks (which is pre-computed as + * BLNKSUM). + * ASSUMED: len is greater than CHK_OFFSET. (len is where our 0 padding + * starts, no point in summing zero's) + */ + 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) +{ + /* parse specified listopt format */ + char *nextpercent, *nextchar; + char buf[4*1024]; + int pos, cpylen; + char *fname; + + nextpercent = strchr(pax_list_opt_format,'%'); + if (nextpercent==NULL) { + /* Strange case: no specifiers? */ + 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; /* do nothing? */ + } + return rc; +} + +static void +delete_keywords(char * pattern) +{ + int i; + /* loop over all keywords, marking any matched as deleted */ + for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) { + if (fnmatch(pattern, o_option_table[i].name, 0) == 0) { + /* Found option: mark deleted */ + o_option_table[i].active = 0; + } + } +} + +/* + * pax_opt() + * handle pax format specific -o options + * Return: + * 0 if ok -1 otherwise + */ + +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; /* Default for pax format */ + /* look up opt->name */ + 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) { + /* Found option: see if already set */ + /* Save it away */ + 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; + } + /* Mark all matches as deleted */ + /* can have multiple -o delete= patterns */ + delete_keywords(opt->value); + break; + case O_OPTION_ACTION_STORE_HEADER2: + if(pax_read_or_list_mode) pids = 1; /* Force -p o for these options */ + 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 { /* SEP_NONE */ + 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); +} + +static 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); + } + /* + printf("Read 1 extended header: type=%c, size=%d\n", + myhd->typeflag, size); + */ + inx=0; + /* loop over buffer collecting attributes */ + 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'; /* end of name */ + } 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) { + /* Found option: see if already set TBD */ + /* Save it away */ + got_option = i; + break; + } + } + if (got_option == -1) { + paxwarn(1,"Unrecognized header keyword: %s",name); + } else { + /* Determine precedence of -o and header attributes */ + 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) { + /* Save this attribute value for use later */ + 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: /* shouldn't happen */ + 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; + } + + /* position file at next header */ + (void)rd_skip(TAR_PAD(size)); + + /* read next header */ + 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); + /* repeat until no more extended headers */ + } + + /* The header about to be returned must now be updated using all the extended + header values collected and any command line options */ + /* Acceleration: check during command option processing. If there are no -o + options, and no changes from any header, do not need to run through this loop. */ + + 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; /* deleted keywords */ + } + header_len = o_option_table[i].header_len; + if (header_len == KW_SKIP_CASE) { + continue; + } + free_it = 0; + /* Calculate values for all non-skip keywords */ + current_value = NULL; + if (o_option_table[i].x_value) { + current_value = *o_option_table[i].x_value; + } + if (!current_value) { /* No -o := */ + if (o_option_table[i].x_value_current) { + current_value = *o_option_table[i].x_value_current; + } + if (current_value) { + /* Must remove it: x header values not valid beyond this header */ + *o_option_table[i].x_value_current = NULL; + free_it = 1; + } else { /* No x values, try globals */ + current_value = *o_option_table[i].g_value; + if (!current_value) { + current_value = *o_option_table[i].g_value_current; + } + } + } + if (current_value) { + /* Update current header with this value */ + /* + printf ("Found current_value:%s for %s, pids=%d\n", + current_value, o_option_table[i].name, pids); + */ + len = strlen(current_value); + if (header_len == KW_ATIME_CASE) { + time_t asecs = strtoul(current_value, NULL, 10); + arcn->sb.st_atimespec.tv_sec = asecs; + } else if (header_len == KW_PATH_CASE) { /* Special case for path keyword */ + path_replaced = 1; + arcn->nlen = len; + strlcpy(arcn->name,current_value,sizeof(arcn->name)); + } else if (header_len >= 0) { // Skip negative values + 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) { + /* pad with ? */ + p[o_option_table[i].header_inx+len] = '\0'; + } + } + } + if (free_it) { + free(current_value); + } + } + } + + if (myhd==hd) return(path_replaced); + + /* must put new header into memory of original */ + memcpy(hd, myhd, sizeof(HD_USTAR)); + + return(path_replaced); +} + +/* + * pax_id() + * determine if a block given to us is a valid pax header. We have to + * be on the lookout for those pesky blocks of all zero's + * Return: + * 0 if a ustar header, -1 otherwise + */ + +int +pax_id(char *blk, int size) +{ + HD_USTAR *hd; + + if (size < BLKMULT) + return(-1); + hd = (HD_USTAR *)blk; + + /* + * check for block of zero's first, a simple and fast test then check + * ustar magic cookie. We should use TMAGLEN, but some USTAR archive + * programs are fouled up and create archives missing the \0. Last we + * check the checksum. If ok we have to assume it is a valid header. + */ + 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)) { + /* Not explicitly pax format, but at least ustar */ + if (act==LIST || act==EXTRACT) { + /* Although insufficient evidence, call it pax format */ + return(0); + } + return(-1); + } + pax_invalid_action = PAX_INVALID_ACTION_BYPASS; /* Default for pax format */ + return(0); +} + +/* + * pax_rd() + * extract the values out of block already determined to be a pax header. + * store the values in the ARCHD parameter. + * Return: + * 0 + */ + +int +pax_rd(ARCHD *arcn, char *buf) +{ + HD_USTAR *hd; + int cnt = 0; + int check_path; + dev_t devmajor; + dev_t devminor; + + /* + * we only get proper sized buffers + */ + 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) { + /* + * pathname derived from extended head or -o option; + * full name is in one string, but length may exceed + * max path so be careful. + */ + if (arcn->nlen > sizeof(arcn->name)) { + paxwarn(1,"pathname from extended header info doesn't fit! (len=%d)\n", + arcn->nlen); + } + } else { + /* + * see if the filename is split into two parts. if so, join the parts. + * we copy the prefix first and add a / between the prefix and name. + */ + 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)); + } + } + + /* + * follow the spec to the letter. we should only have mode bits, strip + * off all other crud we may be passed. + */ + 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); + /* When we have extended header for size, prefer it over hd->size */ + if (size_x_current) { + sscanf(size_x_current, "%lld", &arcn->sb.st_size); + } +#endif + arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT); + if (arcn->sb.st_atimespec.tv_sec == 0) { // Can be set from header + arcn->sb.st_atime = arcn->sb.st_mtime; + } + arcn->sb.st_ctime = arcn->sb.st_mtime; + + /* + * If we can find the ascii names for gname and uname in the password + * and group files we will use the uid's and gid they bind. Otherwise + * we use the uid and gid values stored in the header. (This is what + * the posix spec wants). + */ + 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); + + /* + * set the defaults, these may be changed depending on the file type + */ + arcn->pad = 0; + arcn->skip = 0; + arcn->sb.st_rdev = (dev_t)0; + + /* + * set the mode and PAX type according to the typeflag in the header + */ + 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; + + /* + * Some programs that create pax archives append a '/' + * to the pathname for directories. This clearly violates + * pax specs, but we will silently strip it off anyway. + */ + if (arcn->name[arcn->nlen - 1] == '/') + arcn->name[--arcn->nlen] = '\0'; + break; + case BLKTYPE: + case CHRTYPE: + /* + * this type requires the rdev field to be set. + */ + 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; + /* + * so printing looks better + */ + arcn->sb.st_mode |= S_IFREG; + arcn->sb.st_nlink = 2; + } + break; + case LONGLINKTYPE: + case LONGNAMETYPE: + /* + * GNU long link/file; we tag these here and let the + * pax internals deal with it -- too ugly otherwise. + */ + 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: + /* + * these types have file data that follows. Set the skip and + * pad fields. + */ + 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) +{ + /* Because ext_header options take precedence over global_header options, apply + global options first, then override with any extended header options */ + 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; /* deleted keywords */ + 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 { /* only handle path for now: others TBD */ + 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; /* deleted keywords */ + 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 { /* only handle path for now: others TBD */ + paxwarn(1, "adjust arcn for extended header options not implemented:%d", i); + } + } + } + if (want_a_m_time_headers) { + /* TBD */ + } +} + +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); +} + +__attribute__((__malloc__)) +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 strdup(header); + pos = nextpercent-header; + memcpy(buf,header, pos); + while (nextpercent++) { + switch (*nextpercent) { + case '%': + buf[pos++]='%'; /* just skip it */ + 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 strdup(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])); +} + +static 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); + } + + /* There might be no fields but a header with a specific name or + times might be wanted */ + + term_char = 1; + memset(hdblk, 0, sizeof(hdblk)); + hd = (HD_USTAR *)hdblk; + memset(pax_eh_datablk, 0, sizeof(pax_eh_datablk)); + + /* generate header */ + hd->typeflag = header_type; + + /* These fields appear to be necessary to be able to treat extended headers + like files in older versions of pax */ + 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); + + /* compute size of data */ + total_len = 0; + for (i=0; i < nfields; i++) { + if (!o_option_table[table[i]].active) continue; /* deleted keywords */ + 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); + /* 3 chars + strlen("atime") + time + # chars in len */ + 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); + /* 3 chars + strlen("mtime") + time + # chars in len */ + len = 3 + 5 + strlen(&time_buffer[0]) + 2; + total_len = emit_extended_header_record(len, total_len, + header_type, "mtime", &time_buffer[0]); + } + + /* Check if all fields were deleted: might not need to generate anything */ + if ((total_len==0) && (header_name_requested == NULL)) return (0); + + if (header_type == PAXGTYPE) nglobal_headers++; + /* substitution of fields in header_name */ + header_name = substitute_percent(header_name, arcn->name); + if (strlen(header_name) == sizeof(hd->name)) { /* must account for name just fits in buffer */ + strncpy(hd->name, header_name, sizeof(hd->name)); + } else { + strlcpy(hd->name, header_name, sizeof(hd->name)); + } + + free(header_name); + header_name = NULL; + 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); + } + + /* write out header */ + if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0) + return(-1); + if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0) + return(-1); + /* write out header data */ + 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); + /* + printf("data written:\n%s",&pax_eh_datablk[0]); + */ + } + + /* + paxwarn(0,"extended header and data written: header type '%c', #items: %d, %d characters", + header_type, nfields, records_size); + */ + return (0); +} + +/* + * pax_wr() + * write a pax header for the file specified in the ARCHD to the archive + * Have to check for file types that cannot be stored and file names that + * are too long. Be careful of the term (last arg) to ul_oct, we only use + * '\0' for the termination character (this is different than picky tar) + * ASSUMED: space after header in header block is zero filled + * Return: + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +int +pax_wr(ARCHD *arcn) +{ + HD_USTAR *hd; + char *pt; + char hdblk[sizeof(HD_USTAR)]; + mode_t mode12only; + int term_char=3; /* orignal setting */ + term_char=1; /* To pass conformance tests 274, 301 */ + const char *size_header_name = "size"; + char size_value[100]; + bzero(size_value, sizeof(size_value)); + + /* + * check for those file system types pax cannot store + */ + if (arcn->type == PAX_SCK) { + paxwarn(1, "Pax cannot archive a socket %s", arcn->org_name); + return(1); + } + + /* + * check the length of the linkname + */ + 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); + /* + * Conformance: test pax:285 wants error code to be non-zero, and + * test tar:12 wants error code from pax to be 0 + */ + return(1); + } + + /* + * split the path name into prefix and name fields (if needed). if + * pt != arcn->name, the name has to be split + */ + 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); + + /* + * zero out the header so we don't have to worry about zero fill below + */ + memset(hdblk, 0, sizeof(hdblk)); + hd = (HD_USTAR *)hdblk; + arcn->pad = 0L; + /* To pass conformance tests 274/301, always set these fields to "zero" */ + ul_oct(0, hd->devmajor, sizeof(hd->devmajor), term_char); + ul_oct(0, hd->devminor, sizeof(hd->devminor), term_char); + + /* + * split the name, or zero out the prefix + */ + if (pt != arcn->name) { + /* + * name was split, pt points at the / where the split is to + * occur, we remove the / and copy the first part to the prefix + */ + *pt = '\0'; + strlcpy(hd->prefix, arcn->name, sizeof(hd->prefix)); + *pt++ = '/'; + } + + /* + * copy the name part. this may be the whole path or the part after + * the prefix + */ + if (strlen(pt) == sizeof(hd->name)) { /* must account for name just fits in buffer */ + strncpy(hd->name, pt, sizeof(hd->name)); + } else { + strlcpy(hd->name, pt, sizeof(hd->name)); + } + + /* + * set the fields in the header that are type dependent + */ + 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)) { /* must account for name just fits in buffer */ + 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: + /* + * file data with this type, set the padding + */ + 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 + /* + * Insert an extended header for size=sb.st_size> since + * octal range of 12 byte string cannot fit > 8GiB files in header. + * This fixes Conformance test pax.343 + */ + int i; + snprintf(size_value, sizeof(size_value), "%lld", arcn->sb.st_size); + for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) { + if (strncasecmp(size_header_name, o_option_table[i].name, o_option_table[i].len) == 0) { + size_x = size_value; + ext_header_entry[ext_header_inx++] = i; + } + } + generate_pax_ext_header_and_data(arcn, ext_header_inx, &ext_header_entry[0], + PAXXTYPE, header_name_x, header_name_x_requested); + } + break; + } + + strncpy(hd->magic, TMAGIC, TMAGLEN); + strncpy(hd->version, TVERSION, TVERSLEN); + + /* + * set the remaining fields. Some versions want all 16 bits of mode + * we better humor them (they really do not meet spec though).... + */ + 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; + } + /* However, Unix conformance tests do not like MORE than 12 mode bits: + remove all beyond (see definition of stat.st_mode structure) */ + 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)); + + /* + * calculate and store the checksum write the header to the archive + * return 0 tells the caller to now write the file data, 1 says no data + * needs to be written + */ + 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: + /* + * header field is out of range + */ + paxwarn(1, "Pax header field is too small for %s", arcn->org_name); + return(1); +} + +/* + * name_split() + * see if the name has to be split for storage in a ustar header. We try + * to fit the entire name in the name field without splitting if we can. + * The split point is always at a / + * Return + * character pointer to split point (always the / that is to be removed + * if the split is not needed, the points is set to the start of the file + * name (it would violate the spec to split there). A NULL is returned if + * the file name is too long + */ + +static char * +name_split(char *name, int len) +{ + char *start; + + /* + * check to see if the file name is small enough to fit in the name + * field. if so just return a pointer to the name. + */ + if (len <= TNMSZ) + return(name); + if (len > (TPFSZ + TNMSZ)) + return(NULL); + + /* + * we start looking at the biggest sized piece that fits in the name + * field. We walk forward looking for a slash to split at. The idea is + * to find the biggest piece to fit in the name field (or the smallest + * prefix we can find) + */ + start = name + len - TNMSZ -1; + if ((*start == '/') && (start == name)) + ++start; /* 101 byte paths with leading '/' are dinged otherwise */ + while ((*start != '\0') && (*start != '/')) + ++start; + + /* + * if we hit the end of the string, this name cannot be split, so we + * cannot store this file. + */ + if (*start == '\0') + return(NULL); + len = start - name; + + /* + * NOTE: /str where the length of str == TNMSZ can not be stored under + * the p1003.1-1990 spec for ustar. We could force a prefix of / and + * the file would then expand on extract to //str. The len == 0 below + * makes this special case follow the spec to the letter. + */ + if ((len >= TPFSZ) || (len == 0)) + return(NULL); + + /* + * ok have a split point, return it to the caller + */ + 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) { + /* name may not be null terminated: it might be as big as the + field, so copy is limited to the max size of the header field */ + 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); +} -- cgit v1.2.3-56-ge451