/* taruib_i.c - 

   Copyright (C) 2004 Jim Lowe

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "taruib.h"
#include "taru.h"
#include "swfork.h"
#include "swlib.h"
#include "uxfio.h"
#include "swpath.h"
#include "swgp.h"
#include "swi.h"
#include "swutilname.h"
#include "xformat.h"
#include "fnmatch_u.h"

#include "debug_config.h"
#ifdef TARUIBNEEDDEBUG
#define TARUIB_E_DEBUG(format) SWBISERROR("TARUIB DEBUG: ", format)
#define TARUIB_E_DEBUG2(format, arg) SWBISERROR2("TARUIB DEBUG: ", format, arg)
#define TARUIB_E_DEBUG3(format, arg, arg1) \
				SWBISERROR3("TARUIB DEBUG: ", format, arg, arg1)
#else
#define TARUIB_E_DEBUG(arg)
#define TARUIB_E_DEBUG2(arg, arg1)
#define TARUIB_E_DEBUG3(arg, arg1, arg2)
#endif 

static int g_taruib_gst_overflow_releaseM;
static int g_taruib_gst_fdM;
static int g_taruib_gst_lenM;
static char g_taruib_gst_bufferM[TARU_BUFSIZ_RES];
	
static char * md5sum_adjunct_pattern[3] = {
	"catalog/*/adjunct_md5sum",
	"*/catalog/*/adjunct_md5sum",
	(char*)NULL };

static char * md5sum_full_pattern[3] = {
	"catalog/*/md5sum",
	"*/catalog/*/md5sum",
	(char*)NULL };

static char * sha1sum_pattern[3] = {
	"catalog/*/sha1sum",
	"*/catalog/*/sha1sum",
	(char*)NULL };

static char * signature_pattern[3] = {
	"catalog/*/" SWBN_SIGNATURE,
	"*/catalog/*/" SWBN_SIGNATURE,
	(char*)NULL };

static char * signature_header_pattern[3] = {
	"catalog/*/" SWBN_SIG_HEADER,
	"*/catalog/*/" SWBN_SIG_HEADER,
	(char*)NULL };

static
char **
determine_pattern_array(int * detpat, char * name) {

	TARUIB_E_DEBUG("ENTERING");
	if (strstr(name, "/md5sum")) {
		*detpat = TARUIB_N_MD5;
		return md5sum_full_pattern;
	} else if (strstr(name, "/adjunct_md5sum")) {
		*detpat = TARUIB_N_ADJUNCT_MD5;
		return md5sum_adjunct_pattern;
	} else if (strstr(name, "/sha1sum")) {
		*detpat = TARUIB_N_SHA1;
		return sha1sum_pattern;
	} else if (
		strstr(name, "/" SWBN_SIGNATURE) &&
		strlen(strstr(name, "/" SWBN_SIGNATURE)) ==
			     strlen("/" SWBN_SIGNATURE)
	) {
		*detpat = TARUIB_N_SIG;
		return signature_pattern;
	} else if (
		strstr(name, "/" SWBN_SIG_HEADER) &&
		strlen(strstr(name, "/" SWBN_SIG_HEADER)) ==
			     strlen("/" SWBN_SIG_HEADER)
	) {
		*detpat = TARUIB_N_SIG_HDR;
		return signature_header_pattern;
	} else {
		*detpat = TARUIB_N_OTHER;
		return (char**)NULL;
	}
}

static
int 
check_useArray(int * useArray, int digest_type, int dfd)
{
	int ret = -1;
	TARUIB_E_DEBUG("ENTERING");
	if (useArray) {
		if (digest_type > 4) return -1;
		if (useArray[digest_type] && digest_type != TARUIB_N_SIG) {
			/*
			* More than one sig or digest file in the catalog.
			* This is sanity security check.
			*/
			ret = -1;
			fprintf(stderr,
				"swbis: catalog file error : type = %d\n",
				digest_type);
		} else {
			useArray[digest_type] ++;
			ret = dfd;
		}
	} else {
		ret = dfd;
	}
	return ret;
}

