OpenSolaris_b135/lib/libpkg/common/dstream.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */



#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <fcntl.h>
#ifdef u3b2
#include <sys/sys3b.h>
#endif	/* u3b2 */
#include <openssl/err.h>
#include "pkglib.h"
#include "pkglibmsgs.h"
#include "pkglocale.h"
#ifdef u3b2
static
struct stat	orig_st_buf; /* Stat structure of original file (3B2/CTC) */
static char	ds_ctcflg;
#endif	/* u3b2 */

/* libadm.a */
extern char	*devattr(char *device, char *attribute);
extern int	pkgnmchk(register char *pkg, register char *spec,
				int presvr4flg);
extern int	getvol(char *device, char *label, int options, char *prompt);

#define	CMDSIZ	512
#define	LSIZE	128
#define	DDPROC		"/usr/bin/dd"
#define	CPIOPROC	"/usr/bin/cpio"

/* device types */

#define	G_TM_TAPE	1   /* Tapemaster controller */
#define	G_XY_DISK	3   /* xy disks */
#define	G_SD_DISK	7   /* scsi sd disk */
#define	G_XT_TAPE	8   /* xt tapes */
#define	G_SF_FLOPPY	9   /* sf floppy */
#define	G_XD_DISK	10  /* xd disks */
#define	G_ST_TAPE	11  /* scsi tape */
#define	G_NS		12  /* noswap pseudo-dev */
#define	G_RAM		13  /* ram pseudo-dev */
#define	G_FT		14  /* tftp */
#define	G_HD		15  /* 386 network disk */
#define	G_FD		16  /* 386 AT disk */
#define	G_FILE		28  /* file, not a device */
#define	G_NO_DEV	29  /* device does not require special treatment */
#define	G_DEV_MAX	30  /* last valid device type */

struct dstoc {
	int	cnt;
	char	pkg[NON_ABI_NAMELNGTH];
	int	nparts;
	long	maxsiz;
	char    volnos[128];
	struct dstoc *next;
} *ds_head, *ds_toc;

#define	ds_nparts	ds_toc->nparts
#define	ds_maxsiz	ds_toc->maxsiz

int	ds_totread; 	/* total number of parts read */
int	ds_fd = -1;
int	ds_curpartcnt = -1;

int	ds_next(char *device, char *instdir);
int	ds_ginit(char *device);
int	ds_close(int pkgendflg);

static FILE	*ds_pp;
static int	ds_realfd = -1; 	/* file descriptor for real device */
static int	ds_read; 	/* number of parts read for current package */
static int	ds_volno; 	/* volume number of current volume */
static int	ds_volcnt; 	/* total number of volumes */
static char	ds_volnos[128]; 	/* parts/volume info */
static char	*ds_device;
static int	ds_volpart;	/* number of parts read in current volume, */
						/* including skipped parts */
static int	ds_bufsize;
static int	ds_skippart; 	/* number of parts skipped in current volume */

static int	ds_getnextvol(char *device);
static int	ds_skip(char *device, int nskip);

void
ds_order(char *list[])
{
	struct dstoc *toc_pt;
	register int j, n;
	char	*pt;

	toc_pt = ds_head;
	n = 0;
	while (toc_pt) {
		for (j = n; list[j]; j++) {
			if (strcmp(list[j], toc_pt->pkg) == 0) {
				/* just swap places in the array */
				pt = list[n];
				list[n++] = list[j];
				list[j] = pt;
			}
		}
		toc_pt = toc_pt->next;
	}
}

static char *pds_header;
static char *ds_header;
static char *ds_header_raw;
static int ds_headsize;

