xref: /illumos-gate/usr/src/lib/libpkg/common/dstream.c (revision e4f7ccbe1e17940fdbde623116bcc8cd761d9ffc)
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 (c) 2017 Peter Tribble.
24  */
25 
26 /*
27  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
32 /* All Rights Reserved */
33 
34 
35 
36 #include <stdio.h>
37 #include <string.h>
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/sysmacros.h>
44 #include <errno.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/statvfs.h>
48 #include <fcntl.h>
49 #include "pkglib.h"
50 #include "pkglibmsgs.h"
51 #include "pkglocale.h"
52 
53 /* libadm.a */
54 extern char	*devattr(char *device, char *attribute);
55 extern int	pkgnmchk(register char *pkg, register char *spec,
56 				int presvr4flg);
57 extern int	getvol(char *device, char *label, int options, char *prompt);
58 
59 #define	CMDSIZ	512
60 #define	LSIZE	128
61 #define	DDPROC		"/usr/bin/dd"
62 #define	CPIOPROC	"/usr/bin/cpio"
63 
64 /* device types */
65 
66 #define	G_TM_TAPE	1   /* Tapemaster controller */
67 #define	G_XY_DISK	3   /* xy disks */
68 #define	G_SD_DISK	7   /* scsi sd disk */
69 #define	G_XT_TAPE	8   /* xt tapes */
70 #define	G_SF_FLOPPY	9   /* sf floppy */
71 #define	G_XD_DISK	10  /* xd disks */
72 #define	G_ST_TAPE	11  /* scsi tape */
73 #define	G_NS		12  /* noswap pseudo-dev */
74 #define	G_RAM		13  /* ram pseudo-dev */
75 #define	G_FT		14  /* tftp */
76 #define	G_HD		15  /* 386 network disk */
77 #define	G_FD		16  /* 386 AT disk */
78 #define	G_FILE		28  /* file, not a device */
79 #define	G_NO_DEV	29  /* device does not require special treatment */
80 #define	G_DEV_MAX	30  /* last valid device type */
81 
82 struct dstoc {
83 	int	cnt;
84 	char	pkg[NON_ABI_NAMELNGTH];
85 	int	nparts;
86 	long	maxsiz;
87 	char    volnos[128];
88 	struct dstoc *next;
89 } *ds_head, *ds_toc;
90 
91 #define	ds_nparts	ds_toc->nparts
92 #define	ds_maxsiz	ds_toc->maxsiz
93 
94 int	ds_totread;	/* total number of parts read */
95 int	ds_fd = -1;
96 int	ds_curpartcnt = -1;
97 
98 int	ds_next(char *device, char *instdir);
99 int	ds_ginit(char *device);
100 int	ds_close(int pkgendflg);
101 
102 static FILE	*ds_pp;
103 static int	ds_realfd = -1;	/* file descriptor for real device */
104 static int	ds_read;	/* number of parts read for current package */
105 static int	ds_volno;	/* volume number of current volume */
106 static int	ds_volcnt;	/* total number of volumes */
107 static char	ds_volnos[128];	/* parts/volume info */
108 static char	*ds_device;
109 static int	ds_volpart;	/* number of parts read in current volume, */
110 						/* including skipped parts */
111 static int	ds_bufsize;
112 static int	ds_skippart;	/* number of parts skipped in current volume */
113 
114 static int	ds_getnextvol(char *device);
115 static int	ds_skip(char *device, int nskip);
116 
117 void
118 ds_order(char *list[])
119 {
120 	struct dstoc *toc_pt;
121 	register int j, n;
122 	char	*pt;
123 
124 	toc_pt = ds_head;
125 	n = 0;
126 	while (toc_pt) {
127 		for (j = n; list[j]; j++) {
128 			if (strcmp(list[j], toc_pt->pkg) == 0) {
129 				/* just swap places in the array */
130 				pt = list[n];
131 				list[n++] = list[j];
132 				list[j] = pt;
133 			}
134 		}
135 		toc_pt = toc_pt->next;
136 	}
137 }
138 
139 static char *pds_header;
140 static char *ds_header;
141 static int ds_headsize;
142 
143 static char *
144 ds_gets(char *buf, int size)
145 {
146 	int length;
147 	char *nextp;
148 
149 	nextp = strchr(pds_header, '\n');
150 	if (nextp == NULL) {
151 		length = strlen(pds_header);
152 		if (length > size)
153 			return (0);
154 		if ((ds_header = (char *)realloc(ds_header,
155 		    ds_headsize + BLK_SIZE)) == NULL)
156 			return (0);
157 		if (read(ds_fd, ds_header + ds_headsize, BLK_SIZE) < BLK_SIZE)
158 			return (0);
159 		ds_headsize += BLK_SIZE;
160 		nextp = strchr(pds_header, '\n');
161 		if (nextp == NULL)
162 			return (0);
163 		*nextp = '\0';
164 		if (length + (int)strlen(pds_header) > size)
165 			return (0);
166 		(void) strncpy(buf + length, pds_header, strlen(pds_header));
167 		buf[length + strlen(pds_header)] = '\0';
168 		pds_header = nextp + 1;
169 		return (buf);
170 	}
171 	*nextp = '\0';
172 	if ((int)strlen(pds_header) > size)
173 		return (0);
174 	(void) strncpy(buf, pds_header, strlen(pds_header));
175 	buf[strlen(pds_header)] = '\0';
176 	pds_header = nextp + 1;
177 	return (buf);
178 }
179 
180 /*
181  * function to determine if media is datastream or mounted
182  * floppy
183  */
184 int
185 ds_readbuf(char *device)
186 {
187 	char buf[BLK_SIZE];
188 
189 	if (ds_fd >= 0)
190 		(void) close(ds_fd);
191 	if ((ds_fd = open(device, O_RDONLY)) >= 0 &&
192 	    read(ds_fd, buf, BLK_SIZE) == BLK_SIZE &&
193 	    strncmp(buf, HDR_PREFIX, 20) == 0) {
194 		if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) {
195 			progerr(pkg_gt(ERR_UNPACK));
196 			logerr(pkg_gt(MSG_MEM));
197 			(void) ds_close(0);
198 			return (0);
199 		}
200 		(void) memcpy(ds_header, buf, BLK_SIZE);
201 		ds_headsize = BLK_SIZE;
202 
203 		if (ds_ginit(device) < 0) {
204 			progerr(pkg_gt(ERR_UNPACK));
205 			logerr(pkg_gt(MSG_OPEN), device, errno);
206 			(void) ds_close(0);
207 			return (0);
208 		}
209 		return (1);
210 	} else if (ds_fd >= 0) {
211 		(void) close(ds_fd);
212 		ds_fd = -1;
213 	}
214 	return (0);
215 }
216 
217 /*
218  * Determine how many additional volumes are needed for current package.
219  * Note: a 0 will occur as first volume number when the package begins
220  * on the next volume.
221  */
222 static int
223 ds_volsum(struct dstoc *toc)
224 {
225 	int curpartcnt, volcnt;
226 	char volnos[128], tmpvol[128];
227 	if (toc->volnos[0]) {
228 		int index, sum;
229 		(void) sscanf(toc->volnos, "%d %[ 0-9]", &curpartcnt, volnos);
230 		volcnt = 0;
231 		sum = curpartcnt;
232 		while (sum < toc->nparts && sscanf(volnos, "%d %[ 0-9]",
233 		    &index, tmpvol) >= 1) {
234 			(void) strcpy(volnos, tmpvol);
235 			volcnt++;
236 			sum += index;
237 		}
238 		/* side effect - set number of parts read on current volume */
239 		ds_volpart = index;
240 		return (volcnt);
241 	}
242 	ds_volpart += toc->nparts;
243 	return (0);
244 }
245 
246 /* initialize ds_curpartcnt and ds_volnos */
247 static void
248 ds_pkginit(void)
249 {
250 	if (ds_toc->volnos[0])
251 		(void) sscanf(ds_toc->volnos, "%d %[ 0-9]", &ds_curpartcnt,
252 		    ds_volnos);
253 	else
254 		ds_curpartcnt = -1;
255 }
256 
257 /*
258  * functions to pass current package info to exec'ed program
259  */
260 void
261 ds_putinfo(char *buf, size_t sz)
262 {
263 	(void) snprintf(buf, sz, "%d %d %d %d %d %d %d %d %d %d %s",
264 	    ds_fd, ds_realfd, ds_volcnt, ds_volno, ds_totread, ds_volpart,
265 	    ds_skippart, ds_bufsize, ds_toc->nparts, ds_toc->maxsiz,
266 	    ds_toc->volnos);
267 }
268 
269 int
270 ds_getinfo(char *string)
271 {
272 	ds_toc = (struct dstoc *)calloc(1, sizeof (struct dstoc));
273 	(void) sscanf(string, "%d %d %d %d %d %d %d %d %d %d %[ 0-9]",
274 	    &ds_fd, &ds_realfd, &ds_volcnt, &ds_volno, &ds_totread,
275 	    &ds_volpart, &ds_skippart, &ds_bufsize, &ds_toc->nparts,
276 	    &ds_toc->maxsiz, ds_toc->volnos);
277 	ds_pkginit();
278 	return (ds_toc->nparts);
279 }
280 
281 /*
282  * Return true if the file descriptor (ds_fd) is open on the package stream.
283  */
284 boolean_t
285 ds_fd_open(void)
286 {
287 	return (ds_fd >= 0 ? B_TRUE : B_FALSE);
288 }
289 
290 /*
291  * Read the source device. Acquire the header data and check it for validity.
292  */
293 int
294 ds_init(char *device, char **pkg, char *norewind)
295 {
296 	struct dstoc *tail, *toc_pt;
297 	char	*ret;
298 	char	cmd[CMDSIZ];
299 	char	line[LSIZE+1];
300 	int	i, n, count = 0, header_size = BLK_SIZE;
301 
302 	if (!ds_header) {	/* If the header hasn't been read yet */
303 		if (ds_fd >= 0)
304 			(void) ds_close(0);
305 
306 		/* always start with rewind device */
307 		if ((ds_fd = open(device, O_RDONLY)) < 0) {
308 			progerr(pkg_gt(ERR_UNPACK));
309 			logerr(pkg_gt(MSG_OPEN), device, errno);
310 			return (-1);
311 		}
312 
313 		/* allocate room for the header equivalent to a block */
314 		if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) {
315 			progerr(pkg_gt(ERR_UNPACK));
316 			logerr(pkg_gt(MSG_MEM));
317 			return (-1);
318 		}
319 
320 		/* initialize the device */
321 		if (ds_ginit(device) < 0) {
322 			(void) ds_close(0);
323 			progerr(pkg_gt(ERR_UNPACK));
324 			logerr(pkg_gt(MSG_OPEN), device, errno);
325 			return (-1);
326 		}
327 
328 		/* read a logical block from the source device */
329 		if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) {
330 			rpterr();
331 			progerr(pkg_gt(ERR_UNPACK));
332 			logerr(pkg_gt(MSG_TOC));
333 			(void) ds_close(0);
334 			return (-1);
335 		}
336 
337 		/*
338 		 * This loop scans the medium for the start of the header.
339 		 * If the above read worked, we skip this. If it did't, this
340 		 * loop will retry the read ten times looking for the header
341 		 * marker string.
342 		 */
343 		while (strncmp(ds_header, HDR_PREFIX, 20) != 0) {
344 			/* only ten tries iff the device rewinds */
345 			if (!norewind || count++ > 10) {
346 				progerr(pkg_gt(ERR_UNPACK));
347 				logerr(pkg_gt(MSG_TOC));
348 				(void) ds_close(0);
349 				return (-1);
350 			}
351 
352 			/* read through to the last block */
353 			if (count > 1)
354 				while (read(ds_fd, ds_header, BLK_SIZE) > 0)
355 					;
356 
357 			/* then close the device */
358 			(void) ds_close(0);
359 
360 			/* and reopen it */
361 			if ((ds_fd = open(norewind, O_RDONLY)) < 0) {
362 				progerr(pkg_gt(ERR_UNPACK));
363 				logerr(pkg_gt(MSG_OPEN), device, errno);
364 				(void) free(ds_header);
365 				return (-1);
366 			}
367 
368 			/* initialize the device */
369 			if (ds_ginit(device) < 0) {
370 				(void) ds_close(0);
371 				progerr(pkg_gt(ERR_UNPACK));
372 				logerr(pkg_gt(MSG_OPEN), device, errno);
373 				return (-1);
374 			}
375 
376 			/* read the block again */
377 			if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) {
378 				rpterr();
379 				progerr(pkg_gt(ERR_UNPACK));
380 				logerr(pkg_gt(MSG_TOC));
381 				(void) ds_close(0);
382 				return (-1);
383 			}
384 		}
385 
386 		/* Now keep scanning until the whole header is in place. */
387 		while (strstr(ds_header, HDR_SUFFIX) == NULL) {
388 			/* We need a bigger buffer */
389 			if ((ds_header = (char *)realloc(ds_header,
390 			    header_size + BLK_SIZE)) == NULL) {
391 				progerr(pkg_gt(ERR_UNPACK));
392 				logerr(pkg_gt(MSG_MEM));
393 				(void) ds_close(0);
394 				return (1);
395 			}
396 
397 			/* clear the new memory */
398 			(void) memset(ds_header + header_size, '\0',
399 			    BLK_SIZE);
400 
401 
402 			/* read a logical block from the source device */
403 			if (read(ds_fd, ds_header + header_size, BLK_SIZE) !=
404 			    BLK_SIZE) {
405 				rpterr();
406 				progerr(pkg_gt(ERR_UNPACK));
407 				logerr(pkg_gt(MSG_TOC));
408 				(void) ds_close(0);
409 				return (-1);
410 			} else
411 				header_size += BLK_SIZE;	/* new size */
412 		}
413 
414 		/*
415 		 * remember rewind device for ds_close to rewind at
416 		 * close
417 		 */
418 		if (count >= 1)
419 			ds_device = device;
420 		ds_headsize = header_size;
421 
422 	}
423 
424 	pds_header = ds_header;
425 
426 	/* read datastream table of contents */
427 	ds_head = tail = (struct dstoc *)0;
428 	ds_volcnt = 1;
429 
430 	while (ret = ds_gets(line, LSIZE)) {
431 		if (strcmp(line, HDR_SUFFIX) == 0)
432 			break;
433 		if (!line[0] || line[0] == '#')
434 			continue;
435 		toc_pt = (struct dstoc *)calloc(1, sizeof (struct dstoc));
436 		if (!toc_pt) {
437 			progerr(pkg_gt(ERR_UNPACK));
438 			logerr(pkg_gt(MSG_MEM));
439 			ecleanup();
440 			(void) free(ds_header);
441 			return (-1);
442 		}
443 		/* LINTED E_SEC_SCANF_UNBOUNDED_COPY */
444 		if (sscanf(line, "%s %d %d %[ 0-9]", toc_pt->pkg,
445 		    &toc_pt->nparts, &toc_pt->maxsiz, toc_pt->volnos) < 3) {
446 			progerr(pkg_gt(ERR_UNPACK));
447 			logerr(pkg_gt(MSG_TOC));
448 			free(toc_pt);
449 			(void) free(ds_header);
450 			ecleanup();
451 			return (-1);
452 		}
453 		if (tail) {
454 			tail->next = toc_pt;
455 			tail = toc_pt;
456 		} else
457 			ds_head = tail = toc_pt;
458 		ds_volcnt += ds_volsum(toc_pt);
459 	}
460 	if (!ret) {
461 		progerr(pkg_gt(ERR_UNPACK));
462 		logerr(pkg_gt(MSG_TOC));
463 		(void) free(ds_header);
464 		return (-1);
465 	}
466 	(void) sighold(SIGINT);
467 	(void) sigrelse(SIGINT);
468 	if (!ds_head) {
469 		progerr(pkg_gt(ERR_UNPACK));
470 		logerr(pkg_gt(MSG_EMPTY));
471 		(void) free(ds_header);
472 		return (-1);
473 	}
474 	/* this could break, thanks to cpio command limit */
475 	(void) snprintf(cmd, sizeof (cmd), "%s -icdumD -C %d",
476 	    CPIOPROC, (int)BLK_SIZE);
477 	n = 0;
478 	for (i = 0; pkg[i]; i++) {
479 		if (strcmp(pkg[i], "all") == 0)
480 			continue;
481 		if (n == 0) {
482 			(void) strlcat(cmd, " ", CMDSIZ);
483 			n = 1;
484 		}
485 		(void) strlcat(cmd, pkg[i], CMDSIZ);
486 		(void) strlcat(cmd, "'/*' ", CMDSIZ);
487 		(void) strlcat(cmd, " ", CMDSIZ);
488 	}
489 
490 	if (n = esystem(cmd, ds_fd, -1)) {
491 		rpterr();
492 		progerr(pkg_gt(ERR_UNPACK));
493 		logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
494 		(void) free(ds_header);
495 		return (-1);
496 	}
497 
498 	ds_toc = ds_head;
499 	ds_totread = 0;
500 	ds_volno = 1;
501 	return (0);
502 }
503 
504 int
505 ds_findpkg(char *device, char *pkg)
506 {
507 	char	*pkglist[2];
508 	int	nskip, ods_volpart;
509 
510 	if (ds_head == NULL) {
511 		pkglist[0] = pkg;
512 		pkglist[1] = NULL;
513 		if (ds_init(device, pkglist, NULL))
514 			return (-1);
515 	}
516 
517 	if (!pkg || pkgnmchk(pkg, "all", 0)) {
518 		progerr(pkg_gt(ERR_UNPACK));
519 		logerr(pkg_gt(MSG_PKGNAME));
520 		return (-1);
521 	}
522 
523 	nskip = 0;
524 	ds_volno = 1;
525 	ds_volpart = 0;
526 	ds_toc = ds_head;
527 	while (ds_toc) {
528 		if (strcmp(ds_toc->pkg, pkg) == 0)
529 			break;
530 		nskip += ds_toc->nparts;
531 		ds_volno += ds_volsum(ds_toc);
532 		ds_toc = ds_toc->next;
533 	}
534 	if (!ds_toc) {
535 		progerr(pkg_gt(ERR_UNPACK));
536 		logerr(pkg_gt(MSG_NOPKG), pkg);
537 		return (-1);
538 	}
539 
540 	ds_pkginit();
541 	ds_skippart = 0;
542 	if (ds_curpartcnt > 0) {
543 		ods_volpart = ds_volpart;
544 		/*
545 		 * skip past archives belonging to last package on current
546 		 * volume
547 		 */
548 		if (ds_volpart > 0 && ds_getnextvol(device))
549 			return (-1);
550 		ds_totread = nskip - ods_volpart;
551 		if (ds_skip(device, ods_volpart))
552 			return (-1);
553 	} else if (ds_curpartcnt < 0) {
554 		if (ds_skip(device, nskip - ds_totread))
555 			return (-1);
556 	} else
557 		ds_totread = nskip;
558 	ds_read = 0;
559 	return (ds_nparts);
560 }
561 
562 /*
563  * Get datastream part
564  * Call for first part should be preceded by
565  * call to ds_findpkg
566  */
567 
568 int
569 ds_getpkg(char *device, int n, char *dstdir)
570 {
571 	struct statvfs64 svfsb;
572 	u_longlong_t free_blocks;
573 
574 	if (ds_read >= ds_nparts)
575 		return (2);
576 
577 	if (ds_read == n)
578 		return (0);
579 	else if ((ds_read > n) || (n > ds_nparts))
580 		return (2);
581 
582 	if (ds_maxsiz > 0) {
583 		if (statvfs64(".", &svfsb)) {
584 			progerr(pkg_gt(ERR_UNPACK));
585 			logerr(pkg_gt(MSG_STATFS), errno);
586 			return (-1);
587 		}
588 		free_blocks = (((long)svfsb.f_frsize > 0) ?
589 		    howmany(svfsb.f_frsize, DEV_BSIZE) :
590 		    howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bfree;
591 		if ((ds_maxsiz + 50) > free_blocks) {
592 			progerr(pkg_gt(ERR_UNPACK));
593 			logerr(pkg_gt(MSG_NOSPACE), ds_maxsiz+50, free_blocks);
594 			return (-1);
595 		}
596 	}
597 	return (ds_next(device, dstdir));
598 }
599 
600 static int
601 ds_getnextvol(char *device)
602 {
603 	char prompt[128];
604 	int n;
605 
606 	if (ds_close(0))
607 		return (-1);
608 	(void) sprintf(prompt,
609 	    pkg_gt("Insert %%v %d of %d into %%p"),
610 	    ds_volno, ds_volcnt);
611 	if (n = getvol(device, NULL, 0, prompt))
612 		return (n);
613 	if ((ds_fd = open(device, O_RDONLY)) < 0)
614 		return (-1);
615 	if (ds_ginit(device) < 0) {
616 		(void) ds_close(0);
617 		return (-1);
618 	}
619 	ds_volpart = 0;
620 	return (0);
621 }
622 
623 /*
624  * called by ds_findpkg to skip past archives for unwanted packages
625  * in current volume
626  */
627 static int
628 ds_skip(char *device, int nskip)
629 {
630 	char	cmd[CMDSIZ];
631 	int	n, onskip = nskip;
632 
633 	while (nskip--) {
634 		/* skip this one */
635 		(void) snprintf(cmd, sizeof (cmd),
636 		    "%s -ictD -C %d > /dev/null", CPIOPROC, (int)BLK_SIZE);
637 		if (n = esystem(cmd, ds_fd, -1)) {
638 			rpterr();
639 			progerr(pkg_gt(ERR_UNPACK));
640 			logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
641 			nskip = onskip;
642 			if (ds_volno == 1 || ds_volpart > 0)
643 				return (n);
644 			if (n = ds_getnextvol(device))
645 				return (n);
646 		}
647 	}
648 	ds_totread += onskip;
649 	ds_volpart = onskip;
650 	ds_skippart = onskip;
651 	return (0);
652 }
653 
654 /* skip to end of package if necessary */
655 void
656 ds_skiptoend(char *device)
657 {
658 	if (ds_read < ds_nparts && ds_curpartcnt < 0)
659 		(void) ds_skip(device, ds_nparts - ds_read);
660 }
661 
662 int
663 ds_next(char *device, char *instdir)
664 {
665 	char	cmd[CMDSIZ], tmpvol[128];
666 	int	nparts, n, index;
667 
668 	/*CONSTCOND*/
669 	while (1) {
670 		if (ds_read + 1 > ds_curpartcnt && ds_curpartcnt >= 0) {
671 			ds_volno++;
672 			if (n = ds_getnextvol(device))
673 				return (n);
674 			(void) sscanf(ds_volnos, "%d %[ 0-9]", &index, tmpvol);
675 			(void) strcpy(ds_volnos, tmpvol);
676 			ds_curpartcnt += index;
677 		}
678 		(void) snprintf(cmd, sizeof (cmd), "%s -icdumD -C %d",
679 		    CPIOPROC, (int)BLK_SIZE);
680 		if (n = esystem(cmd, ds_fd, -1)) {
681 			rpterr();
682 			progerr(pkg_gt(ERR_UNPACK));
683 			logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
684 		}
685 		if (ds_read == 0)
686 			nparts = 0;
687 		else
688 			nparts = ds_toc->nparts;
689 		if (n || (n = ckvolseq(instdir, ds_read + 1, nparts))) {
690 			if (ds_volno == 1 || ds_volpart > ds_skippart)
691 				return (-1);
692 
693 			if (n = ds_getnextvol(device))
694 				return (n);
695 			continue;
696 		}
697 		ds_read++;
698 		ds_totread++;
699 		ds_volpart++;
700 
701 		return (0);
702 	}
703 	/*NOTREACHED*/
704 }
705 
706 /*
707  * ds_ginit: Determine the device being accessed, set the buffer size,
708  * and perform any device specific initialization.
709  */
710 
711 int
712 ds_ginit(char *device)
713 {
714 	int oflag;
715 	char *pbufsize, cmd[CMDSIZ];
716 	int fd2, fd;
717 
718 	if ((pbufsize = devattr(device, "bufsize")) != NULL) {
719 		ds_bufsize = atoi(pbufsize);
720 		(void) free(pbufsize);
721 	} else
722 		ds_bufsize = BLK_SIZE;
723 	oflag = fcntl(ds_fd, F_GETFL, 0);
724 
725 	if (ds_bufsize > BLK_SIZE) {
726 		if (oflag & O_WRONLY)
727 			fd = 1;
728 		else
729 			fd = 0;
730 		fd2 = fcntl(fd, F_DUPFD, fd);
731 		(void) close(fd);
732 		(void) fcntl(ds_fd, F_DUPFD, fd);
733 		if (fd)
734 			(void) snprintf(cmd, sizeof (cmd),
735 			    "%s obs=%d 2>/dev/null", DDPROC, ds_bufsize);
736 		else
737 			(void) snprintf(cmd, sizeof (cmd),
738 			    "%s ibs=%d 2>/dev/null", DDPROC, ds_bufsize);
739 		if ((ds_pp = popen(cmd, fd ? "w" : "r")) == NULL) {
740 			progerr(pkg_gt(ERR_TRANSFER));
741 			logerr(pkg_gt(MSG_POPEN), cmd, errno);
742 			return (-1);
743 		}
744 		(void) close(fd);
745 		(void) fcntl(fd2, F_DUPFD, fd);
746 		(void) close(fd2);
747 		ds_realfd = ds_fd;
748 		ds_fd = fileno(ds_pp);
749 	}
750 	return (ds_bufsize);
751 }
752 
753 int
754 ds_close(int pkgendflg)
755 {
756 	int n, ret = 0;
757 
758 	if (pkgendflg) {
759 		if (ds_header)
760 			(void) free(ds_header);
761 		ds_header = (char *)NULL;
762 		ds_totread = 0;
763 	}
764 
765 	if (ds_pp) {
766 		(void) pclose(ds_pp);
767 		ds_pp = 0;
768 		(void) close(ds_realfd);
769 		ds_realfd = -1;
770 		ds_fd = -1;
771 	} else if (ds_fd >= 0) {
772 		(void) close(ds_fd);
773 		ds_fd = -1;
774 	}
775 
776 	if (ds_device) {
777 		/* rewind device */
778 		if ((n = open(ds_device, 0)) >= 0)
779 			(void) close(n);
780 		ds_device = NULL;
781 	}
782 	return (ret);
783 }
784