static
int
verbose_decode(int * useArray, int * detpat,
				char * prepath,
				char * name,
				int nullfd,
				int digest_type, 
				int verbose,
				int md5digestfd,
				int sha1digestfd,
				int sigfd)
{
	int i = 0;
	int ret;
	int passfd = nullfd;
	char * s;
	char ** pattern_array;
	STROB * tmp;

	TARUIB_E_DEBUG("ENTERING");
	if (verbose == 0) return nullfd;

	if (prepath && strlen(prepath)) {
		/*
		* Normalize the name (exclude the prepath).
		*/
		s = strstr(name, prepath);
		if (s == (char*)NULL) {
			/* 
			* error 
			*/
			return -1;
		} else if (s == name) {
			/*
			* Normalize name
			*/
			name += (strlen(prepath));
			if (*name == '/') name++;
		} else {
			/* 
			* error 
			*/
			return -1;
		}
	}

	if (detpat == NULL) {
		if(digest_type == TARUIB_N_ADJUNCT_MD5) {
			pattern_array = md5sum_adjunct_pattern;
		} else if(digest_type == TARUIB_N_SIG) {
			pattern_array = signature_pattern;
		} else if(digest_type == TARUIB_N_MD5) {
			pattern_array = md5sum_full_pattern;
		} else if(digest_type == TARUIB_N_SHA1) {
			pattern_array = sha1sum_pattern;
		} else if(digest_type == TARUIB_N_SIG_HDR) {
			pattern_array = signature_header_pattern;
		} else {
			pattern_array = NULL;
			fprintf(stderr, "internal error in verbose_decode\n");
			return -1;
		}
	} else {
		pattern_array = determine_pattern_array(detpat, name);
		digest_type = *detpat;
		if (pattern_array == NULL) return nullfd;
	}

	tmp = strob_open(10);
	while(pattern_array[i]) {
		if (!name || !strlen(name)) {
			fprintf(stderr,
				"internal error in taruib::verbose_decode\n");
			strob_close(tmp);
			return nullfd;
		}
		ret = fnmatch(pattern_array[i], name, 1);
		if (ret == 0) {
			if (digest_type == TARUIB_N_MD5 && md5digestfd >= 0) {
				passfd = check_useArray(useArray,
							digest_type,
							md5digestfd);
			} else if (digest_type == TARUIB_N_SIG && sigfd >= 0) {
				passfd = check_useArray(useArray,
							digest_type,
							sigfd);
			} else if (digest_type == TARUIB_N_SHA1 && sha1digestfd >= 0) {
				passfd = check_useArray(useArray,
							digest_type,
							sha1digestfd);
			} else if (digest_type == TARUIB_N_SIG_HDR) {
				passfd = nullfd;
			} else {
				if (detpat) {
					passfd = nullfd;
				} else {
					passfd = STDERR_FILENO;
				}
			}
			break;
		}
		i++;
	}
	strob_close(tmp);
	return passfd;
}

static
int
check_signature_name(char * sigfilename, char * prevname) 
{
	int ret;
	char * p;
	char * s;

	TARUIB_E_DEBUG("ENTERING");
	s = strstr(sigfilename, "/" SWBN_SIGNATURE);
	if (!s || *(s + strlen("/" SWBN_SIGNATURE)) != '\0' ) {
		return 1;
	}	
	s++;
	*s = '\0';
	if ((p=strstr(prevname, sigfilename)) == NULL || p != prevname) {
		ret = 1;
	} else {
		ret = 0;
	}
	*s = 's';
	return ret;
}

static
int
do_sig_safety_checks(int memfd, int sigsize, int whichsig)
{
	/*
	* the signature data can only contain a leading printable block
	* of chars followed by a contigous block of nulls.
	*/
	char * data;
	int data_len;
	int cnt = 0;
	char * s;	
	int foundnull = 0;
	int foundchar = 0;
	int which_offset = 0;
	int nsig;

	if (uxfio_get_dynamic_buffer(memfd, &data, NULL, &data_len) < 0) {
		return -1;
	}

	if (whichsig == 0) {
		/*
		* sanity check
		*/
		if (data_len < sigsize) {
			return -3;
		}
	}

	if (whichsig > 0) {
		/*
		* sanity check
		*/
		if (data_len < (whichsig * sigsize)) {
			return -4;
		}
	}

	if (data_len % sigsize) {
		/*
		* sanity check
		*/
		return -5;
	}

	nsig = data_len / sigsize;

	if (nsig < 1) {
		/*
		* sanity check
		*/
		return -6;
	}

	if (whichsig == 0) {
		which_offset = (nsig-1) * sigsize;
	} else if (whichsig > 0) {
		which_offset = (whichsig-1) * sigsize;
	} else {
		return -7;
	}

	s = data + which_offset;
	while (cnt < sigsize) {
		switch ((int)(*s)) {
			case '\0':
				if (!foundchar) {
					fprintf(stderr,
						"Corrupt char in sigfile\n");
					return 3;
				}
				foundnull++;
				break;

			default:
				if (foundnull) {
					fprintf(stderr,
						"Corrupt char in sigfile\n");
					return 2;
				}
				if ( !isprint((int)(*s)) && 
						*s != '\x0a' && *s != '\x0d') {
					fprintf(stderr, 
						"Corrupt char in sigfile\n");
					return 1;
				}
				foundchar++;
				break;
		}
		s++;
		cnt++;
	}
	return 0;
}