static char *
ds_gets(char *buf, int size)
{
	int length;
	char *nextp;

	nextp = strchr(pds_header, '\n');
	if (nextp == NULL) {
		length = strlen(pds_header);
		if (length > size)
			return (0);
		if ((ds_header = (char *)realloc(ds_header,
		    ds_headsize + BLK_SIZE)) == NULL)
			return (0);
		if (read(ds_fd, ds_header + ds_headsize, BLK_SIZE) < BLK_SIZE)
			return (0);
		ds_headsize += BLK_SIZE;
		nextp = strchr(pds_header, '\n');
		if (nextp == NULL)
			return (0);
		*nextp = '\0';
		if (length + (int)strlen(pds_header) > size)
			return (0);
		(void) strncpy(buf + length, pds_header, strlen(pds_header));
		buf[length + strlen(pds_header)] = '\0';
		pds_header = nextp + 1;
		return (buf);
	}
	*nextp = '\0';
	if ((int)strlen(pds_header) > size)
		return (0);
	(void) strncpy(buf, pds_header, strlen(pds_header));
	buf[strlen(pds_header)] = '\0';
	pds_header = nextp + 1;
	return (buf);
}

/*
 * function to determine if media is datastream or mounted
 * floppy
 */
int
ds_readbuf(char *device)
{
	char buf[BLK_SIZE];

	if (ds_fd >= 0)
		(void) close(ds_fd);
	if ((ds_fd = open(device, O_RDONLY)) >= 0 &&
	    read(ds_fd, buf, BLK_SIZE) == BLK_SIZE &&
	    strncmp(buf, HDR_PREFIX, 20) == 0) {
		if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) {
			progerr(pkg_gt(ERR_UNPACK));
			logerr(pkg_gt(MSG_MEM));
			(void) ds_close(0);
			return (0);
		}
		memcpy(ds_header, buf, BLK_SIZE);
		ds_headsize = BLK_SIZE;

		if (ds_ginit(device) < 0) {
			progerr(pkg_gt(ERR_UNPACK));
			logerr(pkg_gt(MSG_OPEN), device, errno);
			(void) ds_close(0);
			return (0);
		}
		return (1);
	} else if (ds_fd >= 0) {
		(void) close(ds_fd);
		ds_fd = -1;
	}
	return (0);
}

/*
 * Determine how many additional volumes are needed for current package.
 * Note: a 0 will occur as first volume number when the package begins
 * on the next volume.
 */
static int
ds_volsum(struct dstoc *toc)
{
	int curpartcnt, volcnt;
	char volnos[128], tmpvol[128];
	if (toc->volnos[0]) {
		int index, sum;
		sscanf(toc->volnos, "%d %[ 0-9]", &curpartcnt, volnos);
		volcnt = 0;
		sum = curpartcnt;
		while (sum < toc->nparts && sscanf(volnos, "%d %[ 0-9]",
		    &index, tmpvol) >= 1) {
			(void) strcpy(volnos, tmpvol);
			volcnt++;
			sum += index;
		}
		/* side effect - set number of parts read on current volume */
		ds_volpart = index;
		return (volcnt);
	}
	ds_volpart += toc->nparts;
	return (0);
}

/* initialize ds_curpartcnt and ds_volnos */
static void
ds_pkginit(void)
{
	if (ds_toc->volnos[0])
		sscanf(ds_toc->volnos, "%d %[ 0-9]", &ds_curpartcnt, ds_volnos);
	else
		ds_curpartcnt = -1;
}

/*
 * functions to pass current package info to exec'ed program
 */
void
ds_putinfo(char *buf)
{
	(void) sprintf(buf, "%d %d %d %d %d %d %d %d %d %d %s",
	    ds_fd, ds_realfd, ds_volcnt, ds_volno, ds_totread, ds_volpart,
	    ds_skippart, ds_bufsize, ds_toc->nparts, ds_toc->maxsiz,
	    ds_toc->volnos);
}

int
ds_getinfo(char *string)
{
	ds_toc = (struct dstoc *)calloc(1, sizeof (struct dstoc));
	(void) sscanf(string, "%d %d %d %d %d %d %d %d %d %d %[ 0-9]",
	    &ds_fd, &ds_realfd, &ds_volcnt, &ds_volno, &ds_totread,
	    &ds_volpart, &ds_skippart, &ds_bufsize, &ds_toc->nparts,
	    &ds_toc->maxsiz, ds_toc->volnos);
	ds_pkginit();
	return (ds_toc->nparts);
}

