xref: /illumos-gate/usr/src/lib/libpkg/common/dstream.c (revision c85864d8472aaccb47ceb468ebd9b3a85b66d161)
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
53 static
54 struct stat	orig_st_buf; /* Stat structure of original file (3B2/CTC) */
55 static char	ds_ctcflg;
56 #endif	/* u3b2 */
57 
58 /* libadm.a */
59 extern char	*devattr(char *device, char *attribute);
60 extern int	pkgnmchk(register char *pkg, register char *spec,
61 				int presvr4flg);
62 extern 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 
87 struct 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 
99 int	ds_totread; 	/* total number of parts read */
100 int	ds_fd = -1;
101 int	ds_curpartcnt = -1;
102 
103 int	ds_next(char *device, char *instdir);
104 int	ds_ginit(char *device);
105 int	ds_close(int pkgendflg);
106 
107 static FILE	*ds_pp;
108 static int	ds_realfd = -1; 	/* file descriptor for real device */
109 static int	ds_read; 	/* number of parts read for current package */
110 static int	ds_volno; 	/* volume number of current volume */
111 static int	ds_volcnt; 	/* total number of volumes */
112 static char	ds_volnos[128]; 	/* parts/volume info */
113 static char	*ds_device;
114 static int	ds_volpart;	/* number of parts read in current volume, */
115 						/* including skipped parts */
116 static int	ds_bufsize;
117 static int	ds_skippart; 	/* number of parts skipped in current volume */
118 
119 static int	ds_getnextvol(char *device);
120 static int	ds_skip(char *device, int nskip);
121 
122 void
123 ds_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 
144 static char *pds_header;
145 static char *ds_header;
146 static char *ds_header_raw;
147 static int ds_headsize;
148 
149 static char *
150 ds_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  */
190 int
191 ds_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  */
228 static int
229 ds_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 */
253 static void
254 ds_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  */
265 void
266 ds_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 
274 int
275 ds_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  */
289 boolean_t
290 ds_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  */
298 int
299 ds_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 
527 int
528 ds_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 
591 int
592 ds_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 
627 static int
628 ds_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  */
654 static int
655 ds_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 */
686 void
687 ds_skiptoend(char *device)
688 {
689 	if (ds_read < ds_nparts && ds_curpartcnt < 0)
690 		(void) ds_skip(device, ds_nparts - ds_read);
691 }
692 
693 int
694 ds_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  */
754 int
755 BIO_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  */
791 int
792 BIO_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 
822 int
823 ds_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 */
942 lab:
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 
972 int
973 ds_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