]>
git.ipfire.org Git - people/stevee/selinux-policy.git/blob - support/fc_sort.c
1 /* Copyright 2005, Tresys Technology
3 * Some parts of this came from matchpathcon.c in libselinux
6 /* PURPOSE OF THIS PROGRAM
7 * The original setfiles sorting algorithm did not take into
8 * account regular expression specificity. With the current
9 * strict and targeted policies this is not an issue because
10 * the file contexts are partially hand sorted and concatenated
11 * in the right order so that the matches are generally correct.
12 * The way reference policy and loadable policy modules handle
13 * file contexts makes them come out in an unpredictable order
14 * and therefore setfiles (or this standalone tool) need to sort
15 * the regular expressions in a deterministic and stable way.
18 #define BUF_SIZE 4096;
26 typedef unsigned char bool_t
;
29 * A node used in a linked list of file contexts.c
30 * Each node contains the regular expression, the type and
31 * the context, as well as information about the regular
32 * expression. The regular expression data (meta, stem_len
33 * and str_len) can be filled in by using the fc_fill_data
34 * function after the regular expression has been loaded.
35 * next points to the next node in the linked list.
37 typedef struct file_context_node
{
44 struct file_context_node
*next
;
45 } file_context_node_t
;
47 void file_context_node_destroy(file_context_node_t
*x
)
56 /* file_context_bucket
57 * A node used in a linked list of buckets that contain
58 * file_context_node's.
59 * Each node contains a pointer to a file_context_node which
60 * is the header of its linked list. This linked list is the
61 * content of this bucket.
62 * next points to the next bucket in the linked list.
64 typedef struct file_context_bucket
{
65 file_context_node_t
*data
;
66 struct file_context_bucket
*next
;
67 } file_context_bucket_t
;
72 * Compares two file contexts' regular expressions and returns:
73 * -1 if a is less specific than b
74 * 0 if a and be are equally specific
75 * 1 if a is more specific than b
76 * The comparison is based on the following statements,
77 * in order from most important to least important, given a and b:
78 * If a is a regular expression and b is not,
79 * -> a is less specific than b.
80 * If a's stem length is shorter than b's stem length,
81 * -> a is less specific than b.
82 * If a's string length is shorter than b's string length,
83 * -> a is less specific than b.
84 * If a does not have a specified type and b does not,
85 * -> a is less specific than b.
87 int fc_compare(file_context_node_t
*a
, file_context_node_t
*b
)
89 /* Check to see if either a or b have meta characters
90 * and the other doesn't. */
91 if (a
->meta
&& !b
->meta
)
93 if (b
->meta
&& !a
->meta
)
96 /* Check to see if either a or b have a shorter stem
97 * length than the other. */
98 if (a
->stem_len
< b
->stem_len
)
100 if (b
->stem_len
< a
->stem_len
)
103 /* Check to see if either a or b have a shorter string
104 * length than the other. */
105 if (a
->str_len
< b
->str_len
)
107 if (b
->str_len
< a
->str_len
)
110 /* Check to see if either a or b has a specified type
111 * and the other doesn't. */
112 if (!a
->file_type
&& b
->file_type
)
114 if (!b
->file_type
&& a
->file_type
)
117 /* If none of the above conditions were satisfied,
118 * then a and b are equally specific. */
125 * Merges two sorted file context linked lists into one
127 * Pass two lists a and b, and after the completion of fc_merge,
128 * the final list is contained in a, and b is empty.
130 file_context_node_t
*fc_merge(file_context_node_t
*a
,
131 file_context_node_t
*b
)
133 file_context_node_t
*a_current
;
134 file_context_node_t
*b_current
;
135 file_context_node_t
*temp
;
136 file_context_node_t
*jumpto
;
140 /* If a is a empty list, and b is not,
141 * set a as b and proceed to the end. */
144 /* If b is an empty list, leave a as it is. */
147 /* Make it so the list a has the lesser
148 * first element always. */
149 if (fc_compare(a
, b
) == 1) {
157 /* Merge by inserting b's nodes in between a's nodes. */
158 while (a_current
->next
&& b_current
) {
159 jumpto
= a_current
->next
;
161 /* Insert b's nodes in between the current a node
162 * and the next a node.*/
163 while (b_current
&& a_current
->next
&&
164 fc_compare(a_current
->next
,
168 temp
= a_current
->next
;
169 a_current
->next
= b_current
;
170 b_current
= b_current
->next
;
171 a_current
->next
->next
= temp
;
172 a_current
= a_current
->next
;
175 /* Skip all the inserted node from b to the
176 * next node in the original a. */
181 /* if there is anything left in b to be inserted,
184 a_current
->next
= b_current
;
194 * Sorts file contexts from least specific to more specific.
195 * The bucket linked list is passed and after the completion
196 * of the fc_merge_sort function, there is only one bucket
197 * (pointed to by master) that contains a linked list
198 * of all the file contexts, in sorted order.
199 * Explanation of the algorithm:
200 * The algorithm implemented in fc_merge_sort is an iterative
201 * implementation of merge sort.
202 * At first, each bucket has a linked list of file contexts
203 * that are 1 element each.
204 * Each pass, each odd numbered bucket is merged into the bucket
205 * before it. This halves the number of buckets each pass.
206 * It will continue passing over the buckets (as described above)
207 * until there is only one bucket left, containing the list of
208 * file contexts, sorted.
210 void fc_merge_sort(file_context_bucket_t
*master
)
214 file_context_bucket_t
*current
;
215 file_context_bucket_t
*temp
;
217 /* Loop until master is the only bucket left
218 * so that this will stop when master contains
219 * the sorted list. */
220 while (master
->next
) {
223 /* This loop merges buckets two-by-two. */
229 fc_merge(current
->data
,
230 current
->next
->data
);
234 temp
= current
->next
;
235 current
->next
= current
->next
->next
;
242 current
= current
->next
;
252 * This processes a regular expression in a file context
253 * and sets the data held in file_context_node, namely
254 * meta, str_len and stem_len.
255 * The following changes are made to fc_node after the
256 * the completion of the function:
257 * fc_node->meta = 1 if path has a meta character, 0 if not.
258 * fc_node->str_len = The string length of the entire path
259 * fc_node->stem_len = The number of characters up until
260 * the first meta character.
262 void fc_fill_data(file_context_node_t
*fc_node
)
267 fc_node
->stem_len
= 0;
268 fc_node
->str_len
= 0;
270 /* Process until the string termination character
272 * Note: this while loop has been adapted from
273 * spec_hasMetaChars in matchpathcon.c from
274 * libselinux-1.22. */
275 while (fc_node
->path
[c
] != '\0') {
276 switch (fc_node
->path
[c
]) {
287 /* If a meta character is found,
292 /* If a escape character is found,
293 * skip the next character. */
296 /* If no meta character has been found yet,
297 * add one to the stem length. */
309 * This program takes in two arguments, the input filename and the
310 * output filename. The input file should be syntactically correct.
311 * Overall what is done in the main is read in the file and store each
312 * line of code, sort it, then output it to the output file.
314 int main(int argc
, char *argv
[])
317 size_t start
, finish
, regex_len
, context_len
;
318 size_t line_len
, buf_len
, i
, j
;
319 char *input_name
, *output_name
, *line_buf
;
321 file_context_node_t
*temp
;
322 file_context_node_t
*head
;
323 file_context_node_t
*current
;
324 file_context_bucket_t
*master
;
325 file_context_bucket_t
*bcurrent
;
327 FILE *in_file
, *out_file
;
330 /* Check for the correct number of command line arguments. */
332 fprintf(stderr
, "Usage: %s <infile> <outfile>\n",argv
[0]);
336 input_name
= argv
[1];
337 output_name
= argv
[2];
341 /* Open the input file. */
342 if (!(in_file
= fopen(input_name
, "r"))) {
343 fprintf(stderr
, "Error: failure opening input file for read.\n");
347 /* Initialize the head of the linked list. */
348 head
= current
= (file_context_node_t
*)malloc(sizeof(file_context_node_t
));
350 /* Parse the file into a file_context linked list. */
353 while ( getline(&line_buf
, &buf_len
, in_file
) != -1 ){
354 line_len
= strlen(line_buf
);
355 if( line_len
== 0 || line_len
== 1)
357 /* Get rid of whitespace from the front of the line. */
358 for (i
= 0; i
< line_len
; i
++) {
359 if (!isspace(line_buf
[i
]))
366 /* Check if the line isn't empty and isn't a comment */
367 if (line_buf
[i
] == '#')
370 /* We have a valid line - allocate a new node. */
371 temp
= (file_context_node_t
*)malloc(sizeof(file_context_node_t
));
373 fprintf(stderr
, "Error: failure allocating memory.\n");
377 memset(temp
, 0, sizeof(file_context_node_t
));
379 /* Parse out the regular expression from the line. */
383 while (i
< line_len
&& (!isspace(line_buf
[i
])))
388 regex_len
= finish
- start
;
390 if (regex_len
== 0) {
391 file_context_node_destroy(temp
);
398 temp
->path
= (char*)strndup(&line_buf
[start
], regex_len
);
400 file_context_node_destroy(temp
);
402 fprintf(stderr
, "Error: failure allocating memory.\n");
406 /* Get rid of whitespace after the regular expression. */
407 for (; i
< line_len
; i
++) {
409 if (!isspace(line_buf
[i
]))
414 file_context_node_destroy(temp
);
419 /* Parse out the type from the line (if it
421 if (line_buf
[i
] == '-') {
422 temp
->file_type
= (char *)malloc(sizeof(char) * 3);
423 if (!(temp
->file_type
)) {
424 fprintf(stderr
, "Error: failure allocating memory.\n");
428 if( i
+ 2 >= line_len
) {
429 file_context_node_destroy(temp
);
435 /* Fill the type into the array. */
436 temp
->file_type
[0] = line_buf
[i
];
437 temp
->file_type
[1] = line_buf
[i
+ 1];
439 temp
->file_type
[2] = 0;
441 /* Get rid of whitespace after the type. */
442 for (; i
< line_len
; i
++) {
443 if (!isspace(line_buf
[i
]))
449 file_context_node_destroy(temp
);
455 /* Parse out the context from the line. */
457 while (i
< line_len
&& (!isspace(line_buf
[i
])))
461 context_len
= finish
- start
;
463 temp
->context
= (char*)strndup(&line_buf
[start
], context_len
);
464 if (!temp
->context
) {
465 file_context_node_destroy(temp
);
467 fprintf(stderr
, "Error: failure allocating memory.\n");
471 /* Set all the data about the regular
475 /* Link this line of code at the end of
476 * the linked list. */
477 current
->next
= temp
;
478 current
= current
->next
;
487 /* Create the bucket linked list from the earlier linked list. */
488 current
= head
->next
;
490 (file_context_bucket_t
*)
491 malloc(sizeof(file_context_bucket_t
));
493 /* Go until all the nodes have been put in individual buckets. */
495 /* Copy over the file context line into the bucket. */
496 bcurrent
->data
= current
;
497 current
= current
->next
;
499 /* Detatch the node in the bucket from the old list. */
500 bcurrent
->data
->next
= NULL
;
502 /* If there should be another bucket, put one at the end. */
505 (file_context_bucket_t
*)
506 malloc(sizeof(file_context_bucket_t
));
507 if (!(bcurrent
->next
)) {
509 ("Error: failure allocating memory.\n");
513 /* Make sure the new bucket thinks it's the end of the
515 bcurrent
->next
->next
= NULL
;
517 bcurrent
= bcurrent
->next
;
522 /* Sort the bucket list. */
523 fc_merge_sort(master
);
525 /* Open the output file. */
526 if (!(out_file
= fopen(argv
[2], "w"))) {
527 printf("Error: failure opening output file for write.\n");
531 /* Output the sorted file_context linked list to the output file. */
532 current
= master
->data
;
534 /* Output the path. */
535 fprintf(out_file
, "%s\t\t", current
->path
);
537 /* Output the type, if there is one. */
538 if (current
->file_type
) {
539 fprintf(out_file
, "%s\t", current
->file_type
);
542 /* Output the context. */
543 fprintf(out_file
, "%s\n", current
->context
);
545 /* Remove the node. */
547 current
= current
->next
;
549 file_context_node_destroy(temp
);