static
int
do_header_safety_checks(XFORMAT * package,
		char * name,
		char * prevname,
		char * signature_header)
{
	int type;
	mode_t mode;
	char * linkname;
	int sigsize;
	unsigned long trusted_sigsize;
	char * header_bytes;
	int ret = 0;
	
	TARUIB_E_DEBUG("ENTERING");
	if (check_signature_name(name, prevname)) {
		fprintf(stderr, 
		"Warning: *** The file name [%s]\n"
		"         *** indicate strong possibility of tampering.\n"
		"         *** Use of this package is a security risk.\n", 
									name);
		return -1;
	}

	header_bytes = taru_get_recorded_header(package->taruM, (int *)NULL);
	type = xformat_get_tar_typeflag(package);
	mode = xformat_get_perms(package);
	linkname = xformat_get_linkname(package, NULL);
	sigsize = xformat_get_filesize(package);
	if (
			S_ISDIR(mode) == 0 && 	
			(
			(mode & S_ISUID) ||
			(mode & S_ISGID) ||
			(mode & S_ISVTX) 
			)
	) {
		fprintf(stderr, 
		"Warning: *** The file mode permissions on %s\n"
		"         *** indicate strong possibility of tampering.\n"
		"         *** Use of this package is a security risk.\n",
									name);
		return -2;
	}

	if (type != REGTYPE) {
		fprintf(stderr, 
		"Warning: *** The file type flag on %s\n"
		"         *** indicate strong possibility of tampering.\n"
		"         *** Use of this package is a security risk.\n",
									name);
		return -2;
	}
	
	if (linkname && strlen(linkname)) {
		return -3;
	}

	if (signature_header) {
		/*
		* trusted_sigsize is the file data length of the
		* sig_header attribute file.
		*/
		taru_otoul(signature_header+THB_BO_size, &trusted_sigsize);
		if (header_bytes && (int)trusted_sigsize == sigsize) {
			ret = memcmp(signature_header, 	
					header_bytes, TARRECORDSIZE);
			if (ret) ret = -5;
		} else {
			ret = -6;
		}
	} else {

	}

	/*
	* <= 0 is an error
	* > 0 (512 or 1024) is OK
	*/
	if (ret < 0) return ret;
	if (ret > 0) return -ret;
	return sigsize;
}

static
int
read_catalog_section(void * vp, int ifd)
{
	XFORMAT * package = (XFORMAT *)vp;
	int nullfd;
	int copyret;
	int ret;
	int retval = 0;
        int path_ret;
	int is_catalog;
	STROB * namebuf = strob_open(100);
	char * name;
	SWPATH * swpath = swpath_open("");
	int header_bytes;

	TARUIB_E_DEBUG("ENTERING");
	nullfd = open("/dev/null", O_RDWR);
	if (!swpath) return -21;
	if (nullfd < 0) return -22;		
	if (ifd < 0) return -32;		

	taruib_set_fd(nullfd);
	taruib_set_overflow_release(0);
	while ((ret = xformat_read_header(package)) > 0) {
		header_bytes = ret;
		retval += ret;
		if (xformat_is_end_of_archive(package)){
			/* error */
			close(nullfd);
			swpath_close(swpath);
			strob_close(namebuf);
			return -1;
		}
		xformat_get_name(package, namebuf);
		name = strob_str(namebuf);
		path_ret = swpath_parse_path(swpath, name);
		if (path_ret < 0) {
			/*
			* Fatal error
			*/
			fprintf(stderr, 
				"read_catalog_section:"
					" error parsing path [%s].\n", name);
			return -1;
		}
		is_catalog = swpath_get_is_catalog(swpath);
		if (is_catalog != SWPATH_CTYPE_STORE) {
			/*
			* Either -1 leading dir, or 1 catalog.
			*
			* skip it.
			*/
			
			taruib_set_overflow_release(0); /* OFF */
			taruib_set_fd(0); /* Turn OFF */
			copyret = xformat_copy_pass(package, nullfd, ifd);
			if (copyret < 0) {
				swpath_close(swpath);
				strob_close(namebuf);
				close(nullfd);
				return copyret;
			}
			taruib_set_fd(nullfd);
			taruib_clear_buffer();
		} else {
			taruib_clear_buffer();
			swpath_close(swpath);
			strob_close(namebuf);
			close(nullfd);
			return 0;
		}
	}
	taruib_clear_buffer();
	swpath_close(swpath);
	strob_close(namebuf);
	close(nullfd);
	return 1;
}

