xref: /freebsd/sbin/restore/interactive.c (revision a2f733abcff64628b7771a47089628b7327a88bd)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1985, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #ifndef lint
33 #endif /* not lint */
34 
35 #include <sys/cdefs.h>
36 #include <sys/param.h>
37 #include <sys/stat.h>
38 
39 #include <ufs/ufs/dinode.h>
40 #include <ufs/ufs/dir.h>
41 #include <protocols/dumprestore.h>
42 
43 #include <ctype.h>
44 #include <glob.h>
45 #include <limits.h>
46 #include <setjmp.h>
47 #include <stdint.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 
52 #include "restore.h"
53 #include "extern.h"
54 
55 /*
56  * Things to handle interruptions.
57  */
58 static int runshell;
59 static jmp_buf reset;
60 static char *nextarg = NULL;
61 
62 /*
63  * Structure and routines associated with listing directories.
64  */
65 struct afile {
66 	ino_t	fnum;		/* inode number of file */
67 	char	*fname;		/* file name */
68 	short	len;		/* name length */
69 	char	prefix;		/* prefix character */
70 	char	postfix;	/* postfix character */
71 };
72 struct arglist {
73 	int	freeglob;	/* glob structure needs to be freed */
74 	int	argcnt;		/* next globbed argument to return */
75 	glob_t	glob;		/* globbing information */
76 	char	*cmd;		/* the current command */
77 };
78 
79 static char	*copynext(char *, char *);
80 static int	 fcmp(const void *, const void *);
81 static void	 formatf(struct afile *, int);
82 static void	 getcmd(char *, char *, char *, size_t, struct arglist *);
83 struct dirent	*glob_readdir(void *);
84 static int	 glob_stat(const char *, struct stat *);
85 static void	 mkentry(char *, struct direct *, struct afile *);
86 static void	 printlist(char *, char *);
87 
88 /*
89  * Read and execute commands from the terminal.
90  */
91 void
92 runcmdshell(void)
93 {
94 	struct entry *np;
95 	ino_t ino;
96 	struct arglist arglist;
97 	char curdir[MAXPATHLEN];
98 	char name[MAXPATHLEN];
99 	char cmd[BUFSIZ];
100 
101 	arglist.freeglob = 0;
102 	arglist.argcnt = 0;
103 	arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
104 	arglist.glob.gl_opendir = rst_opendir;
105 	arglist.glob.gl_readdir = glob_readdir;
106 	arglist.glob.gl_closedir = rst_closedir;
107 	arglist.glob.gl_lstat = glob_stat;
108 	arglist.glob.gl_stat = glob_stat;
109 	canon("/", curdir, sizeof(curdir));
110 loop:
111 	if (setjmp(reset) != 0) {
112 		if (arglist.freeglob != 0) {
113 			arglist.freeglob = 0;
114 			arglist.argcnt = 0;
115 			globfree(&arglist.glob);
116 		}
117 		nextarg = NULL;
118 		volno = 0;
119 	}
120 	runshell = 1;
121 	getcmd(curdir, cmd, name, sizeof(name), &arglist);
122 	switch (cmd[0]) {
123 	/*
124 	 * Add elements to the extraction list.
125 	 */
126 	case 'a':
127 		if (strncmp(cmd, "add", strlen(cmd)) != 0)
128 			goto bad;
129 		ino = dirlookup(name);
130 		if (ino == 0)
131 			break;
132 		if (mflag)
133 			pathcheck(name);
134 		treescan(name, ino, addfile);
135 		break;
136 	/*
137 	 * Change working directory.
138 	 */
139 	case 'c':
140 		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
141 			goto bad;
142 		ino = dirlookup(name);
143 		if (ino == 0)
144 			break;
145 		if (inodetype(ino) == LEAF) {
146 			fprintf(stderr, "%s: not a directory\n", name);
147 			break;
148 		}
149 		(void) strcpy(curdir, name);
150 		break;
151 	/*
152 	 * Delete elements from the extraction list.
153 	 */
154 	case 'd':
155 		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
156 			goto bad;
157 		np = lookupname(name);
158 		if (np == NULL || (np->e_flags & NEW) == 0) {
159 			fprintf(stderr, "%s: not on extraction list\n", name);
160 			break;
161 		}
162 		treescan(name, np->e_ino, deletefile);
163 		break;
164 	/*
165 	 * Extract the requested list.
166 	 */
167 	case 'e':
168 		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
169 			goto bad;
170 		createfiles();
171 		createlinks();
172 		setdirmodes(0);
173 		if (dflag)
174 			checkrestore();
175 		volno = 0;
176 		break;
177 	/*
178 	 * List available commands.
179 	 */
180 	case 'h':
181 		if (strncmp(cmd, "help", strlen(cmd)) != 0)
182 			goto bad;
183 	case '?':
184 		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
185 			"Available commands are:\n",
186 			"\tls [arg] - list directory\n",
187 			"\tcd arg - change directory\n",
188 			"\tpwd - print current directory\n",
189 			"\tadd [arg] - add `arg' to list of",
190 			" files to be extracted\n",
191 			"\tdelete [arg] - delete `arg' from",
192 			" list of files to be extracted\n",
193 			"\textract - extract requested files\n",
194 			"\tsetmodes - set modes of requested directories\n",
195 			"\tquit - immediately exit program\n",
196 			"\twhat - list dump header information\n",
197 			"\tverbose - toggle verbose flag",
198 			" (useful with ``ls'')\n",
199 			"\thelp or `?' - print this list\n",
200 			"If no `arg' is supplied, the current",
201 			" directory is used\n");
202 		break;
203 	/*
204 	 * List a directory.
205 	 */
206 	case 'l':
207 		if (strncmp(cmd, "ls", strlen(cmd)) != 0)
208 			goto bad;
209 		printlist(name, curdir);
210 		break;
211 	/*
212 	 * Print current directory.
213 	 */
214 	case 'p':
215 		if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
216 			goto bad;
217 		if (curdir[1] == '\0')
218 			fprintf(stderr, "/\n");
219 		else
220 			fprintf(stderr, "%s\n", &curdir[1]);
221 		break;
222 	/*
223 	 * Quit.
224 	 */
225 	case 'q':
226 		if (strncmp(cmd, "quit", strlen(cmd)) != 0)
227 			goto bad;
228 		return;
229 	case 'x':
230 		if (strncmp(cmd, "xit", strlen(cmd)) != 0)
231 			goto bad;
232 		return;
233 	/*
234 	 * Toggle verbose mode.
235 	 */
236 	case 'v':
237 		if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
238 			goto bad;
239 		if (vflag) {
240 			fprintf(stderr, "verbose mode off\n");
241 			vflag = 0;
242 			break;
243 		}
244 		fprintf(stderr, "verbose mode on\n");
245 		vflag++;
246 		break;
247 	/*
248 	 * Just restore requested directory modes.
249 	 */
250 	case 's':
251 		if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
252 			goto bad;
253 		setdirmodes(FORCE);
254 		break;
255 	/*
256 	 * Print out dump header information.
257 	 */
258 	case 'w':
259 		if (strncmp(cmd, "what", strlen(cmd)) != 0)
260 			goto bad;
261 		printdumpinfo();
262 		break;
263 	/*
264 	 * Turn on debugging.
265 	 */
266 	case 'D':
267 		if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
268 			goto bad;
269 		if (dflag) {
270 			fprintf(stderr, "debugging mode off\n");
271 			dflag = 0;
272 			break;
273 		}
274 		fprintf(stderr, "debugging mode on\n");
275 		dflag++;
276 		break;
277 	/*
278 	 * Unknown command.
279 	 */
280 	default:
281 	bad:
282 		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
283 		break;
284 	}
285 	goto loop;
286 }
287 
288 /*
289  * Read and parse an interactive command.
290  * The first word on the line is assigned to "cmd". If
291  * there are no arguments on the command line, then "curdir"
292  * is returned as the argument. If there are arguments
293  * on the line they are returned one at a time on each
294  * successive call to getcmd. Each argument is first assigned
295  * to "name". If it does not start with "/" the pathname in
296  * "curdir" is prepended to it. Finally "canon" is called to
297  * eliminate any embedded ".." components.
298  */
299 static void
300 getcmd(char *curdir, char *cmd, char *name, size_t size, struct arglist *ap)
301 {
302 	char *cp;
303 	static char input[BUFSIZ];
304 	char output[BUFSIZ];
305 #	define rawname input	/* save space by reusing input buffer */
306 
307 	/*
308 	 * Check to see if still processing arguments.
309 	 */
310 	if (ap->argcnt > 0)
311 		goto retnext;
312 	if (nextarg != NULL)
313 		goto getnext;
314 	/*
315 	 * Read a command line and trim off trailing white space.
316 	 */
317 	do	{
318 		fprintf(stderr, "restore > ");
319 		(void) fflush(stderr);
320 		if (fgets(input, BUFSIZ, terminal) == NULL) {
321 			strcpy(cmd, "quit");
322 			return;
323 		}
324 	} while (input[0] == '\n');
325 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
326 		/* trim off trailing white space and newline */;
327 	*++cp = '\0';
328 	/*
329 	 * Copy the command into "cmd".
330 	 */
331 	cp = copynext(input, cmd);
332 	ap->cmd = cmd;
333 	/*
334 	 * If no argument, use curdir as the default.
335 	 */
336 	if (*cp == '\0') {
337 		(void) strncpy(name, curdir, size);
338 		name[size - 1] = '\0';
339 		return;
340 	}
341 	nextarg = cp;
342 	/*
343 	 * Find the next argument.
344 	 */
345 getnext:
346 	cp = copynext(nextarg, rawname);
347 	if (*cp == '\0')
348 		nextarg = NULL;
349 	else
350 		nextarg = cp;
351 	/*
352 	 * If it is an absolute pathname, canonicalize it and return it.
353 	 */
354 	if (rawname[0] == '/') {
355 		canon(rawname, name, size);
356 	} else {
357 		/*
358 		 * For relative pathnames, prepend the current directory to
359 		 * it then canonicalize and return it.
360 		 */
361 		snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
362 		canon(output, name, size);
363 	}
364 	switch (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob)) {
365 	case GLOB_NOSPACE:
366 		fprintf(stderr, "%s: out of memory\n", ap->cmd);
367 		break;
368 	case GLOB_NOMATCH:
369 		fprintf(stderr, "%s %s: no such file or directory\n", ap->cmd, name);
370 		break;
371 	}
372 	if (ap->glob.gl_pathc == 0)
373 		return;
374 	ap->freeglob = 1;
375 	ap->argcnt = ap->glob.gl_pathc;
376 
377 retnext:
378 	strncpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
379 	name[size - 1] = '\0';
380 	if (--ap->argcnt == 0) {
381 		ap->freeglob = 0;
382 		globfree(&ap->glob);
383 	}
384 #	undef rawname
385 }
386 
387 /*
388  * Strip off the next token of the input.
389  */
390 static char *
391 copynext(char *input, char *output)
392 {
393 	char *cp, *bp;
394 	char quote;
395 
396 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
397 		/* skip to argument */;
398 	bp = output;
399 	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
400 		/*
401 		 * Handle back slashes.
402 		 */
403 		if (*cp == '\\') {
404 			if (*++cp == '\0') {
405 				fprintf(stderr,
406 					"command lines cannot be continued\n");
407 				continue;
408 			}
409 			*bp++ = *cp++;
410 			continue;
411 		}
412 		/*
413 		 * The usual unquoted case.
414 		 */
415 		if (*cp != '\'' && *cp != '"') {
416 			*bp++ = *cp++;
417 			continue;
418 		}
419 		/*
420 		 * Handle single and double quotes.
421 		 */
422 		quote = *cp++;
423 		while (*cp != quote && *cp != '\0')
424 			*bp++ = *cp++;
425 		if (*cp++ == '\0') {
426 			fprintf(stderr, "missing %c\n", quote);
427 			cp--;
428 			continue;
429 		}
430 	}
431 	*bp = '\0';
432 	return (cp);
433 }
434 
435 /*
436  * Canonicalize file names to always start with ``./'' and
437  * remove any embedded "." and ".." components.
438  */
439 void
440 canon(char *rawname, char *canonname, size_t len)
441 {
442 	char *cp, *np;
443 
444 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
445 		(void) strcpy(canonname, "");
446 	else if (rawname[0] == '/')
447 		(void) strcpy(canonname, ".");
448 	else
449 		(void) strcpy(canonname, "./");
450 	if (strlen(canonname) + strlen(rawname) >= len) {
451 		fprintf(stderr, "canonname: not enough buffer space\n");
452 		done(1);
453 	}
454 
455 	(void) strcat(canonname, rawname);
456 	/*
457 	 * Eliminate multiple and trailing '/'s
458 	 */
459 	for (cp = np = canonname; *np != '\0'; cp++) {
460 		*cp = *np++;
461 		while (*cp == '/' && *np == '/')
462 			np++;
463 	}
464 	*cp = '\0';
465 	if (*--cp == '/')
466 		*cp = '\0';
467 	/*
468 	 * Eliminate extraneous "." and ".." from pathnames.
469 	 */
470 	for (np = canonname; *np != '\0'; ) {
471 		np++;
472 		cp = np;
473 		while (*np != '/' && *np != '\0')
474 			np++;
475 		if (np - cp == 1 && *cp == '.') {
476 			cp--;
477 			(void) strcpy(cp, np);
478 			np = cp;
479 		}
480 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
481 			cp--;
482 			while (cp > &canonname[1] && *--cp != '/')
483 				/* find beginning of name */;
484 			(void) strcpy(cp, np);
485 			np = cp;
486 		}
487 	}
488 }
489 
490 /*
491  * Do an "ls" style listing of a directory
492  */
493 static void
494 printlist(char *name, char *basename)
495 {
496 	struct afile *fp, *list, *listp;
497 	struct direct *dp;
498 	struct afile single;
499 	RST_DIR *dirp;
500 	int entries, len, namelen;
501 	char locname[MAXPATHLEN];
502 
503 	dp = pathsearch(name);
504 	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
505 	    (!vflag && dp->d_ino == UFS_WINO))
506 		return;
507 	if ((dirp = rst_opendir(name)) == NULL) {
508 		entries = 1;
509 		list = &single;
510 		mkentry(name, dp, list);
511 		len = strlen(basename) + 1;
512 		if (strlen(name) - len > single.len) {
513 			freename(single.fname);
514 			single.fname = savename(&name[len]);
515 			single.len = strlen(single.fname);
516 		}
517 	} else {
518 		entries = 0;
519 		while ((dp = rst_readdir(dirp)))
520 			entries++;
521 		rst_closedir(dirp);
522 		list = (struct afile *)malloc(entries * sizeof(struct afile));
523 		if (list == NULL) {
524 			fprintf(stderr, "ls: out of memory\n");
525 			return;
526 		}
527 		if ((dirp = rst_opendir(name)) == NULL)
528 			panic("directory reopen failed\n");
529 		fprintf(stderr, "%s:\n", name);
530 		entries = 0;
531 		listp = list;
532 		(void)strlcpy(locname, name, MAXPATHLEN);
533 		(void)strlcat(locname, "/", MAXPATHLEN);
534 		namelen = strlen(locname);
535 		while ((dp = rst_readdir(dirp))) {
536 			if (dp == NULL)
537 				break;
538 			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
539 				continue;
540 			if (!vflag && (dp->d_ino == UFS_WINO ||
541 			     strcmp(dp->d_name, ".") == 0 ||
542 			     strcmp(dp->d_name, "..") == 0))
543 				continue;
544 			locname[namelen] = '\0';
545 			if (namelen + dp->d_namlen >= MAXPATHLEN) {
546 				fprintf(stderr, "%s%s: name exceeds %d char\n",
547 					locname, dp->d_name, MAXPATHLEN);
548 			} else {
549 				(void)strlcat(locname, dp->d_name, MAXPATHLEN);
550 				mkentry(locname, dp, listp++);
551 				entries++;
552 			}
553 		}
554 		rst_closedir(dirp);
555 		if (entries == 0) {
556 			fprintf(stderr, "\n");
557 			free(list);
558 			return;
559 		}
560 		qsort((char *)list, entries, sizeof(struct afile), fcmp);
561 	}
562 	formatf(list, entries);
563 	if (dirp != NULL) {
564 		for (fp = listp - 1; fp >= list; fp--)
565 			freename(fp->fname);
566 		fprintf(stderr, "\n");
567 		free(list);
568 	}
569 }
570 
571 /*
572  * Read the contents of a directory.
573  */
574 static void
575 mkentry(char *name, struct direct *dp, struct afile *fp)
576 {
577 	char *cp;
578 	struct entry *np;
579 
580 	fp->fnum = dp->d_ino;
581 	fp->fname = savename(dp->d_name);
582 	for (cp = fp->fname; *cp; cp++)
583 		if (!vflag && !isprint((unsigned char)*cp))
584 			*cp = '?';
585 	fp->len = cp - fp->fname;
586 	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
587 		fp->prefix = '^';
588 	else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
589 		fp->prefix = '*';
590 	else
591 		fp->prefix = ' ';
592 	switch(dp->d_type) {
593 
594 	default:
595 		fprintf(stderr, "Warning: undefined file type %d\n",
596 		    dp->d_type);
597 		/* FALLTHROUGH */
598 	case DT_REG:
599 		fp->postfix = ' ';
600 		break;
601 
602 	case DT_LNK:
603 		fp->postfix = '@';
604 		break;
605 
606 	case DT_FIFO:
607 	case DT_SOCK:
608 		fp->postfix = '=';
609 		break;
610 
611 	case DT_CHR:
612 	case DT_BLK:
613 		fp->postfix = '#';
614 		break;
615 
616 	case DT_WHT:
617 		fp->postfix = '%';
618 		break;
619 
620 	case DT_UNKNOWN:
621 	case DT_DIR:
622 		if (inodetype(dp->d_ino) == NODE)
623 			fp->postfix = '/';
624 		else
625 			fp->postfix = ' ';
626 		break;
627 	}
628 	return;
629 }
630 
631 /*
632  * Print out a pretty listing of a directory
633  */
634 static void
635 formatf(struct afile *list, int nentry)
636 {
637 	struct afile *fp, *endlist;
638 	int width, bigino, haveprefix, havepostfix;
639 	int i, j, w, precision, columns, lines;
640 
641 	width = 0;
642 	haveprefix = 0;
643 	havepostfix = 0;
644 	bigino = UFS_ROOTINO;
645 	endlist = &list[nentry];
646 	for (fp = &list[0]; fp < endlist; fp++) {
647 		if (bigino < fp->fnum)
648 			bigino = fp->fnum;
649 		if (width < fp->len)
650 			width = fp->len;
651 		if (fp->prefix != ' ')
652 			haveprefix = 1;
653 		if (fp->postfix != ' ')
654 			havepostfix = 1;
655 	}
656 	if (haveprefix)
657 		width++;
658 	if (havepostfix)
659 		width++;
660 	if (vflag) {
661 		for (precision = 0, i = bigino; i > 0; i /= 10)
662 			precision++;
663 		width += precision + 1;
664 	}
665 	width++;
666 	columns = 81 / width;
667 	if (columns == 0)
668 		columns = 1;
669 	lines = howmany(nentry, columns);
670 	for (i = 0; i < lines; i++) {
671 		for (j = 0; j < columns; j++) {
672 			fp = &list[j * lines + i];
673 			if (vflag) {
674 				fprintf(stderr, "%*ju ",
675 				    precision, (uintmax_t)fp->fnum);
676 				fp->len += precision + 1;
677 			}
678 			if (haveprefix) {
679 				putc(fp->prefix, stderr);
680 				fp->len++;
681 			}
682 			fprintf(stderr, "%s", fp->fname);
683 			if (havepostfix) {
684 				putc(fp->postfix, stderr);
685 				fp->len++;
686 			}
687 			if (fp + lines >= endlist) {
688 				fprintf(stderr, "\n");
689 				break;
690 			}
691 			for (w = fp->len; w < width; w++)
692 				putc(' ', stderr);
693 		}
694 	}
695 }
696 
697 /*
698  * Skip over directory entries that are not on the tape
699  *
700  * First have to get definition of a dirent.
701  */
702 #undef DIRBLKSIZ
703 #include <dirent.h>
704 #undef d_ino
705 
706 struct dirent *
707 glob_readdir(void *dirp)
708 {
709 	struct direct *dp;
710 	static struct dirent adirent;
711 
712 	while ((dp = rst_readdir(dirp)) != NULL) {
713 		if (!vflag && dp->d_ino == UFS_WINO)
714 			continue;
715 		if (dflag || TSTINO(dp->d_ino, dumpmap))
716 			break;
717 	}
718 	if (dp == NULL)
719 		return (NULL);
720 	adirent.d_fileno = dp->d_ino;
721 	adirent.d_namlen = dp->d_namlen;
722 	memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
723 	return (&adirent);
724 }
725 
726 /*
727  * Return st_mode information in response to stat or lstat calls
728  */
729 static int
730 glob_stat(const char *name, struct stat *stp)
731 {
732 	struct direct *dp;
733 
734 	dp = pathsearch(name);
735 	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
736 	    (!vflag && dp->d_ino == UFS_WINO))
737 		return (-1);
738 	if (inodetype(dp->d_ino) == NODE)
739 		stp->st_mode = IFDIR;
740 	else
741 		stp->st_mode = IFREG;
742 	return (0);
743 }
744 
745 /*
746  * Comparison routine for qsort.
747  */
748 static int
749 fcmp(const void *f1, const void *f2)
750 {
751 	return (strcoll(((struct afile *)f1)->fname,
752 	    ((struct afile *)f2)->fname));
753 }
754 
755 /*
756  * respond to interrupts
757  */
758 void
759 onintr(int signo __unused)
760 {
761 	if (command == 'i' && runshell)
762 		longjmp(reset, 1);
763 	if (reply("restore interrupted, continue") == FAIL)
764 		done(1);
765 }
766