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
do_base_dir(char * dirpath,struct item * incld_lst,struct item * excld_lst,int (* func)(char *,char *,DIR *,int))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
do_list_item(char * dirpath,char * pat,int flags,struct item * excld_lst,DIR * pdir,struct item ** symlk_lst,int (* func)(char *,char *,DIR *,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
slash_cnt(char * str)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
func_dir_path(char * path,int (* func)(char *,char *,DIR *,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
process_symlk(char * lkpath,char * relpath,int rel_lastpos,struct item ** symlk,int (* func)(char *,char *,DIR *,int))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
discont_srch(int flags,char * pat)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
prtitem(char * str,struct item * hd)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 */
715