xref: /titanic_44/usr/src/cmd/fs.d/cachefs/cachefspack/docmds.c (revision d2ec54f7875f7e05edd56195adbeb593c947763f)
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 */
715