/*
 * Return true if the file descriptor (ds_fd) is open on the package stream.
 */
boolean_t
ds_fd_open(void)
{
	return (ds_fd >= 0 ? B_TRUE : B_FALSE);
}

/*
 * Read the source device. Acquire the header data and check it for validity.
 */
int
ds_init(char *device, char **pkg, char *norewind)
{
	struct dstoc *tail, *toc_pt;
	char	*ret;
	char	cmd[CMDSIZ];
	char	line[LSIZE+1];
	int	i, n, count = 0, header_size = BLK_SIZE;

	if (!ds_header) { 	/* If the header hasn't been read yet */
		if (ds_fd >= 0)
			(void) ds_close(0);

		/* always start with rewind device */
		if ((ds_fd = open(device, O_RDONLY)) < 0) {
			progerr(pkg_gt(ERR_UNPACK));
			logerr(pkg_gt(MSG_OPEN), device, errno);
			return (-1);
		}

		/* allocate room for the header equivalent to a block */
		if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) {
			progerr(pkg_gt(ERR_UNPACK));
			logerr(pkg_gt(MSG_MEM));
			return (-1);
		}

		/* initialize the device */
		if (ds_ginit(device) < 0) {
			(void) ds_close(0);
			progerr(pkg_gt(ERR_UNPACK));
			logerr(pkg_gt(MSG_OPEN), device, errno);
			return (-1);
		}

		/* read a logical block from the source device */
		if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) {
			rpterr();
			progerr(pkg_gt(ERR_UNPACK));
			logerr(pkg_gt(MSG_TOC));
			(void) ds_close(0);
			return (-1);
		}

		/*
		 * This loop scans the medium for the start of the header.
		 * If the above read worked, we skip this. If it did't, this
		 * loop will retry the read ten times looking for the header
		 * marker string.
		 */
		while (strncmp(ds_header, HDR_PREFIX, 20) != 0) {
			/* only ten tries iff the device rewinds */
			if (!norewind || count++ > 10) {
				progerr(pkg_gt(ERR_UNPACK));
				logerr(pkg_gt(MSG_TOC));
				(void) ds_close(0);
				return (-1);
			}

			/* read through to the last block */
			if (count > 1)
				while (read(ds_fd, ds_header, BLK_SIZE) > 0)
					;

			/* then close the device */
			(void) ds_close(0);

			/* and reopen it */
			if ((ds_fd = open(norewind, O_RDONLY)) < 0) {
				progerr(pkg_gt(ERR_UNPACK));
				logerr(pkg_gt(MSG_OPEN), device, errno);
				(void) free(ds_header);
				return (-1);
			}

			/* initialize the device */
			if (ds_ginit(device) < 0) {
				(void) ds_close(0);
				progerr(pkg_gt(ERR_UNPACK));
				logerr(pkg_gt(MSG_OPEN), device, errno);
				return (-1);
			}

			/* read the block again */
			if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) {
				rpterr();
				progerr(pkg_gt(ERR_UNPACK));
				logerr(pkg_gt(MSG_TOC));
				(void) ds_close(0);
				return (-1);
			}
		}

		/* Now keep scanning until the whole header is in place. */
		while (strstr(ds_header, HDR_SUFFIX) == NULL) {
			/* We need a bigger buffer */
			if ((ds_header = (char *)realloc(ds_header,
			    header_size + BLK_SIZE)) == NULL) {
				progerr(pkg_gt(ERR_UNPACK));
				logerr(pkg_gt(MSG_MEM));
				(void) ds_close(0);
				return (1);
			}

			/* clear the new memory */
			(void) memset(ds_header + header_size, '\0',
			    BLK_SIZE);


			/* read a logical block from the source device */
			if (read(ds_fd, ds_header + header_size, BLK_SIZE) !=
			    BLK_SIZE) {
				rpterr();
				progerr(pkg_gt(ERR_UNPACK));
				logerr(pkg_gt(MSG_TOC));
				(void) ds_close(0);
				return (-1);
			} else
				header_size += BLK_SIZE;	/* new size */
		}

		/*
		 * remember rewind device for ds_close to rewind at
		 * close
		 */
		if (count >= 1)
			ds_device = device;
		ds_headsize = header_size;

	}

	pds_header = ds_header;

	/* save raw copy of header for later use in BIO_dump_header */
	if ((ds_header_raw = (char *)malloc(header_size)) == NULL) {
		progerr(pkg_gt(ERR_UNPACK));
		logerr(pkg_gt(MSG_MEM));
		(void) ds_close(0);
		return (1);
	}
	memcpy(ds_header_raw, ds_header, header_size);

	/* read datastream table of contents */
	ds_head = tail = (struct dstoc *)0;
	ds_volcnt = 1;

	while (ret = ds_gets(line, LSIZE)) {
		if (strcmp(line, HDR_SUFFIX) == 0)
			break;
		if (!line[0] || line[0] == '#')
			continue;
		toc_pt = (struct dstoc *)calloc(1, sizeof (struct dstoc));
		if (!toc_pt) {
			progerr(pkg_gt(ERR_UNPACK));
			logerr(pkg_gt(MSG_MEM));
			ecleanup();
			(void) free(ds_header);
			return (-1);
		}
		if (sscanf(line, "%s %d %d %[ 0-9]", toc_pt->pkg,
		    &toc_pt->nparts, &toc_pt->maxsiz, toc_pt->volnos) < 3) {
			progerr(pkg_gt(ERR_UNPACK));
			logerr(pkg_gt(MSG_TOC));
			free(toc_pt);
			(void) free(ds_header);
			ecleanup();
			return (-1);
		}
		if (tail) {
			tail->next = toc_pt;
			tail = toc_pt;
		} else
			ds_head = tail = toc_pt;
		ds_volcnt += ds_volsum(toc_pt);
	}
	if (!ret) {
		progerr(pkg_gt(ERR_UNPACK));
		logerr(pkg_gt(MSG_TOC));
		(void) free(ds_header);
		return (-1);
	}
	sighold(SIGINT);
	sigrelse(SIGINT);
	if (!ds_head) {
		progerr(pkg_gt(ERR_UNPACK));
		logerr(pkg_gt(MSG_EMPTY));
		(void) free(ds_header);
		return (-1);
	}
	/* this could break, thanks to cpio command limit */
