• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * File contexts backend for labeling system
3  *
4  * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
5  */
6 
7 #include <fcntl.h>
8 #include <stdarg.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <limits.h>
14 #include <regex.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include "callbacks.h"
19 #include "label_internal.h"
20 
21 /*
22  * Internals, mostly moved over from matchpathcon.c
23  */
24 
25 /* A file security context specification. */
26 typedef struct spec {
27 	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
28 	char *regex_str;	/* regular expession string for diagnostics */
29 	char *type_str;		/* type string for diagnostic messages */
30 	regex_t regex;		/* compiled regular expression */
31 	char regcomp;           /* regex_str has been compiled to regex */
32 	mode_t mode;		/* mode format value */
33 	int matches;		/* number of matching pathnames */
34 	int hasMetaChars;	/* regular expression has meta-chars */
35 	int stem_id;		/* indicates which stem-compression item */
36 	size_t prefix_len;      /* length of fixed path prefix */
37 } spec_t;
38 
39 /* A regular expression stem */
40 typedef struct stem {
41 	char *buf;
42 	int len;
43 } stem_t;
44 
45 /* Our stored configuration */
46 struct saved_data {
47 	/*
48 	 * The array of specifications, initially in the same order as in
49 	 * the specification file. Sorting occurs based on hasMetaChars.
50 	 */
51 	spec_t *spec_arr;
52 	unsigned int nspec;
53 	unsigned int ncomp;
54 
55 	/*
56 	 * The array of regular expression stems.
57 	 */
58 	stem_t *stem_arr;
59 	int num_stems;
60 	int alloc_stems;
61 };
62 
63 /* Return the length of the text that can be considered the stem, returns 0
64  * if there is no identifiable stem */
get_stem_from_spec(const char * const buf)65 static int get_stem_from_spec(const char *const buf)
66 {
67 	const char *tmp = strchr(buf + 1, '/');
68 	const char *ind;
69 
70 	if (!tmp)
71 		return 0;
72 
73 	for (ind = buf; ind < tmp; ind++) {
74 		if (strchr(".^$?*+|[({", (int)*ind))
75 			return 0;
76 	}
77 	return tmp - buf;
78 }
79 
80 /* return the length of the text that is the stem of a file name */
get_stem_from_file_name(const char * const buf)81 static int get_stem_from_file_name(const char *const buf)
82 {
83 	const char *tmp = strchr(buf + 1, '/');
84 
85 	if (!tmp)
86 		return 0;
87 	return tmp - buf;
88 }
89 
90 /* find the stem of a file spec, returns the index into stem_arr for a new
91  * or existing stem, (or -1 if there is no possible stem - IE for a file in
92  * the root directory or a regex that is too complex for us). */
find_stem_from_spec(struct saved_data * data,const char * buf)93 static int find_stem_from_spec(struct saved_data *data, const char *buf)
94 {
95 	int i, num = data->num_stems;
96 	int stem_len = get_stem_from_spec(buf);
97 
98 	if (!stem_len)
99 		return -1;
100 	for (i = 0; i < num; i++) {
101 		if (stem_len == data->stem_arr[i].len
102 		    && !strncmp(buf, data->stem_arr[i].buf, stem_len))
103 			return i;
104 	}
105 	if (data->alloc_stems == num) {
106 		stem_t *tmp_arr;
107 		data->alloc_stems = data->alloc_stems * 2 + 16;
108 		tmp_arr = (stem_t *) realloc(data->stem_arr,
109 				  sizeof(stem_t) * data->alloc_stems);
110 		if (!tmp_arr)
111 			return -1;
112 		data->stem_arr = tmp_arr;
113 	}
114 	data->stem_arr[num].len = stem_len;
115 	data->stem_arr[num].buf = (char *) malloc(stem_len + 1);
116 	if (!data->stem_arr[num].buf)
117 		return -1;
118 	memcpy(data->stem_arr[num].buf, buf, stem_len);
119 	data->stem_arr[num].buf[stem_len] = '\0';
120 	data->num_stems++;
121 	buf += stem_len;
122 	return num;
123 }
124 
125 /* find the stem of a file name, returns the index into stem_arr (or -1 if
126  * there is no match - IE for a file in the root directory or a regex that is
127  * too complex for us).  Makes buf point to the text AFTER the stem. */
find_stem_from_file(struct saved_data * data,const char ** buf)128 static int find_stem_from_file(struct saved_data *data, const char **buf)
129 {
130 	int i;
131 	int stem_len = get_stem_from_file_name(*buf);
132 
133 	if (!stem_len)
134 		return -1;
135 	for (i = 0; i < data->num_stems; i++) {
136 		if (stem_len == data->stem_arr[i].len
137 		    && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) {
138 			*buf += stem_len;
139 			return i;
140 		}
141 	}
142 	return -1;
143 }
144 
145 /*
146  * Warn about duplicate specifications.
147  */
nodups_specs(struct saved_data * data,const char * path)148 static int nodups_specs(struct saved_data *data, const char *path)
149 {
150 	int rc = 0;
151 	unsigned int ii, jj;
152 	struct spec *curr_spec, *spec_arr = data->spec_arr;
153 
154 	for (ii = 0; ii < data->nspec; ii++) {
155 		curr_spec = &spec_arr[ii];
156 		for (jj = ii + 1; jj < data->nspec; jj++) {
157 			if ((!strcmp
158 			     (spec_arr[jj].regex_str, curr_spec->regex_str))
159 			    && (!spec_arr[jj].mode || !curr_spec->mode
160 				|| spec_arr[jj].mode == curr_spec->mode)) {
161 				rc = -1;
162 				errno = EINVAL;
163 				if (strcmp
164 				    (spec_arr[jj].lr.ctx_raw,
165 				     curr_spec->lr.ctx_raw)) {
166 					selinux_log
167 						(SELINUX_ERROR,
168 						 "%s: Multiple different specifications for %s  (%s and %s).\n",
169 						 path, curr_spec->regex_str,
170 						 spec_arr[jj].lr.ctx_raw,
171 						 curr_spec->lr.ctx_raw);
172 				} else {
173 					selinux_log
174 						(SELINUX_ERROR,
175 						 "%s: Multiple same specifications for %s.\n",
176 						 path, curr_spec->regex_str);
177 				}
178 			}
179 		}
180 	}
181 	return rc;
182 }
183 
184 /* Determine if the regular expression specification has any meta characters. */
spec_hasMetaChars(struct spec * spec)185 static void spec_hasMetaChars(struct spec *spec)
186 {
187 	char *c;
188 	size_t len;
189 	char *end;
190 
191 	c = spec->regex_str;
192 	len = strlen(spec->regex_str);
193 	end = c + len;
194 
195 	spec->hasMetaChars = 0;
196 	spec->prefix_len = len;
197 
198 	/* Look at each character in the RE specification string for a
199 	 * meta character. Return when any meta character reached. */
200 	while (c != end) {
201 		switch (*c) {
202 		case '.':
203 		case '^':
204 		case '$':
205 		case '?':
206 		case '*':
207 		case '+':
208 		case '|':
209 		case '[':
210 		case '(':
211 		case '{':
212 			spec->hasMetaChars = 1;
213 			spec->prefix_len = c - spec->regex_str;
214 			return;
215 		case '\\':	/* skip the next character */
216 			c++;
217 			break;
218 		default:
219 			break;
220 
221 		}
222 		c++;
223 	}
224 	return;
225 }
226 
compile_regex(struct saved_data * data,spec_t * spec,char ** errbuf)227 static int compile_regex(struct saved_data *data, spec_t *spec, char **errbuf)
228 {
229 	char *reg_buf, *anchored_regex, *cp;
230 	stem_t *stem_arr = data->stem_arr;
231 	size_t len;
232 	int regerr;
233 
234 	if (spec->regcomp)
235 		return 0; /* already done */
236 
237 	data->ncomp++; /* how many compiled regexes required */
238 
239 	/* Skip the fixed stem. */
240 	reg_buf = spec->regex_str;
241 	if (spec->stem_id >= 0)
242 		reg_buf += stem_arr[spec->stem_id].len;
243 
244 	/* Anchor the regular expression. */
245 	len = strlen(reg_buf);
246 	cp = anchored_regex = (char *) malloc(len + 3);
247 	if (!anchored_regex)
248 		return -1;
249 	/* Create ^...$ regexp.  */
250 	*cp++ = '^';
251 	memcpy(cp, reg_buf, len);
252 	cp += len;
253 	*cp++ = '$';
254 	*cp = '\0';
255 
256 	/* Compile the regular expression. */
257 	regerr = regcomp(&spec->regex, anchored_regex,
258 			 REG_EXTENDED | REG_NOSUB);
259 	if (regerr != 0) {
260 		size_t errsz = 0;
261 		errsz = regerror(regerr, &spec->regex, NULL, 0);
262 		if (errsz && errbuf)
263 			*errbuf = (char *) malloc(errsz);
264 		if (errbuf && *errbuf)
265 			(void)regerror(regerr, &spec->regex,
266 				       *errbuf, errsz);
267 
268 		free(anchored_regex);
269 		return -1;
270 	}
271 	free(anchored_regex);
272 
273 	/* Done. */
274 	spec->regcomp = 1;
275 
276 	return 0;
277 }
278 
279 
process_line(struct selabel_handle * rec,const char * path,const char * prefix,char * line_buf,int pass,unsigned lineno)280 static int process_line(struct selabel_handle *rec,
281 			const char *path, const char *prefix,
282 			char *line_buf, int pass, unsigned lineno)
283 {
284 	int items, len;
285 	char buf1[BUFSIZ], buf2[BUFSIZ], buf3[BUFSIZ];
286 	char *buf_p, *regex = buf1, *type = buf2, *context = buf3;
287 	struct saved_data *data = (struct saved_data *)rec->data;
288 	spec_t *spec_arr = data->spec_arr;
289 	unsigned int nspec = data->nspec;
290 
291 	len = strlen(line_buf);
292 	if (line_buf[len - 1] == '\n')
293 		line_buf[len - 1] = 0;
294 	buf_p = line_buf;
295 	while (isspace(*buf_p))
296 		buf_p++;
297 	/* Skip comment lines and empty lines. */
298 	if (*buf_p == '#' || *buf_p == 0)
299 		return 0;
300 	items = sscanf(line_buf, "%255s %255s %255s", regex, type, context);
301 	if (items < 2) {
302 		selinux_log(SELINUX_WARNING,
303 			    "%s:  line %d is missing fields, skipping\n", path,
304 			    lineno);
305 		return 0;
306 	} else if (items == 2) {
307 		/* The type field is optional. */
308 		context = type;
309 		type = NULL;
310 	}
311 
312 	len = get_stem_from_spec(regex);
313 	if (len && prefix && strncmp(prefix, regex, len)) {
314 		/* Stem of regex does not match requested prefix, discard. */
315 		return 0;
316 	}
317 
318 	if (pass == 1) {
319 		/* On the second pass, process and store the specification in spec. */
320 		char *errbuf = NULL;
321 		spec_arr[nspec].stem_id = find_stem_from_spec(data, regex);
322 		spec_arr[nspec].regex_str = strdup(regex);
323 		if (!spec_arr[nspec].regex_str) {
324 			selinux_log(SELINUX_WARNING,
325 				   "%s:  out of memory at line %d on regex %s\n",
326 				   path, lineno, regex);
327 			return -1;
328 
329 		}
330 		if (rec->validating && compile_regex(data, &spec_arr[nspec], &errbuf)) {
331 			selinux_log(SELINUX_WARNING,
332 				   "%s:  line %d has invalid regex %s:  %s\n",
333 				   path, lineno, regex,
334 				   (errbuf ? errbuf : "out of memory"));
335 		}
336 
337 		/* Convert the type string to a mode format */
338 		spec_arr[nspec].mode = 0;
339 		if (!type)
340 			goto skip_type;
341 		spec_arr[nspec].type_str = strdup(type);
342 		len = strlen(type);
343 		if (type[0] != '-' || len != 2) {
344 			selinux_log(SELINUX_WARNING,
345 				    "%s:  line %d has invalid file type %s\n",
346 				    path, lineno, type);
347 			return 0;
348 		}
349 		switch (type[1]) {
350 		case 'b':
351 			spec_arr[nspec].mode = S_IFBLK;
352 			break;
353 		case 'c':
354 			spec_arr[nspec].mode = S_IFCHR;
355 			break;
356 		case 'd':
357 			spec_arr[nspec].mode = S_IFDIR;
358 			break;
359 		case 'p':
360 			spec_arr[nspec].mode = S_IFIFO;
361 			break;
362 		case 'l':
363 			spec_arr[nspec].mode = S_IFLNK;
364 			break;
365 		case 's':
366 			spec_arr[nspec].mode = S_IFSOCK;
367 			break;
368 		case '-':
369 			spec_arr[nspec].mode = S_IFREG;
370 			break;
371 		default:
372 			selinux_log(SELINUX_WARNING,
373 				    "%s:  line %d has invalid file type %s\n",
374 				    path, lineno, type);
375 			return 0;
376 		}
377 
378 	skip_type:
379 		spec_arr[nspec].lr.ctx_raw = strdup(context);
380 
381 		if (strcmp(context, "<<none>>") && rec->validating) {
382 			if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
383 				selinux_log(SELINUX_WARNING,
384 					    "%s:  line %d has invalid context %s\n",
385 					    path, lineno, spec_arr[nspec].lr.ctx_raw);
386 			}
387 		}
388 
389 		/* Determine if specification has
390 		 * any meta characters in the RE */
391 		spec_hasMetaChars(&spec_arr[nspec]);
392 	}
393 
394 	data->nspec = ++nspec;
395 	return 0;
396 }
397 
init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned n)398 static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
399 		unsigned n)
400 {
401 	struct saved_data *data = (struct saved_data *)rec->data;
402 	const char *path = NULL;
403 	const char *prefix = NULL;
404 	FILE *fp;
405 	FILE *localfp = NULL;
406 	FILE *homedirfp = NULL;
407 	char local_path[PATH_MAX + 1];
408 	char homedir_path[PATH_MAX + 1];
409 	char line_buf[BUFSIZ];
410 	unsigned int lineno, pass, i, j, maxnspec;
411 	spec_t *spec_copy = NULL;
412 	int status = -1, baseonly = 0;
413 	struct stat sb;
414 
415 	/* Process arguments */
416 	while (n--)
417 		switch(opts[n].type) {
418 		case SELABEL_OPT_PATH:
419 			path = opts[n].value;
420 			break;
421 		case SELABEL_OPT_SUBSET:
422 			prefix = opts[n].value;
423 			break;
424 		case SELABEL_OPT_BASEONLY:
425 			baseonly = !!opts[n].value;
426 			break;
427 		}
428 
429 	/* Open the specification file. */
430 	if ((fp = fopen(path, "r")) == NULL)
431 		return -1;
432 
433 	if (fstat(fileno(fp), &sb) < 0)
434 		return -1;
435 	if (!S_ISREG(sb.st_mode)) {
436 		errno = EINVAL;
437 		return -1;
438 	}
439 
440 	if (!baseonly) {
441 		snprintf(homedir_path, sizeof(homedir_path), "%s.homedirs",
442 			 path);
443 		homedirfp = fopen(homedir_path, "r");
444 
445 		snprintf(local_path, sizeof(local_path), "%s.local", path);
446 		localfp = fopen(local_path, "r");
447 	}
448 
449 	/*
450 	 * Perform two passes over the specification file.
451 	 * The first pass counts the number of specifications and
452 	 * performs simple validation of the input.  At the end
453 	 * of the first pass, the spec array is allocated.
454 	 * The second pass performs detailed validation of the input
455 	 * and fills in the spec array.
456 	 */
457 	maxnspec = UINT_MAX / sizeof(spec_t);
458 	for (pass = 0; pass < 2; pass++) {
459 		lineno = 0;
460 		data->nspec = 0;
461 		data->ncomp = 0;
462 		while (fgets(line_buf, sizeof line_buf - 1, fp)
463 		       && data->nspec < maxnspec) {
464 			if (process_line(rec, path, prefix, line_buf,
465 					 pass, ++lineno) != 0)
466 				goto finish;
467 		}
468 		if (pass == 1) {
469 			status = nodups_specs(data, path);
470 			if (status)
471 				goto finish;
472 		}
473 		lineno = 0;
474 		if (homedirfp)
475 			while (fgets(line_buf, sizeof line_buf - 1, homedirfp)
476 			       && data->nspec < maxnspec) {
477 				if (process_line
478 				    (rec, homedir_path, prefix,
479 				     line_buf, pass, ++lineno) != 0)
480 					goto finish;
481 			}
482 
483 		lineno = 0;
484 		if (localfp)
485 			while (fgets(line_buf, sizeof line_buf - 1, localfp)
486 			       && data->nspec < maxnspec) {
487 				if (process_line
488 				    (rec, local_path, prefix, line_buf,
489 				     pass, ++lineno) != 0)
490 					goto finish;
491 			}
492 
493 		if (pass == 0) {
494 			if (data->nspec == 0) {
495 				status = 0;
496 				goto finish;
497 			}
498 			if (NULL == (data->spec_arr =
499 				     (spec_t *) malloc(sizeof(spec_t) * data->nspec)))
500 				goto finish;
501 			memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
502 			maxnspec = data->nspec;
503 			rewind(fp);
504 			if (homedirfp)
505 				rewind(homedirfp);
506 			if (localfp)
507 				rewind(localfp);
508 		}
509 	}
510 
511 	/* Move exact pathname specifications to the end. */
512 	spec_copy = (spec_t *) malloc(sizeof(spec_t) * data->nspec);
513 	if (!spec_copy)
514 		goto finish;
515 	j = 0;
516 	for (i = 0; i < data->nspec; i++)
517 		if (data->spec_arr[i].hasMetaChars)
518 			memcpy(&spec_copy[j++],
519 			       &data->spec_arr[i], sizeof(spec_t));
520 	for (i = 0; i < data->nspec; i++)
521 		if (!data->spec_arr[i].hasMetaChars)
522 			memcpy(&spec_copy[j++],
523 			       &data->spec_arr[i], sizeof(spec_t));
524 	free(data->spec_arr);
525 	data->spec_arr = spec_copy;
526 
527 	status = 0;
528 finish:
529 	fclose(fp);
530 	if (data->spec_arr != spec_copy)
531 		free(data->spec_arr);
532 	if (homedirfp)
533 		fclose(homedirfp);
534 	if (localfp)
535 		fclose(localfp);
536 	return status;
537 }
538 
539 /*
540  * Backend interface routines
541  */
closef(struct selabel_handle * rec)542 static void closef(struct selabel_handle *rec)
543 {
544 	struct saved_data *data = (struct saved_data *)rec->data;
545 	struct spec *spec;
546 	struct stem *stem;
547 	unsigned int i;
548 
549 	for (i = 0; i < data->nspec; i++) {
550 		spec = &data->spec_arr[i];
551 		free(spec->regex_str);
552 		free(spec->type_str);
553 		free(spec->lr.ctx_raw);
554 		free(spec->lr.ctx_trans);
555 		if (spec->regcomp)
556 			regfree(&spec->regex);
557 	}
558 
559 	for (i = 0; i < (unsigned int)data->num_stems; i++) {
560 		stem = &data->stem_arr[i];
561 		free(stem->buf);
562 	}
563 
564 	if (data->spec_arr)
565 		free(data->spec_arr);
566 	if (data->stem_arr)
567 		free(data->stem_arr);
568 
569 	free(data);
570 }
571 
lookup_common(struct selabel_handle * rec,const char * key,int type,bool partial)572 static struct selabel_lookup_rec *lookup_common(struct selabel_handle *rec,
573 						const char *key, int type,
574 						bool partial)
575 {
576 	struct saved_data *data = (struct saved_data *)rec->data;
577 	spec_t *spec_arr = data->spec_arr;
578 	int i, rc, file_stem;
579 	mode_t mode = (mode_t)type;
580 	const char *buf;
581 	struct selabel_lookup_rec *ret = NULL;
582 	char *clean_key = NULL;
583 	const char *prev_slash, *next_slash;
584 	unsigned int sofar = 0;
585 	size_t keylen = strlen(key);
586 
587 	if (!data->nspec) {
588 		errno = ENOENT;
589 		goto finish;
590 	}
591 
592 	/* Remove duplicate slashes */
593 	if ((next_slash = strstr(key, "//"))) {
594 		clean_key = (char *) malloc(strlen(key) + 1);
595 		if (!clean_key)
596 			goto finish;
597 		prev_slash = key;
598 		while (next_slash) {
599 			memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash);
600 			sofar += next_slash - prev_slash;
601 			prev_slash = next_slash + 1;
602 			next_slash = strstr(prev_slash, "//");
603 		}
604 		strcpy(clean_key + sofar, prev_slash);
605 		key = clean_key;
606 	}
607 
608 	buf = key;
609 	file_stem = find_stem_from_file(data, &buf);
610 	mode &= S_IFMT;
611 
612 	/*
613 	 * Check for matching specifications in reverse order, so that
614 	 * the last matching specification is used.
615 	 */
616 	for (i = data->nspec - 1; i >= 0; i--) {
617 		/* if the spec in question matches no stem or has the same
618 		 * stem as the file AND if the spec in question has no mode
619 		 * specified or if the mode matches the file mode then we do
620 		 * a regex check        */
621 		if ((spec_arr[i].stem_id == -1
622 		     || spec_arr[i].stem_id == file_stem)
623 		    && (!mode || !spec_arr[i].mode
624 			|| mode == spec_arr[i].mode)) {
625 			if (compile_regex(data, &spec_arr[i], NULL) < 0)
626 				goto finish;
627 			if (spec_arr[i].stem_id == -1)
628 				rc = regexec(&spec_arr[i].regex, key, 0, 0, 0);
629 			else
630 				rc = regexec(&spec_arr[i].regex, buf, 0, 0, 0);
631 
632 			if (rc == 0) {
633 				spec_arr[i].matches++;
634 				break;
635 			}
636 
637 			if (partial) {
638 				/*
639 				 * We already checked above to see if the
640 				 * key has any direct match.  Now we just need
641 				 * to check for partial matches.
642 				 * Since POSIX regex functions do not support
643 				 * partial match, we crudely approximate it
644 				 * via a prefix match.
645 				 * This is imprecise and could yield
646 				 * false positives or negatives but
647 				 * appears to work with our current set of
648 				 * regex strings.
649 				 * Convert to using pcre partial match
650 				 * if/when pcre becomes available in Android.
651 				 */
652 				if (spec_arr[i].prefix_len > 1 &&
653 				    !strncmp(key, spec_arr[i].regex_str,
654 					     keylen < spec_arr[i].prefix_len ?
655 					     keylen : spec_arr[i].prefix_len))
656 					break;
657 			}
658 
659 			if (rc == REG_NOMATCH)
660 				continue;
661 			/* else it's an error */
662 			goto finish;
663 		}
664 	}
665 
666 	if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
667 		/* No matching specification. */
668 		errno = ENOENT;
669 		goto finish;
670 	}
671 
672 	ret = &spec_arr[i].lr;
673 
674 finish:
675 	free(clean_key);
676 	return ret;
677 }
678 
lookup(struct selabel_handle * rec,const char * key,int type)679 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
680 					 const char *key, int type)
681 {
682 	return lookup_common(rec, key, type, false);
683 }
684 
partial_match(struct selabel_handle * rec,const char * key)685 static bool partial_match(struct selabel_handle *rec, const char *key)
686 {
687 	return lookup_common(rec, key, 0, true) ? true : false;
688 }
689 
stats(struct selabel_handle * rec)690 static void stats(struct selabel_handle *rec)
691 {
692 	struct saved_data *data = (struct saved_data *)rec->data;
693 	unsigned int i, nspec = data->nspec;
694 	spec_t *spec_arr = data->spec_arr;
695 
696 	for (i = 0; i < nspec; i++) {
697 		if (spec_arr[i].matches == 0) {
698 			if (spec_arr[i].type_str) {
699 				selinux_log(SELINUX_WARNING,
700 				    "Warning!  No matches for (%s, %s, %s)\n",
701 				    spec_arr[i].regex_str,
702 				    spec_arr[i].type_str,
703 				    spec_arr[i].lr.ctx_raw);
704 			} else {
705 				selinux_log(SELINUX_WARNING,
706 				    "Warning!  No matches for (%s, %s)\n",
707 				    spec_arr[i].regex_str,
708 				    spec_arr[i].lr.ctx_raw);
709 			}
710 		}
711 	}
712 }
713 
selabel_file_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)714 int selabel_file_init(struct selabel_handle *rec, const struct selinux_opt *opts,
715 		      unsigned nopts)
716 {
717 	struct saved_data *data;
718 
719 	data = (struct saved_data *)malloc(sizeof(*data));
720 	if (!data)
721 		return -1;
722 	memset(data, 0, sizeof(*data));
723 
724 	rec->data = data;
725 	rec->func_close = &closef;
726 	rec->func_stats = &stats;
727 	rec->func_lookup = &lookup;
728 	rec->func_partial_match = &partial_match;
729 
730 	return init(rec, opts, nopts);
731 }
732