xref: /csrg-svn/sbin/dump/tape.c (revision 24181)
122040Sdist /*
222040Sdist  * Copyright (c) 1980 Regents of the University of California.
322040Sdist  * All rights reserved.  The Berkeley software License Agreement
422040Sdist  * specifies the terms and conditions for redistribution.
522040Sdist  */
622040Sdist 
717527Ssam #ifndef lint
8*24181Smckusick static char sccsid[] = "@(#)tape.c	5.2 (Berkeley) 08/05/85";
922040Sdist #endif not lint
1017527Ssam 
111425Sroot #include "dump.h"
12*24181Smckusick #include <sys/file.h>
131425Sroot 
1410911Ssam char	(*tblock)[TP_BSIZE];	/* Pointer to malloc()ed buffer for tape */
1510911Ssam int	writesize;		/* Size of malloc()ed buffer for tape */
164774Smckusic int	trecno = 0;
1718012Smckusick extern	int ntrec;		/* blocking factor on tape */
18*24181Smckusick extern	int cartridge;
19*24181Smckusick int	tenths; 		/* length of tape used per block written */
201425Sroot 
2110911Ssam /*
22*24181Smckusick  * Concurrent dump mods (Caltech) - disk block reading and tape writing
2318012Smckusick  * are exported to several slave processes.  While one slave writes the
2418012Smckusick  * tape, the others read disk blocks; they pass control of the tape in
25*24181Smckusick  * a ring via flock().	The parent process traverses the filesystem and
26*24181Smckusick  * sends spclrec()'s and lists of daddr's to each slave via pipes.
27*24181Smckusick  *
28*24181Smckusick  * from "@(#)dumptape.c 2.1 (Berkeley+Caltech mods) 4/7/85";
2910911Ssam  */
3018012Smckusick struct req {			/* instruction packets sent to slaves */
3118012Smckusick 	daddr_t dblk;
3218012Smckusick 	int count;
3318012Smckusick } *req;
3418012Smckusick int reqsiz;
3518012Smckusick 
36*24181Smckusick #define SLAVES 3		/* 1 slave writing, 1 reading, 1 for slack */
37*24181Smckusick int slavepid[SLAVES];
3818012Smckusick int slavefd[SLAVES];		/* Pipes from master to each slave */
3918012Smckusick int rotor;			/* Current slave number */
4018012Smckusick int master;			/* Pid of master, for sending error signals */
4118012Smckusick int trace = 0;			/* Protocol trace; easily patchable with adb */
4218012Smckusick #define  tmsg	if (trace) msg
4318012Smckusick 
4418012Smckusick /*
45*24181Smckusick /* Allocate tape buffer contiguous with the array of instruction
46*24181Smckusick  * packets, so flusht() can write them together with one write().
47*24181Smckusick  * Align tape buffer on page boundary to speed up tape write().
4818012Smckusick  */
4910911Ssam alloctape()
5010911Ssam {
5110911Ssam 
52*24181Smckusick 	int pgoff = getpagesize() - 1;
5310911Ssam 	writesize = ntrec * TP_BSIZE;
54*24181Smckusick 	/*
55*24181Smckusick 	 * 92185 NEEDS 0.4"; 92181 NEEDS 0.8" to start/stop (see TU80 manual)
56*24181Smckusick 	 */
57*24181Smckusick 	tenths = writesize/density + (cartridge ? 16 : density == 625 ? 4 : 8);
58*24181Smckusick 
5918012Smckusick 	reqsiz = ntrec * sizeof(struct req);
60*24181Smckusick 	req = (struct req *)malloc(reqsiz + writesize + pgoff);
61*24181Smckusick 	if (req == NULL)
62*24181Smckusick 		return(0);
63*24181Smckusick 	tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff);
64*24181Smckusick 	req = (struct req *)tblock;
65*24181Smckusick 	req = &req[-ntrec];	/* Cmd packets go in front of tape buffer */
66*24181Smckusick 	return(1);
6710911Ssam }
6810911Ssam 
6918012Smckusick /*
70*24181Smckusick  * Make copy of spclrec, to later send to tape writer.
7118012Smckusick  */
721425Sroot taprec(dp)
735329Smckusic 	char *dp;
741425Sroot {
751425Sroot 
7618012Smckusick 	tmsg("taprec %d\n", trecno);
7718012Smckusick 	req[trecno].dblk = (daddr_t)0;
7818012Smckusick 	req[trecno].count = 1;
79*24181Smckusick 	*(union u_spcl *)(*tblock++) = *(union u_spcl *)dp;	/* movc3 */
80*24181Smckusick 	trecno++;
811425Sroot 	spcl.c_tapea++;
82*24181Smckusick 	if(trecno >= ntrec)
831425Sroot 		flusht();
841425Sroot }
851425Sroot 
864774Smckusic dmpblk(blkno, size)
874774Smckusic 	daddr_t blkno;
884774Smckusic 	int size;
891425Sroot {
9018012Smckusick 	int tpblks, dblkno;
9118012Smckusick 	register int avail;
921425Sroot 
935329Smckusic 	dblkno = fsbtodb(sblock, blkno);
9418012Smckusick 	tpblks = size / TP_BSIZE;
9518012Smckusick 	while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
9618012Smckusick 		tmsg("dmpblk %d\n", avail);
9718012Smckusick 		req[trecno].dblk = dblkno;
9818012Smckusick 		req[trecno].count = avail;
994774Smckusic 		spcl.c_tapea += avail;
100*24181Smckusick 		if ((trecno += avail) >= ntrec)
10118012Smckusick 			flusht();
1025329Smckusic 		dblkno += avail * (TP_BSIZE / DEV_BSIZE);
1035329Smckusic 		tpblks -= avail;
1044774Smckusic 	}
1051425Sroot }
1061425Sroot 
1071425Sroot int	nogripe = 0;
1081425Sroot 
10918012Smckusick tperror() {
11018012Smckusick 	if (pipeout) {
11118012Smckusick 		msg("Tape write error on %s\n", tape);
11218012Smckusick 		msg("Cannot recover\n");
11318012Smckusick 		dumpabort();
11418012Smckusick 		/* NOTREACHED */
11518012Smckusick 	}
11618012Smckusick 	msg("Tape write error on tape %d\n", tapeno);
11718012Smckusick 	broadcast("TAPE ERROR!\n");
11818012Smckusick 	if (!query("Do you want to restart?"))
11918012Smckusick 		dumpabort();
12018012Smckusick 	msg("This tape will rewind.  After it is rewound,\n");
12118012Smckusick 	msg("replace the faulty tape with a new one;\n");
12218012Smckusick 	msg("this dump volume will be rewritten.\n");
123*24181Smckusick 	killall();
12418012Smckusick 	nogripe = 1;
12518012Smckusick 	close_rewind();
12618012Smckusick 	Exit(X_REWRITE);
12718012Smckusick }
12818012Smckusick 
12918012Smckusick #ifdef RDUMP
13018012Smckusick tflush(cnt)
13118012Smckusick 	int cnt;
13218012Smckusick {
13318012Smckusick 	int i;
13418012Smckusick 
13518012Smckusick 	for (i = 0; i < ntrec; i++)
13618012Smckusick 		spclrec();
13718012Smckusick }
13818012Smckusick #endif RDUMP
13918012Smckusick 
1401425Sroot flusht()
1411425Sroot {
14218012Smckusick 	int sig, siz = (char *)tblock - (char *)req;
1431425Sroot 
14418012Smckusick 	tmsg("flusht %d\n", siz);
145*24181Smckusick 	sig = sigblock(1 << SIGINT-1);		/* Don't abort pipe write */
146*24181Smckusick 	if (write(slavefd[rotor], req, siz) != siz) {
147*24181Smckusick 		perror("  DUMP: pipe error in command to slave");
148*24181Smckusick 		dumpabort();
149*24181Smckusick 	}
15018012Smckusick 	sigsetmask(sig);
15118012Smckusick 	if (++rotor >= SLAVES) rotor = 0;
15218012Smckusick 	tblock = (char (*)[TP_BSIZE]) &req[ntrec];
1531425Sroot 	trecno = 0;
154*24181Smckusick 	asize += tenths;
15510911Ssam 	blockswritten += ntrec;
15612331Smckusick 	if (!pipeout && asize > tsize) {
1571425Sroot 		close_rewind();
1581425Sroot 		otape();
1591425Sroot 	}
1601425Sroot 	timeest();
1611425Sroot }
1621425Sroot 
1631425Sroot rewind()
1641425Sroot {
165*24181Smckusick 	int f;
16612331Smckusick 
16712331Smckusick 	if (pipeout)
16812331Smckusick 		return;
16918012Smckusick 	for (f = 0; f < SLAVES; f++)
17018012Smckusick 		close(slavefd[f]);
17118012Smckusick 	while (wait(NULL) >= 0)    ;	/* wait for any signals from slaves */
17218012Smckusick 	msg("Tape rewinding\n");
17318012Smckusick #ifdef RDUMP
17418012Smckusick 	rmtclose();
17518012Smckusick 	while (rmtopen(tape, 0) < 0)
17618012Smckusick 		sleep(10);
17718012Smckusick 	rmtclose();
1781425Sroot #else
1793214Swnj 	close(to);
1803214Swnj 	while ((f = open(tape, 0)) < 0)
1813214Swnj 		sleep (10);
1823214Swnj 	close(f);
183*24181Smckusick #endif RDUMP
1841425Sroot }
1851425Sroot 
1861425Sroot close_rewind()
1871425Sroot {
18818012Smckusick 	rewind();
18918012Smckusick 	if (!nogripe) {
1901425Sroot 		msg("Change Tapes: Mount tape #%d\n", tapeno+1);
1911425Sroot 		broadcast("CHANGE TAPES!\7\7\n");
1921425Sroot 	}
19318012Smckusick 	while (!query("Is the new tape mounted and ready to go?"))
19418012Smckusick 		if (query("Do you want to abort?"))
1951425Sroot 			dumpabort();
1961425Sroot }
1971425Sroot 
1981425Sroot /*
19918012Smckusick  *	We implement taking and restoring checkpoints on the tape level.
2001425Sroot  *	When each tape is opened, a new process is created by forking; this
2011425Sroot  *	saves all of the necessary context in the parent.  The child
2021425Sroot  *	continues the dump; the parent waits around, saving the context.
2031425Sroot  *	If the child returns X_REWRITE, then it had problems writing that tape;
2041425Sroot  *	this causes the parent to fork again, duplicating the context, and
2051425Sroot  *	everything continues as if nothing had happened.
2061425Sroot  */
2071425Sroot 
2081425Sroot otape()
2091425Sroot {
2101425Sroot 	int	parentpid;
2111425Sroot 	int	childpid;
2121425Sroot 	int	status;
2131425Sroot 	int	waitpid;
214*24181Smckusick 	int	(*interrupt)();
2151425Sroot 
2161425Sroot 	parentpid = getpid();
2171425Sroot 
2181425Sroot     restore_check_point:
219*24181Smckusick 	interrupt = signal(SIGINT, SIG_IGN);
2201425Sroot 	childpid = fork();
22118012Smckusick 	if (childpid < 0) {
2221425Sroot 		msg("Context save fork fails in parent %d\n", parentpid);
2231425Sroot 		Exit(X_ABORT);
2241425Sroot 	}
22518012Smckusick 	if (childpid != 0) {
2261425Sroot 		/*
2271425Sroot 		 *	PARENT:
2281425Sroot 		 *	save the context by waiting
2291425Sroot 		 *	until the child doing all of the work returns.
23018012Smckusick 		 *	don't catch the interrupt
2311425Sroot 		 */
2321425Sroot #ifdef TDEBUG
2331425Sroot 		msg("Tape: %d; parent process: %d child process %d\n",
2341425Sroot 			tapeno+1, parentpid, childpid);
2351425Sroot #endif TDEBUG
23618012Smckusick 		while ((waitpid = wait(&status)) != childpid)
23718012Smckusick 			msg("Parent %d waiting for child %d has another child %d return\n",
23818012Smckusick 				parentpid, childpid, waitpid);
23918012Smckusick 		if (status & 0xFF) {
2401425Sroot 			msg("Child %d returns LOB status %o\n",
2411425Sroot 				childpid, status&0xFF);
2421425Sroot 		}
2431425Sroot 		status = (status >> 8) & 0xFF;
2441425Sroot #ifdef TDEBUG
24518012Smckusick 		switch(status) {
2461425Sroot 			case X_FINOK:
2471425Sroot 				msg("Child %d finishes X_FINOK\n", childpid);
2481425Sroot 				break;
2491425Sroot 			case X_ABORT:
2501425Sroot 				msg("Child %d finishes X_ABORT\n", childpid);
2511425Sroot 				break;
2521425Sroot 			case X_REWRITE:
2531425Sroot 				msg("Child %d finishes X_REWRITE\n", childpid);
2541425Sroot 				break;
2551425Sroot 			default:
25618012Smckusick 				msg("Child %d finishes unknown %d\n",
25718012Smckusick 				    childpid, status);
2581425Sroot 				break;
2591425Sroot 		}
2601425Sroot #endif TDEBUG
26118012Smckusick 		switch(status) {
2621425Sroot 			case X_FINOK:
2631425Sroot 				Exit(X_FINOK);
2641425Sroot 			case X_ABORT:
2651425Sroot 				Exit(X_ABORT);
2661425Sroot 			case X_REWRITE:
2671425Sroot 				goto restore_check_point;
2681425Sroot 			default:
2691425Sroot 				msg("Bad return code from dump: %d\n", status);
2701425Sroot 				Exit(X_ABORT);
2711425Sroot 		}
2721425Sroot 		/*NOTREACHED*/
2731425Sroot 	} else {	/* we are the child; just continue */
2741425Sroot #ifdef TDEBUG
2751425Sroot 		sleep(4);	/* allow time for parent's message to get out */
2761425Sroot 		msg("Child on Tape %d has parent %d, my pid = %d\n",
2771425Sroot 			tapeno+1, parentpid, getpid());
2781425Sroot #endif
279*24181Smckusick 		signal(SIGINT, interrupt);
28018012Smckusick #ifdef RDUMP
28118012Smckusick 		while ((to = rmtopen(tape, 2)) < 0)
28218012Smckusick #else
28318012Smckusick 		while ((to = pipeout ? 1 : creat(tape, 0666)) < 0)
284*24181Smckusick #endif RDUMP
28518012Smckusick 			if (!query("Cannot open tape.  Do you want to retry the open?"))
28618012Smckusick 				dumpabort();
2871425Sroot 
28818012Smckusick 		enslave();  /* Share open tape file descriptor with slaves */
28918012Smckusick 
2901425Sroot 		asize = 0;
2911425Sroot 		tapeno++;		/* current tape sequence */
2921425Sroot 		newtape++;		/* new tape signal */
2931425Sroot 		spcl.c_volume++;
2941425Sroot 		spcl.c_type = TS_TAPE;
2951425Sroot 		spclrec();
2961425Sroot 		if (tapeno > 1)
2971425Sroot 			msg("Tape %d begins with blocks from ino %d\n",
2981425Sroot 				tapeno, ino);
2991425Sroot 	}
3001425Sroot }
3011425Sroot 
3021425Sroot dumpabort()
3031425Sroot {
30418012Smckusick 	if (master != 0 && master != getpid())
305*24181Smckusick 		kill(master, SIGPIPE);
306*24181Smckusick 	else {
307*24181Smckusick 		killall();
308*24181Smckusick 		msg("The ENTIRE dump is aborted.\n");
309*24181Smckusick 	}
3101425Sroot 	Exit(X_ABORT);
3111425Sroot }
3121425Sroot 
3131425Sroot Exit(status)
3141425Sroot {
3151425Sroot #ifdef TDEBUG
3161425Sroot 	msg("pid = %d exits with status %d\n", getpid(), status);
3171425Sroot #endif TDEBUG
3181925Swnj 	exit(status);
3191425Sroot }
32018012Smckusick 
321*24181Smckusick /*
322*24181Smckusick  * prefer pipe(), but flock() barfs on them
323*24181Smckusick  */
324*24181Smckusick lockfile(fd)
325*24181Smckusick 	int fd[2];
326*24181Smckusick {
327*24181Smckusick 	char tmpname[20];
32818012Smckusick 
329*24181Smckusick 	strcpy(tmpname, "/tmp/dumplockXXXXXX");
330*24181Smckusick 	mktemp(tmpname);
331*24181Smckusick 	if ((fd[1] = creat(tmpname, 0400)) < 0)
332*24181Smckusick 		return(fd[1]);
333*24181Smckusick 	fd[0] = open(tmpname, 0);
334*24181Smckusick 	unlink(tmpname);
335*24181Smckusick 	return (fd[0] < 0 ? fd[0] : 0);
336*24181Smckusick }
337*24181Smckusick 
33818012Smckusick enslave()
33918012Smckusick {
340*24181Smckusick 	int first[2], prev[2], next[2], cmd[2];     /* file descriptors */
341*24181Smckusick 	register int i, j;
34218012Smckusick 
34318012Smckusick 	master = getpid();
344*24181Smckusick 	signal(SIGPIPE, dumpabort);  /* Slave quit/died/killed -> abort */
345*24181Smckusick 	signal(SIGIOT, tperror);     /* SIGIOT -> restart from checkpoint */
346*24181Smckusick 	lockfile(first);
347*24181Smckusick 	for (i = 0; i < SLAVES; i++) {
348*24181Smckusick 		if (i == 0) {
349*24181Smckusick 			prev[0] = first[1];
350*24181Smckusick 			prev[1] = first[0];
351*24181Smckusick 		} else {
352*24181Smckusick 			prev[0] = next[0];
353*24181Smckusick 			prev[1] = next[1];
354*24181Smckusick 			flock(prev[1], LOCK_EX);
355*24181Smckusick 		}
356*24181Smckusick 		next[0] = first[0];
357*24181Smckusick 		next[1] = first[1];	    /* Last slave loops back */
358*24181Smckusick 		if ((i < SLAVES-1 && lockfile(next) < 0) || pipe(cmd) < 0
359*24181Smckusick 				|| (slavepid[i] = fork()) < 0) {
360*24181Smckusick 			perror("  DUMP: too many slaves (recompile smaller)");
36118012Smckusick 			dumpabort();
36218012Smckusick 		}
36318012Smckusick 		slavefd[i] = cmd[1];
364*24181Smckusick 		if (slavepid[i] == 0) {		    /* Slave starts up here */
36518012Smckusick 			for (j = 0; j <= i; j++)
36618012Smckusick 				close(slavefd[j]);
367*24181Smckusick 			signal(SIGINT, SIG_IGN);     /* Master handles these */
368*24181Smckusick 			signal(SIGTERM, SIG_IGN);
369*24181Smckusick 			doslave(i, cmd[0], prev, next);
37018012Smckusick 			Exit(X_FINOK);
37118012Smckusick 		}
37218012Smckusick 		close(cmd[0]);
373*24181Smckusick 		if (i > 0) {
374*24181Smckusick 			close(prev[0]);
375*24181Smckusick 			close(prev[1]);
376*24181Smckusick 		}
37718012Smckusick 	}
378*24181Smckusick 	close(first[0]);
379*24181Smckusick 	close(first[1]);
380*24181Smckusick 	master = 0; rotor = 0;
38118012Smckusick }
38218012Smckusick 
383*24181Smckusick killall()
38418012Smckusick {
385*24181Smckusick 	register int i;
38619982Smckusick 
387*24181Smckusick 	for (i = 0; i < SLAVES; i++)
388*24181Smckusick 		if (slavepid[i] > 0)
389*24181Smckusick 			kill(slavepid[i], SIGKILL);
39018012Smckusick }
39118012Smckusick 
392*24181Smckusick /*
393*24181Smckusick  * Synchronization - each process has a lockfile, and shares file
394*24181Smckusick  * descriptors to the following process's lockfile.  When our write
395*24181Smckusick  * completes, we release our lock on the following process's lock-
396*24181Smckusick  * file, allowing the following process to lock it and proceed. We
397*24181Smckusick  * get the lock back for the next cycle by swapping descriptors.
398*24181Smckusick  */
399*24181Smckusick doslave(mynum,cmd,prev,next)
400*24181Smckusick 	int mynum, cmd, prev[2], next[2];
40119982Smckusick {
402*24181Smckusick 	register int toggle = 0, firstdone = mynum;
40319982Smckusick 
404*24181Smckusick 	tmsg("slave %d\n", mynum);
40518012Smckusick 	close(fi);
40618012Smckusick 	if ((fi = open(disk, 0)) < 0) {		/* Need our own seek pointer */
407*24181Smckusick 		perror("  DUMP: slave couldn't reopen disk");
408*24181Smckusick 		kill(master, SIGPIPE);		/* dumpabort */
40918012Smckusick 		Exit(X_ABORT);
41018012Smckusick 	}
411*24181Smckusick 	/*
412*24181Smckusick 	 * Get list of blocks to dump
413*24181Smckusick 	 */
414*24181Smckusick 	while (readpipe(cmd, req, reqsiz) > 0) {
41518012Smckusick 		register struct req *p = req;
41618012Smckusick 		for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
41718012Smckusick 			if (p->dblk) {
418*24181Smckusick 				tmsg("%d READS %d\n", mynum, p->count);
41918012Smckusick 				bread(p->dblk, tblock[trecno],
42018012Smckusick 				    p->count * TP_BSIZE);
42118012Smckusick 			} else {
422*24181Smckusick 				tmsg("%d PIPEIN %d\n", mynum, p->count);
423*24181Smckusick 				if (p->count != 1 ||
424*24181Smckusick 				    readpipe(cmd, tblock[trecno], TP_BSIZE) <= 0) {
425*24181Smckusick 					msg("Master/slave protocol botched");
426*24181Smckusick 					dumpabort();
427*24181Smckusick 				}
42818012Smckusick 			}
42918012Smckusick 		}
430*24181Smckusick 		flock(prev[toggle], LOCK_EX);	/* Wait our turn */
431*24181Smckusick 		tmsg("%d WRITE\n", mynum);
43218012Smckusick #ifdef RDUMP
433*24181Smckusick #ifndef sun	/* Defer checking first write until next one is started */
434*24181Smckusick 		rmtwrite0(writesize);
435*24181Smckusick 		rmtwrite1(tblock[0],writesize);
436*24181Smckusick 		if (firstdone == 0) firstdone = -1;
437*24181Smckusick 		else if (rmtwrite2() != writesize) {
438*24181Smckusick 			rmtwrite2();		/* Don't care if another err */
43918012Smckusick #else
440*24181Smckusick 		/* Asynchronous writes can hang Suns; do it synchronously */
441*24181Smckusick 		if (rmtwrite(tblock[0],writesize) != writesize) {
442*24181Smckusick #endif sun
443*24181Smckusick #else		/* Local tape drive */
444*24181Smckusick 		if (write(to,tblock[0],writesize) != writesize) {
44518012Smckusick 			perror(tape);
446*24181Smckusick #endif RDUMP
44718012Smckusick 			kill(master, SIGIOT);	/* restart from checkpoint */
448*24181Smckusick 			for (;;) sigpause(0);
44918012Smckusick 		}
450*24181Smckusick 		toggle ^= 1;
451*24181Smckusick 		flock(next[toggle], LOCK_UN);	/* Next slave's turn */
452*24181Smckusick 	}					/* Also jolts him awake */
453*24181Smckusick #ifdef RDUMP			/* One more time around, to check last write */
454*24181Smckusick #ifndef sun
455*24181Smckusick 	flock(prev[toggle], LOCK_EX);
456*24181Smckusick 	tmsg("%d LAST\n", mynum);
457*24181Smckusick 	if (firstdone < 0 && rmtwrite2() != writesize) {
458*24181Smckusick 		kill(master, SIGIOT);
459*24181Smckusick 		for (;;)
460*24181Smckusick 			sigpause(0);
46118012Smckusick 	}
462*24181Smckusick 	toggle ^= 1;
463*24181Smckusick 	flock(next[toggle], LOCK_UN);
464*24181Smckusick #endif sun
465*24181Smckusick #endif RDUMP
46618012Smckusick }
46719947Smckusick 
46819947Smckusick /*
46919947Smckusick  * Since a read from a pipe may not return all we asked for
47019947Smckusick  * we must loop until we get all we need
47119947Smckusick  */
47219947Smckusick readpipe(fd, buf, cnt)
47319947Smckusick 	int fd;
47419947Smckusick 	char *buf;
47519947Smckusick 	int cnt;
47619947Smckusick {
47719947Smckusick 	int rd, got;
47819947Smckusick 
47919947Smckusick 	for (rd = cnt; rd > 0; rd -= got) {
48019947Smckusick 		got = read(fd, buf, rd);
481*24181Smckusick 		if (got <= 0) {
482*24181Smckusick 			if (rd == cnt && got == 0)
483*24181Smckusick 				return (0);		/* Normal EOF */
484*24181Smckusick 			msg("short pipe read");
485*24181Smckusick 			dumpabort();
486*24181Smckusick 		}
48719947Smckusick 		buf += got;
48819947Smckusick 	}
48919947Smckusick 	return (cnt);
49019947Smckusick }
491