1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <locale.h> 30 #include <sys/types.h> 31 #include <sys/param.h> 32 #include <stdio.h> 33 #include <string.h> 34 #include <errno.h> 35 #include <search.h> 36 37 #include <dirent.h> 38 #include <fnmatch.h> 39 #include <sys/stat.h> 40 41 #include "rules.h" 42 43 extern char *mstrdup(const char *); 44 45 /* 46 * The do_base_dir() function is called when a BASE command is encountered 47 * in the input directives or end-of-file is reach on the input directive 48 * file. This function causes the commands associated with the previous 49 * BASE command to be executed. for example, 50 * 51 * BASE a/b/c 52 * LIST ... 53 * IGNORE ... 54 * BASE d/e/f (this command will cause do_base_dir() to be called for 55 * base directory a/b/c) 56 * 57 * Input arguments: 58 * dirpath - the BASE directory being operated on 59 * incld_lst - A list of strings to be matched obtained from the 60 * LIST commands associated with the BASE directory. 61 * excld_lst - A list of strings to be matched obtained from the 62 * IGNORE commands associated with the BASE directory. 63 * func - A function to be called for each matched file. The 64 * functions allow files to be packed, unpacked, 65 * examined and filenames printed. 66 */ 67 68 void 69 do_base_dir(char *dirpath, struct item *incld_lst, struct item *excld_lst, 70 int (*func)(char *, char *, DIR *, int)) 71 { 72 struct item *iitem; 73 int err; 74 int files_processed = 0; 75 struct item symlk_hd, *symlk, *symlk_sv; 76 struct stat64 statbuf; 77 char linkbuf[MAXPATHLEN]; 78 int sz; 79 char *s; 80 char *mk_base_dir(char *, char *); 81 82 #ifdef DEBUG 83 prtitem("Global IGNOREs", &gign_hd); 84 prtitem("LIST cmds", &list_hd); 85 prtitem("Local IGNOREs", &lign_hd); 86 #endif /* DEBUG */ 87 88 89 symlk = &symlk_hd; 90 symlk_hd.i_next = (struct item *)0; 91 92 iitem = incld_lst->i_next; 93 if (iitem == (struct item *)0) 94 return; 95 while (iitem != (struct item *)0) { 96 #ifdef DEBUG 97 printf("do_base_dir: iitem->i_str = %s iitem->i_flag = %x\n", 98 iitem->i_str, iitem->i_flag); 99 fflush(stdout); 100 #endif /* DEBUG */ 101 err = do_list_item(dirpath, iitem->i_str, 102 iitem->i_flag, excld_lst, 0, &symlk, func); 103 if (err == 0) { 104 fprintf(stderr, 105 gettext("cachefspack: basedir = %s"), 106 dirpath); 107 fprintf(stderr, 108 gettext(" %s - no file(s) selected\n"), 109 iitem->i_str); 110 } 111 iitem = iitem->i_next; 112 }; 113 /* 114 * Invoke 'func' for each component of the BASE 115 * directory. 116 */ 117 func_dir_path(dirpath, func); 118 119 if (lstat64(dirpath, &statbuf) < 0) { 120 perror(gettext("Can't stat base directory")); 121 } else { 122 if (S_ISLNK(statbuf.st_mode)) { 123 sz = readlink(dirpath, linkbuf, MAXPATHLEN-1); 124 if (sz > 0) { 125 linkbuf[sz] = '\0'; 126 s = mk_base_dir(dirpath, linkbuf); 127 if (s != (char *)0) { 128 func_dir_path(s, func); 129 } 130 } 131 } 132 } 133 134 #ifdef DEBUG 135 prtitem("Symbolic Links", &symlk_hd); 136 #endif /* DEBUG */ 137 iitem = symlk_hd.i_next; 138 if (iitem == (struct item *)0) 139 return; 140 while (iitem != (struct item *)0) { 141 #ifdef DEBUG 142 printf("do_bas sl: iitem->i_str = %s iitem->i_flag = %x\n", 143 iitem->i_str, iitem->i_flag); 144 fflush(stdout); 145 #endif /* DEBUG */ 146 files_processed = do_list_item(iitem->i_str, "*", 147 (LF_SYMLINK | LF_REGEX), excld_lst, 0, &symlk, func); 148 if (files_processed) { 149 /* 150 * Invoke 'func' for each component of the BASE 151 * directory. 152 */ 153 func_dir_path(iitem->i_str, func); 154 } 155 symlk_sv = iitem; 156 iitem = iitem->i_next; 157 symlk_hd.i_next = iitem; 158 free(symlk_sv); 159 #ifdef DEBUG 160 prtitem("Symbolic Links loop", &symlk_hd); 161 #endif /* DEBUG */ 162 } 163 } 164 165 /* 166 * The do_list_item() function is called for each LIST item associated with 167 * a BASE directory. It does the work of descending directories and matching 168 * filenames. 169 * 170 * Input arguments: 171 * dirpath - the BASE directory being operated on 172 * pat - The argument from the LIST command to match 173 * flags - Flags which affect how patterns are matched: 174 * LF_STRIP_DOTSLASH - means strip off "." and/or "/" at the 175 * beginning of the pattern to match. 176 * LF_REGEX - Means match the pattern as a regular expression. 177 * Otherwise, an exact match of characters is required. 178 * excld_lst - A list of strings to be matched obtained from the 179 * IGNORE commands associated with the BASE directory. 180 * func - A function to be called for each matched file. The 181 * functions allow files to be packed, unpacked, 182 * examined and filenames printed. 183 * 184 * Return values: 185 * 0 - 'func' NOT invoked for any file 186 * 1 - 'func' invoked for at least 1 file 187 */ 188 int 189 do_list_item(char *dirpath, char *pat, int flags, struct item *excld_lst, 190 DIR *pdir, struct item **symlk_lst, int (*func)(char *, char *, DIR *, int)) 191 { 192 static char statnam[MAXPATHLEN]; 193 static int glastpos = 0; 194 static int basedir_lastpos; 195 static int depth = 0; 196 static int unwind = 0; 197 static int do_dir = 0; 198 static int sl_cnt; 199 static int retval; 200 static char linkbuf[MAXPATHLEN]; 201 DIR *dir, *parent_dir; 202 struct dirent64 *dirent; 203 int match; 204 int err; 205 struct stat64 statbuf; 206 int llastpos; 207 struct item *eitem; 208 int excld_flag; 209 char *p; 210 int diropn; 211 int len; 212 int sz; 213 void process_symlk(); 214 215 strcpy(&statnam[glastpos], dirpath); 216 len = strlen(statnam) - 1; 217 if (statnam[len] != '/') { 218 strcat(statnam, "/"); 219 } 220 parent_dir = pdir; 221 llastpos = glastpos; 222 glastpos = strlen(statnam); 223 if (depth == 0) { 224 basedir_lastpos = glastpos; 225 sl_cnt = slash_cnt(pat); 226 retval = 0; 227 } 228 depth++; 229 230 diropn = 0; 231 dir = opendir(statnam); 232 if (dir == NULL) { 233 fprintf(stderr, gettext("\ncachefspack: %s - "), statnam); 234 perror(gettext("Can't open directory")); 235 goto out; 236 } 237 diropn = 1; 238 239 while (1) { 240 dirent = readdir64(dir); 241 if (dirent == NULL) { /* EOF */ 242 if ((depth-1) > do_dir) { 243 do_dir = depth - 1; 244 } 245 break; 246 } 247 /* 248 * If file is '..' skip it 249 */ 250 if (strcmp(dirent->d_name, "..") == 0) { 251 continue; 252 } 253 /* 254 * Apply excludes if this is not a LISTed directory 255 * NOTE: names from IGNORE commands are matched against the 256 * component name(a name between '/' marks), not the 257 * whole pathname. 258 */ 259 if (flags & LF_SYMLINK) { 260 match = ((depth-1) >= sl_cnt); 261 } else { 262 match = ((depth-1) > sl_cnt); 263 } 264 if (match) { 265 eitem = excld_lst->i_next; 266 excld_flag = 0; 267 while (eitem != (struct item *)0) { 268 match = gmatch(dirent->d_name, eitem->i_str); 269 if (match == 1) { 270 excld_flag = 1; 271 break; 272 } 273 eitem = eitem->i_next; 274 } 275 if (excld_flag == 1) { 276 continue; 277 } 278 } 279 strcpy(&statnam[glastpos], dirent->d_name); 280 err = lstat64(statnam, &statbuf); 281 if (err < 0) { 282 fprintf(stderr, 283 gettext("cachefspack: %s - stat failed"), 284 statnam); 285 perror(gettext(" ")); 286 continue; 287 } 288 p = pat; 289 if (flags & LF_STRIP_DOTSLASH) { 290 if (strncmp(p, "./", 2) == 0) { 291 p += 2; 292 } 293 } 294 if (S_ISDIR(statbuf.st_mode)) { 295 #ifdef DEBUG 296 printf("directory: &statnam[basedir_lastpos] = %s\n", 297 &statnam[basedir_lastpos]); 298 printf("statbuf.st_mode = %o\n", statbuf.st_mode); 299 printf("depth = %d sl_cnt = %d\n", depth, sl_cnt); 300 fflush(stdout); 301 #endif /* DEBUG */ 302 if ((depth-1) == sl_cnt) { 303 if (flags & LF_REGEX) { 304 match = 305 gmatch(&statnam[basedir_lastpos], 306 p); 307 } else { 308 match = 309 (strcmp(&statnam[basedir_lastpos], 310 p) == 0); 311 } 312 if (match) { 313 /* 314 * Don't descend '.' directory 315 * but match it 316 */ 317 if (strcmp(dirent->d_name, ".") != 0) { 318 do_list_item(dirent->d_name, 319 "*", flags, excld_lst, 320 dir, symlk_lst, func); 321 } else { 322 if ((depth-1) > do_dir) { 323 do_dir = depth - 1; 324 } 325 (void) func(statnam, 326 dirent->d_name, 327 dir, depth); 328 } 329 retval = 1; 330 if (unwind = discont_srch(flags, p)) { 331 goto out; 332 } 333 } 334 continue; 335 } 336 /* 337 * Don't descend '.' directory 338 */ 339 if (strcmp(dirent->d_name, ".") != 0) { 340 do_list_item(dirent->d_name, p, flags, 341 excld_lst, dir, symlk_lst, func); 342 } 343 if (unwind) { 344 goto out; 345 } 346 continue; 347 } 348 if (S_ISLNK(statbuf.st_mode)) { 349 if (flags & LF_SYMLINK) 350 continue; 351 #ifdef DEBUG 352 printf("sym link : &statnam[basedir_lastpos] = %s\n", 353 &statnam[basedir_lastpos]); 354 printf("statbuf.st_mode = %o\n", statbuf.st_mode); 355 printf("statnam = %s\n", statnam); 356 #endif /* DEBUG */ 357 /* 358 * Symbolic link was explicitly specified or matches a 359 * regular expression in a LIST item. Thus we follow 360 * the link. Otherwise, just call 'func' for the link 361 * name. 362 */ 363 #ifdef DEBUG 364 printf("depth = %d sl_cnt = %d\n", depth, sl_cnt); 365 fflush(stdout); 366 #endif /* DEBUG */ 367 if ((depth-1) == sl_cnt) { 368 if (flags & LF_REGEX) { 369 match = 370 gmatch(&statnam[basedir_lastpos], 371 p); 372 } else { 373 match = 374 (strcmp(&statnam[basedir_lastpos], 375 p) == 0); 376 } 377 #ifdef DEBUG 378 printf("match = %d\n", match); 379 fflush(stdout); 380 #endif /* DEBUG */ 381 if (match) { 382 if ((depth-1) > do_dir) { 383 do_dir = depth - 1; 384 } 385 retval = 1; 386 (void) func(statnam, dirent->d_name, 387 dir, depth); 388 sz = readlink( 389 statnam, linkbuf, MAXPATHLEN-1); 390 #ifdef DEBUG 391 printf("linkbuf = %s\n", linkbuf); 392 printf("sz = %d\n", sz); 393 fflush(stdout); 394 #endif /* DEBUG */ 395 if (sz < 0) { 396 continue; 397 } 398 linkbuf[sz] = '\0'; 399 process_symlk(linkbuf, statnam, 400 glastpos, symlk_lst, func); 401 if (unwind = discont_srch(flags, p)) { 402 goto out; 403 } 404 } 405 } 406 if ((depth-1) > sl_cnt) { 407 if ((depth-1) > do_dir) { 408 do_dir = depth - 1; 409 } 410 retval = 1; 411 (void) func(statnam, dirent->d_name, dir, 412 depth); 413 sz = readlink(statnam, linkbuf, MAXPATHLEN-1); 414 #ifdef DEBUG 415 printf("linkbuf = %s\n", linkbuf); 416 printf("sz = %d\n", sz); 417 fflush(stdout); 418 #endif /* DEBUG */ 419 if (sz < 0) { 420 continue; 421 } 422 linkbuf[sz] = '\0'; 423 process_symlk(linkbuf, statnam, glastpos, 424 symlk_lst, func); 425 if (unwind = discont_srch(flags, p)) { 426 goto out; 427 } 428 } 429 continue; 430 } 431 /* 432 * File must be a regular file - 433 * Does it match the specified pattern? 434 */ 435 #ifdef DEBUG 436 printf("reg file : &statnam[basedir_lastpos] = %s p = %s\n", 437 &statnam[basedir_lastpos], p); 438 printf("statbuf.st_mode = %o\n", statbuf.st_mode); 439 fflush(stdout); 440 #endif /* DEBUG */ 441 if (flags & LF_REGEX) { 442 match = gmatch(&statnam[basedir_lastpos], p); 443 } else { 444 match = (strcmp(&statnam[basedir_lastpos], p) == 0); 445 } 446 if (!match) { 447 continue; 448 } 449 if ((depth - 1) > do_dir) { 450 do_dir = depth - 1; 451 } 452 retval = 1; 453 (void) func(statnam, dirent->d_name, dir, depth); 454 /* 455 * If the file is an executable, check to see if shared 456 * libraries need to be packed. 457 */ 458 if (statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 459 process_executable(statnam, func); 460 } 461 462 if (unwind = discont_srch(flags, p)) { 463 goto out; 464 } 465 } 466 out: 467 depth--; 468 if (depth == 0) { 469 unwind = 0; 470 } 471 statnam[glastpos] = '\0'; 472 if (do_dir) { 473 do_dir--; 474 #ifdef DEBUG 475 printf("out: call func\n"); 476 fflush(stdout); 477 printf("out: statnam = %s\n", statnam); 478 fflush(stdout); 479 printf("out: &statnam[llastpos] = %s\n", &statnam[llastpos]); 480 fflush(stdout); 481 #endif /* DEBUG */ 482 if (func(statnam, &statnam[llastpos], parent_dir, depth) < 0) { 483 do_dir = 0; 484 } 485 } 486 glastpos = llastpos; 487 if (diropn) 488 closedir(dir); 489 return (retval); 490 } 491 492 /* 493 * Count all the '/' characters in the string except for those 494 * in the first character position and last character position 495 * of the string. 496 */ 497 int 498 slash_cnt(char *str) 499 { 500 char *p = str; 501 int len; 502 int i; 503 int count = 0; 504 505 #ifdef DEBUG 506 printf("slash_cnt: str = %s", str); 507 #endif /* DEBUG */ 508 /* 509 * NOTE //a, /a and ./a are the same 510 */ 511 if (*p == '.') 512 p++; 513 while (*p == '/') 514 p++; 515 len = strlen(str) - 1; 516 for (i = 0; i < len; i++) { 517 if (*p == '/') { 518 count++; 519 i--; 520 while (*p == '/') { 521 p++; 522 i++; 523 } 524 } else { 525 p++; 526 } 527 } 528 #ifdef DEBUG 529 printf(" count = %d\n", count); 530 fflush(stdout); 531 #endif /* DEBUG */ 532 return (count); 533 } 534 535 /* 536 * For each directory in the path name, call 'func'. 537 */ 538 int 539 func_dir_path(char *path, int (*func)(char *, char *, DIR *, int)) 540 { 541 char *dnam; 542 char *fnam; 543 char *pathtmp; 544 DIR *dir; 545 char *get_fname(char *); 546 char *get_dirname(char *); 547 ENTRY hitem, *hitemp; 548 549 #ifdef DEBUG 550 printf("func_dir_path: path = %s\n", path); 551 fflush(stdout); 552 #endif /* DEBUG */ 553 fnam = path; 554 dnam = path; 555 pathtmp = mstrdup(path); 556 while (fnam != NULL) { 557 558 fnam = get_fname(dnam); 559 dnam = get_dirname(dnam); 560 if (fnam != (char *)0) { 561 if (strcmp(fnam, "..") == 0) { 562 free(pathtmp); 563 pathtmp = mstrdup(dnam); 564 continue; 565 } 566 } 567 #ifdef DEBUG 568 if (fnam != (char *)0) { 569 printf("func_dir_path: fnam = %s\n", fnam); 570 } 571 printf("func_dir_path: dnam = %s pathtmp = %s\n", 572 dnam, pathtmp); 573 fflush(stdout); 574 #endif /* DEBUG */ 575 576 hitem.key = mstrdup(pathtmp); 577 hitem.data = 0; 578 hitemp = hsearch(hitem, FIND); 579 if (hitemp != NULL) { 580 /* 581 * If hash item data is 0, item has not been packed. 582 * If hash item data is 1, item has been packed. 583 */ 584 #ifdef DEBUG 585 printf("func_dir_path: key = %s hitemp->data = %x\n", 586 hitemp->key, hitemp->data); 587 fflush(stdout); 588 #endif /* DEBUG */ 589 if (hitemp->data == (char *)1) 590 break; 591 hitemp->data = (char *)1; 592 } else { 593 hitem.key = mstrdup(pathtmp); 594 hitem.data = (char *)1; 595 if (hsearch(hitem, ENTER) == NULL) { 596 fprintf(stderr, 597 gettext("cachefspack: hash table full\n")); 598 } 599 } 600 601 dir = opendir(dnam); 602 if (dir != NULL) { 603 if (func(pathtmp, fnam, dir, 0) < 0) { 604 #ifdef DEBUG 605 printf("func_dir_path: errno = %d\n", errno); 606 fflush(stdout); 607 #endif /* DEBUG */ 608 closedir(dir); 609 return (-1); 610 } 611 closedir(dir); 612 } else { 613 printf(gettext("cachefspack: error opening dir -")); 614 printf("%s\n", dnam); 615 fflush(stdout); 616 } 617 618 free(pathtmp); 619 pathtmp = mstrdup(dnam); 620 } 621 free(pathtmp); 622 return (0); 623 } 624 void 625 process_symlk(char *lkpath, char *relpath, int rel_lastpos, 626 struct item **symlk, int (*func)(char *, char *, DIR *, int)) 627 { 628 struct stat64 lstatbuf; 629 char *l; 630 struct item *add_item(struct item *, char *, int); 631 int len; 632 633 /* 634 * if the link has a relative pathname, append the name to 635 * current path. 636 */ 637 if (*lkpath != '/') { 638 len = strlen(lkpath); 639 if ((len + rel_lastpos + 2) > MAXPATHLEN) { 640 fprintf(stderr, gettext("can't process sym link - %s"), 641 lkpath); 642 return; 643 } 644 strcpy(&relpath[rel_lastpos], lkpath); 645 l = relpath; 646 } else { 647 l = lkpath; 648 } 649 #ifdef DEBUG 650 printf("process_symlk: lkpath = %s\n", lkpath); 651 printf("process_symlk: l = %s\n", l); 652 printf("lstatbuf.st_mode = %o\n", lstatbuf.st_mode); 653 fflush(stdout); 654 #endif /* DEBUG */ 655 if (lstat64(l, &lstatbuf) < 0) { 656 fprintf(stderr, gettext("Can't lstat sym link - %s"), l); 657 perror(" "); 658 return; 659 } 660 if (S_ISDIR(lstatbuf.st_mode)) { 661 *symlk = add_item(*symlk, l, 0); 662 } 663 if (S_ISREG(lstatbuf.st_mode)) { 664 func_dir_path(l, func); 665 } 666 } 667 668 int 669 discont_srch(int flags, char *pat) 670 { 671 char *wild; 672 673 #ifdef DEBUG 674 printf("discont_srch: flags = %x pat = %s\n", flags, pat); 675 fflush(stdout); 676 #endif /* DEBUG */ 677 678 /* 679 * if patterns are NOT being matched as regular expressions 680 * we can have at most 1 match. We got it so quit. 681 */ 682 if ((flags & LF_REGEX) != LF_REGEX) { 683 #ifdef DEBUG 684 printf("discont_srch: ! LF_REGEX\n"); 685 fflush(stdout); 686 #endif /* DEBUG */ 687 return (1); 688 } 689 /* 690 * if the pattern does not contain wildcard characters and 691 * we have found a match we are done. 692 */ 693 if (WILDCARD(wild, pat) == NULL) { 694 #ifdef DEBUG 695 printf("discont_srch: wild = %x\n", wild); 696 fflush(stdout); 697 #endif /* DEBUG */ 698 return (1); 699 } 700 return (0); 701 } 702 703 #ifdef DEBUG 704 prtitem(char *str, struct item *hd) 705 { 706 struct item *p = hd->i_next; 707 708 printf("\n%s\n\n", str); 709 while (p != (struct item *)0) { 710 printf("str = %s\n", p->i_str); 711 p = p->i_next; 712 } 713 } 714 #endif /* DEBUG */