1 #ifndef lint 2 static char sccsid[] = "@(#)utilities.c 3.15 (Berkeley) 83/08/11"; 3 #endif 4 5 /* Copyright (c) 1983 Regents of the University of California */ 6 7 #include "restore.h" 8 9 char *copynext(); 10 11 /* 12 * Insure that all the components of a pathname exist. 13 */ 14 pathcheck(name) 15 char *name; 16 { 17 register char *cp; 18 struct entry *ep; 19 char *start; 20 21 start = index(name, '/'); 22 if (start == 0) 23 return; 24 for (cp = start; *cp != '\0'; cp++) { 25 if (*cp != '/') 26 continue; 27 *cp = '\0'; 28 ep = lookupname(name); 29 if (ep == NIL) { 30 ep = addentry(name, psearch(name), NODE); 31 newnode(ep); 32 ep->e_flags |= NEW|KEEP; 33 } 34 *cp = '/'; 35 } 36 } 37 38 /* 39 * Change a name to a unique temporary name. 40 */ 41 mktempname(ep) 42 register struct entry *ep; 43 { 44 char oldname[MAXPATHLEN]; 45 46 if (ep->e_flags & TMPNAME) 47 badentry(ep, "mktempname: called with TMPNAME"); 48 ep->e_flags |= TMPNAME; 49 (void) strcpy(oldname, myname(ep)); 50 freename(ep->e_name); 51 ep->e_name = savename(gentempname(ep)); 52 ep->e_namlen = strlen(ep->e_name); 53 renameit(oldname, myname(ep)); 54 } 55 56 /* 57 * Generate a temporary name for an entry. 58 */ 59 char * 60 gentempname(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"); 71 (void) sprintf(name, "%s%d%d", TMPHDR, i, ep->e_ino); 72 return (name); 73 } 74 75 /* 76 * Rename a file or directory. 77 */ 78 renameit(from, to) 79 char *from, *to; 80 { 81 if (rename(from, to) < 0) { 82 fprintf(stderr, "Warning: cannot rename %s to %s", from, to); 83 (void) fflush(stderr); 84 perror(""); 85 return; 86 } 87 vprintf(stdout, "rename %s to %s\n", from, to); 88 } 89 90 /* 91 * Create a new node (directory). 92 */ 93 newnode(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); 101 if (mkdir(cp, 0777) < 0) { 102 fprintf(stderr, "Warning: "); 103 (void) fflush(stderr); 104 perror(cp); 105 return; 106 } 107 vprintf(stdout, "Make node %s\n", cp); 108 } 109 110 /* 111 * Remove an old node (directory). 112 */ 113 removenode(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"); 122 ep->e_flags |= REMOVED; 123 ep->e_flags &= ~TMPNAME; 124 cp = myname(ep); 125 if (rmdir(cp) < 0) { 126 fprintf(stderr, "Warning: "); 127 (void) fflush(stderr); 128 perror(cp); 129 return; 130 } 131 vprintf(stdout, "Remove node %s\n", cp); 132 } 133 134 /* 135 * Remove a leaf. 136 */ 137 removeleaf(ep) 138 register struct entry *ep; 139 { 140 char *cp; 141 142 if (ep->e_type != LEAF) 143 badentry(ep, "removeleaf: not a leaf"); 144 ep->e_flags |= REMOVED; 145 ep->e_flags &= ~TMPNAME; 146 cp = myname(ep); 147 if (unlink(cp) < 0) { 148 fprintf(stderr, "Warning: "); 149 (void) fflush(stderr); 150 perror(cp); 151 return; 152 } 153 vprintf(stdout, "Remove leaf %s\n", cp); 154 } 155 156 /* 157 * Create a link. 158 */ 159 linkit(existing, new, type) 160 char *existing, *new; 161 int type; 162 { 163 164 if (type == SYMLINK) { 165 if (symlink(existing, new) < 0) { 166 fprintf(stderr, 167 "Warning: cannot create symbolic link %s->%s", 168 new, existing); 169 (void) fflush(stderr); 170 perror(""); 171 return; 172 } 173 } else if (type == HARDLINK) { 174 if (link(existing, new) < 0) { 175 fprintf(stderr, 176 "Warning: cannot create hard link %s->%s", 177 new, existing); 178 (void) fflush(stderr); 179 perror(""); 180 return; 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 */ 192 ino_t 193 lowerbnd(start) 194 ino_t start; 195 { 196 register struct entry *ep; 197 198 for ( ; start < maxino; start++) { 199 ep = lookupino(start); 200 if (ep == NIL || ep->e_type == NODE) 201 continue; 202 if (ep->e_flags & (NEW|EXTRACT)) 203 return (start); 204 } 205 return (start); 206 } 207 208 /* 209 * find highest number file (below "start") that needs to be extracted 210 */ 211 ino_t 212 upperbnd(start) 213 ino_t start; 214 { 215 register struct entry *ep; 216 217 for ( ; start > ROOTINO; start--) { 218 ep = lookupino(start); 219 if (ep == NIL || ep->e_type == NODE) 220 continue; 221 if (ep->e_flags & (NEW|EXTRACT)) 222 return (start); 223 } 224 return (start); 225 } 226 227 /* 228 * report on a badly formed entry 229 */ 230 badentry(ep, msg) 231 register struct entry *ep; 232 char *msg; 233 { 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)); 244 if (ep->e_next != NIL) 245 fprintf(stderr, "next hashchain name: %s\n", myname(ep->e_next)); 246 fprintf(stderr, "entry type: %s\n", 247 ep->e_type == NODE ? "NODE" : "LEAF"); 248 fprintf(stderr, "inode number: %ld\n", ep->e_ino); 249 panic("flags: %s\n", flagvalues(ep)); 250 } 251 252 /* 253 * Construct a string indicating the active flag bits of an entry. 254 */ 255 char * 256 flagvalues(ep) 257 register struct entry *ep; 258 { 259 static char flagbuf[BUFSIZ]; 260 261 (void) strcpy(flagbuf, "|NIL"); 262 flagbuf[0] = '\0'; 263 if (ep->e_flags & REMOVED) 264 (void) strcat(flagbuf, "|REMOVED"); 265 if (ep->e_flags & TMPNAME) 266 (void) strcat(flagbuf, "|TMPNAME"); 267 if (ep->e_flags & EXTRACT) 268 (void) strcat(flagbuf, "|EXTRACT"); 269 if (ep->e_flags & NEW) 270 (void) strcat(flagbuf, "|NEW"); 271 if (ep->e_flags & KEEP) 272 (void) strcat(flagbuf, "|KEEP"); 273 return (&flagbuf[1]); 274 } 275 276 /* 277 * Check to see if a name is on a dump tape. 278 */ 279 ino_t 280 dirlookup(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. 294 */ 295 canon(rawname, canonname) 296 char *rawname, *canonname; 297 { 298 register char *cp, *np; 299 int len; 300 301 if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0) 302 (void) strcpy(canonname, ""); 303 else if (rawname[0] == '/') 304 (void) strcpy(canonname, "."); 305 else 306 (void) strcpy(canonname, "./"); 307 (void) strcat(canonname, rawname); 308 len = strlen(canonname) - 1; 309 if (canonname[len] == '/') 310 canonname[len] = '\0'; 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 } 327 } 328 329 /* 330 * Elicit a reply. 331 */ 332 reply(question) 333 char *question; 334 { 335 char c; 336 337 fprintf(stderr, "%s? ", question); 338 do { 339 fprintf(stderr, "[yn] "); 340 (void) fflush(stderr); 341 c = getc(terminal); 342 while (c != '\n' && getc(terminal) != '\n') 343 /* void */; 344 } while (c != 'y' && c != 'n'); 345 if (c == 'y') 346 return (GOOD); 347 return (FAIL); 348 } 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 */ 361 getcmd(curdir, cmd, name) 362 char *curdir, *cmd, *name; 363 { 364 register char *cp; 365 static char *nextarg = NULL; 366 static char input[BUFSIZ]; 367 char output[BUFSIZ]; 368 # define rawname input /* save space by reusing input buffer */ 369 370 /* 371 * Check to see if still processing arguments. 372 */ 373 if (nextarg != NULL) 374 goto getnext; 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 */ 393 cp = copynext(input, cmd); 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 */ 405 getnext: 406 cp = copynext(nextarg, rawname); 407 if (*cp == '\0') 408 nextarg = NULL; 409 else 410 nextarg = cp; 411 /* 412 * If it an absolute pathname, canonicalize it and return it. 413 */ 414 if (rawname[0] == '/') { 415 canon(rawname, name); 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, "/"); 424 (void) strcat(output, rawname); 425 canon(output, name); 426 # undef rawname 427 } 428 429 /* 430 * Strip off the next token of the input. 431 */ 432 char * 433 copynext(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); 476 } 477 478 /* 479 * respond to interrupts 480 */ 481 onintr() 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 */ 495 panic(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 } 507