must flush stderr before reading from the terminal
[unix-history] / usr / src / sbin / restore / utilities.c
CommitLineData
e0519353
KM
1/* Copyright (c) 1983 Regents of the University of California */
2
3#ifndef lint
c9e0e8d3 4static char sccsid[] = "@(#)utilities.c 3.12 (Berkeley) 83/05/06";
e0519353
KM
5#endif
6
7#include "restore.h"
8
9/*
10 * Insure that all the components of a pathname exist.
11 */
16e2b8d5 12pathcheck(name)
e0519353
KM
13 char *name;
14{
15 register char *cp;
16 struct entry *ep;
9f13f26d 17 char *start;
e0519353
KM
18
19 start = index(name, '/');
9f13f26d 20 if (start == 0)
16e2b8d5 21 return;
e0519353
KM
22 for (cp = start; *cp != '\0'; cp++) {
23 if (*cp != '/')
24 continue;
25 *cp = '\0';
26 ep = lookupname(name);
27 if (ep == NIL) {
7432ff81 28 ep = addentry(name, psearch(name), NODE);
e0519353 29 newnode(ep);
7432ff81 30 ep->e_flags |= NEW|KEEP;
e0519353
KM
31 }
32 *cp = '/';
33 }
34}
35
36/*
37 * Change a name to a unique temporary name.
38 */
39mktempname(ep)
40 register struct entry *ep;
41{
16e2b8d5 42 char oldname[MAXPATHLEN];
e0519353
KM
43
44 if (ep->e_flags & TMPNAME)
45 badentry(ep, "mktempname: called with TMPNAME");
46 ep->e_flags |= TMPNAME;
7432ff81 47 (void) strcpy(oldname, myname(ep));
16e2b8d5
KM
48 freename(ep->e_name);
49 ep->e_name = savename(gentempname(ep));
50 ep->e_namlen = strlen(ep->e_name);
e0519353
KM
51 renameit(oldname, myname(ep));
52}
53
16e2b8d5
KM
54/*
55 * Generate a temporary name for an entry.
56 */
57char *
58gentempname(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");
e9a184e3 69 (void) sprintf(name, "%s%d%d", TMPHDR, i, ep->e_ino);
16e2b8d5
KM
70 return (name);
71}
72
e0519353
KM
73/*
74 * Rename a file or directory.
75 */
76renameit(from, to)
77 char *from, *to;
78{
79 if (rename(from, to) < 0) {
d66e0e37
KM
80 fprintf(stderr, "Warning: cannot rename %s to %s\n", from, to);
81 perror("rename");
82 return;
e0519353
KM
83 }
84 vprintf(stdout, "rename %s to %s\n", from, to);
85}
86
87/*
88 * Create a new node (directory).
89 */
90newnode(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);
9f13f26d 98 if (mkdir(cp, 0777) < 0) {
d4ca0635 99 fprintf(stderr, "Warning: ");
7432ff81 100 (void) fflush(stderr);
d4ca0635
KM
101 perror(cp);
102 return;
e0519353
KM
103 }
104 vprintf(stdout, "Make node %s\n", cp);
105}
106
107/*
108 * Remove an old node (directory).
109 */
110removenode(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");
d66e0e37
KM
119 ep->e_flags |= REMOVED;
120 ep->e_flags &= ~TMPNAME;
e0519353
KM
121 cp = myname(ep);
122 if (rmdir(cp) < 0) {
d66e0e37 123 fprintf(stderr, "Warning: ");
7432ff81 124 (void) fflush(stderr);
d66e0e37
KM
125 perror(cp);
126 return;
e0519353 127 }
e0519353
KM
128 vprintf(stdout, "Remove node %s\n", cp);
129}
130
131/*
132 * Remove a leaf.
133 */
134removeleaf(ep)
135 register struct entry *ep;
136{
137 char *cp;
138
139 if (ep->e_type != LEAF)
140 badentry(ep, "removeleaf: not a leaf");
d66e0e37
KM
141 ep->e_flags |= REMOVED;
142 ep->e_flags &= ~TMPNAME;
e0519353
KM
143 cp = myname(ep);
144 if (unlink(cp) < 0) {
d66e0e37 145 fprintf(stderr, "Warning: ");
7432ff81 146 (void) fflush(stderr);
d66e0e37
KM
147 perror(cp);
148 return;
e0519353 149 }
e0519353
KM
150 vprintf(stdout, "Remove leaf %s\n", cp);
151}
152
153/*
154 * Create a link.
155 */
156linkit(existing, new, type)
157 char *existing, *new;
158 int type;
159{
160
161 if (type == SYMLINK) {
162 if (symlink(existing, new) < 0) {
d66e0e37
KM
163 fprintf(stderr,
164 "Warning: cannot create symbolic link %s->%s\n",
e0519353 165 new, existing);
d66e0e37
KM
166 perror("symlink");
167 return;
e0519353
KM
168 }
169 } else if (type == HARDLINK) {
170 if (link(existing, new) < 0) {
d66e0e37
KM
171 fprintf(stderr,
172 "Warning: cannot create hard link %s->%s\n",
e0519353 173 new, existing);
d66e0e37
KM
174 perror("link");
175 return;
e0519353
KM
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 */
187ino_t
188lowerbnd(start)
189 ino_t start;
190{
191 register struct entry *ep;
192
193 for ( ; start < maxino; start++) {
194 ep = lookupino(start);
7432ff81 195 if (ep == NIL || ep->e_type == NODE)
e0519353 196 continue;
16e2b8d5 197 if (ep->e_flags & (NEW|EXTRACT))
e0519353
KM
198 return (start);
199 }
200 return (start);
201}
202
203/*
204 * find highest number file (below "start") that needs to be extracted
205 */
206ino_t
207upperbnd(start)
208 ino_t start;
209{
210 register struct entry *ep;
211
212 for ( ; start > ROOTINO; start--) {
213 ep = lookupino(start);
7432ff81 214 if (ep == NIL || ep->e_type == NODE)
e0519353 215 continue;
16e2b8d5 216 if (ep->e_flags & (NEW|EXTRACT))
e0519353
KM
217 return (start);
218 }
219 return (start);
220}
221
222/*
223 * report on a badly formed entry
224 */
225badentry(ep, msg)
226 register struct entry *ep;
227 char *msg;
228{
e0519353
KM
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));
16e2b8d5
KM
239 if (ep->e_next != NIL)
240 fprintf(stderr, "next hashchain name: %s\n", myname(ep->e_next));
e0519353
KM
241 fprintf(stderr, "entry type: %s\n",
242 ep->e_type == NODE ? "NODE" : "LEAF");
243 fprintf(stderr, "inode number: %ld\n", ep->e_ino);
55f85ba7
KM
244 panic("flags: %s\n", flagvalues(ep));
245}
246
247/*
248 * Construct a string indicating the active flag bits of an entry.
249 */
250char *
251flagvalues(ep)
252 register struct entry *ep;
253{
254 static char flagbuf[BUFSIZ];
255
256 (void) strcpy(flagbuf, "|NIL");
e0519353
KM
257 flagbuf[0] = '\0';
258 if (ep->e_flags & REMOVED)
55f85ba7 259 (void) strcat(flagbuf, "|REMOVED");
e0519353 260 if (ep->e_flags & TMPNAME)
55f85ba7 261 (void) strcat(flagbuf, "|TMPNAME");
e0519353 262 if (ep->e_flags & EXTRACT)
55f85ba7 263 (void) strcat(flagbuf, "|EXTRACT");
e0519353 264 if (ep->e_flags & NEW)
55f85ba7 265 (void) strcat(flagbuf, "|NEW");
e0519353 266 if (ep->e_flags & KEEP)
55f85ba7
KM
267 (void) strcat(flagbuf, "|KEEP");
268 return (&flagbuf[1]);
e0519353
KM
269}
270
38bbfe41 271/*
7432ff81
KM
272 * Check to see if a name is on a dump tape.
273 */
274ino_t
275dirlookup(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.
38bbfe41
KM
289 */
290canon(rawname, canonname)
291 char *rawname, *canonname;
292{
7432ff81 293 register char *cp, *np;
38bbfe41
KM
294 int len;
295
296 if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
16e2b8d5 297 (void) strcpy(canonname, "");
38bbfe41 298 else if (rawname[0] == '/')
16e2b8d5 299 (void) strcpy(canonname, ".");
38bbfe41 300 else
16e2b8d5
KM
301 (void) strcpy(canonname, "./");
302 (void) strcat(canonname, rawname);
38bbfe41
KM
303 len = strlen(canonname) - 1;
304 if (canonname[len] == '/')
305 canonname[len] = '\0';
7432ff81
KM
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 }
38bbfe41
KM
322}
323
e0519353 324/*
7432ff81 325 * Elicit a reply.
e0519353
KM
326 */
327reply(question)
328 char *question;
329{
330 char c;
331
332 fprintf(stderr, "%s? ", question);
333 do {
334 fprintf(stderr, "[yn] ");
c9e0e8d3 335 fflush(stderr);
7432ff81
KM
336 c = getc(terminal);
337 while (c != '\n' && getc(terminal) != '\n')
e0519353
KM
338 /* void */;
339 } while (c != 'y' && c != 'n');
340 if (c == 'y')
341 return (GOOD);
342 return (FAIL);
343}
7432ff81
KM
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 */
356getcmd(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 */
404getnext:
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 */
434onintr()
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 */
448panic(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}