static
int
taruib_write_signedfile_if_i(void * vp, int ofd,
				char * sigfile,
				int md5digestfd,
				int sha1digestfd,
				int sigfd)
{
	XFORMAT * package = (XFORMAT *)vp;
	int i;
	int nullfd = open("/dev/null", O_RDWR);
	int memfd = -1;
	int ifd = xformat_get_ifd(package);
	int passfd;
	int copyret;
	int ret;
	int retval = 0;
	int is_catalog;
	char * name;
	char * prepath;
	int debug = 0;
	int header_bytes;
	int pattern_code = 0;
	int verbose = 1;
	int path_ret;
	int detected_pattern_code;
	STROB * namebuf = strob_open(100);
	STROB * oldnamebuf = strob_open(100);
	SWPATH * swpath = swpath_open("");
	int got_sig = 0;
	int useArray[5];
	int sigsize = 0;
	int sighdr_len = 0;
	int got_md5 = 0;
	int got_sha1 = 0;
	char * sighdr_addr = NULL;

	TARUIB_E_DEBUG("ENTERING");
	for(i=0; i<5; i++) useArray[i] = 0;

	if (!swpath) return -21;
	if (nullfd < 0) return -22;		
	if (ifd < 0) return -32;		

	taruib_set_fd(nullfd);
	taruib_set_overflow_release(0);

	taru_set_header_recording(package->taruM, 1/*ON*/);
	while ((ret = xformat_read_header(package)) > 0) {
		header_bytes = ret;
		retval += ret;
		if (xformat_is_end_of_archive(package)){
			/* error */
			close(nullfd);
			swpath_close(swpath);
			strob_close(oldnamebuf);
			strob_close(namebuf);
			return -1;
		}
		strob_strcpy(oldnamebuf, strob_str(namebuf));
		xformat_get_name(package, namebuf);
		name = strob_str(namebuf);
		path_ret = swpath_parse_path(swpath, name);
		if (path_ret < 0) {
			fprintf(stderr,
				"taruib_write_signedfile_if:"
				" error parsing path [%s].\n", name);
			TARUIB_E_DEBUG("LEAVING");
			return -2;
		}

		is_catalog = swpath_get_is_catalog(swpath);
		if (debug) fprintf(stderr, "%s : %d\n", name, is_catalog); 
		prepath = swpath_get_prepath(swpath);
			
		/*
		* Include the the leading directories in the storage file.
		*/
		if (is_catalog == SWPATH_CTYPE_CAT) {
			/*
			* 1 == catalog.
			*
			* skip it.
			*/
				
			taruib_set_overflow_release(0); /* OFF */
			taruib_set_fd(0); /* Turn OFF */

			passfd = verbose_decode(useArray, 
						&detected_pattern_code,
						prepath, name, nullfd, 
						pattern_code, verbose,
						md5digestfd,
						sha1digestfd,
						sigfd);
			pattern_code = detected_pattern_code;	

			if (passfd < 0) {
				/*
				* More than one sig or digest file in the 
				* catalog. This is sanity security check.
				*/
				copyret = -1;
			} else if (pattern_code == TARUIB_N_OTHER) {
				copyret = xformat_copy_pass(package,
								passfd,
								ifd);
			} else if (pattern_code == TARUIB_N_SIG_HDR) {
				if (got_sig) {
					/*
					* Sanity check, error.
					*/
					return -3;
				}
				memfd = swlib_open_memfd();
				copyret = xformat_copy_pass(package,
								memfd,
								ifd);
				sighdr_addr = uxfio_get_fd_mem(memfd,
							&sighdr_len);

				if (sighdr_addr == NULL ||
					(
					sighdr_len != 512 &&
					sighdr_len != 1024
					)
					) {
					/*
					* Sanity check, error.
					*/
					return -4;
				}			
			} else if (pattern_code == TARUIB_N_SIG) {
				/*
				* As a sanity and security check,
				* check the header for signs of tampering.
				* since the signature archive header is 
				* not in the signed stream.
				*/
				if ((sigsize=do_header_safety_checks(package,
						strob_str(namebuf),
						strob_str(oldnamebuf),
						sighdr_addr
						)) < 0) {
					/*
					* Error or corruption or tampering.
					*/
					fprintf(stderr, 
"\nWarning: *** The signature header checks failed (status=%d)\n"
"         *** This indicates possibility of tampering or corruption.\n"
"         *** Use of this package is a security risk.\n", sigsize);
					
					return -5;
				}

				/*
				* Write the sigfile.
				*/
				/*
				if (got_sig) {
					fprintf(stderr,	
					"%s: multiple signature files.\n", swlib_utilname_get());
					return -6;
				}
				*/
				got_sig++;
				copyret = xformat_copy_pass(package, 
							passfd, ifd);

				if ((sigsize != 512 && sigsize != 1024) || 
					sigsize != copyret) {
					fprintf(stderr, 
"\nWarning: *** The signature file size and data length does not match. (%d %d)\n"
"         *** This indicates possibility of tampering or corruption.\n"
"         *** Use of this package is a security risk.\n",
						sigsize, copyret);
						return -7;
				}
			} else if (pattern_code == TARUIB_N_MD5) {
				/*
				* Got the md5sum.
				*/
				got_md5++;
				copyret = xformat_copy_pass(package,
							passfd,
							ifd);
			} else if (pattern_code == TARUIB_N_SHA1) {
				/*
				* Got the sha1.
				*/
				got_sha1++;
				copyret = xformat_copy_pass(package,
							passfd,
							ifd);
			} else if (passfd == nullfd) {
				/*
				* FIXME: This copies the padding and data
				* slack bytes too (which does not matter for
				* tar formats).
				*/
				copyret = xformat_copy_pass(package,
								passfd,
								ifd);
			} else {
				fprintf(stderr, "internal error loc = 5a\n");
				copyret = -1;
			}

			if (copyret < 0) {
				swpath_close(swpath);
				strob_close(oldnamebuf);
				strob_close(namebuf);
				close(nullfd);
				return -8;
			}
			taruib_set_fd(nullfd);
			taruib_clear_buffer();

		} else if (is_catalog == SWPATH_CTYPE_DIR) {
			/*
			* leading dir  -- pass it thru.
			*/
			if (debug) fprintf(stderr, "in leading dirs\n"); 
			taruib_set_fd(ofd);
			taruib_clear_buffer();
		} else {
			/*
			*  0, the storage structure.
			*/
			taruib_set_fd(ofd); /* Turn ON */
			taruib_clear_buffer();
			retval = ret;
			taruib_set_fd(0); /* turn pass-thru buffer off */
			/*
			* Now break, the rest of the package is written below.
			*/
			break;
		}

		/*
		 * Prepare to read the next header
		 */
		taruib_set_overflow_release(1); /* ON */
	}
	taru_set_header_recording(package->taruM, 0 /*OFF*/);
	strob_close(oldnamebuf);
	strob_close(namebuf);
	swpath_close(swpath);
	close(nullfd);
	if (got_sha1 && !got_md5) {
		/*
		* anomaly, disallow it
		*/
		return -9;
	}
	if (!got_sig && !got_md5 && !got_sha1) {
		return -TARUIB_TEXIT_NOT_SIGNED;
	}
	if (!got_sig || !got_md5) {
		/*
		* Dead code. Here for safety.
		*/
		return -10;
	}
	if (sigsize == 0) {
		return -11;
	}
	if (got_sig && got_md5) {
		/*
		* sha1 is optional to
		* preserve back compatibility.
		*/
		return sigsize;
	}
	return -12;
}