#ifndef SUNOS41
	(void) sprintf(cmd, "%s -icdumD -C %d", CPIOPROC, (int)BLK_SIZE);
#else
	(void) sprintf(cmd, "%s -icdum -C %d", CPIOPROC, (int)BLK_SIZE);
#endif
	n = 0;
	for (i = 0; pkg[i]; i++) {
		if (strcmp(pkg[i], "all") == 0)
			continue;
		if (n == 0) {
			strcat(cmd, " ");
			n = 1;
		}
		strlcat(cmd, pkg[i], CMDSIZ);
		strlcat(cmd, "'/*' ", CMDSIZ);

		/* extract signature too, if present. */
		strlcat(cmd, SIGNATURE_FILENAME, CMDSIZ);
		strlcat(cmd, " ", CMDSIZ);
	}

	/*
	 * if we are extracting all packages (pkgs == NULL),
	 * signature will automatically be extracted
	 */
	if (n = esystem(cmd, ds_fd, -1)) {
		rpterr();
		progerr(pkg_gt(ERR_UNPACK));
		logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
		(void) free(ds_header);
		return (-1);
	}

	ds_toc = ds_head;
	ds_totread = 0;
	ds_volno = 1;
	return (0);
}

int
ds_findpkg(char *device, char *pkg)
{
	char	*pkglist[2];
	int	nskip, ods_volpart;

	if (ds_head == NULL) {
		pkglist[0] = pkg;
		pkglist[1] = NULL;
		if (ds_init(device, pkglist, NULL))
			return (-1);
	}

	if (!pkg || pkgnmchk(pkg, "all", 0)) {
		progerr(pkg_gt(ERR_UNPACK));
		logerr(pkg_gt(MSG_PKGNAME));
		return (-1);
	}

	nskip = 0;
	ds_volno = 1;
	ds_volpart = 0;
	ds_toc = ds_head;
	while (ds_toc) {
		if (strcmp(ds_toc->pkg, pkg) == 0)
			break;
		nskip += ds_toc->nparts;
		ds_volno += ds_volsum(ds_toc);
		ds_toc = ds_toc->next;
	}
	if (!ds_toc) {
		progerr(pkg_gt(ERR_UNPACK));
		logerr(pkg_gt(MSG_NOPKG), pkg);
		return (-1);
	}

	ds_pkginit();
	ds_skippart = 0;
	if (ds_curpartcnt > 0) {
		ods_volpart = ds_volpart;
		/*
		 * skip past archives belonging to last package on current
		 * volume
		 */
		if (ds_volpart > 0 && ds_getnextvol(device))
			return (-1);
		ds_totread = nskip - ods_volpart;
		if (ds_skip(device, ods_volpart))
			return (-1);
	} else if (ds_curpartcnt < 0) {
		if (ds_skip(device, nskip - ds_totread))
			return (-1);
	} else
		ds_totread = nskip;
	ds_read = 0;
	return (ds_nparts);
}

