1 /* $NetBSD: utilities.c,v 1.18 2004/10/22 22:38:38 bouyer Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)utilities.c 8.5 (Berkeley) 4/28/95"; 36 #else 37 __RCSID("$NetBSD: utilities.c,v 1.18 2004/10/22 22:38:38 bouyer Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/param.h> 42 #include <sys/stat.h> 43 44 #include <ufs/ufs/dinode.h> 45 #include <ufs/ufs/dir.h> 46 47 #include <err.h> 48 #include <errno.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 54 #include "restore.h" 55 #include "extern.h" 56 57 /* 58 * Insure that all the components of a pathname exist. 59 */ 60 void 61 pathcheck(name) 62 char *name; 63 { 64 char *cp; 65 struct entry *ep; 66 char *start; 67 68 start = strchr(name, '/'); 69 if (start == 0) 70 return; 71 for (cp = start; *cp != '\0'; cp++) { 72 if (*cp != '/') 73 continue; 74 *cp = '\0'; 75 ep = lookupname(name); 76 if (ep == NULL) { 77 /* Safe; we know the pathname exists in the dump. */ 78 ep = addentry(name, pathsearch(name)->d_ino, NODE); 79 newnode(ep); 80 } 81 ep->e_flags |= NEW|KEEP; 82 *cp = '/'; 83 } 84 } 85 86 /* 87 * Change a name to a unique temporary name. 88 */ 89 void 90 mktempname(ep) 91 struct entry *ep; 92 { 93 char oldname[MAXPATHLEN]; 94 95 if (ep->e_flags & TMPNAME) 96 badentry(ep, "mktempname: called with TMPNAME"); 97 ep->e_flags |= TMPNAME; 98 (void) strcpy(oldname, myname(ep)); 99 freename(ep->e_name); 100 ep->e_name = savename(gentempname(ep)); 101 ep->e_namlen = strlen(ep->e_name); 102 renameit(oldname, myname(ep)); 103 } 104 105 /* 106 * Generate a temporary name for an entry. 107 */ 108 char * 109 gentempname(ep) 110 struct entry *ep; 111 { 112 static char name[MAXPATHLEN]; 113 struct entry *np; 114 long i = 0; 115 116 for (np = lookupino(ep->e_ino); 117 np != NULL && np != ep; np = np->e_links) 118 i++; 119 if (np == NULL) 120 badentry(ep, "not on ino list"); 121 (void) snprintf(name, sizeof(name), "%s%ld%d", TMPHDR, (long) i, 122 ep->e_ino); 123 return (name); 124 } 125 126 /* 127 * Rename a file or directory. 128 */ 129 void 130 renameit(from, to) 131 char *from, *to; 132 { 133 if (!Nflag && rename(from, to) < 0) { 134 fprintf(stderr, "warning: cannot rename %s to %s: %s\n", 135 from, to, strerror(errno)); 136 return; 137 } 138 vprintf(stdout, "rename %s to %s\n", from, to); 139 } 140 141 /* 142 * Create a new node (directory). 143 */ 144 void 145 newnode(np) 146 struct entry *np; 147 { 148 char *cp; 149 150 if (np->e_type != NODE) 151 badentry(np, "newnode: not a node"); 152 cp = myname(np); 153 if (!Nflag && mkdir(cp, 0777) < 0) { 154 np->e_flags |= EXISTED; 155 fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); 156 return; 157 } 158 vprintf(stdout, "Make node %s\n", cp); 159 } 160 161 /* 162 * Remove an old node (directory). 163 */ 164 void 165 removenode(ep) 166 struct entry *ep; 167 { 168 char *cp; 169 170 if (ep->e_type != NODE) 171 badentry(ep, "removenode: not a node"); 172 if (ep->e_entries != NULL) 173 badentry(ep, "removenode: non-empty directory"); 174 ep->e_flags |= REMOVED; 175 ep->e_flags &= ~TMPNAME; 176 cp = myname(ep); 177 if (!Nflag && rmdir(cp) < 0) { 178 fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); 179 return; 180 } 181 vprintf(stdout, "Remove node %s\n", cp); 182 } 183 184 /* 185 * Remove a leaf. 186 */ 187 void 188 removeleaf(ep) 189 struct entry *ep; 190 { 191 char *cp; 192 193 if (ep->e_type != LEAF) 194 badentry(ep, "removeleaf: not a leaf"); 195 ep->e_flags |= REMOVED; 196 ep->e_flags &= ~TMPNAME; 197 cp = myname(ep); 198 if (!Nflag && unlink(cp) < 0) { 199 fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); 200 return; 201 } 202 vprintf(stdout, "Remove leaf %s\n", cp); 203 } 204 205 /* 206 * Create a link. 207 */ 208 int 209 linkit(existing, new, type) 210 char *existing, *new; 211 int type; 212 { 213 214 if (type == SYMLINK) { 215 if (!Nflag && symlink(existing, new) < 0) { 216 fprintf(stderr, 217 "warning: cannot create symbolic link %s->%s: %s\n", 218 new, existing, strerror(errno)); 219 return (FAIL); 220 } 221 } else if (type == HARDLINK) { 222 if (!Nflag && link(existing, new) < 0) { 223 fprintf(stderr, 224 "warning: cannot create hard link %s->%s: %s\n", 225 new, existing, strerror(errno)); 226 return (FAIL); 227 } 228 } else { 229 panic("linkit: unknown type %d\n", type); 230 return (FAIL); 231 } 232 vprintf(stdout, "Create %s link %s->%s\n", 233 type == SYMLINK ? "symbolic" : "hard", new, existing); 234 return (GOOD); 235 } 236 237 /* 238 * Create a whiteout. 239 */ 240 int 241 addwhiteout(name) 242 char *name; 243 { 244 245 if (!Nflag && mknod(name, S_IFWHT, 0) < 0) { 246 fprintf(stderr, "warning: cannot create whiteout %s: %s\n", 247 name, strerror(errno)); 248 return (FAIL); 249 } 250 vprintf(stdout, "Create whiteout %s\n", name); 251 return (GOOD); 252 } 253 254 /* 255 * Delete a whiteout. 256 */ 257 void 258 delwhiteout(ep) 259 struct entry *ep; 260 { 261 char *name; 262 263 if (ep->e_type != LEAF) 264 badentry(ep, "delwhiteout: not a leaf"); 265 ep->e_flags |= REMOVED; 266 ep->e_flags &= ~TMPNAME; 267 name = myname(ep); 268 if (!Nflag && undelete(name) < 0) { 269 fprintf(stderr, "warning: cannot delete whiteout %s: %s\n", 270 name, strerror(errno)); 271 return; 272 } 273 vprintf(stdout, "Delete whiteout %s\n", name); 274 } 275 276 /* 277 * find lowest number file (above "start") that needs to be extracted 278 */ 279 ino_t 280 lowerbnd(start) 281 ino_t start; 282 { 283 struct entry *ep; 284 285 for ( ; start < maxino; start++) { 286 ep = lookupino(start); 287 if (ep == NULL || ep->e_type == NODE) 288 continue; 289 if (ep->e_flags & (NEW|EXTRACT)) 290 return (start); 291 } 292 return (start); 293 } 294 295 /* 296 * find highest number file (below "start") that needs to be extracted 297 */ 298 ino_t 299 upperbnd(start) 300 ino_t start; 301 { 302 struct entry *ep; 303 304 for ( ; start > ROOTINO; start--) { 305 ep = lookupino(start); 306 if (ep == NULL || ep->e_type == NODE) 307 continue; 308 if (ep->e_flags & (NEW|EXTRACT)) 309 return (start); 310 } 311 return (start); 312 } 313 314 /* 315 * report on a badly formed entry 316 */ 317 void 318 badentry(ep, message) 319 struct entry *ep; 320 char *message; 321 { 322 323 fprintf(stderr, "bad entry: %s\n", message); 324 fprintf(stderr, "name: %s\n", myname(ep)); 325 fprintf(stderr, "parent name %s\n", myname(ep->e_parent)); 326 if (ep->e_sibling != NULL) 327 fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling)); 328 if (ep->e_entries != NULL) 329 fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries)); 330 if (ep->e_links != NULL) 331 fprintf(stderr, "next link name: %s\n", myname(ep->e_links)); 332 if (ep->e_next != NULL) 333 fprintf(stderr, 334 "next hashchain name: %s\n", myname(ep->e_next)); 335 fprintf(stderr, "entry type: %s\n", 336 ep->e_type == NODE ? "NODE" : "LEAF"); 337 fprintf(stderr, "inode number: %ld\n", (long)ep->e_ino); 338 panic("flags: %s\n", flagvalues(ep)); 339 } 340 341 /* 342 * Construct a string indicating the active flag bits of an entry. 343 */ 344 char * 345 flagvalues(ep) 346 struct entry *ep; 347 { 348 static char flagbuf[BUFSIZ]; 349 350 (void) strcpy(flagbuf, "|NIL"); 351 flagbuf[0] = '\0'; 352 if (ep->e_flags & REMOVED) 353 (void) strcat(flagbuf, "|REMOVED"); 354 if (ep->e_flags & TMPNAME) 355 (void) strcat(flagbuf, "|TMPNAME"); 356 if (ep->e_flags & EXTRACT) 357 (void) strcat(flagbuf, "|EXTRACT"); 358 if (ep->e_flags & NEW) 359 (void) strcat(flagbuf, "|NEW"); 360 if (ep->e_flags & KEEP) 361 (void) strcat(flagbuf, "|KEEP"); 362 if (ep->e_flags & EXISTED) 363 (void) strcat(flagbuf, "|EXISTED"); 364 return (&flagbuf[1]); 365 } 366 367 /* 368 * Check to see if a name is on a dump tape. 369 */ 370 ino_t 371 dirlookup(name) 372 const char *name; 373 { 374 struct direct *dp; 375 ino_t ino; 376 377 ino = ((dp = pathsearch(name)) == NULL) ? 0 : dp->d_ino; 378 379 if (ino == 0 || TSTINO(ino, dumpmap) == 0) 380 fprintf(stderr, "%s is not on the tape\n", name); 381 return (ino); 382 } 383 384 /* 385 * Elicit a reply. 386 */ 387 int 388 reply(question) 389 char *question; 390 { 391 char c; 392 393 do { 394 fprintf(stderr, "%s? [yn] ", question); 395 (void) fflush(stderr); 396 c = getc(terminal); 397 while (c != '\n' && getc(terminal) != '\n') 398 if (feof(terminal)) 399 return (FAIL); 400 } while (c != 'y' && c != 'n'); 401 if (c == 'y') 402 return (GOOD); 403 return (FAIL); 404 } 405 406 /* 407 * handle unexpected inconsistencies 408 */ 409 #include <stdarg.h> 410 411 void 412 panic(const char *fmt, ...) 413 { 414 va_list ap; 415 416 va_start(ap, fmt); 417 vfprintf(stderr, fmt, ap); 418 va_end(ap); 419 if (yflag) 420 return; 421 if (reply("abort") == GOOD) { 422 if (reply("dump core") == GOOD) 423 abort(); 424 exit(1); 425 } 426 } 427 428 void 429 writemtree(const char *name, const char *type, 430 const uid_t uid, const gid_t gid, const mode_t mode, const u_long flags) 431 { 432 char *sep = ""; 433 if ((name[0] != '.') || (name[1] != '/' && name[1] != '\0')) 434 fprintf(Mtreefile, "./"); 435 fprintf(Mtreefile, "%s type=%s uid=%d gid=%d mode=%#4.4o", 436 name, type, uid, gid, 437 mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISTXT)); 438 if (flags != 0) 439 fprintf(Mtreefile, " flags="); 440 if (flags & UF_NODUMP) { 441 fprintf(Mtreefile, "nodump"); 442 sep=","; 443 } 444 if (flags & UF_IMMUTABLE) { 445 fprintf(Mtreefile, "%suchg", sep); 446 sep=","; 447 } 448 if (flags & UF_APPEND) { 449 fprintf(Mtreefile, "%suappnd", sep); 450 sep=","; 451 } 452 if (flags & UF_OPAQUE) { 453 fprintf(Mtreefile, "%sopaque", sep); 454 sep=","; 455 } 456 if (flags & SF_ARCHIVED) { 457 fprintf(Mtreefile, "%sarch", sep); 458 sep=","; 459 } 460 if (flags & SF_IMMUTABLE) { 461 fprintf(Mtreefile, "%sschg", sep); 462 sep=","; 463 } 464 if (flags & SF_APPEND) { 465 fprintf(Mtreefile, "%ssappnd", sep); 466 sep=","; 467 } 468 fprintf(Mtreefile, "\n"); 469 if (ferror(Mtreefile)) 470 err(1, "error writing to mtree file"); 471 } 472