xref: /original-bsd/sbin/restore/utilities.c (revision 92d3de31)
1 /* Copyright (c) 1983 Regents of the University of California */
2 
3 #ifndef lint
4 static char sccsid[] = "@(#)utilities.c	3.12	(Berkeley)	83/05/06";
5 #endif
6 
7 #include "restore.h"
8 
9 /*
10  * Insure that all the components of a pathname exist.
11  */
12 pathcheck(name)
13 	char *name;
14 {
15 	register char *cp;
16 	struct entry *ep;
17 	char *start;
18 
19 	start = index(name, '/');
20 	if (start == 0)
21 		return;
22 	for (cp = start; *cp != '\0'; cp++) {
23 		if (*cp != '/')
24 			continue;
25 		*cp = '\0';
26 		ep = lookupname(name);
27 		if (ep == NIL) {
28 			ep = addentry(name, psearch(name), NODE);
29 			newnode(ep);
30 			ep->e_flags |= NEW|KEEP;
31 		}
32 		*cp = '/';
33 	}
34 }
35 
36 /*
37  * Change a name to a unique temporary name.
38  */
39 mktempname(ep)
40 	register struct entry *ep;
41 {
42 	char oldname[MAXPATHLEN];
43 
44 	if (ep->e_flags & TMPNAME)
45 		badentry(ep, "mktempname: called with TMPNAME");
46 	ep->e_flags |= TMPNAME;
47 	(void) strcpy(oldname, myname(ep));
48 	freename(ep->e_name);
49 	ep->e_name = savename(gentempname(ep));
50 	ep->e_namlen = strlen(ep->e_name);
51 	renameit(oldname, myname(ep));
52 }
53 
54 /*
55  * Generate a temporary name for an entry.
56  */
57 char *
58 gentempname(ep)
59 	struct entry *ep;
60 {
61 	static char name[MAXPATHLEN];
62 	struct entry *np;
63 	long i = 0;
64 
65 	for (np = lookupino(ep->e_ino); np != NIL && np != ep; np = np->e_links)
66 		i++;
67 	if (np == NIL)
68 		badentry(ep, "not on ino list");
69 	(void) sprintf(name, "%s%d%d", TMPHDR, i, ep->e_ino);
70 	return (name);
71 }
72 
73 /*
74  * Rename a file or directory.
75  */
76 renameit(from, to)
77 	char *from, *to;
78 {
79 	if (rename(from, to) < 0) {
80 		fprintf(stderr, "Warning: cannot rename %s to %s\n", from, to);
81 		perror("rename");
82 		return;
83 	}
84 	vprintf(stdout, "rename %s to %s\n", from, to);
85 }
86 
87 /*
88  * Create a new node (directory).
89  */
90 newnode(np)
91 	struct entry *np;
92 {
93 	char *cp;
94 
95 	if (np->e_type != NODE)
96 		badentry(np, "newnode: not a node");
97 	cp = myname(np);
98 	if (mkdir(cp, 0777) < 0) {
99 		fprintf(stderr, "Warning: ");
100 		(void) fflush(stderr);
101 		perror(cp);
102 		return;
103 	}
104 	vprintf(stdout, "Make node %s\n", cp);
105 }
106 
107 /*
108  * Remove an old node (directory).
109  */
110 removenode(ep)
111 	register struct entry *ep;
112 {
113 	char *cp;
114 
115 	if (ep->e_type != NODE)
116 		badentry(ep, "removenode: not a node");
117 	if (ep->e_entries != NIL)
118 		badentry(ep, "removenode: non-empty directory");
119 	ep->e_flags |= REMOVED;
120 	ep->e_flags &= ~TMPNAME;
121 	cp = myname(ep);
122 	if (rmdir(cp) < 0) {
123 		fprintf(stderr, "Warning: ");
124 		(void) fflush(stderr);
125 		perror(cp);
126 		return;
127 	}
128 	vprintf(stdout, "Remove node %s\n", cp);
129 }
130 
131 /*
132  * Remove a leaf.
133  */
134 removeleaf(ep)
135 	register struct entry *ep;
136 {
137 	char *cp;
138 
139 	if (ep->e_type != LEAF)
140 		badentry(ep, "removeleaf: not a leaf");
141 	ep->e_flags |= REMOVED;
142 	ep->e_flags &= ~TMPNAME;
143 	cp = myname(ep);
144 	if (unlink(cp) < 0) {
145 		fprintf(stderr, "Warning: ");
146 		(void) fflush(stderr);
147 		perror(cp);
148 		return;
149 	}
150 	vprintf(stdout, "Remove leaf %s\n", cp);
151 }
152 
153 /*
154  * Create a link.
155  */
156 linkit(existing, new, type)
157 	char *existing, *new;
158 	int type;
159 {
160 
161 	if (type == SYMLINK) {
162 		if (symlink(existing, new) < 0) {
163 			fprintf(stderr,
164 				"Warning: cannot create symbolic link %s->%s\n",
165 				new, existing);
166 			perror("symlink");
167 			return;
168 		}
169 	} else if (type == HARDLINK) {
170 		if (link(existing, new) < 0) {
171 			fprintf(stderr,
172 				"Warning: cannot create hard link %s->%s\n",
173 				new, existing);
174 			perror("link");
175 			return;
176 		}
177 	} else {
178 		panic("linkit: unknown type %d\n", type);
179 	}
180 	vprintf(stdout, "Create %s link %s->%s\n",
181 		type == SYMLINK ? "symbolic" : "hard", new, existing);
182 }
183 
184 /*
185  * find lowest number file (above "start") that needs to be extracted
186  */
187 ino_t
188 lowerbnd(start)
189 	ino_t start;
190 {
191 	register struct entry *ep;
192 
193 	for ( ; start < maxino; start++) {
194 		ep = lookupino(start);
195 		if (ep == NIL || ep->e_type == NODE)
196 			continue;
197 		if (ep->e_flags & (NEW|EXTRACT))
198 			return (start);
199 	}
200 	return (start);
201 }
202 
203 /*
204  * find highest number file (below "start") that needs to be extracted
205  */
206 ino_t
207 upperbnd(start)
208 	ino_t start;
209 {
210 	register struct entry *ep;
211 
212 	for ( ; start > ROOTINO; start--) {
213 		ep = lookupino(start);
214 		if (ep == NIL || ep->e_type == NODE)
215 			continue;
216 		if (ep->e_flags & (NEW|EXTRACT))
217 			return (start);
218 	}
219 	return (start);
220 }
221 
222 /*
223  * report on a badly formed entry
224  */
225 badentry(ep, msg)
226 	register struct entry *ep;
227 	char *msg;
228 {
229 
230 	fprintf(stderr, "bad entry: %s\n", msg);
231 	fprintf(stderr, "name: %s\n", myname(ep));
232 	fprintf(stderr, "parent name %s\n", myname(ep->e_parent));
233 	if (ep->e_sibling != NIL)
234 		fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling));
235 	if (ep->e_entries != NIL)
236 		fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries));
237 	if (ep->e_links != NIL)
238 		fprintf(stderr, "next link name: %s\n", myname(ep->e_links));
239 	if (ep->e_next != NIL)
240 		fprintf(stderr, "next hashchain name: %s\n", myname(ep->e_next));
241 	fprintf(stderr, "entry type: %s\n",
242 		ep->e_type == NODE ? "NODE" : "LEAF");
243 	fprintf(stderr, "inode number: %ld\n", ep->e_ino);
244 	panic("flags: %s\n", flagvalues(ep));
245 }
246 
247 /*
248  * Construct a string indicating the active flag bits of an entry.
249  */
250 char *
251 flagvalues(ep)
252 	register struct entry *ep;
253 {
254 	static char flagbuf[BUFSIZ];
255 
256 	(void) strcpy(flagbuf, "|NIL");
257 	flagbuf[0] = '\0';
258 	if (ep->e_flags & REMOVED)
259 		(void) strcat(flagbuf, "|REMOVED");
260 	if (ep->e_flags & TMPNAME)
261 		(void) strcat(flagbuf, "|TMPNAME");
262 	if (ep->e_flags & EXTRACT)
263 		(void) strcat(flagbuf, "|EXTRACT");
264 	if (ep->e_flags & NEW)
265 		(void) strcat(flagbuf, "|NEW");
266 	if (ep->e_flags & KEEP)
267 		(void) strcat(flagbuf, "|KEEP");
268 	return (&flagbuf[1]);
269 }
270 
271 /*
272  * Check to see if a name is on a dump tape.
273  */
274 ino_t
275 dirlookup(name)
276 	char *name;
277 {
278 	ino_t ino;
279 
280 	ino = psearch(name);
281 	if (ino == 0 || BIT(ino, dumpmap) == 0)
282 		fprintf(stderr, "%s is not on tape\n", name);
283 	return (ino);
284 }
285 
286 /*
287  * Canonicalize file names to always start with ``./'' and
288  * remove any imbedded ".." components.
289  */
290 canon(rawname, canonname)
291 	char *rawname, *canonname;
292 {
293 	register char *cp, *np;
294 	int len;
295 
296 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
297 		(void) strcpy(canonname, "");
298 	else if (rawname[0] == '/')
299 		(void) strcpy(canonname, ".");
300 	else
301 		(void) strcpy(canonname, "./");
302 	(void) strcat(canonname, rawname);
303 	len = strlen(canonname) - 1;
304 	if (canonname[len] == '/')
305 		canonname[len] = '\0';
306 	/*
307 	 * Eliminate extraneous ".." from pathnames.
308 	 */
309 	for (np = canonname; *np != '\0'; ) {
310 		np++;
311 		cp = np;
312 		while (*np != '/' && *np != '\0')
313 			np++;
314 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
315 			cp--;
316 			while (cp > &canonname[1] && *--cp != '/')
317 				/* find beginning of name */;
318 			(void) strcpy(cp, np);
319 			np = cp;
320 		}
321 	}
322 }
323 
324 /*
325  * Elicit a reply.
326  */
327 reply(question)
328 	char *question;
329 {
330 	char c;
331 
332 	fprintf(stderr, "%s? ", question);
333 	do	{
334 		fprintf(stderr, "[yn] ");
335 		fflush(stderr);
336 		c = getc(terminal);
337 		while (c != '\n' && getc(terminal) != '\n')
338 			/* void */;
339 	} while (c != 'y' && c != 'n');
340 	if (c == 'y')
341 		return (GOOD);
342 	return (FAIL);
343 }
344 
345 /*
346  * Read and parse an interactive command.
347  * The first word on the line is assigned to "cmd". If
348  * there are no arguments on the command line, then "curdir"
349  * is returned as the argument. If there are arguments
350  * on the line they are returned one at a time on each
351  * successive call to getcmd. Each argument is first assigned
352  * to "name". If it does not start with "/" the pathname in
353  * "curdir" is prepended to it. Finally "canon" is called to
354  * eliminate any embedded ".." components.
355  */
356 getcmd(curdir, cmd, name)
357 	char *curdir, *cmd, *name;
358 {
359 	register char *cp, *bp;
360 	char output[BUFSIZ];
361 	static char input[BUFSIZ];
362 	static char *nextarg = NULL;
363 
364 	/*
365 	 * Check to see if still processing arguments.
366 	 */
367 	if (nextarg != NULL)
368 		goto getnext;
369 	nextarg = NULL;
370 	/*
371 	 * Read a command line and trim off trailing white space.
372 	 */
373 	do	{
374 		fprintf(stderr, "restore > ");
375 		(void) fflush(stderr);
376 		(void) fgets(input, BUFSIZ, terminal);
377 	} while (!feof(terminal) && input[0] == '\n');
378 	if (feof(terminal)) {
379 		(void) strcpy(cmd, "quit");
380 		return;
381 	}
382 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
383 		/* trim off trailing white space and newline */;
384 	*++cp = '\0';
385 	/*
386 	 * Copy the command into "cmd".
387 	 */
388 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
389 		/* skip to command */;
390 	for (bp = cmd; *cp != ' ' && *cp != '\t' && *cp != '\0'; )
391 		*bp++ = *cp++;
392 	*bp = '\0';
393 	/*
394 	 * If no argument, use curdir as the default.
395 	 */
396 	if (*cp == '\0') {
397 		(void) strcpy(name, curdir);
398 		return;
399 	}
400 	nextarg = cp;
401 	/*
402 	 * Find the next argument.
403 	 */
404 getnext:
405 	for (cp = nextarg + 1; *cp == ' ' || *cp == '\t'; cp++)
406 		/* skip to argument */;
407 	for (bp = cp; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++)
408 		/* skip to end of argument */;
409 	if (*cp == '\0')
410 		nextarg = NULL;
411 	else
412 		nextarg = cp;
413 	*cp = '\0';
414 	/*
415 	 * If it an absolute pathname, canonicalize it and return it.
416 	 */
417 	if (*bp == '/') {
418 		canon(bp, name);
419 		return;
420 	}
421 	/*
422 	 * For relative pathnames, prepend the current directory to
423 	 * it then canonicalize and return it.
424 	 */
425 	(void) strcpy(output, curdir);
426 	(void) strcat(output, "/");
427 	(void) strcat(output, bp);
428 	canon(output, name);
429 }
430 
431 /*
432  * respond to interrupts
433  */
434 onintr()
435 {
436 	if (reply("restore interrupted, continue") == FAIL)
437 		done(1);
438 	if (signal(SIGINT, onintr) == SIG_IGN)
439 		(void) signal(SIGINT, SIG_IGN);
440 	if (signal(SIGTERM, onintr) == SIG_IGN)
441 		(void) signal(SIGTERM, SIG_IGN);
442 }
443 
444 /*
445  * handle unexpected inconsistencies
446  */
447 /* VARARGS1 */
448 panic(msg, d1, d2)
449 	char *msg;
450 	long d1, d2;
451 {
452 
453 	fprintf(stderr, msg, d1, d2);
454 	if (reply("abort") == GOOD) {
455 		if (reply("dump core") == GOOD)
456 			abort();
457 		done(1);
458 	}
459 }
460