/*
 * Get datastream part
 * Call for first part should be preceded by
 * call to ds_findpkg
 */

int
ds_getpkg(char *device, int n, char *dstdir)
{
	struct statvfs64 svfsb;
	u_longlong_t free_blocks;

	if (ds_read >= ds_nparts)
		return (2);

	if (ds_read == n)
		return (0);
	else if ((ds_read > n) || (n > ds_nparts))
		return (2);

	if (ds_maxsiz > 0) {
		if (statvfs64(".", &svfsb)) {
			progerr(pkg_gt(ERR_UNPACK));
			logerr(pkg_gt(MSG_STATFS), errno);
			return (-1);
		}
#ifdef SUNOS41
		free_blocks = svfsb.f_bfree * howmany(svfsb.f_bsize, DEV_BSIZE);
#else	/* !SUNOS41 */
		free_blocks = (((long)svfsb.f_frsize > 0) ?
			    howmany(svfsb.f_frsize, DEV_BSIZE) :
			    howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bfree;
#endif	/* SUNOS41 */
		if ((ds_maxsiz + 50) > free_blocks) {
			progerr(pkg_gt(ERR_UNPACK));
			logerr(pkg_gt(MSG_NOSPACE), ds_maxsiz+50, free_blocks);
			return (-1);
		}
	}
	return (ds_next(device, dstdir));
}

static int
ds_getnextvol(char *device)
{
	char prompt[128];
	int n;

	if (ds_close(0))
		return (-1);
	(void) sprintf(prompt,
	    pkg_gt("Insert %%v %d of %d into %%p"),
	    ds_volno, ds_volcnt);
	if (n = getvol(device, NULL, NULL, prompt))
		return (n);
	if ((ds_fd = open(device, O_RDONLY)) < 0)
		return (-1);
	if (ds_ginit(device) < 0) {
		(void) ds_close(0);
		return (-1);
	}
	ds_volpart = 0;
	return (0);
}

/*
 * called by ds_findpkg to skip past archives for unwanted packages
 * in current volume
 */
static int
ds_skip(char *device, int nskip)
{
	char	cmd[CMDSIZ];
	int	n, onskip = nskip;

	while (nskip--) {
		/* skip this one */
#ifndef SUNOS41
		(void) sprintf(cmd, "%s -ictD -C %d > /dev/null",
#else
		(void) sprintf(cmd, "%s -ict -C %d > /dev/null",
#endif
		    CPIOPROC, (int)BLK_SIZE);
		if (n = esystem(cmd, ds_fd, -1)) {
			rpterr();
			progerr(pkg_gt(ERR_UNPACK));
			logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
			nskip = onskip;
			if (ds_volno == 1 || ds_volpart > 0)
				return (n);
			if (n = ds_getnextvol(device))
				return (n);
		}
	}
	ds_totread += onskip;
	ds_volpart = onskip;
	ds_skippart = onskip;
	return (0);
}

/* skip to end of package if necessary */
void
ds_skiptoend(char *device)
{
	if (ds_read < ds_nparts && ds_curpartcnt < 0)
		(void) ds_skip(device, ds_nparts - ds_read);
}

int
ds_next(char *device, char *instdir)
{
	char	cmd[CMDSIZ], tmpvol[128];
	int	nparts, n, index;

	/*CONSTCOND*/
	while (1) {
		if (ds_read + 1 > ds_curpartcnt && ds_curpartcnt >= 0) {
			ds_volno++;
			if (n = ds_getnextvol(device))
				return (n);
			(void) sscanf(ds_volnos, "%d %[ 0-9]", &index, tmpvol);
			(void) strcpy(ds_volnos, tmpvol);
			ds_curpartcnt += index;
		}
#ifndef SUNOS41
		(void) sprintf(cmd, "%s -icdumD -C %d",
#else
		(void) sprintf(cmd, "%s -icdum -C %d",
#endif
		    CPIOPROC, (int)BLK_SIZE);
		if (n = esystem(cmd, ds_fd, -1)) {
			rpterr();
			progerr(pkg_gt(ERR_UNPACK));
			logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
		}
		if (ds_read == 0)
			nparts = 0;
		else
			nparts = ds_toc->nparts;
		if (n || (n = ckvolseq(instdir, ds_read + 1, nparts))) {
			if (ds_volno == 1 || ds_volpart > ds_skippart)
				return (-1);

			if (n = ds_getnextvol(device))
				return (n);
			continue;
		}
		ds_read++;
		ds_totread++;
		ds_volpart++;

		return (0);
	}
	/*NOTREACHED*/
}

/*
 * Name:		BIO_ds_dump
 * Description:	Dumps all data from the static 'ds_fd' file handle into
 *		the supplied BIO.
 *
 * Arguments:	err - where to record any errors.
 *		device - Description of device being dumped into,
 *			for error reporting
 *		bio - BIO object to dump data into
 *
 * Returns :	zero - successfully dumped all data to EOF
 *		non-zero - some failure occurred.
 */
int
BIO_ds_dump(PKG_ERR *err, char *device, BIO *bio)
{
	int	amtread;
	char	readbuf[BLK_SIZE];

	/*
	 * note this will read to the end of the device, so it won't
	 * work for character devices since we don't know when the
	 * end of the CPIO archive is
	 */
	while ((amtread = read(ds_fd, readbuf, BLK_SIZE)) != 0) {
		if (BIO_write(bio, readbuf, amtread) != amtread) {
			pkgerr_add(err, PKGERR_WRITE, ERR_WRITE, device,
			    ERR_error_string(ERR_get_error(), NULL));
			return (1);
		}
	}

	return (0);
	/*NOTREACHED*/
}


/*
 * Name:		BIO_ds_dump_header
 * Description:	Dumps all ds_headsize bytes from the
 *		static 'ds_header_raw' character array
 *		to the supplied BIO.
 *
 * Arguments:	err - where to record any errors.
 *		bio - BIO object to dump data into
 *
 * Returns :	zero - successfully dumped all raw
 *		header characters
 *		non-zero - some failure occurred.
 */
int
BIO_ds_dump_header(PKG_ERR *err, BIO *bio)
{

	char	zeros[BLK_SIZE];

	memset(zeros, 0, BLK_SIZE);

	if (BIO_write(bio, ds_header_raw, ds_headsize) != ds_headsize) {
		pkgerr_add(err, PKGERR_WRITE, ERR_WRITE, "bio",
		    ERR_error_string(ERR_get_error(), NULL));
		return (1);
	}

	return (0);
}

/*
 * ds_ginit: Determine the device being accessed, set the buffer size,
 * and perform any device specific initialization.  For the 3B2,
 * a device with major number of 17 (0x11) is an internal hard disk,
 * unless the minor number is 128 (0x80) in which case it is an internal
 * floppy disk.  Otherwise, get the system configuration
 * table and check it by comparing slot numbers to major numbers.
 * For the special case of the 3B2 CTC several unusual things must be done.
 * To enable
 * streaming mode on the CTC, the file descriptor must be closed, re-opened
 * (with O_RDWR and O_CTSPECIAL flags set), the STREAMON ioctl(2) command
 * issued, and the file descriptor re-re-opened either read-only or write_only.
 */

int
ds_ginit(char *device)
{
#ifdef u3b2
	major_t maj;
	minor_t min;
	int nflag, i, count, size;
	struct s3bconf *buffer;
	struct s3bc *table;
	struct stat st_buf;
	int devtype;
	char buf[BLK_SIZE];
	int fd2, fd;
#endif	/* u3b2 */
	int oflag;
	char *pbufsize, cmd[CMDSIZ];
	int fd2, fd;

	if ((pbufsize = devattr(device, "bufsize")) != NULL) {
		ds_bufsize = atoi(pbufsize);
		(void) free(pbufsize);
	} else
		ds_bufsize = BLK_SIZE;
	oflag = fcntl(ds_fd, F_GETFL, 0);
#ifdef u3b2
	devtype = G_NO_DEV;
	if (fstat(ds_fd, &st_buf) == -1)
		return (-1);
	if (!S_ISCHR(st_buf.st_mode) && !S_ISBLK(st_buf.st_mode))
		goto lab;

	/*
	 * We'll have to add a remote attribute to stat but this should
	 * work for now.
	 */
	else if (st_buf.st_dev & 0x8000)	/* if remote  rdev */
		goto lab;

	maj = major(st_buf.st_rdev);
	min = minor(st_buf.st_rdev);
	if (maj == 0x11) { /* internal hard or floppy disk */
		if (min & 0x80)
			devtype = G_3B2_FD; /* internal floppy disk */
		else
			devtype = G_3B2_HD; /* internal hard disk */
	} else {
		if (sys3b(S3BCONF, (struct s3bconf *)&count, sizeof (count)) ==
		    -1)
			return (-1);
		size = sizeof (int) + (count * sizeof (struct s3bconf));
		buffer = (struct s3bconf *)malloc((unsigned)size);
		if (sys3b(S3BCONF, buffer, size) == -1)
			return (-1);
		table = (struct s3bc *)((char *)buffer + sizeof (int));
		for (i = 0; i < count; i++) {
			if (maj == (int)table->board) {
				if (strncmp(table->name, "CTC", 3) == 0) {
					devtype = G_3B2_CTC;
					break;
				} else if (strncmp(table->name, "TAPE", 4)
						== 0) {
					devtype = G_TAPE;
					break;
				}
				/* other possible devices can go here */
			}
			table++;
		}
	}
	switch (devtype) {
		case G_3B2_CTC:	/* do special CTC initialization */
			ds_bufsize = pbufsize ? ds_bufsize : 15872;
			if (fstat(ds_fd, &orig_st_buf) < 0) {
				ds_bufsize = -1;
				break;
			}
			nflag = (O_RDWR | O_CTSPECIAL);
			(void) close(ds_fd);
			if ((ds_fd = open(device, nflag, 0666)) != -1) {
				if (ioctl(ds_fd, STREAMON) != -1) {
					(void) close(ds_fd);
					nflag = (oflag == O_WRONLY) ?
					    O_WRONLY : O_RDONLY;
					if ((ds_fd =
					    open(device, nflag, 0666)) == -1) {
						rpterr();
						progerr(
						    pkg_gt(ERR_TRANSFER));
						logerr(pkg_gt(MSG_OPEN),
						    device, errno);
						return (-1);
					}
					ds_bufsize = 15872;
				}
			} else
				ds_bufsize = -1;
			if (oflag == O_RDONLY && ds_header && ds_totread == 0)
				/* Have already read in first block of header */
				read(ds_fd, buf, BLK_SIZE);
			ds_ctcflg = 1;

			break;
		case G_NO_DEV:
		case G_3B2_HD:
		case G_3B2_FD:
		case G_TAPE:
		case G_SCSI_HD: /* not developed yet */
		case G_SCSI_FD:
		case G_SCSI_9T:
		case G_SCSI_Q24:
		case G_SCSI_Q120:
		case G_386_HD:
		case G_386_FD:
		case G_386_Q24:
			ds_bufsize = pbufsize ? ds_bufsize : BLK_SIZE;
			break;
		default:
			ds_bufsize = -1;
			errno = ENODEV;
	} /* devtype */
lab:
#endif	/* u3b2 */
	if (ds_bufsize > BLK_SIZE) {
		if (oflag & O_WRONLY)
			fd = 1;
		else
			fd = 0;
		fd2 = fcntl(fd, F_DUPFD, fd);
		(void) close(fd);
		fcntl(ds_fd, F_DUPFD, fd);
		if (fd)
			sprintf(cmd, "%s obs=%d 2>/dev/null", DDPROC,
			    ds_bufsize);
		else
			sprintf(cmd, "%s ibs=%d 2>/dev/null", DDPROC,
			    ds_bufsize);
		if ((ds_pp = popen(cmd, fd ? "w" : "r")) == NULL) {
			progerr(pkg_gt(ERR_TRANSFER));
			logerr(pkg_gt(MSG_POPEN), cmd, errno);
			return (-1);
		}
		(void) close(fd);
		fcntl(fd2, F_DUPFD, fd);
		(void) close(fd2);
		ds_realfd = ds_fd;
		ds_fd = fileno(ds_pp);
	}
	return (ds_bufsize);
}

int
ds_close(int pkgendflg)
{
#ifdef u3b2
	int cnt, mode;
	char *ptr;
	struct stat statbuf;
#endif	/* u3b2 */
	int n, ret = 0;

#ifdef u3b2
	if (ds_pp && ds_ctcflg) {
		ds_ctcflg = 0;
		if ((mode = fcntl(ds_realfd, F_GETFL, 0)) < 0) {
			ret = -1;
		} else if (mode & O_WRONLY) {
		/*
		 * pipe to dd write process,
		 * make sure one more buffer
		 * gets written out
		 */
			if ((ptr = calloc(BLK_SIZE, 1)) == NULL) {
				ret = -1;
			/* pad to bufsize */
			} else {
				cnt = ds_bufsize;
				while (cnt > 0) {
					if ((n = write(ds_fd, ptr,
					    BLK_SIZE)) < 0) {
						ret = -1;
						break;
					}
					cnt -= n;
				}
				(void) free(ptr);
			}
		}
	}
#endif
	if (pkgendflg) {
		if (ds_header)
			(void) free(ds_header);
		ds_header = (char *)NULL;
		ds_totread = 0;
	}

	if (ds_pp) {
		(void) pclose(ds_pp);
		ds_pp = 0;
		(void) close(ds_realfd);
		ds_realfd = -1;
		ds_fd = -1;
	} else if (ds_fd >= 0) {
		(void) close(ds_fd);
		ds_fd = -1;
	}

	if (ds_device) {
		/* rewind device */
		if ((n = open(ds_device, 0)) >= 0)
			(void) close(n);
		ds_device = NULL;
	}
	return (ret);
}