dstream.c revision 9781:ccf49524d5dc
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28/* All Rights Reserved */
29
30
31
32#include <stdio.h>
33#include <string.h>
34#include <signal.h>
35#include <stdlib.h>
36#include <unistd.h>
37#include <sys/types.h>
38#include <sys/param.h>
39#include <sys/sysmacros.h>
40#include <errno.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <sys/statvfs.h>
44#include <fcntl.h>
45#ifdef u3b2
46#include <sys/sys3b.h>
47#endif	/* u3b2 */
48#include <openssl/err.h>
49#include "pkglib.h"
50#include "pkglibmsgs.h"
51#include "pkglocale.h"
52#ifdef u3b2
53static
54struct stat	orig_st_buf; /* Stat structure of original file (3B2/CTC) */
55static char	ds_ctcflg;
56#endif	/* u3b2 */
57
58/* libadm.a */
59extern char	*devattr(char *device, char *attribute);
60extern int	pkgnmchk(register char *pkg, register char *spec,
61				int presvr4flg);
62extern int	getvol(char *device, char *label, int options, char *prompt);
63
64#define	CMDSIZ	512
65#define	LSIZE	128
66#define	DDPROC		"/usr/bin/dd"
67#define	CPIOPROC	"/usr/bin/cpio"
68
69/* device types */
70
71#define	G_TM_TAPE	1   /* Tapemaster controller */
72#define	G_XY_DISK	3   /* xy disks */
73#define	G_SD_DISK	7   /* scsi sd disk */
74#define	G_XT_TAPE	8   /* xt tapes */
75#define	G_SF_FLOPPY	9   /* sf floppy */
76#define	G_XD_DISK	10  /* xd disks */
77#define	G_ST_TAPE	11  /* scsi tape */
78#define	G_NS		12  /* noswap pseudo-dev */
79#define	G_RAM		13  /* ram pseudo-dev */
80#define	G_FT		14  /* tftp */
81#define	G_HD		15  /* 386 network disk */
82#define	G_FD		16  /* 386 AT disk */
83#define	G_FILE		28  /* file, not a device */
84#define	G_NO_DEV	29  /* device does not require special treatment */
85#define	G_DEV_MAX	30  /* last valid device type */
86
87struct dstoc {
88	int	cnt;
89	char	pkg[NON_ABI_NAMELNGTH];
90	int	nparts;
91	long	maxsiz;
92	char    volnos[128];
93	struct dstoc *next;
94} *ds_head, *ds_toc;
95
96#define	ds_nparts	ds_toc->nparts
97#define	ds_maxsiz	ds_toc->maxsiz
98
99int	ds_totread; 	/* total number of parts read */
100int	ds_fd = -1;
101int	ds_curpartcnt = -1;
102
103int	ds_next(char *device, char *instdir);
104int	ds_ginit(char *device);
105int	ds_close(int pkgendflg);
106
107static FILE	*ds_pp;
108static int	ds_realfd = -1; 	/* file descriptor for real device */
109static int	ds_read; 	/* number of parts read for current package */
110static int	ds_volno; 	/* volume number of current volume */
111static int	ds_volcnt; 	/* total number of volumes */
112static char	ds_volnos[128]; 	/* parts/volume info */
113static char	*ds_device;
114static int	ds_volpart;	/* number of parts read in current volume, */
115						/* including skipped parts */
116static int	ds_bufsize;
117static int	ds_skippart; 	/* number of parts skipped in current volume */
118
119static int	ds_getnextvol(char *device);
120static int	ds_skip(char *device, int nskip);
121
122void
123ds_order(char *list[])
124{
125	struct dstoc *toc_pt;
126	register int j, n;
127	char	*pt;
128
129	toc_pt = ds_head;
130	n = 0;
131	while (toc_pt) {
132		for (j = n; list[j]; j++) {
133			if (strcmp(list[j], toc_pt->pkg) == 0) {
134				/* just swap places in the array */
135				pt = list[n];
136				list[n++] = list[j];
137				list[j] = pt;
138			}
139		}
140		toc_pt = toc_pt->next;
141	}
142}
143
144static char *pds_header;
145static char *ds_header;
146static char *ds_header_raw;
147static int ds_headsize;
148
149static char *
150ds_gets(char *buf, int size)
151{
152	int length;
153	char *nextp;
154
155	nextp = strchr(pds_header, '\n');
156	if (nextp == NULL) {
157		length = strlen(pds_header);
158		if (length > size)
159			return (0);
160		if ((ds_header = (char *)realloc(ds_header,
161		    ds_headsize + BLK_SIZE)) == NULL)
162			return (0);
163		if (read(ds_fd, ds_header + ds_headsize, BLK_SIZE) < BLK_SIZE)
164			return (0);
165		ds_headsize += BLK_SIZE;
166		nextp = strchr(pds_header, '\n');
167		if (nextp == NULL)
168			return (0);
169		*nextp = '\0';
170		if (length + (int)strlen(pds_header) > size)
171			return (0);
172		(void) strncpy(buf + length, pds_header, strlen(pds_header));
173		buf[length + strlen(pds_header)] = '\0';
174		pds_header = nextp + 1;
175		return (buf);
176	}
177	*nextp = '\0';
178	if ((int)strlen(pds_header) > size)
179		return (0);
180	(void) strncpy(buf, pds_header, strlen(pds_header));
181	buf[strlen(pds_header)] = '\0';
182	pds_header = nextp + 1;
183	return (buf);
184}
185
186/*
187 * function to determine if media is datastream or mounted
188 * floppy
189 */
190int
191ds_readbuf(char *device)
192{
193	char buf[BLK_SIZE];
194
195	if (ds_fd >= 0)
196		(void) close(ds_fd);
197	if ((ds_fd = open(device, O_RDONLY)) >= 0 &&
198	    read(ds_fd, buf, BLK_SIZE) == BLK_SIZE &&
199	    strncmp(buf, HDR_PREFIX, 20) == 0) {
200		if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) {
201			progerr(pkg_gt(ERR_UNPACK));
202			logerr(pkg_gt(MSG_MEM));
203			(void) ds_close(0);
204			return (0);
205		}
206		memcpy(ds_header, buf, BLK_SIZE);
207		ds_headsize = BLK_SIZE;
208
209		if (ds_ginit(device) < 0) {
210			progerr(pkg_gt(ERR_UNPACK));
211			logerr(pkg_gt(MSG_OPEN), device, errno);
212			(void) ds_close(0);
213			return (0);
214		}
215		return (1);
216	} else if (ds_fd >= 0) {
217		(void) close(ds_fd);
218		ds_fd = -1;
219	}
220	return (0);
221}
222
223/*
224 * Determine how many additional volumes are needed for current package.
225 * Note: a 0 will occur as first volume number when the package begins
226 * on the next volume.
227 */
228static int
229ds_volsum(struct dstoc *toc)
230{
231	int curpartcnt, volcnt;
232	char volnos[128], tmpvol[128];
233	if (toc->volnos[0]) {
234		int index, sum;
235		sscanf(toc->volnos, "%d %[ 0-9]", &curpartcnt, volnos);
236		volcnt = 0;
237		sum = curpartcnt;
238		while (sum < toc->nparts && sscanf(volnos, "%d %[ 0-9]",
239		    &index, tmpvol) >= 1) {
240			(void) strcpy(volnos, tmpvol);
241			volcnt++;
242			sum += index;
243		}
244		/* side effect - set number of parts read on current volume */
245		ds_volpart = index;
246		return (volcnt);
247	}
248	ds_volpart += toc->nparts;
249	return (0);
250}
251
252/* initialize ds_curpartcnt and ds_volnos */
253static void
254ds_pkginit(void)
255{
256	if (ds_toc->volnos[0])
257		sscanf(ds_toc->volnos, "%d %[ 0-9]", &ds_curpartcnt, ds_volnos);
258	else
259		ds_curpartcnt = -1;
260}
261
262/*
263 * functions to pass current package info to exec'ed program
264 */
265void
266ds_putinfo(char *buf)
267{
268	(void) sprintf(buf, "%d %d %d %d %d %d %d %d %d %d %s",
269	    ds_fd, ds_realfd, ds_volcnt, ds_volno, ds_totread, ds_volpart,
270	    ds_skippart, ds_bufsize, ds_toc->nparts, ds_toc->maxsiz,
271	    ds_toc->volnos);
272}
273
274int
275ds_getinfo(char *string)
276{
277	ds_toc = (struct dstoc *)calloc(1, sizeof (struct dstoc));
278	(void) sscanf(string, "%d %d %d %d %d %d %d %d %d %d %[ 0-9]",
279	    &ds_fd, &ds_realfd, &ds_volcnt, &ds_volno, &ds_totread,
280	    &ds_volpart, &ds_skippart, &ds_bufsize, &ds_toc->nparts,
281	    &ds_toc->maxsiz, ds_toc->volnos);
282	ds_pkginit();
283	return (ds_toc->nparts);
284}
285
286/*
287 * Return true if the file descriptor (ds_fd) is open on the package stream.
288 */
289boolean_t
290ds_fd_open(void)
291{
292	return (ds_fd >= 0 ? B_TRUE : B_FALSE);
293}
294
295/*
296 * Read the source device. Acquire the header data and check it for validity.
297 */
298int
299ds_init(char *device, char **pkg, char *norewind)
300{
301	struct dstoc *tail, *toc_pt;
302	char	*ret;
303	char	cmd[CMDSIZ];
304	char	line[LSIZE+1];
305	int	i, n, count = 0, header_size = BLK_SIZE;
306
307	if (!ds_header) { 	/* If the header hasn't been read yet */
308		if (ds_fd >= 0)
309			(void) ds_close(0);
310
311		/* always start with rewind device */
312		if ((ds_fd = open(device, O_RDONLY)) < 0) {
313			progerr(pkg_gt(ERR_UNPACK));
314			logerr(pkg_gt(MSG_OPEN), device, errno);
315			return (-1);
316		}
317
318		/* allocate room for the header equivalent to a block */
319		if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) {
320			progerr(pkg_gt(ERR_UNPACK));
321			logerr(pkg_gt(MSG_MEM));
322			return (-1);
323		}
324
325		/* initialize the device */
326		if (ds_ginit(device) < 0) {
327			(void) ds_close(0);
328			progerr(pkg_gt(ERR_UNPACK));
329			logerr(pkg_gt(MSG_OPEN), device, errno);
330			return (-1);
331		}
332
333		/* read a logical block from the source device */
334		if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) {
335			rpterr();
336			progerr(pkg_gt(ERR_UNPACK));
337			logerr(pkg_gt(MSG_TOC));
338			(void) ds_close(0);
339			return (-1);
340		}
341
342		/*
343		 * This loop scans the medium for the start of the header.
344		 * If the above read worked, we skip this. If it did't, this
345		 * loop will retry the read ten times looking for the header
346		 * marker string.
347		 */
348		while (strncmp(ds_header, HDR_PREFIX, 20) != 0) {
349			/* only ten tries iff the device rewinds */
350			if (!norewind || count++ > 10) {
351				progerr(pkg_gt(ERR_UNPACK));
352				logerr(pkg_gt(MSG_TOC));
353				(void) ds_close(0);
354				return (-1);
355			}
356
357			/* read through to the last block */
358			if (count > 1)
359				while (read(ds_fd, ds_header, BLK_SIZE) > 0)
360					;
361
362			/* then close the device */
363			(void) ds_close(0);
364
365			/* and reopen it */
366			if ((ds_fd = open(norewind, O_RDONLY)) < 0) {
367				progerr(pkg_gt(ERR_UNPACK));
368				logerr(pkg_gt(MSG_OPEN), device, errno);
369				(void) free(ds_header);
370				return (-1);
371			}
372
373			/* initialize the device */
374			if (ds_ginit(device) < 0) {
375				(void) ds_close(0);
376				progerr(pkg_gt(ERR_UNPACK));
377				logerr(pkg_gt(MSG_OPEN), device, errno);
378				return (-1);
379			}
380
381			/* read the block again */
382			if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) {
383				rpterr();
384				progerr(pkg_gt(ERR_UNPACK));
385				logerr(pkg_gt(MSG_TOC));
386				(void) ds_close(0);
387				return (-1);
388			}
389		}
390
391		/* Now keep scanning until the whole header is in place. */
392		while (strstr(ds_header, HDR_SUFFIX) == NULL) {
393			/* We need a bigger buffer */
394			if ((ds_header = (char *)realloc(ds_header,
395			    header_size + BLK_SIZE)) == NULL) {
396				progerr(pkg_gt(ERR_UNPACK));
397				logerr(pkg_gt(MSG_MEM));
398				(void) ds_close(0);
399				return (1);
400			}
401
402			/* clear the new memory */
403			(void) memset(ds_header + header_size, '\0',
404			    BLK_SIZE);
405
406
407			/* read a logical block from the source device */
408			if (read(ds_fd, ds_header + header_size, BLK_SIZE) !=
409			    BLK_SIZE) {
410				rpterr();
411				progerr(pkg_gt(ERR_UNPACK));
412				logerr(pkg_gt(MSG_TOC));
413				(void) ds_close(0);
414				return (-1);
415			} else
416				header_size += BLK_SIZE;	/* new size */
417		}
418
419		/*
420		 * remember rewind device for ds_close to rewind at
421		 * close
422		 */
423		if (count >= 1)
424			ds_device = device;
425		ds_headsize = header_size;
426
427	}
428
429	pds_header = ds_header;
430
431	/* save raw copy of header for later use in BIO_dump_header */
432	if ((ds_header_raw = (char *)malloc(header_size)) == NULL) {
433		progerr(pkg_gt(ERR_UNPACK));
434		logerr(pkg_gt(MSG_MEM));
435		(void) ds_close(0);
436		return (1);
437	}
438	memcpy(ds_header_raw, ds_header, header_size);
439
440	/* read datastream table of contents */
441	ds_head = tail = (struct dstoc *)0;
442	ds_volcnt = 1;
443
444	while (ret = ds_gets(line, LSIZE)) {
445		if (strcmp(line, HDR_SUFFIX) == 0)
446			break;
447		if (!line[0] || line[0] == '#')
448			continue;
449		toc_pt = (struct dstoc *)calloc(1, sizeof (struct dstoc));
450		if (!toc_pt) {
451			progerr(pkg_gt(ERR_UNPACK));
452			logerr(pkg_gt(MSG_MEM));
453			ecleanup();
454			(void) free(ds_header);
455			return (-1);
456		}
457		if (sscanf(line, "%s %d %d %[ 0-9]", toc_pt->pkg,
458		    &toc_pt->nparts, &toc_pt->maxsiz, toc_pt->volnos) < 3) {
459			progerr(pkg_gt(ERR_UNPACK));
460			logerr(pkg_gt(MSG_TOC));
461			free(toc_pt);
462			(void) free(ds_header);
463			ecleanup();
464			return (-1);
465		}
466		if (tail) {
467			tail->next = toc_pt;
468			tail = toc_pt;
469		} else
470			ds_head = tail = toc_pt;
471		ds_volcnt += ds_volsum(toc_pt);
472	}
473	if (!ret) {
474		progerr(pkg_gt(ERR_UNPACK));
475		logerr(pkg_gt(MSG_TOC));
476		(void) free(ds_header);
477		return (-1);
478	}
479	sighold(SIGINT);
480	sigrelse(SIGINT);
481	if (!ds_head) {
482		progerr(pkg_gt(ERR_UNPACK));
483		logerr(pkg_gt(MSG_EMPTY));
484		(void) free(ds_header);
485		return (-1);
486	}
487	/* this could break, thanks to cpio command limit */
488#ifndef SUNOS41
489	(void) sprintf(cmd, "%s -icdumD -C %d", CPIOPROC, (int)BLK_SIZE);
490#else
491	(void) sprintf(cmd, "%s -icdum -C %d", CPIOPROC, (int)BLK_SIZE);
492#endif
493	n = 0;
494	for (i = 0; pkg[i]; i++) {
495		if (strcmp(pkg[i], "all") == 0)
496			continue;
497		if (n == 0) {
498			strcat(cmd, " ");
499			n = 1;
500		}
501		strlcat(cmd, pkg[i], CMDSIZ);
502		strlcat(cmd, "'/*' ", CMDSIZ);
503
504		/* extract signature too, if present. */
505		strlcat(cmd, SIGNATURE_FILENAME, CMDSIZ);
506		strlcat(cmd, " ", CMDSIZ);
507	}
508
509	/*
510	 * if we are extracting all packages (pkgs == NULL),
511	 * signature will automatically be extracted
512	 */
513	if (n = esystem(cmd, ds_fd, -1)) {
514		rpterr();
515		progerr(pkg_gt(ERR_UNPACK));
516		logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
517		(void) free(ds_header);
518		return (-1);
519	}
520
521	ds_toc = ds_head;
522	ds_totread = 0;
523	ds_volno = 1;
524	return (0);
525}
526
527int
528ds_findpkg(char *device, char *pkg)
529{
530	char	*pkglist[2];
531	int	nskip, ods_volpart;
532
533	if (ds_head == NULL) {
534		pkglist[0] = pkg;
535		pkglist[1] = NULL;
536		if (ds_init(device, pkglist, NULL))
537			return (-1);
538	}
539
540	if (!pkg || pkgnmchk(pkg, "all", 0)) {
541		progerr(pkg_gt(ERR_UNPACK));
542		logerr(pkg_gt(MSG_PKGNAME));
543		return (-1);
544	}
545
546	nskip = 0;
547	ds_volno = 1;
548	ds_volpart = 0;
549	ds_toc = ds_head;
550	while (ds_toc) {
551		if (strcmp(ds_toc->pkg, pkg) == 0)
552			break;
553		nskip += ds_toc->nparts;
554		ds_volno += ds_volsum(ds_toc);
555		ds_toc = ds_toc->next;
556	}
557	if (!ds_toc) {
558		progerr(pkg_gt(ERR_UNPACK));
559		logerr(pkg_gt(MSG_NOPKG), pkg);
560		return (-1);
561	}
562
563	ds_pkginit();
564	ds_skippart = 0;
565	if (ds_curpartcnt > 0) {
566		ods_volpart = ds_volpart;
567		/*
568		 * skip past archives belonging to last package on current
569		 * volume
570		 */
571		if (ds_volpart > 0 && ds_getnextvol(device))
572			return (-1);
573		ds_totread = nskip - ods_volpart;
574		if (ds_skip(device, ods_volpart))
575			return (-1);
576	} else if (ds_curpartcnt < 0) {
577		if (ds_skip(device, nskip - ds_totread))
578			return (-1);
579	} else
580		ds_totread = nskip;
581	ds_read = 0;
582	return (ds_nparts);
583}
584
585/*
586 * Get datastream part
587 * Call for first part should be preceded by
588 * call to ds_findpkg
589 */
590
591int
592ds_getpkg(char *device, int n, char *dstdir)
593{
594	struct statvfs64 svfsb;
595	u_longlong_t free_blocks;
596
597	if (ds_read >= ds_nparts)
598		return (2);
599
600	if (ds_read == n)
601		return (0);
602	else if ((ds_read > n) || (n > ds_nparts))
603		return (2);
604
605	if (ds_maxsiz > 0) {
606		if (statvfs64(".", &svfsb)) {
607			progerr(pkg_gt(ERR_UNPACK));
608			logerr(pkg_gt(MSG_STATFS), errno);
609			return (-1);
610		}
611#ifdef SUNOS41
612		free_blocks = svfsb.f_bfree * howmany(svfsb.f_bsize, DEV_BSIZE);
613#else	/* !SUNOS41 */
614		free_blocks = (((long)svfsb.f_frsize > 0) ?
615			    howmany(svfsb.f_frsize, DEV_BSIZE) :
616			    howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bfree;
617#endif	/* SUNOS41 */
618		if ((ds_maxsiz + 50) > free_blocks) {
619			progerr(pkg_gt(ERR_UNPACK));
620			logerr(pkg_gt(MSG_NOSPACE), ds_maxsiz+50, free_blocks);
621			return (-1);
622		}
623	}
624	return (ds_next(device, dstdir));
625}
626
627static int
628ds_getnextvol(char *device)
629{
630	char prompt[128];
631	int n;
632
633	if (ds_close(0))
634		return (-1);
635	(void) sprintf(prompt,
636	    pkg_gt("Insert %%v %d of %d into %%p"),
637	    ds_volno, ds_volcnt);
638	if (n = getvol(device, NULL, NULL, prompt))
639		return (n);
640	if ((ds_fd = open(device, O_RDONLY)) < 0)
641		return (-1);
642	if (ds_ginit(device) < 0) {
643		(void) ds_close(0);
644		return (-1);
645	}
646	ds_volpart = 0;
647	return (0);
648}
649
650/*
651 * called by ds_findpkg to skip past archives for unwanted packages
652 * in current volume
653 */
654static int
655ds_skip(char *device, int nskip)
656{
657	char	cmd[CMDSIZ];
658	int	n, onskip = nskip;
659
660	while (nskip--) {
661		/* skip this one */
662#ifndef SUNOS41
663		(void) sprintf(cmd, "%s -ictD -C %d > /dev/null",
664#else
665		(void) sprintf(cmd, "%s -ict -C %d > /dev/null",
666#endif
667		    CPIOPROC, (int)BLK_SIZE);
668		if (n = esystem(cmd, ds_fd, -1)) {
669			rpterr();
670			progerr(pkg_gt(ERR_UNPACK));
671			logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
672			nskip = onskip;
673			if (ds_volno == 1 || ds_volpart > 0)
674				return (n);
675			if (n = ds_getnextvol(device))
676				return (n);
677		}
678	}
679	ds_totread += onskip;
680	ds_volpart = onskip;
681	ds_skippart = onskip;
682	return (0);
683}
684
685/* skip to end of package if necessary */
686void
687ds_skiptoend(char *device)
688{
689	if (ds_read < ds_nparts && ds_curpartcnt < 0)
690		(void) ds_skip(device, ds_nparts - ds_read);
691}
692
693int
694ds_next(char *device, char *instdir)
695{
696	char	cmd[CMDSIZ], tmpvol[128];
697	int	nparts, n, index;
698
699	/*CONSTCOND*/
700	while (1) {
701		if (ds_read + 1 > ds_curpartcnt && ds_curpartcnt >= 0) {
702			ds_volno++;
703			if (n = ds_getnextvol(device))
704				return (n);
705			(void) sscanf(ds_volnos, "%d %[ 0-9]", &index, tmpvol);
706			(void) strcpy(ds_volnos, tmpvol);
707			ds_curpartcnt += index;
708		}
709#ifndef SUNOS41
710		(void) sprintf(cmd, "%s -icdumD -C %d",
711#else
712		(void) sprintf(cmd, "%s -icdum -C %d",
713#endif
714		    CPIOPROC, (int)BLK_SIZE);
715		if (n = esystem(cmd, ds_fd, -1)) {
716			rpterr();
717			progerr(pkg_gt(ERR_UNPACK));
718			logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
719		}
720		if (ds_read == 0)
721			nparts = 0;
722		else
723			nparts = ds_toc->nparts;
724		if (n || (n = ckvolseq(instdir, ds_read + 1, nparts))) {
725			if (ds_volno == 1 || ds_volpart > ds_skippart)
726				return (-1);
727
728			if (n = ds_getnextvol(device))
729				return (n);
730			continue;
731		}
732		ds_read++;
733		ds_totread++;
734		ds_volpart++;
735
736		return (0);
737	}
738	/*NOTREACHED*/
739}
740
741/*
742 * Name:		BIO_ds_dump
743 * Description:	Dumps all data from the static 'ds_fd' file handle into
744 *		the supplied BIO.
745 *
746 * Arguments:	err - where to record any errors.
747 *		device - Description of device being dumped into,
748 *			for error reporting
749 *		bio - BIO object to dump data into
750 *
751 * Returns :	zero - successfully dumped all data to EOF
752 *		non-zero - some failure occurred.
753 */
754int
755BIO_ds_dump(PKG_ERR *err, char *device, BIO *bio)
756{
757	int	amtread;
758	char	readbuf[BLK_SIZE];
759
760	/*
761	 * note this will read to the end of the device, so it won't
762	 * work for character devices since we don't know when the
763	 * end of the CPIO archive is
764	 */
765	while ((amtread = read(ds_fd, readbuf, BLK_SIZE)) != 0) {
766		if (BIO_write(bio, readbuf, amtread) != amtread) {
767			pkgerr_add(err, PKGERR_WRITE, ERR_WRITE, device,
768			    ERR_error_string(ERR_get_error(), NULL));
769			return (1);
770		}
771	}
772
773	return (0);
774	/*NOTREACHED*/
775}
776
777
778/*
779 * Name:		BIO_ds_dump_header
780 * Description:	Dumps all ds_headsize bytes from the
781 *		static 'ds_header_raw' character array
782 *		to the supplied BIO.
783 *
784 * Arguments:	err - where to record any errors.
785 *		bio - BIO object to dump data into
786 *
787 * Returns :	zero - successfully dumped all raw
788 *		header characters
789 *		non-zero - some failure occurred.
790 */
791int
792BIO_ds_dump_header(PKG_ERR *err, BIO *bio)
793{
794
795	char	zeros[BLK_SIZE];
796
797	memset(zeros, 0, BLK_SIZE);
798
799	if (BIO_write(bio, ds_header_raw, ds_headsize) != ds_headsize) {
800		pkgerr_add(err, PKGERR_WRITE, ERR_WRITE, "bio",
801		    ERR_error_string(ERR_get_error(), NULL));
802		return (1);
803	}
804
805	return (0);
806}
807
808/*
809 * ds_ginit: Determine the device being accessed, set the buffer size,
810 * and perform any device specific initialization.  For the 3B2,
811 * a device with major number of 17 (0x11) is an internal hard disk,
812 * unless the minor number is 128 (0x80) in which case it is an internal
813 * floppy disk.  Otherwise, get the system configuration
814 * table and check it by comparing slot numbers to major numbers.
815 * For the special case of the 3B2 CTC several unusual things must be done.
816 * To enable
817 * streaming mode on the CTC, the file descriptor must be closed, re-opened
818 * (with O_RDWR and O_CTSPECIAL flags set), the STREAMON ioctl(2) command
819 * issued, and the file descriptor re-re-opened either read-only or write_only.
820 */
821
822int
823ds_ginit(char *device)
824{
825#ifdef u3b2
826	major_t maj;
827	minor_t min;
828	int nflag, i, count, size;
829	struct s3bconf *buffer;
830	struct s3bc *table;
831	struct stat st_buf;
832	int devtype;
833	char buf[BLK_SIZE];
834	int fd2, fd;
835#endif	/* u3b2 */
836	int oflag;
837	char *pbufsize, cmd[CMDSIZ];
838	int fd2, fd;
839
840	if ((pbufsize = devattr(device, "bufsize")) != NULL) {
841		ds_bufsize = atoi(pbufsize);
842		(void) free(pbufsize);
843	} else
844		ds_bufsize = BLK_SIZE;
845	oflag = fcntl(ds_fd, F_GETFL, 0);
846#ifdef u3b2
847	devtype = G_NO_DEV;
848	if (fstat(ds_fd, &st_buf) == -1)
849		return (-1);
850	if (!S_ISCHR(st_buf.st_mode) && !S_ISBLK(st_buf.st_mode))
851		goto lab;
852
853	/*
854	 * We'll have to add a remote attribute to stat but this should
855	 * work for now.
856	 */
857	else if (st_buf.st_dev & 0x8000)	/* if remote  rdev */
858		goto lab;
859
860	maj = major(st_buf.st_rdev);
861	min = minor(st_buf.st_rdev);
862	if (maj == 0x11) { /* internal hard or floppy disk */
863		if (min & 0x80)
864			devtype = G_3B2_FD; /* internal floppy disk */
865		else
866			devtype = G_3B2_HD; /* internal hard disk */
867	} else {
868		if (sys3b(S3BCONF, (struct s3bconf *)&count, sizeof (count)) ==
869		    -1)
870			return (-1);
871		size = sizeof (int) + (count * sizeof (struct s3bconf));
872		buffer = (struct s3bconf *)malloc((unsigned)size);
873		if (sys3b(S3BCONF, buffer, size) == -1)
874			return (-1);
875		table = (struct s3bc *)((char *)buffer + sizeof (int));
876		for (i = 0; i < count; i++) {
877			if (maj == (int)table->board) {
878				if (strncmp(table->name, "CTC", 3) == 0) {
879					devtype = G_3B2_CTC;
880					break;
881				} else if (strncmp(table->name, "TAPE", 4)
882						== 0) {
883					devtype = G_TAPE;
884					break;
885				}
886				/* other possible devices can go here */
887			}
888			table++;
889		}
890	}
891	switch (devtype) {
892		case G_3B2_CTC:	/* do special CTC initialization */
893			ds_bufsize = pbufsize ? ds_bufsize : 15872;
894			if (fstat(ds_fd, &orig_st_buf) < 0) {
895				ds_bufsize = -1;
896				break;
897			}
898			nflag = (O_RDWR | O_CTSPECIAL);
899			(void) close(ds_fd);
900			if ((ds_fd = open(device, nflag, 0666)) != -1) {
901				if (ioctl(ds_fd, STREAMON) != -1) {
902					(void) close(ds_fd);
903					nflag = (oflag == O_WRONLY) ?
904					    O_WRONLY : O_RDONLY;
905					if ((ds_fd =
906					    open(device, nflag, 0666)) == -1) {
907						rpterr();
908						progerr(
909						    pkg_gt(ERR_TRANSFER));
910						logerr(pkg_gt(MSG_OPEN),
911						    device, errno);
912						return (-1);
913					}
914					ds_bufsize = 15872;
915				}
916			} else
917				ds_bufsize = -1;
918			if (oflag == O_RDONLY && ds_header && ds_totread == 0)
919				/* Have already read in first block of header */
920				read(ds_fd, buf, BLK_SIZE);
921			ds_ctcflg = 1;
922
923			break;
924		case G_NO_DEV:
925		case G_3B2_HD:
926		case G_3B2_FD:
927		case G_TAPE:
928		case G_SCSI_HD: /* not developed yet */
929		case G_SCSI_FD:
930		case G_SCSI_9T:
931		case G_SCSI_Q24:
932		case G_SCSI_Q120:
933		case G_386_HD:
934		case G_386_FD:
935		case G_386_Q24:
936			ds_bufsize = pbufsize ? ds_bufsize : BLK_SIZE;
937			break;
938		default:
939			ds_bufsize = -1;
940			errno = ENODEV;
941	} /* devtype */
942lab:
943#endif	/* u3b2 */
944	if (ds_bufsize > BLK_SIZE) {
945		if (oflag & O_WRONLY)
946			fd = 1;
947		else
948			fd = 0;
949		fd2 = fcntl(fd, F_DUPFD, fd);
950		(void) close(fd);
951		fcntl(ds_fd, F_DUPFD, fd);
952		if (fd)
953			sprintf(cmd, "%s obs=%d 2>/dev/null", DDPROC,
954			    ds_bufsize);
955		else
956			sprintf(cmd, "%s ibs=%d 2>/dev/null", DDPROC,
957			    ds_bufsize);
958		if ((ds_pp = popen(cmd, fd ? "w" : "r")) == NULL) {
959			progerr(pkg_gt(ERR_TRANSFER));
960			logerr(pkg_gt(MSG_POPEN), cmd, errno);
961			return (-1);
962		}
963		(void) close(fd);
964		fcntl(fd2, F_DUPFD, fd);
965		(void) close(fd2);
966		ds_realfd = ds_fd;
967		ds_fd = fileno(ds_pp);
968	}
969	return (ds_bufsize);
970}
971
972int
973ds_close(int pkgendflg)
974{
975#ifdef u3b2
976	int cnt, mode;
977	char *ptr;
978	struct stat statbuf;
979#endif	/* u3b2 */
980	int n, ret = 0;
981
982#ifdef u3b2
983	if (ds_pp && ds_ctcflg) {
984		ds_ctcflg = 0;
985		if ((mode = fcntl(ds_realfd, F_GETFL, 0)) < 0) {
986			ret = -1;
987		} else if (mode & O_WRONLY) {
988		/*
989		 * pipe to dd write process,
990		 * make sure one more buffer
991		 * gets written out
992		 */
993			if ((ptr = calloc(BLK_SIZE, 1)) == NULL) {
994				ret = -1;
995			/* pad to bufsize */
996			} else {
997				cnt = ds_bufsize;
998				while (cnt > 0) {
999					if ((n = write(ds_fd, ptr,
1000					    BLK_SIZE)) < 0) {
1001						ret = -1;
1002						break;
1003					}
1004					cnt -= n;
1005				}
1006				(void) free(ptr);
1007			}
1008		}
1009	}
1010#endif
1011	if (pkgendflg) {
1012		if (ds_header)
1013			(void) free(ds_header);
1014		ds_header = (char *)NULL;
1015		ds_totread = 0;
1016	}
1017
1018	if (ds_pp) {
1019		(void) pclose(ds_pp);
1020		ds_pp = 0;
1021		(void) close(ds_realfd);
1022		ds_realfd = -1;
1023		ds_fd = -1;
1024	} else if (ds_fd >= 0) {
1025		(void) close(ds_fd);
1026		ds_fd = -1;
1027	}
1028
1029	if (ds_device) {
1030		/* rewind device */
1031		if ((n = open(ds_device, 0)) >= 0)
1032			(void) close(n);
1033		ds_device = NULL;
1034	}
1035	return (ret);
1036}
1037