xref: /titanic_44/usr/src/cmd/fs.d/cachefs/cachefspack/rules.c (revision e71ca95ca6de23d33b54cb55cefdef30bc7c969b)
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 (c) 1996, by Sun Microsystems, Inc.
24  * All Rights Reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <locale.h>
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <sys/param.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <sys/stat.h>
38 #include <stdlib.h>
39 
40 #include "rules.h"
41 
42 char * lex(FILE *);
43 
44 extern char *mstrdup(const char *);
45 extern void *mmalloc(size_t size);
46 
47 void
48 read_rules(FILE *file, int (*rulefunc)())
49 {
50 	char *s;
51 	int base_active = 0;
52 	int list_ent_cnt = 0;
53 	int gign_ent_cnt = 0;
54 	int lign_ent_cnt = 0;
55 	struct item *add_item();
56 	struct item *fitem, *sitem;
57 	char version[20];
58 
59 	last_gign = &gign_hd;
60 	gign_hd.i_next = (struct item *)0;
61 	gign_hd.i_str = (char *)0;
62 	list_hd.i_next = (struct item *)0;
63 	list_hd.i_str = (char *)0;
64 	while (s = lex(file)) {
65 		if (s == (char *)0)
66 			break;
67 		if (*s == '#')
68 			continue;
69 		if (*s == '*')
70 			continue;
71 		if (strcmp(s, BASE) == 0) {
72 #ifdef DEBUG
73 			printf("BASE base_active = %d\n", base_active);
74 #endif /* DEBUG */
75 			if (base_active) {
76 				/*
77 				 * Tack local IGNORE strings to end of globals
78 				 */
79 				if (lign_hd.i_next != (struct item *)0) {
80 					last_gign->i_next = &lign_hd;
81 				}
82 				/*
83 				 * Process directives for previous BASE command
84 				 * if there was one. Also free up LIST items
85 				 * and local IGNORE items.
86 				 */
87 				do_base_dir(basedir, &list_hd, &gign_hd,
88 				    rulefunc);
89 				/*
90 				 * Free up space from LIST item list
91 				 */
92 				fitem  = list_hd.i_next;
93 				if (fitem != (struct item *)0) {
94 					while (fitem != (struct item *)0) {
95 						free(fitem->i_str);
96 						sitem = fitem->i_next;
97 						free(fitem);
98 						fitem = sitem;
99 					}
100 				}
101 				/*
102 				 * Free up space from local IGNORE item list
103 				 */
104 				fitem  = lign_hd.i_next;
105 				if (fitem != (struct item *)0) {
106 					while (fitem != (struct item *)0) {
107 						free(fitem->i_str);
108 						sitem = fitem->i_next;
109 						free(fitem);
110 						fitem = sitem;
111 					}
112 				}
113 				last_gign->i_next = (struct item *)0;
114 			}
115 			base_active = 1;
116 			/*
117 			 * Reset LIST item list and local IGNORE item
118 			 * list to be empty.
119 			 */
120 			last_list = &list_hd;
121 			list_hd.i_next = (struct item *)0;
122 			list_hd.i_str = (char *)0;
123 			last_lign = &lign_hd;
124 			lign_hd.i_next = (struct item *)0;
125 			lign_hd.i_str = (char *)0;
126 			/*
127 			 * Get BASE directory specified
128 			 */
129 			s = lex(0);
130 			if (s == (char *)0) {
131 				fprintf(stderr, gettext("cachefspack: "));
132 				fprintf(stderr, gettext(
133 				    "illegal BASE command\n"));
134 				return;
135 			}
136 
137 			if (*s == '$') {
138 				/*
139 				 * String starts with a '$', it must be an
140 				 * environment variable
141 				 */
142 				s = getenv(&s[1]);
143 				if (s == (char *)NULL) {
144 					fprintf(stderr,
145 					    gettext("cachefspack: "));
146 					fprintf(stderr,
147 					    gettext("Can't find "
148 					    "environment variable\n"));
149 					exit(1);
150 				}
151 			}
152 			basedir = mstrdup(s);
153 #ifdef DEBUG
154 			printf("basedir = %s\n", basedir);
155 #endif /* DEBUG */
156 			continue;
157 		}
158 		if (strcmp(s, IGNORE) == 0) {
159 #ifdef DEBUG
160 			printf("IGNORE - base_active = %d\n", base_active);
161 #endif /* DEBUG */
162 			if (base_active) {
163 				/*
164 				 * Local IGNORE rule
165 				 */
166 				while ((s = lex(0))
167 				    != 0) {
168 					last_lign = add_item(last_lign, s,
169 					    def_lign_flags);
170 				}
171 			} else {
172 				/*
173 				 * Global IGNORE rule
174 				 */
175 				while ((s = lex(0)) != 0) {
176 					last_gign = add_item(last_gign, s,
177 					    def_gign_flags);
178 				}
179 			}
180 			continue;
181 		}
182 		if (strcmp(s, LIST) == 0) {
183 #ifdef DEBUG
184 			printf("LIST\n");
185 #endif /* DEBUG */
186 			if (!base_active) {
187 				fprintf(stderr,
188 				    gettext(
189 				    "cachefspack: skipping LIST command - "));
190 				fprintf(stderr,
191 				    gettext(" no active base\n"));
192 				continue;
193 			}
194 			while ((s = lex(0)) != 0) {
195 				last_list = add_item(last_list, s,
196 				    def_list_flags);
197 			}
198 			continue;
199 		}
200 		if (strcmp(s, VERSION) == 0) {
201 			sprintf(version, "%d.%d", VERMAJOR, VERMINOR);
202 			s = lex(0);
203 			if (s == (char *)0) {
204 				fprintf(stderr, gettext("cachefspack: "));
205 				fprintf(stderr, gettext("missing version\n"));
206 				fprintf(stderr, gettext("cachefspack: "));
207 				fprintf(stderr, gettext(
208 				    "version = %d.%d\n"), VERMAJOR, VERMINOR);
209 				exit(1);
210 			}
211 			if (strcmp(version, s) != 0) {
212 				fprintf(stderr, gettext(
213 				    "cachefspack: "));
214 				fprintf(stderr, gettext(
215 				    "WARNING - version of packing rules "));
216 				fprintf(stderr, gettext(
217 				    "does not match cachefspack version\n"));
218 				fprintf(stderr, gettext(
219 				    "version = %d.%d\n"), VERMAJOR, VERMINOR);
220 			}
221 		}
222 	}
223 	/*
224 	 * Tack local IGNORE strings to end of globals
225 	 */
226 	if (lign_hd.i_next != (struct item *)0) {
227 		last_gign->i_next = &lign_hd;
228 	}
229 	do_base_dir(basedir, &list_hd, &gign_hd, rulefunc);
230 }
231 
232 struct item *
233 add_item(struct item *last_item, char *str, int flags)
234 {
235 	struct item * add_cmd_items();
236 
237 	if (*str == CMDCHAR) {
238 		last_item = add_cmd_items(last_item, &str[1], bang_list_flags);
239 	} else {
240 		last_item->i_next = (struct item *)mmalloc(
241 		    sizeof (struct item));
242 		last_item = last_item->i_next;
243 		last_item->i_str = mstrdup(str);
244 		last_item->i_flag = flags;
245 		last_item->i_next = (struct item *)0;
246 	}
247 	return (last_item);
248 }
249 
250 struct item *
251 add_cmd_items(struct item *last_item, char *str, int flags)
252 {
253 	FILE *fd;
254 	char inbuf[MAX_RULE_SZ];
255 	char *olddir = NULL;
256 	char *s;
257 	void getcmd(char *, char *);
258 
259 	if ((basedir != NULL) && (basedir[0] != '\0')) {
260 		olddir = getcwd(NULL, MAXPATHLEN + 1);
261 		if (olddir == NULL) {
262 			fprintf(stderr, gettext("cannot malloc buffer\n"));
263 			exit(1);
264 		}
265 
266 		if (chdir(basedir) != 0) {
267 			fprintf(stderr, gettext("cannot chdir to %s: %s\n"),
268 			    basedir, strerror(errno));
269 			exit(1);
270 		}
271 	}
272 
273 	getcmd(str, inbuf);
274 	fd = popen(inbuf, "r");
275 	if (fd == NULL) {
276 		fprintf(stderr, gettext("cachefspack: LIST can't execute - "));
277 		fprintf(stderr, "%s\n", inbuf);
278 		exit(1);
279 	}
280 
281 	while (s = lex(fd)) {
282 		last_item = add_item(last_item, s, flags);
283 		while (s = lex(0)) {
284 			last_item = add_item(last_item, s, flags);
285 		}
286 	}
287 	if (pclose(fd) < 0) {
288 		fprintf(stderr, gettext("cachefspack: can't close pipe\n"));
289 	}
290 
291 	if (olddir != NULL) {
292 		if (chdir(olddir) != 0) {
293 			fprintf(stderr, gettext("cannot return to %s: %s\n"),
294 			    olddir, strerror(errno));
295 			exit(1);
296 		}
297 		free(olddir);
298 	}
299 
300 	return (last_item);
301 }
302 
303 void
304 getcmd(char *str, char *buf)
305 {
306 	char *s;
307 
308 	strcpy(buf, str);
309 	strcat(buf, " ");
310 	while (s = lex(0)) {
311 		strcat(buf, s);
312 		strcat(buf, " ");
313 	}
314 #ifdef DEBUG
315 	printf("getcmd: cmd = %s\n", buf);
316 #endif /* DEBUG */
317 }
318 
319 /*
320  * routine:
321  *	lex
322  *
323  * purpose:
324  *	my own version of strtok that handles quoting and escaping
325  *
326  * parameters:
327  *	string to be lexed (or 0 for same string)
328  *
329  * returns:
330  *	pointer to next token
331  *
332  * notes:
333  *	this routine makes no changes to the string it is passed,
334  *	copying tokens into a static buffer.
335  */
336 char *
337 lex(FILE *fd)
338 {	char c, delim;
339 	char *p;
340 	const char *s;
341 	static const char *savep = 0;
342 	static char namebuf[MAX_RULE_SZ];
343 	static char inbuf[MAX_RULE_SZ];
344 	int len, space_left;
345 	char *err;
346 
347 	/*
348 	 * if the file descriptor is non-zero read a new command. Otherwise
349 	 * get fields from current line.
350 	 */
351 	if (fd != 0) {
352 		len = 0;
353 		space_left = sizeof (inbuf);
354 		while ((err = fgets(&inbuf[len], space_left, fd)) != NULL) {
355 			len = strlen(inbuf);
356 			if (len == 1) {
357 				/*
358 				 * must be a blank line starting command.
359 				 * If a blank line occurs after the start of
360 				 * a command, blanks will be included in the
361 				 * command.
362 				 */
363 				len = 0;
364 				continue;
365 			}
366 			len -= 2;
367 			space_left -= len;
368 			s = (char *)((int)inbuf + len);
369 			/*
370 			 * Continuation character
371 			 */
372 			if (strcmp(s, "\\\n") == 0) {
373 				continue;
374 			}
375 			break;
376 		}
377 		if (err == NULL) {
378 			return (err);
379 		}
380 		s = inbuf;
381 	} else {
382 		if (savep == 0)
383 			return (0);
384 		s = savep;
385 	}
386 	savep = 0;
387 
388 	/* skip over leading white space	*/
389 	while (isspace(*s))
390 		s++;
391 	if (*s == 0) {
392 		return (0);
393 	}
394 
395 	/* see if this is a quoted string	*/
396 	c = *s;
397 	if (c == '\'' || c == '"') {
398 		delim = c;
399 		s++;
400 	} else
401 		delim = 0;
402 
403 	/* copy the token into the buffer	*/
404 	for (p = namebuf; (c = *s) != 0; s++) {
405 		if ((p - namebuf) >= sizeof (namebuf)) {
406 			savep = 0;
407 			return (0);
408 		}
409 		/* literal escape		*/
410 		if (c == '\\') {
411 			s++;
412 			*p++ = *s;
413 			continue;
414 		}
415 
416 		/* closing delimiter		*/
417 		if (c == delim) {
418 			s++;
419 			break;
420 		}
421 
422 		/* delimiting white space	*/
423 		if (delim == 0 && isspace(c))
424 			break;
425 
426 		/* ordinary characters		*/
427 		*p++ = *s;
428 	}
429 
430 
431 	/* remember where we left off		*/
432 	savep = *s ? s : 0;
433 
434 	/* null terminate and return the buffer	*/
435 	*p = 0;
436 	return (namebuf);
437 }
438 
439 char *
440 mk_base_dir(char *path, char *linkpath)
441 {
442 	static char pathb[MAXPATHLEN];
443 	char *dnam;
444 	char *get_dirname(char *);
445 	int len;
446 
447 	/*
448 	 * absolute path name
449 	 */
450 	if (*linkpath == '/') {
451 		strcpy(pathb, linkpath);
452 	} else {
453 		/*
454 		 * relative path
455 		 */
456 		dnam = get_dirname(path);
457 		if (dnam == (char *)0) {
458 			return ((char *) 0);
459 		}
460 		strcpy(pathb, dnam);
461 		len = strlen(pathb);
462 		if (len == 0)
463 			return (pathb);
464 		if (pathb[len-1] != '/')
465 		    strcat(pathb, "/");
466 		if (strncmp(linkpath, "../", 3) == 0) {
467 			/*
468 			 * path is relative to directory containing sym link
469 			 * remove "../" from beginning of linkpath
470 			 */
471 			strcat(pathb, &linkpath[3]);
472 		} else {
473 			/*
474 			 * path is relative to directory containing sym link
475 			 */
476 			strcat(pathb, linkpath);
477 		}
478 	}
479 	return (pathb);
480 }
481