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