1 /* $NetBSD: file.c,v 1.1.1.1 2008/09/30 19:00:27 joerg Exp $ */ 2 3 #if HAVE_CONFIG_H 4 #include "config.h" 5 #endif 6 #include <nbcompat.h> 7 #if HAVE_SYS_CDEFS_H 8 #include <sys/cdefs.h> 9 #endif 10 #if HAVE_SYS_PARAM_H 11 #include <sys/param.h> 12 #endif 13 #if HAVE_SYS_QUEUE_H 14 #include <sys/queue.h> 15 #endif 16 #ifndef lint 17 #if 0 18 static const char *rcsid = "from FreeBSD Id: file.c,v 1.29 1997/10/08 07:47:54 charnier Exp"; 19 #else 20 __RCSID("$NetBSD: file.c,v 1.1.1.1 2008/09/30 19:00:27 joerg Exp $"); 21 #endif 22 #endif 23 24 /* 25 * FreeBSD install - a package for the installation and maintainance 26 * of non-core utilities. 27 * 28 * Redistribution and use in source and binary forms, with or without 29 * modification, are permitted provided that the following conditions 30 * are met: 31 * 1. Redistributions of source code must retain the above copyright 32 * notice, this list of conditions and the following disclaimer. 33 * 2. Redistributions in binary form must reproduce the above copyright 34 * notice, this list of conditions and the following disclaimer in the 35 * documentation and/or other materials provided with the distribution. 36 * 37 * Jordan K. Hubbard 38 * 18 July 1993 39 * 40 * Miscellaneous file access utilities. 41 * 42 */ 43 44 #include "lib.h" 45 46 #if HAVE_SYS_WAIT_H 47 #include <sys/wait.h> 48 #endif 49 50 #if HAVE_ASSERT_H 51 #include <assert.h> 52 #endif 53 #if HAVE_ERR_H 54 #include <err.h> 55 #endif 56 #if HAVE_GLOB_H 57 #include <glob.h> 58 #endif 59 #if HAVE_NETDB_H 60 #include <netdb.h> 61 #endif 62 #if HAVE_PWD_H 63 #include <pwd.h> 64 #endif 65 #if HAVE_TIME_H 66 #include <time.h> 67 #endif 68 #if HAVE_FCNTL_H 69 #include <fcntl.h> 70 #endif 71 72 73 /* 74 * Quick check to see if a file (or dir ...) exists 75 */ 76 Boolean 77 fexists(const char *fname) 78 { 79 struct stat dummy; 80 if (!lstat(fname, &dummy)) 81 return TRUE; 82 return FALSE; 83 } 84 85 /* 86 * Quick check to see if something is a directory 87 */ 88 Boolean 89 isdir(const char *fname) 90 { 91 struct stat sb; 92 93 if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode)) 94 return TRUE; 95 else 96 return FALSE; 97 } 98 99 /* 100 * Check if something is a link to a directory 101 */ 102 Boolean 103 islinktodir(const char *fname) 104 { 105 struct stat sb; 106 107 if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode)) { 108 if (stat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode)) 109 return TRUE; /* link to dir! */ 110 else 111 return FALSE; /* link to non-dir */ 112 } else 113 return FALSE; /* non-link */ 114 } 115 116 /* 117 * Check if something is a link that points to nonexistant target. 118 */ 119 Boolean 120 isbrokenlink(const char *fname) 121 { 122 struct stat sb; 123 124 if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode)) { 125 if (stat(fname, &sb) != FAIL) 126 return FALSE; /* link target exists! */ 127 else 128 return TRUE; /* link target missing*/ 129 } else 130 return FALSE; /* non-link */ 131 } 132 133 /* 134 * Check to see if file is a dir, and is empty 135 */ 136 Boolean 137 isemptydir(const char *fname) 138 { 139 if (isdir(fname) || islinktodir(fname)) { 140 DIR *dirp; 141 struct dirent *dp; 142 143 dirp = opendir(fname); 144 if (!dirp) 145 return FALSE; /* no perms, leave it alone */ 146 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { 147 if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) { 148 closedir(dirp); 149 return FALSE; 150 } 151 } 152 (void) closedir(dirp); 153 return TRUE; 154 } 155 return FALSE; 156 } 157 158 /* 159 * Check if something is a regular file 160 */ 161 Boolean 162 isfile(const char *fname) 163 { 164 struct stat sb; 165 if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) 166 return TRUE; 167 return FALSE; 168 } 169 170 /* 171 * Check to see if file is a file and is empty. If nonexistent or not 172 * a file, say "it's empty", otherwise return TRUE if zero sized. 173 */ 174 Boolean 175 isemptyfile(const char *fname) 176 { 177 struct stat sb; 178 if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) { 179 if (sb.st_size != 0) 180 return FALSE; 181 } 182 return TRUE; 183 } 184 185 /* This struct defines the leading part of a valid URL name */ 186 typedef struct url_t { 187 char *u_s; /* the leading part of the URL */ 188 int u_len; /* its length */ 189 } url_t; 190 191 /* A table of valid leading strings for URLs */ 192 static const url_t urls[] = { 193 {"ftp://", 6}, 194 {"http://", 7}, 195 {NULL} 196 }; 197 198 /* 199 * Returns length of leading part of any URL from urls table, or -1 200 */ 201 int 202 URLlength(const char *fname) 203 { 204 const url_t *up; 205 int i; 206 207 if (fname != (char *) NULL) { 208 for (i = 0; isspace((unsigned char) *fname); i++) { 209 fname++; 210 } 211 for (up = urls; up->u_s; up++) { 212 if (strncmp(fname, up->u_s, up->u_len) == 0) { 213 return i + up->u_len; /* ... + sizeof(up->u_s); - HF */ 214 } 215 } 216 } 217 return -1; 218 } 219 220 /* 221 * Returns the host part of a URL 222 */ 223 const char * 224 fileURLHost(const char *fname, char *where, int max) 225 { 226 const char *ret; 227 int i; 228 229 assert(where != NULL); 230 assert(max > 0); 231 232 if ((i = URLlength(fname)) < 0) { /* invalid URL? */ 233 errx(EXIT_FAILURE, "fileURLhost called with a bad URL: `%s'", fname); 234 } 235 fname += i; 236 /* Do we have a place to stick our work? */ 237 ret = where; 238 while (*fname && *fname != '/' && --max) 239 *where++ = *fname++; 240 *where = '\0'; 241 242 return ret; 243 } 244 245 /* 246 * Returns the filename part of a URL 247 */ 248 const char * 249 fileURLFilename(const char *fname, char *where, int max) 250 { 251 const char *ret; 252 int i; 253 254 assert(where != NULL); 255 assert(max > 0); 256 257 if ((i = URLlength(fname)) < 0) { /* invalid URL? */ 258 errx(EXIT_FAILURE, "fileURLFilename called with a bad URL: `%s'", fname); 259 } 260 fname += i; 261 /* Do we have a place to stick our work? */ 262 ret = where; 263 while (*fname && *fname != '/') 264 ++fname; 265 if (*fname == '/') { 266 while (*fname && --max) 267 *where++ = *fname++; 268 } 269 *where = '\0'; 270 271 return ret; 272 } 273 274 /* 275 * Try and fetch a file by URL, returning the directory name for where 276 * it's unpacked, if successful. To be handed to leave_playpen() later. 277 */ 278 char * 279 fileGetURL(const char *spec) 280 { 281 char host[MAXHOSTNAMELEN], file[MaxPathSize]; 282 const char *cp; 283 char *rp; 284 char pen[MaxPathSize]; 285 int rc; 286 287 rp = NULL; 288 if (!IS_URL(spec)) { 289 errx(EXIT_FAILURE, "fileGetURL was called with non-URL arg '%s'", spec); 290 } 291 292 /* Some sanity checks on the URL */ 293 cp = fileURLHost(spec, host, MAXHOSTNAMELEN); 294 if (!*cp) { 295 warnx("URL `%s' has bad host part!", spec); 296 return NULL; 297 } 298 cp = fileURLFilename(spec, file, MaxPathSize); 299 if (!*cp) { 300 warnx("URL `%s' has bad filename part!", spec); 301 return NULL; 302 } 303 304 if (Verbose) 305 printf("Trying to fetch %s.\n", spec); 306 307 pen[0] = '\0'; 308 rp = make_playpen(pen, sizeof(pen), 0); 309 if (rp == NULL) { 310 printf("Error: Unable to construct a new playpen for FTP!\n"); 311 return NULL; 312 } 313 314 rp = strdup(pen); 315 rc = unpackURL(spec, pen); 316 if (rc < 0) { 317 leave_playpen(rp); /* Don't leave dir hang around! */ 318 319 printf("Error on unpackURL('%s', '%s')\n", spec, pen); 320 return NULL; 321 } 322 return rp; 323 } 324 325 static char * 326 resolvepattern1(const char *name) 327 { 328 static char tmp[MaxPathSize]; 329 char *cp; 330 331 if (IS_URL(name)) { 332 /* some package depends on a wildcard pkg */ 333 int rc; 334 335 rc = expandURL(tmp, name); 336 if (rc < 0) { 337 return NULL; 338 } 339 if (Verbose) 340 printf("'%s' expanded to '%s'\n", name, tmp); 341 return tmp; /* return expanded URL w/ corrent pkg */ 342 } 343 else if (ispkgpattern(name)) { 344 cp = find_best_matching_file(dirname_of(name), basename_of(name), 1, 0); 345 if (cp) { 346 snprintf(tmp, sizeof(tmp), "%s/%s", dirname_of(name), cp); 347 free(cp); 348 return tmp; 349 } 350 } else { 351 if (isfile(name)) { 352 strlcpy(tmp, name, sizeof(tmp)); 353 return tmp; 354 } 355 } 356 357 return NULL; 358 } 359 360 static char * 361 resolvepattern(const char *name) 362 { 363 char tmp[MaxPathSize]; 364 char *cp; 365 const char *suf; 366 367 cp = resolvepattern1(name); 368 if (cp != NULL) 369 return cp; 370 371 if (ispkgpattern(name)) 372 return NULL; 373 374 suf = suffix_of(name); 375 if (!strcmp(suf, "tbz") || !strcmp(suf, "tgz")) 376 return NULL; 377 378 /* add suffix and try */ 379 snprintf(tmp, sizeof(tmp), "%s.tbz", name); 380 cp = resolvepattern1(tmp); 381 if (cp != NULL) 382 return cp; 383 snprintf(tmp, sizeof(tmp), "%s.tgz", name); 384 cp = resolvepattern1(tmp); 385 if (cp != NULL) 386 return cp; 387 388 /* add version number wildcard and try */ 389 snprintf(tmp, sizeof(tmp), "%s-[0-9]*", name); 390 return resolvepattern1(tmp); 391 } 392 393 /* 394 * Look for filename/pattern "fname" in 395 * Returns a full path/URL where the pkg can be found 396 */ 397 char * 398 fileFindByPath(const char *fname) 399 { 400 char tmp[MaxPathSize]; 401 struct path *path; 402 403 /* 404 * 1. if fname is an absolute pathname or a URL, 405 * just use it. 406 */ 407 if (IS_FULLPATH(fname) || IS_URL(fname)) 408 return resolvepattern(fname); 409 410 /* 411 * 2. otherwise, use PKG_PATH. 412 */ 413 TAILQ_FOREACH(path, &PkgPath, pl_entry) { 414 char *cp; 415 const char *cp2 = path->pl_path; 416 417 if (Verbose) 418 printf("trying PKG_PATH %s\n", cp2); 419 420 if (IS_FULLPATH(cp2) || IS_URL(cp2)) { 421 snprintf(tmp, sizeof(tmp), "%s/%s", cp2, fname); 422 } 423 else { 424 char cwdtmp[MaxPathSize]; 425 if (getcwd(cwdtmp, sizeof(cwdtmp)) == NULL) 426 errx(EXIT_FAILURE, "getcwd"); 427 snprintf(tmp, sizeof(tmp), "%s/%s/%s", cwdtmp, cp2, fname); 428 } 429 cp = resolvepattern(tmp); 430 if (cp) 431 return cp; 432 } 433 434 #if 0 435 /* 436 * 3. finally, search current directory. 437 */ 438 snprintf(tmp, sizeof(tmp), "./%s", fname); 439 return resolvepattern(tmp); 440 #else 441 return NULL; 442 #endif 443 } 444 445 /* 446 * Expect "fname" to point at a file, and read it into 447 * the buffer returned. 448 */ 449 char * 450 fileGetContents(char *fname) 451 { 452 char *contents; 453 struct stat sb; 454 int fd; 455 456 if (stat(fname, &sb) == FAIL) { 457 cleanup(0); 458 errx(2, "can't stat '%s'", fname); 459 } 460 461 contents = (char *) malloc((size_t) (sb.st_size) + 1); 462 fd = open(fname, O_RDONLY, 0); 463 if (fd == FAIL) { 464 cleanup(0); 465 errx(2, "unable to open '%s' for reading", fname); 466 } 467 if (read(fd, contents, (size_t) sb.st_size) != (size_t) sb.st_size) { 468 cleanup(0); 469 errx(2, "short read on '%s' - did not get %lld bytes", 470 fname, (long long) sb.st_size); 471 } 472 close(fd); 473 contents[(size_t) sb.st_size] = '\0'; 474 return contents; 475 } 476 477 /* 478 * Takes a filename and package name, returning (in "try") the canonical 479 * "preserve" name for it. 480 */ 481 Boolean 482 make_preserve_name(char *try, size_t max, char *name, char *file) 483 { 484 int len, i; 485 486 if ((len = strlen(file)) == 0) 487 return FALSE; 488 else 489 i = len - 1; 490 strncpy(try, file, max); 491 if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */ 492 --i; 493 for (; i; i--) { 494 if (try[i] == '/') { 495 try[i + 1] = '.'; 496 strncpy(&try[i + 2], &file[i + 1], max - i - 2); 497 break; 498 } 499 } 500 if (!i) { 501 try[0] = '.'; 502 strncpy(try + 1, file, max - 1); 503 } 504 /* I should probably be called rude names for these inline assignments */ 505 strncat(try, ".", max -= strlen(try)); 506 strncat(try, name, max -= strlen(name)); 507 strncat(try, ".", max--); 508 strncat(try, "backup", max -= 6); 509 return TRUE; 510 } 511 512 /* 513 * Write the contents of "str" to a file 514 */ 515 void 516 write_file(char *name, char *str) 517 { 518 size_t len; 519 FILE *fp; 520 521 if ((fp = fopen(name, "w")) == (FILE *) NULL) { 522 cleanup(0); 523 errx(2, "cannot fopen '%s' for writing", name); 524 } 525 len = strlen(str); 526 if (fwrite(str, 1, len, fp) != len) { 527 cleanup(0); 528 errx(2, "short fwrite on '%s', tried to write %ld bytes", 529 name, (long) len); 530 } 531 if (fclose(fp)) { 532 cleanup(0); 533 errx(2, "failure to fclose '%s'", name); 534 } 535 } 536 537 void 538 copy_file(char *dir, char *fname, char *to) 539 { 540 char fpath[MaxPathSize]; 541 542 (void) snprintf(fpath, sizeof(fpath), "%s%s%s", 543 (fname[0] != '/') ? dir : "", 544 (fname[0] != '/') ? "/" : "", 545 fname); 546 if (fexec("cp", "-r", fpath, to, NULL)) { 547 cleanup(0); 548 errx(2, "could not perform 'cp -r %s %s'", fpath, to); 549 } 550 } 551 552 void 553 move_file(char *dir, char *fname, char *to) 554 { 555 char fpath[MaxPathSize]; 556 557 (void) snprintf(fpath, sizeof(fpath), "%s%s%s", 558 (fname[0] != '/') ? dir : "", 559 (fname[0] != '/') ? "/" : "", 560 fname); 561 if (fexec("mv", fpath, to, NULL)) { 562 cleanup(0); 563 errx(2, "could not perform 'mv %s %s'", fpath, to); 564 } 565 } 566 567 void 568 move_files(const char *dir, const char *pattern, const char *to) 569 { 570 char fpath[MaxPathSize]; 571 glob_t globbed; 572 size_t i; 573 574 (void) snprintf(fpath, sizeof(fpath), "%s/%s", dir, pattern); 575 if ((i=glob(fpath, GLOB_NOSORT, NULL, &globbed)) != 0) { 576 switch(i) { 577 case GLOB_NOMATCH: 578 warn("no files matching ``%s'' found", fpath); 579 break; 580 case GLOB_ABORTED: 581 warn("globbing aborted"); 582 break; 583 case GLOB_NOSPACE: 584 warn("out-of-memory during globbing"); 585 break; 586 default: 587 warn("unknown error during globbing"); 588 break; 589 } 590 return; 591 } 592 593 /* Moving globbed files -- we just use mv(1) to do the job */ 594 for (i=0; i<globbed.gl_pathc; i++) 595 if (fexec("mv", globbed.gl_pathv[i], to, NULL)) { 596 cleanup(0); 597 errx(2, "could not perform 'mv %s %s'", globbed.gl_pathv[i], to); 598 } 599 600 return; 601 } 602 603 void 604 remove_files(const char *path, const char *pattern) 605 { 606 char fpath[MaxPathSize]; 607 glob_t globbed; 608 int i; 609 610 (void) snprintf(fpath, sizeof(fpath), "%s/%s", path, pattern); 611 if ((i=glob(fpath, GLOB_NOSORT, NULL, &globbed)) != 0) { 612 switch(i) { 613 case GLOB_NOMATCH: 614 warn("no files matching ``%s'' found", fpath); 615 break; 616 case GLOB_ABORTED: 617 warn("globbing aborted"); 618 break; 619 case GLOB_NOSPACE: 620 warn("out-of-memory during globbing"); 621 break; 622 default: 623 warn("unknown error during globbing"); 624 break; 625 } 626 return; 627 } 628 629 /* deleting globbed files */ 630 for (i=0; i<globbed.gl_pathc; i++) 631 if (unlink(globbed.gl_pathv[i]) < 0) 632 warn("can't delete ``%s''", globbed.gl_pathv[i]); 633 634 return; 635 } 636 637 /* 638 * Unpack a tar file 639 */ 640 int 641 unpack(const char *pkg, const lfile_head_t *filesp) 642 { 643 const char *decompress_cmd = NULL; 644 const char *suf; 645 int count = 0; 646 lfile_t *lfp; 647 char **up_argv; 648 int up_argc = 7; 649 int i = 0; 650 int result; 651 652 if (filesp != NULL) 653 TAILQ_FOREACH(lfp, filesp, lf_link) 654 count++; 655 up_argc += count; 656 up_argv = malloc((count + up_argc + 1) * sizeof(char *)); 657 if (!IS_STDIN(pkg)) { 658 suf = suffix_of(pkg); 659 if (!strcmp(suf, "tbz") || !strcmp(suf, "bz2")) 660 decompress_cmd = BZIP2_CMD; 661 else if (!strcmp(suf, "tgz") || !strcmp(suf, "gz")) 662 decompress_cmd = GZIP_CMD; 663 else if (!strcmp(suf, "tar")) 664 ; /* do nothing */ 665 else 666 errx(EXIT_FAILURE, "don't know how to decompress %s, sorry", pkg); 667 } else 668 decompress_cmd = GZIP_CMD; 669 670 up_argv[i] = (char *)strrchr(TAR_CMD, '/'); 671 if (up_argv[i] == NULL) 672 up_argv[i] = TAR_CMD; 673 else 674 up_argv[i]++; /* skip / character */ 675 if (count > 0) 676 up_argv[++i] = "--fast-read"; 677 if (decompress_cmd != NULL) { 678 up_argv[++i] = "--use-compress-program"; 679 up_argv[++i] = (char *)decompress_cmd; 680 } 681 up_argv[++i] = "-xpf"; 682 up_argv[++i] = (char *)pkg; 683 if (count > 0) 684 TAILQ_FOREACH(lfp, filesp, lf_link) 685 up_argv[++i] = lfp->lf_name; 686 up_argv[++i] = NULL; 687 688 if (Verbose) { 689 printf("running: %s", TAR_CMD); 690 for (i = 1; up_argv[i] != NULL; i++) 691 printf(" %s", up_argv[i]); 692 printf("\n"); 693 } 694 695 result = pfcexec(NULL, TAR_CMD, (const char **)up_argv); 696 free(up_argv); 697 if (result != 0) { 698 warnx("extract of %s failed", pkg); 699 return 1; 700 } 701 702 return 0; 703 } 704 705 /* 706 * Using fmt, replace all instances of: 707 * 708 * %F With the parameter "name" 709 * %D With the parameter "dir" 710 * %B Return the directory part ("base") of %D/%F 711 * %f Return the filename part of %D/%F 712 * 713 * Check that no overflows can occur. 714 */ 715 void 716 format_cmd(char *buf, size_t size, char *fmt, char *dir, char *name) 717 { 718 char scratch[MaxPathSize * 2]; 719 char *bufp; 720 char *cp; 721 722 for (bufp = buf; (int) (bufp - buf) < size && *fmt;) { 723 if (*fmt == '%') { 724 if (*++fmt != 'D' && name == NULL) { 725 cleanup(0); 726 errx(2, "no last file available for '%s' command", buf); 727 } 728 switch (*fmt) { 729 case 'F': 730 strlcpy(bufp, name, size - (int) (bufp - buf)); 731 bufp += strlen(bufp); 732 break; 733 734 case 'D': 735 strlcpy(bufp, dir, size - (int) (bufp - buf)); 736 bufp += strlen(bufp); 737 break; 738 739 case 'B': 740 (void) snprintf(scratch, sizeof(scratch), "%s/%s", dir, name); 741 if ((cp = strrchr(scratch, '/')) == (char *) NULL) { 742 cp = scratch; 743 } 744 *cp = '\0'; 745 strlcpy(bufp, scratch, size - (int) (bufp - buf)); 746 bufp += strlen(bufp); 747 break; 748 749 case 'f': 750 (void) snprintf(scratch, sizeof(scratch), "%s/%s", dir, name); 751 if ((cp = strrchr(scratch, '/')) == (char *) NULL) { 752 cp = scratch; 753 } else { 754 cp++; 755 } 756 strlcpy(bufp, cp, size - (int) (bufp - buf)); 757 bufp += strlen(bufp); 758 break; 759 760 default: 761 *bufp++ = '%'; 762 *bufp++ = *fmt; 763 break; 764 } 765 ++fmt; 766 } else { 767 *bufp++ = *fmt++; 768 } 769 } 770 *bufp = '\0'; 771 } 772