static void
truncate_sigfile(char * sigfile)
{
	int sigfd = open(sigfile, O_RDWR|O_TRUNC|O_CREAT, 0600);
	TARUIB_E_DEBUG("ENTERING");
	if (sigfd < 0) {
		fprintf(stderr, "open failed ( %s ) : %s\n",
				sigfile, strerror(errno));
		return;
	}
	close(sigfd);
	return;
}
			
static
int
write_selected_sig(int sigfd, int memfd, int whichsig, int sigsize)
{

	int len;
	int offset;
	if (whichsig == 0) {
		/*
		* Last signature
		*/
		len = uxfio_lseek(memfd, 0, SEEK_END);
		if (len < 0) return -2;
		offset = len - sigsize;
	} else if (whichsig < 0) {
		/*
		* All signatures.
		*/
		; /* not yet supported */	
		return -1;
	} else {
		/*
		* 1 is first, 2 is second,...
		*/
		offset = sigsize * (whichsig - 1);
	}
	if (uxfio_lseek(memfd, offset, SEEK_SET) != offset) {
		return -1;
	}
	return swlib_pump_amount(sigfd, memfd, sigsize);
}

/* -------------------------------------------------------------- */
/*  Public Routines.                                              */
/* -------------------------------------------------------------- */

/*
* write the sigfile to (char*)sigfile and check the md5sum,
* if valid then write the signed file to ofd.
*/
int
taruib_write_signedfile_if(void * vp, int ofd, char * sigfile,
			int verbose, int whichsig)
{
	pid_t pid[3];
	int status[3];
	int md5dig[2];
	int sha1dig[2];
	int digfile[2];
	int sigpipefd[2];
	char md5digbuf[1024];
	char sha1digbuf[1024];
	char calc_md5digbuf[64];
	char calc_sha1digbuf[64];
	XFORMAT * package = (XFORMAT *)vp;
	int sigsize;
	int ifd = xformat_get_ifd(package);
	int sigfd;
	int ret;
	int pri_ret;
	int memfd;
	int ifret;

	TARUIB_E_DEBUG("ENTERING");
	memset(md5digbuf, '\0', sizeof(md5digbuf));
	memset(calc_md5digbuf, '\0', sizeof(calc_md5digbuf));
	
	if ((ret=uxfio_lseek(ifd, 0, UXFIO_SEEK_VCUR)) != 0) {
		fprintf(stderr, "uxfio_lseek error fd=%d loc=1 ret=%d\n",
								ifd, ret);
		return 1;
	}

	/*
	* This has the affect of reading the catalog portion into memory.
	*/
	uxfio_fcntl(ifd, UXFIO_F_SET_BUFTYPE, UXFIO_BUFTYPE_DYNAMIC_MEM);
	if (read_catalog_section(vp, ifd)) {
		fprintf(stderr, "internal error from read_catalog_section\n");
		return 2;
	}

	/*
	* Now seek back to the beginning.
	*/
	if ((ret = uxfio_lseek(ifd, 0, UXFIO_SEEK_VSET)) != 0) {
		fprintf(stderr, "uxfio_lseek error loc=2 ret=%d\n", ret);
		return 3;
	}

	if (verbose >= SWC_VERBOSE_2) {
		fprintf(stderr, "%s: Archive digest: ", swlib_utilname_get());
	}

	pipe(digfile);
	pipe(sha1dig);
	pipe(md5dig);
	pipe(sigpipefd);
	pid[0] = swfork((sigset_t*)(NULL));
	if (pid[0] < 0) return 1;
	if (pid[0] == 0) {
		int cret;
		close(sha1dig[0]);
		close(md5dig[0]);
		close(digfile[0]);
		close(sigpipefd[0]);
		cret = taruib_write_signedfile_if_i(vp, digfile[1],
					sigfile, md5dig[1],
					sha1dig[1], sigpipefd[1]);
		if (cret <= 0) {
			/*
			* error
			*/
			truncate_sigfile(sigfile);
			_exit(-cret);
		}
		sigsize = cret;
		uxfio_fcntl(ifd, UXFIO_F_SET_BUFACTIVE, UXFIO_OFF);
		if (xformat_file_has_data(package)) {
			if (xformat_copy_pass2(package, digfile[1], 
							ifd, -1) < 0) {
				fprintf(stderr, "taruib_write_signedfile_if:"
					" error from xformat_copy_pass2\n");	
				truncate_sigfile(sigfile);
				_exit(31);
			}
		}
		close(sigpipefd[1]);
		cret = taruib_write_pass_files(vp, digfile[1], -1);
		if (cret < 0) _exit(41);
		if (sigsize == 512) _exit(TARUIB_TEXIT_512);
		if (sigsize == 1024) _exit(TARUIB_TEXIT_1024);
		_exit(1);
	}
	close(sha1dig[1]);
	close(md5dig[1]);
	close(digfile[1]);
	close(sigpipefd[1]);

	calc_sha1digbuf[0] = '\0';
	calc_md5digbuf[0] = '\0';
	swlib_digests(digfile[0], calc_md5digbuf, calc_sha1digbuf);

	md5digbuf[0] = '\0';
	sha1digbuf[0] = '\0';
	/*
	* read the md5 digest from catalog/dfiles/md5sum
	*/
	if ((ret=read(md5dig[0], md5digbuf, 513)) < 0) {
		fprintf(stderr, "taruib_write_signedfile_if:"
			" internal error reading ascii digest loc=1\n");
		truncate_sigfile(sigfile);
		return 4;
	}
	if (ret < 0 || (ret > 0 && ret < 32)) {
		fprintf(stderr, "taruib_write_signedfile_if:"
		" internal error reading ascii digest loc=2, ret=%d\n", ret);
		truncate_sigfile(sigfile);
		return 5;
	}

	/*
	* read the sha1 digest from catalog/dfiles/md5sum
	*/
	if ((ret=read(sha1dig[0], sha1digbuf, 513)) < 0) {
		fprintf(stderr, "taruib_write_signedfile_if:"
			" internal error reading ascii sha1 digest loc=1\n");
		truncate_sigfile(sigfile);
		return 4;
	}
	if (ret < 0 || (ret > 0 && ret < 40)) {
		fprintf(stderr, 
			"taruib_write_signedfile_if:"
			" internal error reading ascii sha1 digest loc=2,"
			" ret=%d\n",
				 ret);
		truncate_sigfile(sigfile);
		return 5;
	}

	/*
	* Read the sigfile from catalog/dfiles/signature into memory.
	*/
	memfd = swlib_open_memfd();
	swlib_pipe_pump(memfd, sigpipefd[0]);

	if (uxfio_lseek(memfd, 0, SEEK_SET) != 0) {
		fprintf(stderr, 
		"taruib_write_signedfile_if: internal error : uxfio_lseek\n");
		truncate_sigfile(sigfile);
		return 7;
	}

	close(sigpipefd[0]);
	close(digfile[0]);
	close(md5dig[0]);
	close(sha1dig[0]);
	swlib_wait_on_all_pids(pid, 1, status, WNOHANG, 0);
	if (WIFEXITED(status[0]) == 0) {
		fprintf(stderr, 
		"swbis: pid[0] exited abnormally : exitval = [%d]\n",
			WEXITSTATUS(status[0]));
		return 8;
	}
	ifret = WEXITSTATUS(status[0]);
	
	if (ifret == TARUIB_TEXIT_NOT_SIGNED) {
		/*
		* Normal error for unsigned package.
		*/
		fprintf(stderr, "%s: Package not signed.\n", swlib_utilname_get());
		return 12;
	}
	if (ifret != TARUIB_TEXIT_512 && ifret != TARUIB_TEXIT_1024) {
		/*
		* abnormal error.
		*/
		fprintf(stderr,
		"swbis: pid[0] exited with status=%d indicating error.\n",
			WEXITSTATUS(status[0]));
		return 12;
	}
	if (ifret == TARUIB_TEXIT_512) 
		sigsize = 512;
	else
		sigsize = 1024;

	pri_ret = 1;
	calc_md5digbuf[32] = '\0';
	md5digbuf[32] = '\0';
	if (strlen(md5digbuf) >= 32 && 
			strncmp(calc_md5digbuf, md5digbuf, 32) == 0) {
		pri_ret = 0;
		if (verbose >= SWC_VERBOSE_2) {
			fprintf(stderr, "md5 OK (Good)\n");
		}
	} else {
		/*
		* md5sum mismatch.
		*/
		pri_ret = 2;
		if (verbose >= 1) {
			fprintf(stderr, "md5 FAIL (Bad)\n");
			if (strlen(md5digbuf) == 0) {
				fprintf(stderr, 
			"%s: Package does not contain an md5 digest.\n", swlib_utilname_get());
			}
		}
	}

	if (verbose >= SWC_VERBOSE_2) {
		fprintf(stderr, "%s: Archive digest: ", swlib_utilname_get());
	}
	calc_sha1digbuf[40] = '\0';
	sha1digbuf[40] = '\0';
	if (strlen(sha1digbuf) >= 40 && 
			strncmp(calc_sha1digbuf, sha1digbuf, 40) == 0) {
		if (verbose >= SWC_VERBOSE_2) {
			fprintf(stderr, "sha1 OK (Good)\n");
		}
	} else {
		/*
		* sha1 mismatch.
		*/
		if (strlen(sha1digbuf)) {
			pri_ret = 1;
			if (verbose >= 1) 
				fprintf(stderr, "sha1 FAIL (Bad)\n");
		}
		if (strlen(sha1digbuf) == 0) {
			if (verbose >= 1) 
				fprintf(stderr, 
			"sha1 : Package does not contain a sha1 digest.\n");
		}
	}
	
	sigfd = open(sigfile, O_RDWR|O_TRUNC|O_CREAT, 0600);
	if (sigfd < 0) {
		fprintf(stderr, "open failed ( %s ) : %s\n",
						sigfile, strerror(errno));
		return 9;
	}

	if (pri_ret == 0) {
		/*
		* Look for evidence of trojan data hiding in the
		* sigfile.
		*/
		if (do_sig_safety_checks(memfd, sigsize, whichsig)) {
			fprintf(stderr, "corrupt signature file\n");
			pri_ret = 10;
		} else {
			/*
			* OK, if were here then the archive md5 matched.
			* and the signature data looked OK. 
			* open and write the sigfile.
			swlib_pipe_pump(sigfd, memfd);
			*/
			write_selected_sig(sigfd, memfd, whichsig, sigsize);
		}
		/*
		* Close the sigfile.
		*/
		close(sigfd);
		uxfio_close(memfd);

		if (verbose >= 4) {
			fprintf(stderr, "%s: md5: %s from archive.\n",
						swlib_utilname_get(), md5digbuf);
			fprintf(stderr, "%s: md5: %s calculated.\n",
						swlib_utilname_get(), calc_md5digbuf);
			fprintf(stderr, "%s: sha1: %s from archive.\n",
						swlib_utilname_get(), sha1digbuf);
			fprintf(stderr, "%s: sha1: %s calculated.\n", 
						swlib_utilname_get(), calc_sha1digbuf);
		}
	
		/*
		* Now emit the signed file out to the user.
		*/

		/*
		* Seek back to the start.
		*/
		if ((ret = uxfio_lseek(ifd, 0, UXFIO_SEEK_VSET)) != 0) {
			fprintf(stderr, "uxfio_lseek error loc=3 ret=%d\n", 
									ret);
			return 10;
		}
		uxfio_fcntl(ifd, UXFIO_F_ARM_AUTO_DISABLE, 1);

		ret = taruib_write_catalog_stream(vp, ofd, 1 /*version*/, 
							0 /* verbose */);
		if (ret <= 0) {
			pri_ret = 9;
		}
	} else {
		/*
		* Md5 mismatch.
		* Emit empty files at gpg to make it content.
		*/
		if (verbose > 2) {
			fprintf(stderr, 
			"swbis: md5: %s from archive.\n", md5digbuf);
			fprintf(stderr, 
			"swbis: md5: %s calculated.\n", calc_md5digbuf);
			fprintf(stderr, 
			"swbis: sha1: %s from archive.\n", sha1digbuf);
			fprintf(stderr, 
			"swbis: sha1: %s calculated.\n", calc_sha1digbuf);
		}
		close(sigfd);
		uxfio_close(memfd);
	}
	return pri_ret;
}

