xref: /csrg-svn/local/ukc/dump/dumptape.c (revision 32047)
132002Spc /*
232002Spc  * Copyright (c) 1980 Regents of the University of California.
332002Spc  * All rights reserved.  The Berkeley software License Agreement
432002Spc  * specifies the terms and conditions for redistribution.
532002Spc  */
632002Spc 
732002Spc #ifndef lint
8*32047Spc static char sccsid[] = "@(#)dumptape.c	1.4 (UKC) 08/11/87	5.8 (Berkeley) 2/23/87";
932002Spc #endif not lint
1032002Spc 
1132028Spc #include "dump.h"
1232002Spc #include <sys/file.h>
1332028Spc #include <sys/ioctl.h>
1432028Spc #include <sys/mtio.h>
1532002Spc 
1632028Spc 
1732003Spc char	(*tblock)[TP_BSIZE];	/* pointer to malloc()ed buffer for tape */
1832003Spc int	writesize;		/* size of malloc()ed buffer for tape */
1932003Spc long	lastspclrec = -1;	/* tape block number of last written header */
2032003Spc int	trecno = 0;		/* next record to write in current block */
2132002Spc extern int ntrec;		/* blocking factor on tape */
2232002Spc extern int cartridge;
2332028Spc extern int rewindoffline;
2432002Spc extern int read(), write();
2532002Spc #ifdef RDUMP
2632002Spc extern char *host;
2732002Spc #endif RDUMP
2832002Spc 
2932002Spc /*
3032002Spc  * Concurrent dump mods (Caltech) - disk block reading and tape writing
3132002Spc  * are exported to several slave processes.  While one slave writes the
3232002Spc  * tape, the others read disk blocks; they pass control of the tape in
3332002Spc  * a ring via flock().	The parent process traverses the filesystem and
3432002Spc  * sends spclrec()'s and lists of daddr's to the slaves via pipes.
3532002Spc  */
3632002Spc struct req {			/* instruction packets sent to slaves */
3732002Spc 	daddr_t dblk;
3832002Spc 	int count;
3932002Spc } *req;
4032002Spc int reqsiz;
4132002Spc 
4232002Spc #define SLAVES 3		/* 1 slave writing, 1 reading, 1 for slack */
4332002Spc int slavefd[SLAVES];		/* pipes from master to each slave */
4432002Spc int slavepid[SLAVES];		/* used by killall() */
4532002Spc int rotor;			/* next slave to be instructed */
4632002Spc int master;			/* pid of master, for sending error signals */
4732002Spc int tenths;			/* length of tape used per block written */
4832002Spc 
alloctape()4932002Spc alloctape()
5032002Spc {
5132002Spc 	int pgoff = getpagesize() - 1;
5232002Spc 
5332002Spc 	writesize = ntrec * TP_BSIZE;
5432002Spc 	reqsiz = ntrec * sizeof(struct req);
5532002Spc 	/*
5632002Spc 	 * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
5732002Spc 	 * (see DEC TU80 User's Guide).  The shorter gaps of 6250-bpi require
5832002Spc 	 * repositioning after stopping, i.e, streaming mode, where the gap is
5932002Spc 	 * variable, 0.30" to 0.45".  The gap is maximal when the tape stops.
6032002Spc 	 */
6132002Spc 	tenths = writesize/density + (cartridge ? 16 : density == 625 ? 5 : 8);
6232002Spc 	/*
6332002Spc 	 * Allocate tape buffer contiguous with the array of instruction
6432002Spc 	 * packets, so flusht() can write them together with one write().
6532002Spc 	 * Align tape buffer on page boundary to speed up tape write().
6632002Spc 	 */
6732002Spc 	req = (struct req *)malloc(reqsiz + writesize + pgoff);
6832002Spc 	if (req == NULL)
6932002Spc 		return(0);
7032002Spc 	tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff);
7132002Spc 	req = (struct req *)tblock - ntrec;
7232002Spc 	return(1);
7332002Spc }
7432002Spc 
7532002Spc 
taprec(dp)7632002Spc taprec(dp)
7732002Spc 	char *dp;
7832002Spc {
7932002Spc 	req[trecno].dblk = (daddr_t)0;
8032002Spc 	req[trecno].count = 1;
8132002Spc 	*(union u_spcl *)(*tblock++) = *(union u_spcl *)dp;	/* movc3 */
8232003Spc 	lastspclrec = spcl.c_tapea;
8332002Spc 	trecno++;
8432002Spc 	spcl.c_tapea++;
8532002Spc 	if(trecno >= ntrec)
8632002Spc 		flusht();
8732002Spc }
8832002Spc 
dmpblk(blkno,size)8932002Spc dmpblk(blkno, size)
9032002Spc 	daddr_t blkno;
9132002Spc 	int size;
9232002Spc {
9332002Spc 	int avail, tpblks, dblkno;
9432002Spc 
9532002Spc 	dblkno = fsbtodb(sblock, blkno);
9632002Spc 	tpblks = size / TP_BSIZE;
9732002Spc 	while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
9832002Spc 		req[trecno].dblk = dblkno;
9932002Spc 		req[trecno].count = avail;
10032002Spc 		trecno += avail;
10132002Spc 		spcl.c_tapea += avail;
10232002Spc 		if (trecno >= ntrec)
10332002Spc 			flusht();
10432002Spc 		dblkno += avail * (TP_BSIZE / DEV_BSIZE);
10532002Spc 		tpblks -= avail;
10632002Spc 	}
10732002Spc }
10832002Spc 
10932002Spc int	nogripe = 0;
11032002Spc 
tperror()11132002Spc tperror() {
11232002Spc 	if (pipeout) {
11332002Spc 		msg("Tape write error on %s\n", tape);
11432002Spc 		msg("Cannot recover\n");
11532002Spc 		dumpabort();
11632002Spc 		/* NOTREACHED */
11732002Spc 	}
11832002Spc 	msg("Tape write error %d feet into tape %d\n", asize/120L, tapeno);
11932002Spc 	broadcast("TAPE ERROR!\n");
12032002Spc 	if (!query("Do you want to restart?"))
12132002Spc 		dumpabort();
12232002Spc 	msg("This tape will rewind.  After it is rewound,\n");
12332002Spc 	msg("replace the faulty tape with a new one;\n");
12432002Spc 	msg("this dump volume will be rewritten.\n");
12532002Spc 	killall();
12632002Spc 	nogripe = 1;
12732002Spc 	close_rewind();
12832002Spc 	Exit(X_REWRITE);
12932002Spc }
13032002Spc 
sigpipe()13132002Spc sigpipe()
13232002Spc {
13332002Spc 
13432002Spc 	msg("Broken pipe\n");
13532002Spc 	dumpabort();
13632002Spc }
13732002Spc 
13832002Spc #ifdef RDUMP
13932002Spc /*
14032002Spc  * compatibility routine
14132002Spc  */
tflush(i)14232002Spc tflush(i)
14332002Spc 	int i;
14432002Spc {
14532002Spc 
14632002Spc 	for (i = 0; i < ntrec; i++)
14732002Spc 		spclrec();
14832002Spc }
14932002Spc #endif RDUMP
15032002Spc 
flusht()15132002Spc flusht()
15232002Spc {
15332002Spc 	int siz = (char *)tblock - (char *)req;
15432002Spc 
15532002Spc 	if (atomic(write, slavefd[rotor], req, siz) != siz) {
15632002Spc 		perror("  DUMP: error writing command pipe");
15732002Spc 		dumpabort();
15832002Spc 	}
15932002Spc 	if (++rotor >= SLAVES) rotor = 0;
16032002Spc 	tblock = (char (*)[TP_BSIZE]) &req[ntrec];
16132002Spc 	trecno = 0;
16232002Spc 	asize += tenths;
16332002Spc 	blockswritten += ntrec;
16432002Spc 	if (!pipeout && asize > tsize) {
16532002Spc 		close_rewind();
16632002Spc 		otape();
16732002Spc 	}
16832002Spc 	timeest();
16932002Spc }
17032002Spc 
rewind()17132002Spc rewind()
17232002Spc {
17332002Spc 	int f;
17432002Spc 
17532002Spc 	if (pipeout)
17632002Spc 		return;
17732002Spc 	for (f = 0; f < SLAVES; f++)
17832002Spc 		close(slavefd[f]);
17932002Spc 	while (wait(NULL) >= 0)    ;	/* wait for any signals from slaves */
18032002Spc 	msg("Tape rewinding\n");
18132002Spc #ifdef RDUMP
18232002Spc 	if (host) {
18332028Spc 		if (rewindoffline) {
18432028Spc 			rewind_offline(to);
18532028Spc 			rmtclose();
18632028Spc 			return;
18732028Spc 		}
18832002Spc 		rmtclose();
18932002Spc 		while (rmtopen(tape, 0) < 0)
19032002Spc 			sleep(10);
19132002Spc 		rmtclose();
19232002Spc 		return;
19332002Spc 	}
19432002Spc #endif RDUMP
19532028Spc 	if (rewindoffline) {
19632028Spc 		rewind_offline(to);
19732028Spc 		close(to);
19832028Spc 		return;
19932028Spc 	}
20032002Spc 	close(to);
20132002Spc 	while ((f = open(tape, 0)) < 0)
20232002Spc 		sleep (10);
20332002Spc 	close(f);
20432002Spc }
20532002Spc 
close_rewind()20632002Spc close_rewind()
20732002Spc {
20832028Spc 	extern int userlabel;
20932028Spc 	char *label;
21032028Spc 	char *createlabel();
21132028Spc 
21232002Spc 	rewind();
21332002Spc 	if (!nogripe) {
21432028Spc 		if (userlabel) {
21532028Spc 			label = createlabel(tapeno+1);
21632028Spc 			msg("Change Tapes: Mount tape labelled `%s' reel %d of this dump\n",
21732028Spc 				label, tapeno+1);
21832028Spc 		}
21932028Spc 		else
22032028Spc 			msg("Change Tapes: Mount tape #%d\n", tapeno+1);
22132002Spc 		broadcast("CHANGE TAPES!\7\7\n");
22232002Spc 	}
22332002Spc 	while (!query("Is the new tape mounted and ready to go?"))
22432002Spc 		if (query("Do you want to abort?")) {
22532002Spc 			dumpabort();
22632002Spc 			/*NOTREACHED*/
22732002Spc 		}
22832002Spc }
22932002Spc 
23032002Spc /*
23132002Spc  *	We implement taking and restoring checkpoints on the tape level.
23232002Spc  *	When each tape is opened, a new process is created by forking; this
23332002Spc  *	saves all of the necessary context in the parent.  The child
23432002Spc  *	continues the dump; the parent waits around, saving the context.
23532002Spc  *	If the child returns X_REWRITE, then it had problems writing that tape;
23632002Spc  *	this causes the parent to fork again, duplicating the context, and
23732002Spc  *	everything continues as if nothing had happened.
23832002Spc  */
23932002Spc 
otape()24032002Spc otape()
24132002Spc {
24232002Spc 	int	parentpid;
24332002Spc 	int	childpid;
24432002Spc 	int	status;
24532002Spc 	int	waitpid;
24632002Spc 	int	(*interrupt)() = signal(SIGINT, SIG_IGN);
24732003Spc 	int	blks, i;
24832028Spc 
24932002Spc 	parentpid = getpid();
25032002Spc 
25132002Spc     restore_check_point:
25232002Spc 	signal(SIGINT, interrupt);
25332002Spc 	/*
25432002Spc 	 *	All signals are inherited...
25532002Spc 	 */
25632002Spc 	childpid = fork();
25732002Spc 	if (childpid < 0) {
25832002Spc 		msg("Context save fork fails in parent %d\n", parentpid);
25932002Spc 		Exit(X_ABORT);
26032002Spc 	}
26132002Spc 	if (childpid != 0) {
26232002Spc 		/*
26332002Spc 		 *	PARENT:
26432002Spc 		 *	save the context by waiting
26532002Spc 		 *	until the child doing all of the work returns.
26632002Spc 		 *	don't catch the interrupt
26732002Spc 		 */
26832002Spc 		signal(SIGINT, SIG_IGN);
26932002Spc #ifdef TDEBUG
27032002Spc 		msg("Tape: %d; parent process: %d child process %d\n",
27132002Spc 			tapeno+1, parentpid, childpid);
27232002Spc #endif TDEBUG
27332002Spc 		while ((waitpid = wait(&status)) != childpid)
27432002Spc 			msg("Parent %d waiting for child %d has another child %d return\n",
27532002Spc 				parentpid, childpid, waitpid);
27632002Spc 		if (status & 0xFF) {
27732002Spc 			msg("Child %d returns LOB status %o\n",
27832002Spc 				childpid, status&0xFF);
27932002Spc 		}
28032002Spc 		status = (status >> 8) & 0xFF;
28132002Spc #ifdef TDEBUG
28232002Spc 		switch(status) {
28332002Spc 			case X_FINOK:
28432002Spc 				msg("Child %d finishes X_FINOK\n", childpid);
28532002Spc 				break;
28632002Spc 			case X_ABORT:
28732002Spc 				msg("Child %d finishes X_ABORT\n", childpid);
28832002Spc 				break;
28932002Spc 			case X_REWRITE:
29032002Spc 				msg("Child %d finishes X_REWRITE\n", childpid);
29132002Spc 				break;
29232002Spc 			default:
29332002Spc 				msg("Child %d finishes unknown %d\n",
29432002Spc 					childpid, status);
29532002Spc 				break;
29632002Spc 		}
29732002Spc #endif TDEBUG
29832002Spc 		switch(status) {
29932002Spc 			case X_FINOK:
30032002Spc 				Exit(X_FINOK);
30132002Spc 			case X_ABORT:
30232002Spc 				Exit(X_ABORT);
30332002Spc 			case X_REWRITE:
30432002Spc 				goto restore_check_point;
30532002Spc 			default:
30632002Spc 				msg("Bad return code from dump: %d\n", status);
30732002Spc 				Exit(X_ABORT);
30832002Spc 		}
30932002Spc 		/*NOTREACHED*/
31032002Spc 	} else {	/* we are the child; just continue */
31132002Spc #ifdef TDEBUG
31232002Spc 		sleep(4);	/* allow time for parent's message to get out */
31332002Spc 		msg("Child on Tape %d has parent %d, my pid = %d\n",
31432002Spc 			tapeno+1, parentpid, getpid());
31532002Spc #endif TDEBUG
316*32047Spc 		while (labelcheck(tapeno+1) < 0)
317*32047Spc 			if (!query("Problem with tape label.  Do you want to retry the open?"))
318*32047Spc 				dumpabort();
31932002Spc #ifdef RDUMP
32032002Spc 		while ((to = (host ? rmtopen(tape, 2) :
32132002Spc 			pipeout ? 1 : creat(tape, 0666))) < 0)
32232002Spc #else RDUMP
32332002Spc 		while ((to = pipeout ? 1 : creat(tape, 0666)) < 0)
32432002Spc #endif RDUMP
32532002Spc 			if (!query("Cannot open tape.  Do you want to retry the open?"))
32632002Spc 				dumpabort();
32732002Spc 
32832002Spc 		enslave();  /* Share open tape file descriptor with slaves */
32932002Spc 
33032002Spc 		asize = 0;
33132002Spc 		tapeno++;		/* current tape sequence */
33232002Spc 		newtape++;		/* new tape signal */
33332003Spc 		blks = 0;
33432003Spc 		if (spcl.c_type != TS_END)
33532003Spc 			for (i = 0; i < spcl.c_count; i++)
33632003Spc 				if (spcl.c_addr[i] != 0)
33732003Spc 					blks++;
33832003Spc 		spcl.c_count = blks + 1 - spcl.c_tapea + lastspclrec;
33932002Spc 		spcl.c_volume++;
34032002Spc 		spcl.c_type = TS_TAPE;
34132003Spc 		spcl.c_flags |= DR_NEWHEADER;
34232002Spc 		spclrec();
34332003Spc 		spcl.c_flags &=~ DR_NEWHEADER;
34432028Spc 		/*
34532028Spc 		 *	It may seem that the logging ought to be done
34632028Spc 		 *	at the end of the write but
34732028Spc 		 *	a)	for cyclic dumps the tape has been destroyed
34832028Spc 		 *	b)	if this dump fails due to a bad tape
34932028Spc 		 *		then the new tape ought to be relabelled
35032028Spc 		 *	c)	if this dump fails for other reasons
35132028Spc 		 *		then the successful dump will also be logged
35232028Spc 		 */
35332028Spc 		log_volume(spcl.c_label);
35432028Spc 
35532028Spc 		if (tapeno > 1){
35632028Spc 			if (userlabel)
35732028Spc 				msg("Tape `%s' (reel %d) begins with blocks from ino %d\n",
35832028Spc 					spcl.c_label, tapeno, ino);
35932028Spc 			else
36032028Spc 				msg("Tape %d begins with blocks from ino %d\n",
36132028Spc 					tapeno, ino);
36232028Spc 		}
36332002Spc 	}
36432002Spc }
36532002Spc 
dumpabort()36632002Spc dumpabort()
36732002Spc {
36832002Spc 	if (master != 0 && master != getpid())
36932002Spc 		kill(master, SIGTERM);	/* Signals master to call dumpabort */
37032002Spc 	else {
37132002Spc 		killall();
37232002Spc 		msg("The ENTIRE dump is aborted.\n");
37332002Spc 	}
37432002Spc 	Exit(X_ABORT);
37532002Spc }
37632002Spc 
Exit(status)37732002Spc Exit(status)
37832002Spc {
37932002Spc #ifdef TDEBUG
38032002Spc 	msg("pid = %d exits with status %d\n", getpid(), status);
38132002Spc #endif TDEBUG
38232002Spc 	exit(status);
38332002Spc }
38432002Spc 
38532002Spc /*
38632002Spc  * could use pipe() for this if flock() worked on pipes
38732002Spc  */
lockfile(fd)38832002Spc lockfile(fd)
38932002Spc 	int fd[2];
39032002Spc {
39132002Spc 	char tmpname[20];
39232002Spc 
39332002Spc 	strcpy(tmpname, "/tmp/dumplockXXXXXX");
39432002Spc 	mktemp(tmpname);
39532002Spc 	if ((fd[1] = creat(tmpname, 0400)) < 0) {
39632002Spc 		msg("Could not create lockfile ");
39732002Spc 		perror(tmpname);
39832002Spc 		dumpabort();
39932002Spc 	}
40032002Spc 	if ((fd[0] = open(tmpname, 0)) < 0) {
40132002Spc 		msg("Could not reopen lockfile ");
40232002Spc 		perror(tmpname);
40332002Spc 		dumpabort();
40432002Spc 	}
40532002Spc 	unlink(tmpname);
40632002Spc }
40732002Spc 
enslave()40832002Spc enslave()
40932002Spc {
41032002Spc 	int first[2], prev[2], next[2], cmd[2];     /* file descriptors */
41132002Spc 	register int i, j;
41232002Spc 
41332002Spc 	master = getpid();
41432002Spc 	signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
41532002Spc 	signal(SIGPIPE, sigpipe);
41632002Spc 	signal(SIGUSR1, tperror);    /* Slave sends SIGUSR1 on tape errors */
41732002Spc 	lockfile(first);
41832002Spc 	for (i = 0; i < SLAVES; i++) {
41932002Spc 		if (i == 0) {
42032002Spc 			prev[0] = first[1];
42132002Spc 			prev[1] = first[0];
42232002Spc 		} else {
42332002Spc 			prev[0] = next[0];
42432002Spc 			prev[1] = next[1];
42532002Spc 			flock(prev[1], LOCK_EX);
42632002Spc 		}
42732002Spc 		if (i < SLAVES - 1) {
42832002Spc 			lockfile(next);
42932002Spc 		} else {
43032002Spc 			next[0] = first[0];
43132002Spc 			next[1] = first[1];	    /* Last slave loops back */
43232002Spc 		}
43332002Spc 		if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0) {
43432002Spc 			msg("too many slaves, %d (recompile smaller) ", i);
43532002Spc 			perror("");
43632002Spc 			dumpabort();
43732002Spc 		}
43832002Spc 		slavefd[i] = cmd[1];
43932002Spc 		if (slavepid[i] == 0) { 	    /* Slave starts up here */
44032002Spc 			for (j = 0; j <= i; j++)
44132002Spc 				close(slavefd[j]);
44232002Spc 			signal(SIGINT, SIG_IGN);    /* Master handles this */
44332002Spc 			doslave(cmd[0], prev, next);
44432002Spc 			Exit(X_FINOK);
44532002Spc 		}
44632002Spc 		close(cmd[0]);
44732002Spc 		if (i > 0) {
44832002Spc 			close(prev[0]);
44932002Spc 			close(prev[1]);
45032002Spc 		}
45132002Spc 	}
45232002Spc 	close(first[0]);
45332002Spc 	close(first[1]);
45432002Spc 	master = 0; rotor = 0;
45532002Spc }
45632002Spc 
killall()45732002Spc killall()
45832002Spc {
45932002Spc 	register int i;
46032002Spc 
46132002Spc 	for (i = 0; i < SLAVES; i++)
46232002Spc 		if (slavepid[i] > 0)
46332002Spc 			kill(slavepid[i], SIGKILL);
46432002Spc }
46532002Spc 
46632002Spc /*
46732002Spc  * Synchronization - each process has a lockfile, and shares file
46832002Spc  * descriptors to the following process's lockfile.  When our write
46932002Spc  * completes, we release our lock on the following process's lock-
47032002Spc  * file, allowing the following process to lock it and proceed. We
47132002Spc  * get the lock back for the next cycle by swapping descriptors.
47232002Spc  */
doslave(cmd,prev,next)47332002Spc doslave(cmd, prev, next)
47432002Spc 	register int cmd, prev[2], next[2];
47532002Spc {
47632002Spc 	register int nread, toggle = 0;
47732002Spc 
47832002Spc 	close(fi);
47932002Spc 	if ((fi = open(disk, 0)) < 0) { 	/* Need our own seek pointer */
48032002Spc 		perror("  DUMP: slave couldn't reopen disk");
48132002Spc 		dumpabort();
48232002Spc 	}
48332002Spc 	/*
48432002Spc 	 * Get list of blocks to dump, read the blocks into tape buffer
48532002Spc 	 */
48632002Spc 	while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) {
48732002Spc 		register struct req *p = req;
48832002Spc 		for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
48932002Spc 			if (p->dblk) {
49032002Spc 				bread(p->dblk, tblock[trecno],
49132002Spc 					p->count * TP_BSIZE);
49232002Spc 			} else {
49332002Spc 				if (p->count != 1 || atomic(read, cmd,
49432002Spc 				    tblock[trecno], TP_BSIZE) != TP_BSIZE) {
49532002Spc 					msg("Master/slave protocol botched.\n");
49632002Spc 					dumpabort();
49732002Spc 				}
49832002Spc 			}
49932002Spc 		}
50032002Spc 		flock(prev[toggle], LOCK_EX);	/* Wait our turn */
50132002Spc 
50232002Spc #ifdef RDUMP
50332002Spc 		if ((host ? rmtwrite(tblock[0], writesize)
50432002Spc 			: write(to, tblock[0], writesize)) != writesize) {
50532002Spc #else RDUMP
50632002Spc 		if (write(to, tblock[0], writesize) != writesize) {
50732002Spc #endif RDUMP
50832002Spc 			kill(master, SIGUSR1);
50932002Spc 			for (;;)
51032002Spc 				sigpause(0);
51132002Spc 		}
51232002Spc 		toggle ^= 1;
51332002Spc 		flock(next[toggle], LOCK_UN);	/* Next slave's turn */
51432002Spc 	}					/* Also jolts him awake */
51532002Spc 	if (nread != 0) {
51632002Spc 		perror("  DUMP: error reading command pipe");
51732002Spc 		dumpabort();
51832002Spc 	}
51932002Spc }
52032002Spc 
52132002Spc /*
52232002Spc  * Since a read from a pipe may not return all we asked for,
52332002Spc  * or a write may not write all we ask if we get a signal,
52432002Spc  * loop until the count is satisfied (or error).
52532002Spc  */
52632002Spc atomic(func, fd, buf, count)
52732002Spc 	int (*func)(), fd, count;
52832002Spc 	char *buf;
52932002Spc {
53032002Spc 	int got, need = count;
53132002Spc 
53232002Spc 	while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
53332002Spc 		buf += got;
53432002Spc 	return (got < 0 ? got : count - need);
53532002Spc }
53632028Spc 
53732028Spc /*
53832028Spc  *	Added by Peter C
53932028Spc  *	put a tape drive offline
54032028Spc  */
rewind_offline(fd)54132028Spc rewind_offline(fd)
54232028Spc {
54332028Spc 	struct mtop mtop;
54432028Spc 	char *eofr;
54532028Spc 	char *eoff;
54632028Spc 
54732028Spc 	eofr = "Cannot write end of file record";
54832028Spc 	eoff = "Cannot put the tape offline";
54932028Spc #ifdef RDUMP
55032028Spc 	if (host) {
55132028Spc 		mtop.mt_op = MTWEOF;
55232028Spc 		mtop.mt_count = 1;
55332028Spc 		if (rmtioctl(fd, MTIOCTOP, &mtop) < 0)
55432028Spc 			perror(eofr);
55532028Spc 		mtop.mt_op = MTWEOF;
55632028Spc 		mtop.mt_count = 1;
55732028Spc 		if (rmtioctl(fd, MTIOCTOP, &mtop) < 0)
55832028Spc 			perror(eofr);
55932028Spc 		mtop.mt_op = MTOFFL;
56032028Spc 		mtop.mt_count = 1;
56132028Spc 		if (rmtioctl(fd, MTIOCTOP, &mtop) < 0)
56232028Spc 			perror(eoff);
56332028Spc 		return;
56432028Spc 	}
56532028Spc #endif RDUMP
56632028Spc 	mtop.mt_op = MTWEOF;
56732028Spc 	mtop.mt_count = 1;
56832028Spc 	if (ioctl(fd, MTIOCTOP, &mtop) < 0)
56932028Spc 		perror(eofr);
57032028Spc 	mtop.mt_op = MTWEOF;
57132028Spc 	mtop.mt_count = 1;
57232028Spc 	if (ioctl(fd, MTIOCTOP, &mtop) < 0)
57332028Spc 		perror(eofr);
57432028Spc 	mtop.mt_op = MTOFFL;
57532028Spc 	mtop.mt_count = 1;
57632028Spc 	if (ioctl(fd, MTIOCTOP, &mtop) < 0)
57732028Spc 		perror(eoff);
57832028Spc 
57932028Spc }
580