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