int
taruib_arfinstall(void * xswi, void * xpackage, 
		void * xswpath, 
		int xofd, 
		char * leadingpath, 
		int do_preview, 
		unsigned long int * statbytes, 
		int * deadman,
		void (*alarm_handler)(int),
		void * xselections)
{
	XFORMAT * package = (XFORMAT*)xpackage;
	SWPATH * swpath = (SWPATH*)xswpath;
	int nullfd;
	int format;
	int output_format;
	char * name;
	int ifd;
	int ret;
	int aret;
	int parseret;
	int pathret;
	int retval = -1;
	int depth;
	int ofd;
	STROB * resolved_path;
	STROB * namebuf;
	STROB * newnamebuf;
	STROB * tmp;
	off_t bytes = 0;
	int tarheaderflags;
	int do_gnu_long_link;
	int namelengthret;
	struct new_cpio_header * file_hdr;
        sigset_t pendmask;

	nullfd = open("/dev/null", O_RDWR);
	newnamebuf = strob_open(100);
	namebuf = strob_open(100);
	tmp = strob_open(10);
	resolved_path = strob_open(10);
	tarheaderflags = xformat_get_tarheader_flags(package);
	file_hdr = (struct new_cpio_header*)(xformat_vfile_hdr(package));
	
	output_format = xformat_get_output_format(package);
	format = xformat_get_format(package);
	ifd = xformat_get_ifd(package);
	if (ifd < 0) return -32;		

	/*
	* Here's the trick (Uh.. Hack):
	*     ofd is really /dev/null; and the real output
	*     is set into taruib_set_fd() which enables the write
	*     thru cache resulting in the output being the same bytes
	*     as the input.
	*/
	ofd = nullfd;
	xformat_set_ofd(package, ofd);
	taruib_initialize_pass_thru_buffer();
	taruib_set_fd(xofd);
	if (alarm_handler) {
		swgp_signal_block(SIGALRM, (sigset_t *)NULL);
	}
	while ((ret = xformat_read_header(package)) > 0  && 
			(!deadman || (deadman && *deadman == 0))) {
		if (alarm_handler) {
			if (statbytes) {
				(*statbytes) = (unsigned long int)bytes;
			}
			swgp_signal_unblock(SIGALRM, (sigset_t *)NULL);
        		sigpending(&pendmask);
			if (sigismember(&pendmask, SIGALRM)) {
                                (*alarm_handler)(SIGALRM);
			}
		}
		if (xformat_is_end_of_archive(package)){
			break;
		}
		bytes += ret;
		xformat_get_name(package, namebuf);
		name = strob_str(namebuf);
		strob_strcpy(newnamebuf, leadingpath);
		swlib_unix_dircat(newnamebuf, name);

		/*
		* Make sure its a normal looking posix pathname.
		*/
		parseret = swpath_parse_path(swpath, name);
		if (parseret < 0) {
			retval = -3;
			goto error;
		}

		pathret = swlib_vrealpath("", 
					name, 
					&depth, 
					resolved_path);

		if (depth <= 1 && strstr(name, "..")) {
			/*
			* Somebody tried to sneak in too many ".."
			*/
			retval = -4;
			goto error;
		}
		
		namelengthret = taru_is_tar_filename_too_long(
			strob_str(newnamebuf), 
			tarheaderflags, 
			&do_gnu_long_link, 
			1 /* is_dir */);

		if (namelengthret) {
			/*
			* Name too long for the output format.
			*/
			retval = -5;
			goto error;
		}

		/*
		* FIXME: Need to test symlinks for too many ".."
		*/

		/*
		* Set the New name in the archive.
		*/
		ahsStaticSetTarFilename(file_hdr, strob_str(newnamebuf));

		/*
		* Clear the buffer, this has the effect of writing the
		* header.
		*/	
		if (alarm_handler) {
			swgp_signal_block(SIGALRM, (sigset_t *)NULL);
		}
		taruib_clear_buffer();

		/*
		* Now write any data associated with the current
		* archive member.
		*/	
		aret = xformat_copy_pass(package, ofd, ifd);
		if (aret < 0) goto error;
		bytes += aret;
	}
	if (alarm_handler) {
		swgp_signal_block(SIGALRM, (sigset_t *)NULL);
	}
	if (deadman && *deadman) return -1;
	taruib_clear_buffer();
	retval = 0;

	/*
	* Pump out the trailer bytes.
	*/
	ret = taru_pump_amount2(ofd, ifd, -1, -1);
	if (ret < 0) {
		fprintf(stderr, "swbis: error in taruib_arfcopy\n");
		retval = -1;
	}
	bytes += aret;
	/*
	* Now do a final clear.
	*/
	taruib_clear_buffer();
error:
	strob_close(resolved_path);
	strob_close(tmp);
	strob_close(namebuf);
	if (alarm_handler) {
		swgp_signal_unblock(SIGALRM, (sigset_t *)NULL);
	}
	return retval;
}
