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