xref: /netbsd/sbin/restore/tape.c (revision bf9ec67e)
1 /*	$NetBSD: tape.c,v 1.46 2002/05/25 23:45:14 wiz Exp $	*/
2 
3 /*
4  * Copyright (c) 1983, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  * (c) UNIX System Laboratories, Inc.
7  * All or some portions of this file are derived from material licensed
8  * to the University of California by American Telephone and Telegraph
9  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
10  * the permission of UNIX System Laboratories, Inc.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40 
41 #include <sys/cdefs.h>
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)tape.c	8.9 (Berkeley) 5/1/95";
45 #else
46 __RCSID("$NetBSD: tape.c,v 1.46 2002/05/25 23:45:14 wiz Exp $");
47 #endif
48 #endif /* not lint */
49 
50 #include <sys/param.h>
51 #include <sys/file.h>
52 #include <sys/ioctl.h>
53 #include <sys/mtio.h>
54 #include <sys/stat.h>
55 
56 #include <ufs/ufs/dinode.h>
57 #include <protocols/dumprestore.h>
58 
59 #include <errno.h>
60 #include <paths.h>
61 #include <setjmp.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <time.h>
66 #include <unistd.h>
67 
68 #include "restore.h"
69 #include "extern.h"
70 
71 static u_int32_t fssize = MAXBSIZE;
72 static int	mt = -1;
73 static int	pipein = 0;
74 static char	magtape[BUFSIZ];
75 static int	blkcnt;
76 static int	numtrec;
77 static char	*tapebuf;
78 static union	u_spcl endoftapemark;
79 static int	blksread;		/* blocks read since last header */
80 static int	tpblksread = 0;		/* TP_BSIZE blocks read */
81 static int	tapesread;
82 static jmp_buf	restart;
83 static int	gettingfile = 0;	/* restart has a valid frame */
84 static char	*host = NULL;
85 
86 static int	ofile;
87 static char	*map;
88 static char	lnkbuf[MAXPATHLEN + 1];
89 static int	pathlen;
90 
91 int		oldinofmt;	/* old inode format conversion required */
92 int		Bcvt;		/* Swap Bytes (for CCI or sun) */
93 static int	Qcvt;		/* Swap quads (for sun) */
94 
95 #define	FLUSHTAPEBUF()	blkcnt = ntrec + 1
96 
97 static void	 accthdr __P((struct s_spcl *));
98 static int	 checksum __P((int *));
99 static void	 findinode __P((struct s_spcl *));
100 static void	 findtapeblksize __P((void));
101 static int	 gethead __P((struct s_spcl *));
102 static void	 readtape __P((char *));
103 static void	 setdumpnum __P((void));
104 static u_long	 swabl __P((u_long));
105 static u_char	*swablong __P((u_char *, int));
106 static u_char	*swabshort __P((u_char *, int));
107 static void	 terminateinput __P((void));
108 static void	 xtrfile __P((char *, long));
109 static void	 xtrlnkfile __P((char *, long));
110 static void	 xtrlnkskip __P((char *, long));
111 static void	 xtrmap __P((char *, long));
112 static void	 xtrmapskip __P((char *, long));
113 static void	 xtrskip __P((char *, long));
114 
115 /*
116  * Set up an input source
117  */
118 void
119 setinput(source)
120 	char *source;
121 {
122 	FLUSHTAPEBUF();
123 	if (bflag)
124 		newtapebuf(ntrec);
125 	else
126 		newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
127 	terminal = stdin;
128 
129 #ifdef RRESTORE
130 	if (strchr(source, ':')) {
131 		host = source;
132 		source = strchr(host, ':');
133 		*source++ = '\0';
134 		if (rmthost(host) == 0)
135 			exit(1);
136 	} else
137 #endif
138 	if (strcmp(source, "-") == 0) {
139 		/*
140 		 * Since input is coming from a pipe we must establish
141 		 * our own connection to the terminal.
142 		 */
143 		terminal = fopen(_PATH_TTY, "r");
144 		if (terminal == NULL) {
145 			(void)fprintf(stderr, "cannot open %s: %s\n",
146 			    _PATH_TTY, strerror(errno));
147 			terminal = fopen(_PATH_DEVNULL, "r");
148 			if (terminal == NULL) {
149 				(void)fprintf(stderr, "cannot open %s: %s\n",
150 				    _PATH_DEVNULL, strerror(errno));
151 				exit(1);
152 			}
153 		}
154 		pipein++;
155 	}
156 	(void) strcpy(magtape, source);
157 }
158 
159 void
160 newtapebuf(size)
161 	long size;
162 {
163 	static int tapebufsize = -1;
164 
165 	ntrec = size;
166 	if (size <= tapebufsize)
167 		return;
168 	if (tapebuf != NULL)
169 		free(tapebuf);
170 	tapebuf = malloc(size * TP_BSIZE);
171 	if (tapebuf == NULL) {
172 		fprintf(stderr, "Cannot allocate space for tape buffer\n");
173 		exit(1);
174 	}
175 	tapebufsize = size;
176 }
177 
178 /*
179  * Verify that the tape drive can be accessed and
180  * that it actually is a dump tape.
181  */
182 void
183 setup()
184 {
185 	int i, j, *ip;
186 	struct stat stbuf;
187 
188 	vprintf(stdout, "Verify tape and initialize maps\n");
189 #ifdef RRESTORE
190 	if (host)
191 		mt = rmtopen(magtape, 0);
192 	else
193 #endif
194 	if (pipein)
195 		mt = 0;
196 	else
197 		mt = open(magtape, O_RDONLY, 0);
198 	if (mt < 0) {
199 		fprintf(stderr, "%s: %s\n", magtape, strerror(errno));
200 		exit(1);
201 	}
202 	volno = 1;
203 	setdumpnum();
204 	FLUSHTAPEBUF();
205 	if (!pipein && !bflag)
206 		findtapeblksize();
207 	if (gethead(&spcl) == FAIL) {
208 		blkcnt--; /* push back this block */
209 		blksread--;
210 		tpblksread--;
211 		cvtflag++;
212 		if (gethead(&spcl) == FAIL) {
213 			fprintf(stderr, "Tape is not a dump tape\n");
214 			exit(1);
215 		}
216 		fprintf(stderr, "Converting to new file system format.\n");
217 	}
218 	if (pipein) {
219 		endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC;
220 		endoftapemark.s_spcl.c_type = TS_END;
221 		ip = (int *)&endoftapemark;
222 		j = sizeof(union u_spcl) / sizeof(int);
223 		i = 0;
224 		do
225 			i += *ip++;
226 		while (--j);
227 		endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
228 	}
229 	if (vflag || command == 't')
230 		printdumpinfo();
231 	dumptime = spcl.c_ddate;
232 	dumpdate = spcl.c_date;
233 	if (stat(".", &stbuf) < 0) {
234 		fprintf(stderr, "cannot stat .: %s\n", strerror(errno));
235 		exit(1);
236 	}
237 	if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE)
238 		fssize = stbuf.st_blksize;
239 	if (((fssize - 1) & fssize) != 0) {
240 		fprintf(stderr, "bad block size %d\n", fssize);
241 		exit(1);
242 	}
243 	if (spcl.c_volume != 1) {
244 		fprintf(stderr, "Tape is not volume 1 of the dump\n");
245 		exit(1);
246 	}
247 	if (gethead(&spcl) == FAIL) {
248 		dprintf(stdout, "header read failed at %d blocks\n", blksread);
249 		panic("no header after volume mark!\n");
250 	}
251 	findinode(&spcl);
252 	if (spcl.c_type != TS_CLRI) {
253 		fprintf(stderr, "Cannot find file removal list\n");
254 		exit(1);
255 	}
256 	maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
257 	dprintf(stdout, "maxino = %d\n", maxino);
258 	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
259 	if (map == NULL)
260 		panic("no memory for active inode map\n");
261 	usedinomap = map;
262 	curfile.action = USING;
263 	getfile(xtrmap, xtrmapskip);
264 	if (spcl.c_type != TS_BITS) {
265 		fprintf(stderr, "Cannot find file dump list\n");
266 		exit(1);
267 	}
268 	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
269 	if (map == (char *)NULL)
270 		panic("no memory for file dump list\n");
271 	dumpmap = map;
272 	curfile.action = USING;
273 	getfile(xtrmap, xtrmapskip);
274 	/*
275 	 * If there may be whiteout entries on the tape, pretend that the
276 	 * whiteout inode exists, so that the whiteout entries can be
277 	 * extracted.
278 	 */
279 	if (oldinofmt == 0)
280 		SETINO(WINO, dumpmap);
281 }
282 
283 /*
284  * Prompt user to load a new dump volume.
285  * "Nextvol" is the next suggested volume to use.
286  * This suggested volume is enforced when doing full
287  * or incremental restores, but can be overrridden by
288  * the user when only extracting a subset of the files.
289  */
290 void
291 getvol(nextvol)
292 	int nextvol;
293 {
294 	int newvol, savecnt, wantnext, i;
295 	union u_spcl tmpspcl;
296 #	define tmpbuf tmpspcl.s_spcl
297 	char buf[TP_BSIZE];
298 
299 	newvol = savecnt = wantnext = 0;
300 	if (nextvol == 1) {
301 		tapesread = 0;
302 		gettingfile = 0;
303 	}
304 	if (pipein) {
305 		if (nextvol != 1)
306 			panic("Changing volumes on pipe input?\n");
307 		if (volno == 1)
308 			return;
309 		goto gethdr;
310 	}
311 	savecnt = blksread;
312 again:
313 	if (pipein)
314 		exit(1); /* pipes do not get a second chance */
315 	if (command == 'R' || command == 'r' || curfile.action != SKIP) {
316 		newvol = nextvol;
317 		wantnext = 1;
318 	} else {
319 		newvol = 0;
320 		wantnext = 0;
321 	}
322 	while (newvol <= 0) {
323 		if (tapesread == 0) {
324 			fprintf(stderr, "%s%s%s%s%s",
325 			    "You have not read any tapes yet.\n",
326 			    "Unless you know which volume your",
327 			    " file(s) are on you should start\n",
328 			    "with the last volume and work",
329 			    " towards the first.\n");
330 			fprintf(stderr,
331 			    "(Use 1 for the first volume/tape, etc.)\n");
332 		} else {
333 			fprintf(stderr, "You have read volumes");
334 			strcpy(buf, ": ");
335 			for (i = 1; i < 32; i++)
336 				if (tapesread & (1 << i)) {
337 					fprintf(stderr, "%s%d", buf, i);
338 					strcpy(buf, ", ");
339 				}
340 			fprintf(stderr, "\n");
341 		}
342 		do	{
343 			fprintf(stderr, "Specify next volume #: ");
344 			(void) fflush(stderr);
345 			(void) fgets(buf, BUFSIZ, terminal);
346 		} while (!feof(terminal) && buf[0] == '\n');
347 		if (feof(terminal))
348 			exit(1);
349 		newvol = atoi(buf);
350 		if (newvol <= 0) {
351 			fprintf(stderr,
352 			    "Volume numbers are positive numerics\n");
353 		}
354 	}
355 	if (newvol == volno) {
356 		tapesread |= 1 << volno;
357 		return;
358 	}
359 	closemt();
360 	fprintf(stderr, "Mount tape volume %d\n", newvol);
361 	fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
362 	fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
363 	(void) fflush(stderr);
364 	(void) fgets(buf, BUFSIZ, terminal);
365 	if (feof(terminal))
366 		exit(1);
367 	if (!strcmp(buf, "none\n")) {
368 		terminateinput();
369 		return;
370 	}
371 	if (buf[0] != '\n') {
372 		(void) strcpy(magtape, buf);
373 		magtape[strlen(magtape) - 1] = '\0';
374 	}
375 #ifdef RRESTORE
376 	if (host)
377 		mt = rmtopen(magtape, 0);
378 	else
379 #endif
380 		mt = open(magtape, O_RDONLY, 0);
381 
382 	if (mt == -1) {
383 		fprintf(stderr, "Cannot open %s\n", magtape);
384 		volno = -1;
385 		goto again;
386 	}
387 gethdr:
388 	volno = newvol;
389 	setdumpnum();
390 	FLUSHTAPEBUF();
391 	if (gethead(&tmpbuf) == FAIL) {
392 		dprintf(stdout, "header read failed at %d blocks\n", blksread);
393 		fprintf(stderr, "tape is not dump tape\n");
394 		volno = 0;
395 		goto again;
396 	}
397 	if (tmpbuf.c_volume != volno) {
398 	  	fprintf(stderr,
399 		"Volume mismatch: expecting %d, tape header claims it is %d\n",
400 		    volno, tmpbuf.c_volume);
401 		volno = 0;
402 		goto again;
403 	}
404 	if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) {
405 		time_t ttime = tmpbuf.c_date;
406 		fprintf(stderr, "Wrong dump date\n\tgot: %s",
407 			ctime(&ttime));
408 		fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
409 		volno = 0;
410 		goto again;
411 	}
412 	tapesread |= 1 << volno;
413 	blksread = savecnt;
414  	/*
415  	 * If continuing from the previous volume, skip over any
416  	 * blocks read already at the end of the previous volume.
417  	 *
418  	 * If coming to this volume at random, skip to the beginning
419  	 * of the next record.
420  	 */
421 	dprintf(stdout, "read %ld recs, tape starts with %ld\n",
422 		(long)tpblksread, (long)tmpbuf.c_firstrec);
423  	if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) {
424  		if (!wantnext) {
425  			tpblksread = tmpbuf.c_firstrec;
426  			for (i = tmpbuf.c_count; i > 0; i--)
427  				readtape(buf);
428  		} else if (tmpbuf.c_firstrec > 0 &&
429 			   tmpbuf.c_firstrec < tpblksread - 1) {
430 			/*
431 			 * -1 since we've read the volume header
432 			 */
433  			i = tpblksread - tmpbuf.c_firstrec - 1;
434 			dprintf(stderr, "Skipping %d duplicate record%s.\n",
435 				i, i > 1 ? "s" : "");
436  			while (--i >= 0)
437  				readtape(buf);
438  		}
439  	}
440 	if (curfile.action == USING) {
441 		if (volno == 1)
442 			panic("active file into volume 1\n");
443 		return;
444 	}
445 	/*
446 	 * Skip up to the beginning of the next record
447 	 */
448 	if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER))
449 		for (i = tmpbuf.c_count; i > 0; i--)
450 			readtape(buf);
451 	(void) gethead(&spcl);
452 	findinode(&spcl);
453 	if (gettingfile) {
454 		gettingfile = 0;
455 		longjmp(restart, 1);
456 	}
457 }
458 
459 /*
460  * Handle unexpected EOF.
461  */
462 static void
463 terminateinput()
464 {
465 
466 	if (gettingfile && curfile.action == USING) {
467 		printf("Warning: %s %s\n",
468 		    "End-of-input encountered while extracting", curfile.name);
469 	}
470 	curfile.name = "<name unknown>";
471 	curfile.action = UNKNOWN;
472 	curfile.dip = NULL;
473 	curfile.ino = maxino;
474 	if (gettingfile) {
475 		gettingfile = 0;
476 		longjmp(restart, 1);
477 	}
478 }
479 
480 /*
481  * handle multiple dumps per tape by skipping forward to the
482  * appropriate one.
483  */
484 static void
485 setdumpnum()
486 {
487 	struct mtop tcom;
488 
489 	if (dumpnum == 1 || volno != 1)
490 		return;
491 	if (pipein) {
492 		fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
493 		exit(1);
494 	}
495 	tcom.mt_op = MTFSF;
496 	tcom.mt_count = dumpnum - 1;
497 #ifdef RRESTORE
498 	if (host)
499 		rmtioctl(MTFSF, dumpnum - 1);
500 	else
501 #endif
502 		if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0)
503 			fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno));
504 }
505 
506 void
507 printdumpinfo()
508 {
509 	time_t ttime;
510 
511 	ttime = spcl.c_date;
512 	fprintf(stdout, "Dump   date: %s", ctime(&ttime));
513 	ttime = spcl.c_ddate;
514 	fprintf(stdout, "Dumped from: %s",
515 	    (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&ttime));
516 	fprintf(stderr, "Level %d dump of %s on %s:%s\n",
517 		spcl.c_level, spcl.c_filesys,
518 		*spcl.c_host? spcl.c_host: "[unknown]", spcl.c_dev);
519 	fprintf(stderr, "Label: %s\n", spcl.c_label);
520 }
521 
522 int
523 extractfile(name)
524 	char *name;
525 {
526 	int flags;
527 	uid_t uid;
528 	gid_t gid;
529 	mode_t mode;
530 	struct timeval timep[2];
531 	struct entry *ep;
532 
533 	curfile.name = name;
534 	curfile.action = USING;
535 	timep[0].tv_sec = curfile.dip->di_atime;
536 	timep[0].tv_usec = curfile.dip->di_atimensec / 1000;
537 	timep[1].tv_sec = curfile.dip->di_mtime;
538 	timep[1].tv_usec = curfile.dip->di_mtimensec / 1000;
539 	uid = curfile.dip->di_uid;
540 	gid = curfile.dip->di_gid;
541 	mode = curfile.dip->di_mode;
542 	flags = curfile.dip->di_flags;
543 	switch (mode & IFMT) {
544 
545 	default:
546 		fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
547 		skipfile();
548 		return (FAIL);
549 
550 	case IFSOCK:
551 		vprintf(stdout, "skipped socket %s\n", name);
552 		skipfile();
553 		return (GOOD);
554 
555 	case IFDIR:
556 		if (mflag) {
557 			ep = lookupname(name);
558 			if (ep == NULL || ep->e_flags & EXTRACT)
559 				panic("unextracted directory %s\n", name);
560 			skipfile();
561 			return (GOOD);
562 		}
563 		vprintf(stdout, "extract file %s\n", name);
564 		return (genliteraldir(name, curfile.ino));
565 
566 	case IFLNK:
567 		lnkbuf[0] = '\0';
568 		pathlen = 0;
569 		getfile(xtrlnkfile, xtrlnkskip);
570 		if (pathlen == 0) {
571 			vprintf(stdout,
572 			    "%s: zero length symbolic link (ignored)\n", name);
573 			return (GOOD);
574 		}
575 		if (uflag)
576 			(void) unlink(name);
577 		if (linkit(lnkbuf, name, SYMLINK) == GOOD) {
578 			(void) lutimes(name, timep);
579 			(void) lchown(name, uid, gid);
580 			(void) lchmod(name, mode);
581 			(void) lchflags(name, flags);
582 			return (GOOD);
583 		}
584 		return (FAIL);
585 
586 	case IFCHR:
587 	case IFBLK:
588 		vprintf(stdout, "extract special file %s\n", name);
589 		if (Nflag) {
590 			skipfile();
591 			return (GOOD);
592 		}
593 		if (uflag)
594 			(void) unlink(name);
595 		if (mknod(name, (mode & (IFCHR | IFBLK)) | 0600,
596 		    (int)curfile.dip->di_rdev) < 0) {
597 			fprintf(stderr, "%s: cannot create special file: %s\n",
598 			    name, strerror(errno));
599 			skipfile();
600 			return (FAIL);
601 		}
602 		skipfile();
603 		(void) utimes(name, timep);
604 		(void) chown(name, uid, gid);
605 		(void) chmod(name, mode);
606 		(void) chflags(name, flags);
607 		return (GOOD);
608 
609 	case IFIFO:
610 		vprintf(stdout, "extract fifo %s\n", name);
611 		if (Nflag) {
612 			skipfile();
613 			return (GOOD);
614 		}
615 		if (uflag)
616 			(void) unlink(name);
617 		if (mkfifo(name, 0600) < 0) {
618 			fprintf(stderr, "%s: cannot create fifo: %s\n",
619 			    name, strerror(errno));
620 			skipfile();
621 			return (FAIL);
622 		}
623 		skipfile();
624 		(void) utimes(name, timep);
625 		(void) chown(name, uid, gid);
626 		(void) chmod(name, mode);
627 		(void) chflags(name, flags);
628 		return (GOOD);
629 
630 	case IFREG:
631 		vprintf(stdout, "extract file %s\n", name);
632 		if (Nflag) {
633 			skipfile();
634 			return (GOOD);
635 		}
636 		if (uflag)
637 			(void) unlink(name);
638 		if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
639 		    0600)) < 0) {
640 			fprintf(stderr, "%s: cannot create file: %s\n",
641 			    name, strerror(errno));
642 			skipfile();
643 			return (FAIL);
644 		}
645 		getfile(xtrfile, xtrskip);
646 		(void) futimes(ofile, timep);
647 		(void) fchown(ofile, uid, gid);
648 		(void) fchmod(ofile, mode);
649 		(void) fchflags(ofile, flags);
650 		(void) close(ofile);
651 		return (GOOD);
652 	}
653 	/* NOTREACHED */
654 }
655 
656 /*
657  * skip over bit maps on the tape
658  */
659 void
660 skipmaps()
661 {
662 
663 	while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
664 		skipfile();
665 }
666 
667 /*
668  * skip over a file on the tape
669  */
670 void
671 skipfile()
672 {
673 
674 	curfile.action = SKIP;
675 	getfile(xtrnull, xtrnull);
676 }
677 
678 /*
679  * Extract a file from the tape.
680  * When an allocated block is found it is passed to the fill function;
681  * when an unallocated block (hole) is found, a zeroed buffer is passed
682  * to the skip function.
683  */
684 void
685 getfile(fill, skip)
686 	void	(*fill) __P((char *, long));
687 	void	(*skip) __P((char *, long));
688 {
689 	int i;
690 	int curblk = 0;
691 	quad_t size = spcl.c_dinode.di_size;
692 	static char clearedbuf[MAXBSIZE];
693 	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
694 	char junk[TP_BSIZE];
695 
696 #ifdef __GNUC__			/* XXX: to shut up gcc warnings */
697 	(void)&curblk;
698 	(void)&size;
699 #endif
700 
701 	if (spcl.c_type == TS_END)
702 		panic("ran off end of tape\n");
703 	if (spcl.c_magic != NFS_MAGIC)
704 		panic("not at beginning of a file\n");
705 	if (!gettingfile && setjmp(restart) != 0)
706 		return;
707 	gettingfile++;
708 loop:
709 	for (i = 0; i < spcl.c_count; i++) {
710 		if (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI ||
711 		    spcl.c_addr[i]) {
712 			readtape(&buf[curblk++][0]);
713 			if (curblk == fssize / TP_BSIZE) {
714 				(*fill)((char *)buf, (long)(size > TP_BSIZE ?
715 				     fssize : (curblk - 1) * TP_BSIZE + size));
716 				curblk = 0;
717 			}
718 		} else {
719 			if (curblk > 0) {
720 				(*fill)((char *)buf, (long)(size > TP_BSIZE ?
721 				     curblk * TP_BSIZE :
722 				     (curblk - 1) * TP_BSIZE + size));
723 				curblk = 0;
724 			}
725 			(*skip)(clearedbuf, (long)(size > TP_BSIZE ?
726 				TP_BSIZE : size));
727 		}
728 		if ((size -= TP_BSIZE) <= 0) {
729 			if (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI) {
730 				/*
731 				 * In this case, the following expression
732 				 * should always be false since the size was
733 				 * initially set to spcl.c_dinode.di_size and
734 				 * it is initialized to spcl.c_count * TP_BSIZE
735 				 * in gethead().
736 				 */
737 				if (!(size == 0 && i == spcl.c_count - 1))
738 					panic("inconsistent map size\n");
739 			} else {
740 				for (i++; i < spcl.c_count; i++)
741 					if (spcl.c_addr[i])
742 						readtape(junk);
743 			}
744 			break;
745 		}
746 	}
747 	if (gethead(&spcl) == GOOD && size > 0) {
748 		if (spcl.c_type == TS_ADDR)
749 			goto loop;
750 		dprintf(stdout,
751 			"Missing address (header) block for %s at %d blocks\n",
752 			curfile.name, blksread);
753 	}
754 	if (curblk > 0)
755 		(*fill)((char *)buf, (long)((curblk * TP_BSIZE) + size));
756 	findinode(&spcl);
757 	gettingfile = 0;
758 }
759 
760 /*
761  * Write out the next block of a file.
762  */
763 static void
764 xtrfile(buf, size)
765 	char	*buf;
766 	long	size;
767 {
768 
769 	if (Nflag)
770 		return;
771 	if (write(ofile, buf, (int) size) == -1) {
772 		fprintf(stderr,
773 		    "write error extracting inode %d, name %s\nwrite: %s\n",
774 			curfile.ino, curfile.name, strerror(errno));
775 		exit(1);
776 	}
777 }
778 
779 /*
780  * Skip over a hole in a file.
781  */
782 /* ARGSUSED */
783 static void
784 xtrskip(buf, size)
785 	char *buf;
786 	long size;
787 {
788 
789 	if (lseek(ofile, size, SEEK_CUR) == -1) {
790 		fprintf(stderr,
791 		    "seek error extracting inode %d, name %s\nlseek: %s\n",
792 			curfile.ino, curfile.name, strerror(errno));
793 		exit(1);
794 	}
795 }
796 
797 /*
798  * Collect the next block of a symbolic link.
799  */
800 static void
801 xtrlnkfile(buf, size)
802 	char	*buf;
803 	long	size;
804 {
805 
806 	pathlen += size;
807 	if (pathlen > MAXPATHLEN) {
808 		fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
809 		    curfile.name, lnkbuf, buf, pathlen);
810 		exit(1);
811 	}
812 	(void) strcat(lnkbuf, buf);
813 }
814 
815 /*
816  * Skip over a hole in a symbolic link (should never happen).
817  */
818 /* ARGSUSED */
819 static void
820 xtrlnkskip(buf, size)
821 	char *buf;
822 	long size;
823 {
824 
825 	fprintf(stderr, "unallocated block in symbolic link %s\n",
826 		curfile.name);
827 	exit(1);
828 }
829 
830 /*
831  * Collect the next block of a bit map.
832  */
833 static void
834 xtrmap(buf, size)
835 	char	*buf;
836 	long	size;
837 {
838 
839 	memmove(map, buf, size);
840 	map += size;
841 }
842 
843 /*
844  * Skip over a hole in a bit map (should never happen).
845  */
846 /* ARGSUSED */
847 static void
848 xtrmapskip(buf, size)
849 	char *buf;
850 	long size;
851 {
852 
853 	panic("hole in map\n");
854 	map += size;
855 }
856 
857 /*
858  * Noop, when an extraction function is not needed.
859  */
860 /* ARGSUSED */
861 void
862 xtrnull(buf, size)
863 	char *buf;
864 	long size;
865 {
866 
867 	return;
868 }
869 
870 /*
871  * Read TP_BSIZE blocks from the input.
872  * Handle read errors, and end of media.
873  */
874 static void
875 readtape(buf)
876 	char *buf;
877 {
878 	int rd, newvol, i;
879 	int cnt, seek_failed;
880 
881 	if (blkcnt < numtrec) {
882 		memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
883 		blksread++;
884 		tpblksread++;
885 		return;
886 	}
887 	for (i = 0; i < ntrec; i++)
888 		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
889 	if (numtrec == 0)
890 		numtrec = ntrec;
891 	cnt = ntrec * TP_BSIZE;
892 	rd = 0;
893 getmore:
894 #ifdef RRESTORE
895 	if (host)
896 		i = rmtread(&tapebuf[rd], cnt);
897 	else
898 #endif
899 		i = read(mt, &tapebuf[rd], cnt);
900 	/*
901 	 * Check for mid-tape short read error.
902 	 * If found, skip rest of buffer and start with the next.
903 	 */
904 	if (!pipein && numtrec < ntrec && i > 0) {
905 		dprintf(stdout, "mid-media short read error.\n");
906 		numtrec = ntrec;
907 	}
908 	/*
909 	 * Handle partial block read.
910 	 */
911 	if (pipein && i == 0 && rd > 0)
912 		i = rd;
913 	else if (i > 0 && i != ntrec * TP_BSIZE) {
914 		if (pipein) {
915 			rd += i;
916 			cnt -= i;
917 			if (cnt > 0)
918 				goto getmore;
919 			i = rd;
920 		} else {
921 			/*
922 			 * Short read. Process the blocks read.
923 			 */
924 			if (i % TP_BSIZE != 0)
925 				vprintf(stdout,
926 				    "partial block read: %d should be %d\n",
927 				    i, ntrec * TP_BSIZE);
928 			numtrec = i / TP_BSIZE;
929 		}
930 	}
931 	/*
932 	 * Handle read error.
933 	 */
934 	if (i < 0) {
935 		fprintf(stderr, "Tape read error while ");
936 		switch (curfile.action) {
937 		default:
938 			fprintf(stderr, "trying to set up tape\n");
939 			break;
940 		case UNKNOWN:
941 			fprintf(stderr, "trying to resynchronize\n");
942 			break;
943 		case USING:
944 			fprintf(stderr, "restoring %s\n", curfile.name);
945 			break;
946 		case SKIP:
947 			fprintf(stderr, "skipping over inode %d\n",
948 				curfile.ino);
949 			break;
950 		}
951 		if (!yflag && !reply("continue"))
952 			exit(1);
953 		i = ntrec * TP_BSIZE;
954 		memset(tapebuf, 0, i);
955 #ifdef RRESTORE
956 		if (host)
957 			seek_failed = (rmtseek(i, 1) < 0);
958 		else
959 #endif
960 			seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
961 
962 		if (seek_failed) {
963 			fprintf(stderr,
964 			    "continuation failed: %s\n", strerror(errno));
965 			exit(1);
966 		}
967 	}
968 	/*
969 	 * Handle end of tape.
970 	 */
971 	if (i == 0) {
972 		vprintf(stdout, "End-of-tape encountered\n");
973 		if (!pipein) {
974 			newvol = volno + 1;
975 			volno = 0;
976 			numtrec = 0;
977 			getvol(newvol);
978 			readtape(buf);
979 			return;
980 		}
981 		if (rd % TP_BSIZE != 0)
982 			panic("partial block read: %d should be %d\n",
983 				rd, ntrec * TP_BSIZE);
984 		terminateinput();
985 		memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE);
986 	}
987 	blkcnt = 0;
988 	memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
989 	blksread++;
990 	tpblksread++;
991 }
992 
993 static void
994 findtapeblksize()
995 {
996 	long i;
997 
998 	for (i = 0; i < ntrec; i++)
999 		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
1000 	blkcnt = 0;
1001 #ifdef RRESTORE
1002 	if (host)
1003 		i = rmtread(tapebuf, ntrec * TP_BSIZE);
1004 	else
1005 #endif
1006 		i = read(mt, tapebuf, ntrec * TP_BSIZE);
1007 
1008 	if (i <= 0) {
1009 		fprintf(stderr, "tape read error: %s\n", strerror(errno));
1010 		exit(1);
1011 	}
1012 	if (i % TP_BSIZE != 0) {
1013 		fprintf(stderr, "Tape block size (%ld) %s (%ld)\n",
1014 			(long)i, "is not a multiple of dump block size",
1015 			(long)TP_BSIZE);
1016 		exit(1);
1017 	}
1018 	ntrec = i / TP_BSIZE;
1019 	numtrec = ntrec;
1020 	vprintf(stdout, "Tape block size is %d\n", ntrec);
1021 }
1022 
1023 void
1024 closemt()
1025 {
1026 
1027 	if (mt < 0)
1028 		return;
1029 #ifdef RRESTORE
1030 	if (host)
1031 		rmtclose();
1032 	else
1033 #endif
1034 		(void) close(mt);
1035 }
1036 
1037 /*
1038  * Read the next block from the tape.
1039  * Check to see if it is one of several vintage headers.
1040  * If it is an old style header, convert it to a new style header.
1041  * If it is not any valid header, return an error.
1042  */
1043 static int
1044 gethead(buf)
1045 	struct s_spcl *buf;
1046 {
1047 	long i;
1048 	union {
1049 		quad_t	qval;
1050 		int32_t	val[2];
1051 	} qcvt;
1052 	union u_ospcl {
1053 		char dummy[TP_BSIZE];
1054 		struct	s_ospcl {
1055 			int32_t   c_type;
1056 			int32_t   c_date;
1057 			int32_t   c_ddate;
1058 			int32_t   c_volume;
1059 			int32_t   c_tapea;
1060 			u_int16_t c_inumber;
1061 			int32_t   c_magic;
1062 			int32_t   c_checksum;
1063 			struct odinode {
1064 				unsigned short odi_mode;
1065 				u_int16_t odi_nlink;
1066 				u_int16_t odi_uid;
1067 				u_int16_t odi_gid;
1068 				int32_t   odi_size;
1069 				int32_t   odi_rdev;
1070 				char      odi_addr[36];
1071 				int32_t   odi_atime;
1072 				int32_t   odi_mtime;
1073 				int32_t   odi_ctime;
1074 			} c_dinode;
1075 			int32_t c_count;
1076 			char    c_addr[256];
1077 		} s_ospcl;
1078 	} u_ospcl;
1079 
1080 	if (!cvtflag) {
1081 		readtape((char *)buf);
1082 		if (buf->c_magic != NFS_MAGIC) {
1083 			if (swabl(buf->c_magic) != NFS_MAGIC)
1084 				return (FAIL);
1085 			if (!Bcvt) {
1086 				vprintf(stdout, "Note: Doing Byte swapping\n");
1087 				Bcvt = 1;
1088 			}
1089 		}
1090 		if (checksum((int *)buf) == FAIL)
1091 			return (FAIL);
1092 		if (Bcvt)
1093 			swabst((u_char *)"8l4s31l528b1l192b2l", (u_char *)buf);
1094 		goto good;
1095 	}
1096 	readtape((char *)(&u_ospcl.s_ospcl));
1097 	memset(buf, 0, (long)TP_BSIZE);
1098 	buf->c_type = u_ospcl.s_ospcl.c_type;
1099 	buf->c_date = u_ospcl.s_ospcl.c_date;
1100 	buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
1101 	buf->c_volume = u_ospcl.s_ospcl.c_volume;
1102 	buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
1103 	buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
1104 	buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
1105 	buf->c_magic = u_ospcl.s_ospcl.c_magic;
1106 	buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
1107 	buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
1108 	buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
1109 	buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
1110 	buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
1111 	buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
1112 	buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime;
1113 	buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime;
1114 	buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime;
1115 	buf->c_count = u_ospcl.s_ospcl.c_count;
1116 	memmove(buf->c_addr, u_ospcl.s_ospcl.c_addr, (long)256);
1117 	if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
1118 	    checksum((int *)(&u_ospcl.s_ospcl)) == FAIL)
1119 		return(FAIL);
1120 	buf->c_magic = NFS_MAGIC;
1121 
1122 good:
1123 	if ((buf->c_dinode.di_size == 0 || buf->c_dinode.di_size > 0xfffffff) &&
1124 	    (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) {
1125 		qcvt.qval = buf->c_dinode.di_size;
1126 		if (qcvt.val[0] || qcvt.val[1]) {
1127 			printf("Note: Doing Quad swapping\n");
1128 			Qcvt = 1;
1129 		}
1130 	}
1131 	if (Qcvt) {
1132 		qcvt.qval = buf->c_dinode.di_size;
1133 		i = qcvt.val[1];
1134 		qcvt.val[1] = qcvt.val[0];
1135 		qcvt.val[0] = i;
1136 		buf->c_dinode.di_size = qcvt.qval;
1137 	}
1138 
1139 	switch (buf->c_type) {
1140 
1141 	case TS_CLRI:
1142 	case TS_BITS:
1143 		/*
1144 		 * Have to patch up missing information in bit map headers
1145 		 */
1146 		buf->c_inumber = 0;
1147 		buf->c_dinode.di_size = buf->c_count * TP_BSIZE;
1148 		break;
1149 
1150 	case TS_TAPE:
1151 		if ((buf->c_flags & DR_NEWINODEFMT) == 0)
1152 			oldinofmt = 1;
1153 		/* fall through */
1154 	case TS_END:
1155 		buf->c_inumber = 0;
1156 		break;
1157 
1158 	case TS_INODE:
1159 	case TS_ADDR:
1160 		break;
1161 
1162 	default:
1163 		panic("gethead: unknown inode type %d\n", buf->c_type);
1164 		break;
1165 	}
1166 	/*
1167 	 * If we are restoring a filesystem with old format inodes,
1168 	 * copy the uid/gid to the new location.
1169 	 */
1170 	if (oldinofmt) {
1171 		buf->c_dinode.di_uid = buf->c_dinode.di_ouid;
1172 		buf->c_dinode.di_gid = buf->c_dinode.di_ogid;
1173 	}
1174 	if (dflag)
1175 		accthdr(buf);
1176 	return(GOOD);
1177 }
1178 
1179 /*
1180  * Check that a header is where it belongs and predict the next header
1181  */
1182 static void
1183 accthdr(header)
1184 	struct s_spcl *header;
1185 {
1186 	static ino_t previno = 0x7fffffff;
1187 	static int prevtype;
1188 	static long predict;
1189 	long blks, i;
1190 
1191 	if (header->c_type == TS_TAPE) {
1192 		fprintf(stderr, "Volume header (%s inode format) ",
1193 		    oldinofmt ? "old" : "new");
1194  		if (header->c_firstrec)
1195  			fprintf(stderr, "begins with record %d",
1196  				header->c_firstrec);
1197  		fprintf(stderr, "\n");
1198 		previno = 0x7fffffff;
1199 		return;
1200 	}
1201 	if (previno == 0x7fffffff)
1202 		goto newcalc;
1203 	switch (prevtype) {
1204 	case TS_BITS:
1205 		fprintf(stderr, "Dumped inodes map header");
1206 		break;
1207 	case TS_CLRI:
1208 		fprintf(stderr, "Used inodes map header");
1209 		break;
1210 	case TS_INODE:
1211 		fprintf(stderr, "File header, ino %d", previno);
1212 		break;
1213 	case TS_ADDR:
1214 		fprintf(stderr, "File continuation header, ino %d", previno);
1215 		break;
1216 	case TS_END:
1217 		fprintf(stderr, "End of tape header");
1218 		break;
1219 	}
1220 	if (predict != blksread - 1)
1221 		fprintf(stderr, "; predicted %ld blocks, got %ld blocks",
1222 			(long)predict, (long)(blksread - 1));
1223 	fprintf(stderr, "\n");
1224 newcalc:
1225 	blks = 0;
1226 	switch (header->c_type) {
1227 	case TS_END:
1228 		break;
1229 	case TS_CLRI:
1230 	case TS_BITS:
1231 		blks = header->c_count;
1232 		break;
1233 	default:
1234 		for (i = 0; i < header->c_count; i++)
1235 			if (header->c_addr[i] != 0)
1236 				blks++;
1237 		break;
1238 	}
1239 	predict = blks;
1240 	blksread = 0;
1241 	prevtype = header->c_type;
1242 	previno = header->c_inumber;
1243 }
1244 
1245 /*
1246  * Find an inode header.
1247  * Complain if had to skip, and complain is set.
1248  */
1249 static void
1250 findinode(header)
1251 	struct s_spcl *header;
1252 {
1253 	static long skipcnt = 0;
1254 	long i;
1255 	char buf[TP_BSIZE];
1256 
1257 	curfile.name = "<name unknown>";
1258 	curfile.action = UNKNOWN;
1259 	curfile.dip = NULL;
1260 	curfile.ino = 0;
1261     top:
1262 	do {
1263 		if (header->c_magic != NFS_MAGIC) {
1264 			skipcnt++;
1265 			while (gethead(header) == FAIL ||
1266 			    header->c_date != dumpdate)
1267 				skipcnt++;
1268 		}
1269 		switch (header->c_type) {
1270 
1271 		case TS_ADDR:
1272 			/*
1273 			 * Skip up to the beginning of the next record
1274 			 */
1275 			for (i = 0; i < header->c_count; i++)
1276 				if (header->c_addr[i])
1277 					readtape(buf);
1278 			while (gethead(header) == FAIL ||
1279 			    header->c_date != dumpdate)
1280 				skipcnt++;
1281 			/* We've read a header; don't drop it. */
1282 			goto top;
1283 
1284 		case TS_INODE:
1285 			curfile.dip = &header->c_dinode;
1286 			curfile.ino = header->c_inumber;
1287 			break;
1288 
1289 		case TS_END:
1290 			curfile.ino = maxino;
1291 			break;
1292 
1293 		case TS_CLRI:
1294 			curfile.name = "<file removal list>";
1295 			break;
1296 
1297 		case TS_BITS:
1298 			curfile.name = "<file dump list>";
1299 			break;
1300 
1301 		case TS_TAPE:
1302 			panic("unexpected tape header\n");
1303 			break;
1304 
1305 		default:
1306 			panic("unknown tape header type %d\n", spcl.c_type);
1307 			break;
1308 
1309 		}
1310 	} while (header->c_type == TS_ADDR);
1311 	if (skipcnt > 0)
1312 		fprintf(stderr, "resync restore, skipped %ld blocks\n",
1313 		    (long)skipcnt);
1314 	skipcnt = 0;
1315 }
1316 
1317 static int
1318 checksum(buf)
1319 	int *buf;
1320 {
1321 	int i, j;
1322 
1323 	j = sizeof(union u_spcl) / sizeof(int);
1324 	i = 0;
1325 	if(!Bcvt) {
1326 		do
1327 			i += *buf++;
1328 		while (--j);
1329 	} else {
1330 		/* What happens if we want to read restore tapes
1331 			for a 16bit int machine??? */
1332 		do
1333 			i += swabl(*buf++);
1334 		while (--j);
1335 	}
1336 
1337 	if (i != CHECKSUM) {
1338 		fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
1339 			curfile.ino, curfile.name);
1340 		return(FAIL);
1341 	}
1342 	return(GOOD);
1343 }
1344 
1345 #ifdef RRESTORE
1346 #include <stdarg.h>
1347 
1348 void
1349 msg(const char *fmt, ...)
1350 {
1351 	va_list ap;
1352 
1353 	va_start(ap, fmt);
1354 	(void)vfprintf(stderr, fmt, ap);
1355 	va_end(ap);
1356 }
1357 #endif /* RRESTORE */
1358 
1359 static u_char *
1360 swabshort(sp, n)
1361 	u_char *sp;
1362 	int n;
1363 {
1364 	char c;
1365 
1366 	while (--n >= 0) {
1367 		c = sp[0]; sp[0] = sp[1]; sp[1] = c;
1368 		sp += 2;
1369 	}
1370 	return (sp);
1371 }
1372 
1373 static u_char *
1374 swablong(sp, n)
1375 	u_char *sp;
1376 	int n;
1377 {
1378 	char c;
1379 
1380 	while (--n >= 0) {
1381 		c = sp[0]; sp[0] = sp[3]; sp[3] = c;
1382 		c = sp[2]; sp[2] = sp[1]; sp[1] = c;
1383 		sp += 4;
1384 	}
1385 	return (sp);
1386 }
1387 
1388 void
1389 swabst(cp, sp)
1390 	u_char *cp, *sp;
1391 {
1392 	int n = 0;
1393 
1394 	while (*cp) {
1395 		switch (*cp) {
1396 		case '0': case '1': case '2': case '3': case '4':
1397 		case '5': case '6': case '7': case '8': case '9':
1398 			n = (n * 10) + (*cp++ - '0');
1399 			continue;
1400 
1401 		case 's': case 'w': case 'h':
1402 			if (n == 0)
1403 				n = 1;
1404 			sp = swabshort(sp, n);
1405 			break;
1406 
1407 		case 'l':
1408 			if (n == 0)
1409 				n = 1;
1410 			sp = swablong(sp, n);
1411 			break;
1412 
1413 		default: /* Any other character, like 'b' counts as byte. */
1414 			if (n == 0)
1415 				n = 1;
1416 			sp += n;
1417 			break;
1418 		}
1419 		cp++;
1420 		n = 0;
1421 	}
1422 }
1423 
1424 static u_long
1425 swabl(x)
1426 	u_long x;
1427 {
1428 	swabst((u_char *)"l", (u_char *)&x);
1429 	return (x);
1430 }
1431