Home | History | Annotate | Download | only in src
      1 #include <stdlib.h>
      2 
      3 #include "cil_flavor.h"
      4 #include "cil_internal.h"
      5 #include "cil_log.h"
      6 #include "cil_tree.h"
      7 
      8 struct cil_args_write {
      9 	FILE *cil_out;
     10 	struct cil_db *db;
     11 };
     12 
     13 static int cil_unfill_expr(struct cil_list *expr_str, char **out_str, int paren);
     14 static int cil_unfill_classperms_list(struct cil_list *classperms, char **out_str, int paren);
     15 static int __cil_write_first_child_helper(struct cil_tree_node *node, void *extra_args);
     16 static int __cil_write_node_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args);
     17 static int __cil_write_last_child_helper(struct cil_tree_node *node, void *extra_args);
     18 
     19 static int __cil_strlist_concat(struct cil_list *str_list, char **out_str, int paren) {
     20 	size_t len = paren ? 3 : 1;
     21 	size_t num_elems = 0;
     22 	char *p = NULL;
     23 	struct cil_list_item *curr;
     24 
     25 	/* get buffer size */
     26 	cil_list_for_each(curr, str_list) {
     27 		len += strlen((char *)curr->data);
     28 		num_elems++;
     29 	}
     30 	if (num_elems != 0) {
     31 		/* add spaces between elements */
     32 		len += num_elems - 1;
     33 	}
     34 	*out_str = cil_malloc(len);
     35 	p = *out_str;
     36 	if (paren)
     37 		*p++ = '(';
     38 	cil_list_for_each(curr, str_list) {
     39 		size_t src_len = strlen((char *)curr->data);
     40 		memcpy(p, curr->data, src_len);
     41 		p += src_len;
     42 		if (curr->next != NULL)
     43 			*p++ = ' ';
     44 	}
     45 	if (paren)
     46 		*p++ = ')';
     47 	*p++ = '\0';
     48 	return SEPOL_OK;
     49 }
     50 
     51 static int __cil_unfill_expr_helper(struct cil_list_item *curr,
     52 			     struct cil_list_item **next, char **out_str, int paren) {
     53 	int rc = SEPOL_ERR;
     54 	char *str = NULL;
     55 	char *operand1 = NULL;
     56 	char *operand2 = NULL;
     57 
     58 	switch(curr->flavor) {
     59 	case CIL_LIST:
     60 		rc = cil_unfill_expr((struct cil_list *)curr->data, &str, paren);
     61 		if (rc != SEPOL_OK)
     62 			goto exit;
     63 		*out_str = str;
     64 		*next = curr->next;
     65 		break;
     66 	case CIL_STRING:
     67 		str = strdup((char *)curr->data);
     68 		if (!str) {
     69 			cil_log(CIL_ERR, "OOM. Unable to copy string.\n");
     70             rc = SEPOL_ERR;
     71 			goto exit;
     72 		}
     73 		*out_str = str;
     74 		*next = curr->next;
     75 		break;
     76 	case CIL_DATUM:
     77 		str = strdup(((struct cil_symtab_datum *)curr->data)->name);
     78 		if (!str) {
     79 			cil_log(CIL_ERR, "OOM. Unable to copy string.\n");
     80             rc = SEPOL_ERR;
     81 			goto exit;
     82 		}
     83 		*out_str = str;
     84 		*next = curr->next;
     85 		break;
     86 	case CIL_OP: {
     87 		char *op_str = NULL;
     88 		size_t len = 0;
     89 		enum cil_flavor op_flavor = (enum cil_flavor)curr->data;
     90 		switch (op_flavor) {
     91 		case CIL_AND:
     92 			op_str = CIL_KEY_AND;
     93 			break;
     94 		case CIL_OR:
     95 			op_str = CIL_KEY_OR;
     96 			break;
     97 		case CIL_NOT:
     98 			op_str = CIL_KEY_NOT;
     99 			break;
    100 		case CIL_ALL:
    101 			op_str = CIL_KEY_ALL;
    102 			break;
    103 		case CIL_EQ:
    104 			op_str = CIL_KEY_EQ;
    105 			break;
    106 		case CIL_NEQ:
    107 			op_str = CIL_KEY_NEQ;
    108 			break;
    109 		case CIL_RANGE:
    110 			op_str = CIL_KEY_RANGE;
    111 			break;
    112 		case CIL_XOR:
    113 			op_str = CIL_KEY_XOR;
    114 			break;
    115 		case CIL_CONS_DOM:
    116 			op_str = CIL_KEY_CONS_DOM;
    117 			break;
    118 		case CIL_CONS_DOMBY:
    119 			op_str = CIL_KEY_CONS_DOMBY;
    120 			break;
    121 		case CIL_CONS_INCOMP:
    122 			op_str = CIL_KEY_CONS_INCOMP;
    123 			break;
    124 		default:
    125 			cil_log(CIL_ERR, "Unknown operator in expression: %d\n", op_flavor);
    126 			goto exit;
    127 			break;
    128 		}
    129 		/* all operands take two args except for 'all' and 'not', which take
    130 		 * one and two, respectively */
    131 		len = strlen(op_str) + 3;
    132 		if (op_flavor == CIL_ALL) {
    133 			*out_str = cil_malloc(len);
    134 			sprintf(*out_str, "(%s)", op_str);
    135 			*next = curr->next;
    136 		} else if (op_flavor == CIL_NOT) {
    137 			rc = __cil_unfill_expr_helper(curr->next, next, &operand1, paren);
    138 			if (rc != SEPOL_OK)
    139 				goto exit;
    140 			len += strlen(operand1) + 1;
    141 			*out_str = cil_malloc(len);
    142 			sprintf(*out_str, "(%s %s)", op_str, operand1);
    143 			// *next already set by recursive call
    144 		} else {
    145 			rc = __cil_unfill_expr_helper(curr->next, next, &operand1, paren);
    146 			if (rc != SEPOL_OK)
    147 				goto exit;
    148 			len += strlen(operand1) + 1;
    149 			// *next contains operand2, but keep track of next after that
    150 			rc = __cil_unfill_expr_helper(*next, next, &operand2, paren);
    151 			if (rc != SEPOL_OK)
    152 				goto exit;
    153 			len += strlen(operand2) + 1;
    154 			*out_str = cil_malloc(len);
    155 			sprintf(*out_str, "(%s %s %s)", op_str, operand1, operand2);
    156 			// *next already set by recursive call
    157 		}
    158 	}
    159 		break;
    160 	case CIL_CONS_OPERAND: {
    161 		enum cil_flavor operand_flavor = (enum cil_flavor)curr->data;
    162 		char *operand_str = NULL;
    163 		switch (operand_flavor) {
    164 		case CIL_CONS_U1:
    165 			operand_str = CIL_KEY_CONS_U1;
    166 			break;
    167 		case CIL_CONS_U2:
    168 			operand_str = CIL_KEY_CONS_U2;
    169 			break;
    170 		case CIL_CONS_U3:
    171 			operand_str = CIL_KEY_CONS_U3;
    172 			break;
    173 		case CIL_CONS_T1:
    174 			operand_str = CIL_KEY_CONS_T1;
    175 			break;
    176 		case CIL_CONS_T2:
    177 			operand_str = CIL_KEY_CONS_T2;
    178 			break;
    179 		case CIL_CONS_T3:
    180 			operand_str = CIL_KEY_CONS_T3;
    181 			break;
    182 		case CIL_CONS_R1:
    183 			operand_str = CIL_KEY_CONS_R1;
    184 			break;
    185 		case CIL_CONS_R2:
    186 			operand_str = CIL_KEY_CONS_R2;
    187 			break;
    188 		case CIL_CONS_R3:
    189 			operand_str = CIL_KEY_CONS_R3;
    190 			break;
    191 		case CIL_CONS_L1:
    192 			operand_str = CIL_KEY_CONS_L1;
    193 			break;
    194 		case CIL_CONS_L2:
    195 			operand_str = CIL_KEY_CONS_L2;
    196 			break;
    197 		case CIL_CONS_H1:
    198 			operand_str = CIL_KEY_CONS_H1;
    199 			break;
    200 		case CIL_CONS_H2:
    201 			operand_str = CIL_KEY_CONS_H2;
    202 			break;
    203 		default:
    204 			cil_log(CIL_ERR, "Unknown operand in expression\n");
    205 			goto exit;
    206 			break;
    207 		}
    208 		str = strdup(operand_str);
    209 		if (!str) {
    210 			cil_log(CIL_ERR, "OOM. Unable to copy string.\n");
    211             rc = SEPOL_ERR;
    212 			goto exit;
    213 		}
    214 		*out_str = str;
    215 		*next = curr->next;
    216 	}
    217 		break;
    218 	default:
    219 		cil_log(CIL_ERR, "Unknown flavor in expression\n");
    220 		goto exit;
    221 		break;
    222 	}
    223 	rc = SEPOL_OK;
    224 exit:
    225 	free(operand1);
    226 	free(operand2);
    227 	return rc;
    228 }
    229 
    230 static int cil_unfill_expr(struct cil_list *expr_str, char **out_str, int paren) {
    231 	int rc = SEPOL_ERR;
    232 
    233 	/* reuse cil_list to keep track of strings */
    234 	struct cil_list *str_list = NULL;
    235 	struct cil_list_item *curr = NULL;
    236 
    237 	cil_list_init(&str_list, CIL_NONE);
    238 
    239 	/* iterate through cil_list, grabbing elements as needed */
    240 	curr = expr_str->head;
    241 	while(curr != NULL) {
    242 		char *str = NULL;
    243 		struct cil_list_item *next = NULL;
    244 
    245 		rc = __cil_unfill_expr_helper(curr, &next, &str, paren);
    246         if (rc != SEPOL_OK)
    247             goto exit;
    248 		cil_list_append(str_list, CIL_STRING, (void *) str);
    249 		str = NULL;
    250 		curr = next;
    251 	}
    252 	rc = __cil_strlist_concat(str_list, out_str, paren);
    253 	if (rc != SEPOL_OK)
    254 		goto exit;
    255 	rc = SEPOL_OK;
    256 exit:
    257 	cil_list_for_each(curr, str_list) {
    258 		free(curr->data);
    259 	}
    260 	cil_list_destroy(&str_list, 0);
    261 	return rc;
    262 }
    263 
    264 static int cil_unfill_cats(struct cil_cats *cats, char **out_str) {
    265 	return cil_unfill_expr(cats->str_expr, out_str, 0);
    266 }
    267 
    268 static int cil_unfill_level(struct cil_level *lvl, char **out_str) {
    269 	int rc = SEPOL_ERR;
    270 	size_t len = 0;
    271 	char *sens, *cats = NULL;
    272 	sens = lvl->sens_str;
    273 	len = strlen(sens) + 3; // '()\0'
    274 	if (lvl->cats != NULL) {
    275 		rc = cil_unfill_cats(lvl->cats, &cats);
    276 		if (rc != SEPOL_OK)
    277 			goto exit;
    278 		len += strlen(cats) + 1;
    279 	}
    280 	*out_str = cil_malloc(len);
    281 	if (cats == NULL) {
    282 		if (sprintf(*out_str, "(%s)", sens) < 0) {
    283 			cil_log(CIL_ERR, "Error unpacking and writing level\n");
    284 			rc = SEPOL_ERR;
    285 			goto exit;
    286 		}
    287 	} else {
    288 		if (sprintf(*out_str, "(%s %s)", sens, cats) < 0) {
    289 			cil_log(CIL_ERR, "Error unpacking and writing level\n");
    290 			rc = SEPOL_ERR;
    291 			goto exit;
    292 		}
    293 	}
    294 	rc = SEPOL_OK;
    295 exit:
    296 	free(cats);
    297 	return rc;
    298 }
    299 
    300 static int cil_unfill_levelrange(struct cil_levelrange *lvlrnge, char **out_str) {
    301 	int rc = SEPOL_ERR;
    302 	size_t len = 0;
    303 	char *low = NULL, *high = NULL;
    304 	if (lvlrnge->low_str != NULL) {
    305 		low = strdup(lvlrnge->low_str);
    306 		if (low == NULL) {
    307 			cil_log(CIL_ERR, "OOM. Unable to copy level string.\n");
    308             rc = SEPOL_ERR;
    309 			goto exit;
    310 		}
    311 	} else {
    312 		rc = cil_unfill_level(lvlrnge->low, &low);
    313 		if (rc != SEPOL_OK)
    314 			goto exit;
    315 	}
    316 	if (lvlrnge->high_str != NULL) {
    317 		high = strdup(lvlrnge->high_str);
    318 		if (high == NULL) {
    319 			cil_log(CIL_ERR, "OOM. Unable to copy level string.\n");
    320             rc = SEPOL_ERR;
    321 			goto exit;
    322 		}
    323 	} else {
    324 		rc = cil_unfill_level(lvlrnge->high, &high);
    325 		if (rc != SEPOL_OK)
    326 			goto exit;
    327 	}
    328 	len = strlen(low) + strlen(high) + 4;
    329 	*out_str = cil_malloc(len);
    330 	if (sprintf(*out_str, "(%s %s)", low, high) < 0) {
    331 		cil_log(CIL_ERR, "Error unpacking and writing levelrange\n");
    332 		rc = SEPOL_ERR;
    333 		goto exit;
    334 	}
    335 	rc = SEPOL_OK;
    336 exit:
    337 	free(low);
    338 	free(high);
    339 	return rc;
    340 }
    341 
    342 static int cil_unfill_context(struct cil_context *context, char **out_str) {
    343 	int rc = SEPOL_ERR;
    344 	size_t len = 0;
    345 	char *user_str, *role_str, *type_str;
    346 	char *range_str = NULL;
    347 
    348 	user_str = context->user_str;
    349 	role_str = context->role_str;
    350 	type_str = context->type_str;
    351 	if (context->range_str != NULL) {
    352 		range_str = strdup(context->range_str);
    353 		if (range_str == NULL) {
    354 			cil_log(CIL_ERR, "OOM. Unable to copy range string.\n");
    355             rc = SEPOL_ERR;
    356 			goto exit;
    357 		}
    358 	} else {
    359 		rc = cil_unfill_levelrange(context->range, &range_str);
    360 		if (rc != SEPOL_OK)
    361 			goto exit;
    362 	}
    363 	len = strlen(user_str) + strlen(role_str) + strlen(type_str)
    364 		+ strlen(range_str) + 6;
    365 	*out_str = cil_malloc(len);
    366 	if (sprintf(*out_str, "(%s %s %s %s)", user_str, role_str, type_str, range_str) < 0) {
    367 		cil_log(CIL_ERR, "Error unpacking and writing context\n");
    368 		rc = SEPOL_ERR;
    369 		goto exit;
    370 	}
    371 	rc = SEPOL_OK;
    372 exit:
    373 	free(range_str);
    374 	return rc;
    375 }
    376 
    377 static int cil_unfill_permx(struct cil_permissionx *permx, char **out_str) {
    378 	size_t len = 3;
    379 	int rc = SEPOL_ERR;
    380 	char *kind, *obj;
    381 	char *expr = NULL;
    382 
    383 	switch (permx->kind) {
    384 	case CIL_PERMX_KIND_IOCTL:
    385 		kind = CIL_KEY_IOCTL;
    386 		break;
    387 	default:
    388 		cil_log(CIL_ERR, "Unknown permissionx kind: %d\n", permx->kind);
    389 		rc = SEPOL_ERR;
    390 		goto exit;
    391 		break;
    392 	}
    393 	obj = permx->obj_str;
    394 	rc = cil_unfill_expr(permx->expr_str, &expr, 1);
    395 	if (rc != SEPOL_OK)
    396 		goto exit;
    397 	len += strlen(kind) + strlen(obj) + strlen(expr) + 2;
    398 	*out_str = cil_malloc(len);
    399 	if (sprintf(*out_str, "(%s %s %s)", kind, obj, expr) < 0) {
    400 		cil_log(CIL_ERR, "Error writing xperm\n");
    401 		rc = SEPOL_ERR;
    402 		goto exit;
    403 	}
    404 	rc = SEPOL_OK;
    405 exit:
    406 	free(expr);
    407 	return rc;
    408 }
    409 
    410 #define cil_write_unsupported(flavor) _cil_write_unsupported(flavor, __LINE__)
    411 static int _cil_write_unsupported(const char *flavor, int line) {
    412 	cil_log(CIL_ERR,
    413 			"flavor \"%s\" is not supported, look in file \"%s\""
    414 			" on line %d to add support.\n", flavor, __FILE__, line);
    415 	return SEPOL_ENOTSUP;
    416 }
    417 
    418 static int cil_write_policycap(struct cil_tree_node *node, FILE *cil_out) {
    419 	struct cil_policycap *polcap = (struct cil_policycap *)node->data;
    420 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_POLICYCAP, polcap->datum.name);
    421 	return SEPOL_OK;
    422 }
    423 
    424 static int cil_write_perm(struct cil_tree_node *node, FILE *cil_out) {
    425 	struct cil_perm *perm = (struct cil_perm *)node->data;
    426 	fprintf(cil_out, "%s", perm->datum.name);
    427 	if (node->next != NULL)
    428 		fprintf(cil_out, " ");
    429 	return SEPOL_OK;
    430 }
    431 
    432 
    433 static int cil_write_class(struct cil_tree_node *node, uint32_t *finished,
    434 		     struct cil_args_write *extra_args) {
    435 	int rc = SEPOL_ERR;
    436 	FILE *cil_out = extra_args->cil_out;
    437 	struct cil_symtab_datum *datum = (struct cil_symtab_datum *)node->data;
    438 	char *class_type = (node->flavor == CIL_CLASS) ? CIL_KEY_CLASS : CIL_KEY_COMMON;
    439 
    440 	/* print preamble */
    441 	fprintf(cil_out, "(%s %s ", class_type, datum->name);
    442 
    443 	if (node->cl_head == NULL) {
    444 		/* no associated perms in this part of tree */
    445 		fprintf(cil_out, "()");
    446 	} else {
    447 
    448 		/* visit subtree (perms) */
    449 		rc = cil_tree_walk(node, __cil_write_node_helper,
    450 				   __cil_write_first_child_helper,
    451 				   __cil_write_last_child_helper,
    452 				   extra_args);
    453 		if (rc != SEPOL_OK)
    454 			goto exit;
    455 	}
    456 
    457 	/* postamble (trailing paren) */
    458 	fprintf(cil_out, ")\n");
    459 	*finished = CIL_TREE_SKIP_HEAD;
    460 	rc = SEPOL_OK;
    461 exit:
    462 	return rc;
    463 }
    464 
    465 static int cil_write_classorder(struct cil_tree_node *node, FILE *cil_out) {
    466 	int rc = SEPOL_ERR;
    467 	char *ord_str = NULL;
    468 	struct cil_classorder *classord = (struct cil_classorder *)node->data;
    469 
    470 	/* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
    471 	rc = cil_unfill_expr(classord->class_list_str, &ord_str, 1);
    472 	if (rc != SEPOL_OK)
    473 		goto exit;
    474 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_CLASSORDER, ord_str);
    475 	rc = SEPOL_OK;
    476 exit:
    477 	free(ord_str);
    478 	return rc;
    479 }
    480 
    481 static int cil_write_classcommon(struct cil_tree_node *node, FILE *cil_out) {
    482 	struct cil_classcommon *classcommon = (struct cil_classcommon *)node->data;
    483 	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_CLASSCOMMON, classcommon->class_str,
    484 		classcommon->common_str);
    485 	return SEPOL_OK;
    486 }
    487 
    488 static int cil_write_sid(struct cil_tree_node *node, FILE *cil_out) {
    489 	struct cil_sid *sid = (struct cil_sid *)node->data;
    490 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_SID, sid->datum.name);
    491 	return SEPOL_OK;
    492 }
    493 
    494 static int cil_write_sidcontext(struct cil_tree_node *node, FILE *cil_out) {
    495 	int rc = SEPOL_ERR;
    496 	char *sid;
    497 	char *ctx_str = NULL;
    498 	struct cil_sidcontext *sidcon = (struct cil_sidcontext *)node->data;
    499 
    500 	sid = sidcon->sid_str;
    501 	if (sidcon->context_str != NULL) {
    502 		ctx_str = strdup(sidcon->context_str);
    503 		if (ctx_str == NULL) {
    504 			cil_log(CIL_ERR, "OOM. Unable to copy context string.\n");
    505             rc = SEPOL_ERR;
    506 			goto exit;
    507 		}
    508 	} else {
    509 		rc = cil_unfill_context(sidcon->context, &ctx_str);
    510 		if (rc != SEPOL_OK)
    511 			goto exit;
    512 	}
    513 	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_SIDCONTEXT, sid, ctx_str);
    514 	rc = SEPOL_OK;
    515 exit:
    516 	free(ctx_str);
    517 	return rc;
    518 }
    519 
    520 static int cil_write_sidorder(struct cil_tree_node *node, FILE *cil_out) {
    521 	int rc = SEPOL_ERR;
    522 	char *ord_str = NULL;
    523 	struct cil_sidorder *sidord = (struct cil_sidorder *)node->data;
    524 
    525 	/* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
    526 	rc = cil_unfill_expr(sidord->sid_list_str, &ord_str, 1);
    527 	if (rc != SEPOL_OK)
    528 		goto exit;
    529 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_SIDORDER, ord_str);
    530 	rc = SEPOL_OK;
    531 exit:
    532 	free(ord_str);
    533 	return rc;
    534 }
    535 
    536 static int cil_write_user(struct cil_tree_node *node, FILE *cil_out) {
    537 	struct cil_user *user = (struct cil_user *)node->data;
    538 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_USER, user->datum.name);
    539 	return SEPOL_OK;
    540 }
    541 
    542 static int cil_write_userrole(struct cil_tree_node *node, FILE *cil_out) {
    543 	struct cil_userrole *userrole = (struct cil_userrole *)node->data;
    544 	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_USERROLE, userrole->user_str,
    545 		userrole->role_str);
    546 	return SEPOL_OK;
    547 }
    548 
    549 static int cil_write_userlevel(struct cil_tree_node *node, FILE *cil_out) {
    550 	struct cil_userlevel *usrlvl = (struct cil_userlevel *)node->data;
    551 	int rc = SEPOL_ERR;
    552 	char *usr;
    553 	char *lvl = NULL;
    554 
    555 	usr = usrlvl->user_str;
    556 	if (usrlvl->level_str != NULL) {
    557 		lvl = strdup(usrlvl->level_str);
    558 		if (lvl == NULL) {
    559 			cil_log(CIL_ERR, "OOM. Unable to copy level string.\n");
    560             rc = SEPOL_ERR;
    561 			goto exit;
    562 		}
    563 	} else {
    564 		rc = cil_unfill_level(usrlvl->level, &lvl);
    565 		if (rc != SEPOL_OK)
    566 			goto exit;
    567 	}
    568 	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_USERLEVEL, usr, lvl);
    569 	rc = SEPOL_OK;
    570 exit:
    571 	free(lvl);
    572 	return rc;
    573 }
    574 
    575 static int cil_write_userrange(struct cil_tree_node *node, FILE *cil_out) {
    576 	struct cil_userrange *usrrng = (struct cil_userrange *)node->data;
    577 	int rc = SEPOL_ERR;
    578 	char *usr;
    579 	char *range = NULL;
    580 
    581 	usr = usrrng->user_str;
    582 	if (usrrng->range_str != NULL) {
    583 		range = strdup(usrrng->range_str);
    584 		if (range == NULL) {
    585 			cil_log(CIL_ERR, "OOM. Unable to copy levelrange string.\n");
    586             rc = SEPOL_ERR;
    587 			goto exit;
    588 		}
    589 	} else {
    590 		rc = cil_unfill_levelrange(usrrng->range, &range);
    591 		if (rc != SEPOL_OK)
    592 			goto exit;
    593 	}
    594 	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_USERRANGE, usr, range);
    595 	rc = SEPOL_OK;
    596 exit:
    597 	free(range);
    598 	return rc;
    599 }
    600 
    601 static int cil_write_role(struct cil_tree_node *node, FILE *cil_out) {
    602 	struct cil_role *role = (struct cil_role *)node->data;
    603 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_ROLE, role->datum.name);
    604 	return SEPOL_OK;
    605 }
    606 
    607 static int cil_write_roletype(struct cil_tree_node *node, FILE *cil_out) {
    608 	struct cil_roletype *roletype = (struct cil_roletype *)node->data;
    609 	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_ROLETYPE, roletype->role_str, roletype->type_str);
    610 	return SEPOL_OK;
    611 }
    612 
    613 static int cil_write_roleattribute(struct cil_tree_node *node, FILE *cil_out) {
    614 	struct cil_roleattribute *roleattr = (struct cil_roleattribute *)node->data;
    615 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_ROLEATTRIBUTE, roleattr->datum.name);
    616 	return SEPOL_OK;
    617 }
    618 
    619 static int cil_write_type(struct cil_tree_node *node, FILE *cil_out) {
    620 	struct cil_type *type = (struct cil_type *)node->data;
    621 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_TYPE, type->datum.name);
    622 	return SEPOL_OK;
    623 }
    624 
    625 static int cil_write_typepermissive(struct cil_tree_node *node, FILE *cil_out) {
    626 	struct cil_typepermissive *type = (struct cil_typepermissive *)node->data;
    627 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_TYPEPERMISSIVE, type->type_str);
    628 	return SEPOL_OK;
    629 }
    630 
    631 static int cil_write_typeattribute(struct cil_tree_node *node, FILE *cil_out) {
    632 	struct cil_typeattribute *typeattr = (struct cil_typeattribute *)node->data;
    633 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_TYPEATTRIBUTE, typeattr->datum.name);
    634 	return SEPOL_OK;
    635 }
    636 
    637 static int cil_write_typeattributeset(struct cil_tree_node *node, FILE *cil_out) {
    638 	int rc = SEPOL_ERR;
    639 	char *typeattr;
    640 	char *set_str = NULL;
    641 	struct cil_typeattributeset *typeattrset = (struct cil_typeattributeset *)node->data;
    642 
    643 	typeattr = typeattrset->attr_str;
    644 	rc = cil_unfill_expr(typeattrset->str_expr, &set_str, 1);
    645 	if (rc != SEPOL_OK)
    646 		goto exit;
    647 
    648 	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_TYPEATTRIBUTESET, typeattr, set_str);
    649 	rc = SEPOL_OK;
    650 exit:
    651 	free(set_str);
    652 	return rc;
    653 }
    654 
    655 static int cil_write_expandtypeattribute(struct cil_tree_node *node, FILE *cil_out)
    656 {
    657 	int rc = SEPOL_ERR;
    658 	char *attr_strs = NULL;
    659 	struct cil_expandtypeattribute *expandattr = (struct cil_expandtypeattribute *)node->data;
    660 
    661 	rc = cil_unfill_expr(expandattr->attr_strs, &attr_strs, 1);
    662 	if (rc != SEPOL_OK)
    663 		goto exit;
    664 
    665 	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_EXPANDTYPEATTRIBUTE, attr_strs,
    666 		expandattr->expand ? CIL_KEY_CONDTRUE : CIL_KEY_CONDFALSE);
    667 	rc = SEPOL_OK;
    668 exit:
    669 	free(attr_strs);
    670 	return rc;
    671 }
    672 
    673 static int cil_write_alias(struct cil_tree_node *node, FILE *cil_out) {
    674 	int rc = SEPOL_ERR;
    675 	char *type;
    676 	struct cil_alias *alias = (struct cil_alias *)node->data;
    677 
    678 	switch (node->flavor) {
    679 	case CIL_TYPEALIAS:
    680 		type = CIL_KEY_TYPEALIAS;
    681 		break;
    682 	case CIL_SENSALIAS:
    683 		type = CIL_KEY_SENSALIAS;
    684 		break;
    685 	case CIL_CATALIAS:
    686 		type = CIL_KEY_CATALIAS;
    687 		break;
    688 	default:
    689 		cil_log(CIL_ERR, "Unknown alias type: %d\n", node->flavor);
    690 		rc = SEPOL_ERR;
    691 		goto exit;
    692 		break;
    693 	}
    694 	fprintf(cil_out, "(%s %s)\n", type, alias->datum.name);
    695 	rc = SEPOL_OK;
    696 exit:
    697 	return rc;
    698 }
    699 
    700 static int cil_write_aliasactual(struct cil_tree_node *node, FILE *cil_out) {
    701 	int rc = SEPOL_ERR;
    702 	char *type, *alias, *actual;
    703 	struct cil_aliasactual *aliasact = (struct cil_aliasactual *)node->data;
    704 
    705 	switch (node->flavor) {
    706 	case CIL_TYPEALIASACTUAL:
    707 		type = CIL_KEY_TYPEALIASACTUAL;
    708 		break;
    709 	case CIL_SENSALIASACTUAL:
    710 		type = CIL_KEY_SENSALIASACTUAL;
    711 		break;
    712 	case CIL_CATALIASACTUAL:
    713 		type = CIL_KEY_CATALIASACTUAL;
    714 		break;
    715 	default:
    716 		cil_log(CIL_ERR, "Unknown alias type: %d\n", node->flavor);
    717 		rc = SEPOL_ERR;
    718 		goto exit;
    719 		break;
    720 	}
    721 	alias = aliasact->alias_str;
    722 	actual = aliasact->actual_str;
    723 	fprintf(cil_out, "(%s %s %s)\n", type, alias, actual);
    724 	rc = SEPOL_OK;
    725 exit:
    726 	return rc;
    727 }
    728 
    729 static int cil_write_nametypetransition(struct cil_tree_node *node, FILE *cil_out) {
    730 	char *src, *tgt, *obj, *res, *name;
    731 	struct cil_nametypetransition *ntrans = (struct cil_nametypetransition *)node->data;
    732 
    733 	src = ntrans->src_str;
    734 	tgt = ntrans->tgt_str;
    735 	obj = ntrans->obj_str;
    736 	res = ntrans->result_str;
    737 	name = ntrans->name_str;
    738 	fprintf(cil_out, "(%s %s %s %s \"%s\" %s)\n", CIL_KEY_TYPETRANSITION,
    739 		src, tgt, obj, name, res);
    740 	return SEPOL_OK;
    741 }
    742 
    743 static int cil_write_avrule_x(struct cil_avrule *avrule, FILE *cil_out) {
    744 	int rc = SEPOL_ERR;
    745 	char *rulekind, *src, *tgt;
    746 	char *xperms = NULL;
    747 
    748 	switch (avrule->rule_kind) {
    749 	case CIL_AVRULE_ALLOWED:
    750 		rulekind = CIL_KEY_ALLOWX;
    751 		break;
    752 	case CIL_AVRULE_AUDITALLOW:
    753 		rulekind = CIL_KEY_AUDITALLOWX;
    754 		break;
    755 	case CIL_AVRULE_DONTAUDIT:
    756 		rulekind = CIL_KEY_DONTAUDITX;
    757 		break;
    758 	case CIL_AVRULE_NEVERALLOW:
    759 		rulekind = CIL_KEY_NEVERALLOWX;
    760 		break;
    761 	default:
    762 		cil_log(CIL_ERR, "Unknown AVRULE type: %d\n", avrule->rule_kind);
    763 		rc = SEPOL_ERR;
    764 		goto exit;
    765 		break;
    766 	}
    767 	src = avrule->src_str;
    768 	tgt = avrule->tgt_str;
    769 
    770 	if (avrule->perms.x.permx_str != NULL) {
    771 		xperms = strdup(avrule->perms.x.permx_str);
    772 		if (xperms == NULL) {
    773 			cil_log(CIL_ERR, "OOM. Unable to copy xperms string.\n");
    774 			rc = SEPOL_ERR;
    775 			goto exit;
    776 		}
    777 	} else {
    778 		rc = cil_unfill_permx(avrule->perms.x.permx, &xperms);
    779 		if (rc != SEPOL_OK)
    780 			goto exit;
    781 	}
    782 	fprintf(cil_out, "(%s %s %s %s)\n", rulekind, src, tgt, xperms);
    783 	rc = SEPOL_OK;
    784 exit:
    785 	free(xperms);
    786 	return rc;
    787 }
    788 
    789 static int cil_write_avrule_orig(struct cil_avrule *avrule, FILE *cil_out) {
    790 	int rc = SEPOL_ERR;
    791 	char *rulekind, *src, *tgt;
    792 	char *classperms = NULL;
    793 
    794 	switch (avrule->rule_kind) {
    795 	case CIL_AVRULE_ALLOWED:
    796 		rulekind = CIL_KEY_ALLOW;
    797 		break;
    798 	case CIL_AVRULE_AUDITALLOW:
    799 		rulekind = CIL_KEY_AUDITALLOW;
    800 		break;
    801 	case CIL_AVRULE_DONTAUDIT:
    802 		rulekind = CIL_KEY_DONTAUDIT;
    803 		break;
    804 	case CIL_AVRULE_NEVERALLOW:
    805 		rulekind = CIL_KEY_NEVERALLOW;
    806 		break;
    807 	default:
    808 		cil_log(CIL_ERR, "Unknown AVRULE type: %d\n", avrule->rule_kind);
    809 		rc = SEPOL_ERR;
    810 		goto exit;
    811 		break;
    812 	}
    813 	src = avrule->src_str;
    814 	tgt = avrule->tgt_str;
    815 
    816 	rc = cil_unfill_classperms_list(avrule->perms.classperms, &classperms, 0);
    817 	if (rc != SEPOL_OK)
    818 		goto exit;
    819 	fprintf(cil_out, "(%s %s %s %s)\n", rulekind, src, tgt, classperms);
    820 	rc = SEPOL_OK;
    821 exit:
    822 	free(classperms);
    823 	return rc;
    824 }
    825 
    826 static int cil_write_avrule(struct cil_tree_node *node, FILE *cil_out) {
    827 	int rc = SEPOL_ERR;
    828 	struct cil_avrule *avrule = (struct cil_avrule *)node->data;
    829 
    830 	if (avrule->is_extended)
    831 		rc = cil_write_avrule_x(avrule, cil_out);
    832 	else
    833 		rc = cil_write_avrule_orig(avrule, cil_out);
    834 	return rc;
    835 }
    836 
    837 static int cil_write_type_rule(struct cil_tree_node *node, FILE *cil_out) {
    838 	int rc = SEPOL_ERR;
    839 	char *type, *src, *tgt, *obj, *res;
    840 	struct cil_type_rule *typerule = (struct cil_type_rule *)node->data;
    841 
    842 	switch (typerule->rule_kind) {
    843 	case CIL_TYPE_TRANSITION:
    844 		type = CIL_KEY_TYPETRANSITION;
    845 		break;
    846 	case CIL_TYPE_MEMBER:
    847 		type = CIL_KEY_TYPEMEMBER;
    848 		break;
    849 	case CIL_TYPE_CHANGE:
    850 		type = CIL_KEY_TYPECHANGE;
    851 		break;
    852 	default:
    853 		cil_log(CIL_ERR, "Unknown TYPERULE type: %d\n", typerule->rule_kind);
    854 		rc = SEPOL_ERR;
    855 		goto exit;
    856 		break;
    857 	}
    858 	src = typerule->src_str;
    859 	tgt = typerule->tgt_str;
    860 	obj = typerule->obj_str;
    861 	res = typerule->result_str;
    862 	fprintf(cil_out, "(%s %s %s %s %s)\n", type, src, tgt, obj, res);
    863 	rc = SEPOL_OK;
    864 exit:
    865 	return rc;
    866 }
    867 
    868 static int cil_write_sens(struct cil_tree_node *node, FILE *cil_out) {
    869 	struct cil_sens *sens = (struct cil_sens *)node->data;
    870 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_SENSITIVITY, sens->datum.name);
    871 	return SEPOL_OK;
    872 }
    873 
    874 static int cil_write_cat(struct cil_tree_node *node, FILE *cil_out) {
    875 	struct cil_cat *cat = (struct cil_cat *)node->data;
    876 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_CATEGORY, cat->datum.name);
    877 	return SEPOL_OK;
    878 }
    879 
    880 static int cil_write_senscat(struct cil_tree_node *node, FILE *cil_out) {
    881 	int rc = SEPOL_ERR;
    882 	char *sens;
    883 	char *cats = NULL;
    884 	struct cil_senscat *senscat = (struct cil_senscat *)node->data;
    885 
    886 	sens = senscat->sens_str;
    887 	rc = cil_unfill_cats(senscat->cats, &cats);
    888 	if (rc != SEPOL_OK)
    889 		goto exit;
    890 	/* TODO: deal with extra/missing parens */
    891 	fprintf(cil_out, "(%s %s (%s))\n", CIL_KEY_SENSCAT, sens, cats);
    892 	rc = SEPOL_OK;
    893 exit:
    894 	free(cats);
    895 	return rc;
    896 }
    897 
    898 static int cil_write_catorder(struct cil_tree_node *node, FILE *cil_out) {
    899 	int rc = SEPOL_ERR;
    900 	char *ord_str = NULL;
    901 	struct cil_catorder *catord = (struct cil_catorder *)node->data;
    902 
    903 	/* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
    904 	rc = cil_unfill_expr(catord->cat_list_str, &ord_str, 1);
    905 	if (rc != SEPOL_OK)
    906 		goto exit;
    907 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_CATORDER, ord_str);
    908 	rc = SEPOL_OK;
    909 exit:
    910 	free(ord_str);
    911 	return rc;
    912 }
    913 
    914 static int cil_write_sensorder(struct cil_tree_node *node, FILE *cil_out) {
    915 	int rc = SEPOL_ERR;
    916 	char *ord_str = NULL;
    917 	struct cil_sensorder *sensord = (struct cil_sensorder *)node->data;
    918 
    919 	/* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
    920 	rc = cil_unfill_expr(sensord->sens_list_str, &ord_str, 1);
    921 	if (rc != SEPOL_OK)
    922 		goto exit;
    923 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_SENSITIVITYORDER, ord_str);
    924 	rc = SEPOL_OK;
    925 exit:
    926 	free(ord_str);
    927 	return rc;
    928 }
    929 
    930 static int cil_write_genfscon(struct cil_tree_node *node, FILE *cil_out) {
    931 	int rc = SEPOL_ERR;
    932 	char *ctx_str = NULL;
    933 
    934 	struct cil_genfscon *genfscon = (struct cil_genfscon *)node->data;
    935 	if (genfscon->context_str != NULL) {
    936 		ctx_str = strdup(genfscon->context_str);
    937 		if (ctx_str == NULL) {
    938 			cil_log(CIL_ERR, "OOM. Unable to copy context string.\n");
    939             rc = SEPOL_ERR;
    940 			goto exit;
    941 		}
    942 	} else {
    943 		rc = cil_unfill_context(genfscon->context, &ctx_str);
    944 		if (rc != SEPOL_OK)
    945 			goto exit;
    946 	}
    947 	fprintf(cil_out, "(%s %s %s %s)\n", CIL_KEY_GENFSCON, genfscon->fs_str,
    948             genfscon->path_str, ctx_str);
    949 	rc = SEPOL_OK;
    950 exit:
    951 	free(ctx_str);
    952 	return rc;
    953 }
    954 
    955 static int cil_unfill_classperms(struct cil_list_item *curr, char **out_str) {
    956 	int rc = SEPOL_ERR;
    957 	size_t len = 3;
    958 	char *class_str;
    959 	char *perms_str = NULL;
    960 	struct cil_classperms *cp = (struct cil_classperms *)curr->data;
    961 
    962 	class_str = cp->class_str;
    963 	len += strlen(class_str) + 1;
    964 
    965 	/* fill_perms just calls gen_expr */
    966 	rc = cil_unfill_expr(cp->perm_strs, &perms_str, 1);
    967 	if (rc != SEPOL_OK)
    968 		goto exit;
    969 	len += strlen(perms_str);
    970 	*out_str = cil_malloc(len);
    971 	sprintf(*out_str, "(%s %s)", class_str, perms_str);
    972 	rc = SEPOL_OK;
    973 exit:
    974 	free(perms_str);
    975 	return rc;
    976 }
    977 
    978 static int cil_unfill_classperms_list(struct cil_list *classperms, char **out_str, int paren) {
    979 	int rc = SEPOL_ERR;
    980 	struct cil_list_item *curr;
    981 	char *str = NULL;
    982 
    983 	/* reuse cil_list to keep track of strings */
    984 	struct cil_list *str_list = NULL;
    985 	cil_list_init(&str_list, CIL_NONE);
    986 	cil_list_for_each(curr, classperms) {
    987 		switch (curr->flavor) {
    988 		case CIL_CLASSPERMS_SET:
    989 			str = strdup(((struct cil_classperms_set *)curr->data)->set_str);
    990 			if (str == NULL) {
    991 				cil_log(CIL_ERR, "OOM. Unable to copy classpermset.\n");
    992                 rc = SEPOL_ERR;
    993 				goto exit;
    994 			}
    995 			break;
    996 		case CIL_CLASSPERMS:
    997 			rc = cil_unfill_classperms(curr, &str);
    998 			if (rc != SEPOL_OK)
    999 				goto exit;
   1000 			break;
   1001 		default:
   1002 			cil_log(CIL_ERR, "Unrecognized classperms flavor\n.");
   1003 			goto exit;
   1004 		}
   1005 		cil_list_append(str_list, CIL_STRING, (void *) str);
   1006 		str = NULL;
   1007 	}
   1008 	rc = __cil_strlist_concat(str_list, out_str, paren);
   1009 	if (rc != SEPOL_OK)
   1010 		goto exit;
   1011 	rc = SEPOL_OK;
   1012 exit:
   1013 	cil_list_for_each(curr, str_list) {
   1014 		free(curr->data);
   1015 	}
   1016 	cil_list_destroy(&str_list, 0);
   1017 	return rc;
   1018 }
   1019 
   1020 static int cil_write_fsuse(struct cil_tree_node *node, FILE *cil_out) {
   1021 	int rc = SEPOL_ERR;
   1022 	struct cil_fsuse *fsuse = (struct cil_fsuse *)node->data;
   1023 	char *type, *fsname;
   1024 	char *ctx_str = NULL;
   1025 
   1026 	switch(fsuse->type) {
   1027 	case CIL_FSUSE_XATTR:
   1028 		type = CIL_KEY_XATTR;
   1029 		break;
   1030 	case CIL_FSUSE_TASK:
   1031 		type = CIL_KEY_TASK;
   1032 		break;
   1033 	case CIL_FSUSE_TRANS:
   1034 		type = CIL_KEY_TRANS;
   1035 		break;
   1036 	default:
   1037 		cil_log(CIL_ERR, "Unrecognized fsuse type\n");
   1038 		rc = SEPOL_ERR;
   1039 		goto exit;
   1040 		break;
   1041 	}
   1042 
   1043 	fsname = fsuse->fs_str;
   1044 	if (fsuse->context_str != NULL) {
   1045 		ctx_str = strdup(fsuse->context_str);
   1046 		if (ctx_str == NULL) {
   1047 			cil_log(CIL_ERR, "OOM. Unable to copy context string.\n");
   1048 			rc = SEPOL_ERR;
   1049 			goto exit;
   1050 		}
   1051 	} else {
   1052 		rc = cil_unfill_context(fsuse->context, &ctx_str);
   1053 		if (rc != SEPOL_OK)
   1054 			goto exit;
   1055 	}
   1056 	fprintf(cil_out, "(%s %s %s %s)\n", CIL_KEY_FSUSE, type, fsname, ctx_str);
   1057 exit:
   1058 	free(ctx_str);
   1059 	return rc;
   1060 }
   1061 
   1062 static int cil_write_constrain(struct cil_tree_node *node, FILE *cil_out) {
   1063 	int rc = SEPOL_ERR;
   1064 	struct cil_constrain *cons = (struct cil_constrain *)node->data;
   1065 	char *flav;
   1066 	char *classperms = NULL;
   1067 	char *expr = NULL;
   1068 
   1069 	flav = (node->flavor == CIL_CONSTRAIN) ? CIL_KEY_CONSTRAIN : CIL_KEY_MLSCONSTRAIN;
   1070 
   1071 	rc = cil_unfill_classperms_list(cons->classperms, &classperms, 0);
   1072 	if (rc != SEPOL_OK)
   1073 		goto exit;
   1074 
   1075 	rc = cil_unfill_expr(cons->str_expr, &expr, 0);
   1076 	if (rc != SEPOL_OK)
   1077 		goto exit;
   1078 
   1079 	fprintf(cil_out, "(%s %s %s)\n", flav, classperms, expr);
   1080 exit:
   1081 	free(classperms);
   1082 	free(expr);
   1083 	return rc;
   1084 }
   1085 
   1086 static int cil_write_handleunknown(struct cil_tree_node *node, FILE *cil_out) {
   1087 	int rc = SEPOL_OK;
   1088 	struct cil_handleunknown *handunknown = (struct cil_handleunknown *)node->data;
   1089 	char *val = NULL;
   1090 	switch (handunknown->handle_unknown) {
   1091 	case SEPOL_ALLOW_UNKNOWN:
   1092 		val = CIL_KEY_HANDLEUNKNOWN_ALLOW;
   1093 		break;
   1094 	case SEPOL_DENY_UNKNOWN:
   1095 		val = CIL_KEY_HANDLEUNKNOWN_DENY;
   1096 		break;
   1097 	case SEPOL_REJECT_UNKNOWN:
   1098 		val = CIL_KEY_HANDLEUNKNOWN_REJECT;
   1099 		break;
   1100 	default:
   1101 		cil_log(CIL_ERR, "Unknown handleunknown value: %d.\n",
   1102 			handunknown->handle_unknown);
   1103 		rc = SEPOL_ERR;
   1104 		goto exit;
   1105 		break;
   1106 	}
   1107 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_HANDLEUNKNOWN, val);
   1108 exit:
   1109 	return rc;
   1110 }
   1111 
   1112 static int cil_write_mls(struct cil_tree_node *node, FILE *cil_out) {
   1113 	int rc = SEPOL_OK;
   1114 	struct cil_mls *mls = (struct cil_mls *)node->data;
   1115 	char *val = NULL;
   1116 	switch (mls->value) {
   1117 	case CIL_TRUE:
   1118 		val = CIL_KEY_CONDTRUE;
   1119 		break;
   1120 	case CIL_FALSE:
   1121 		val = CIL_KEY_CONDFALSE;
   1122 		break;
   1123 	default:
   1124 		cil_log(CIL_ERR, "Unknown mls value: %d.\n", mls->value);
   1125 		rc = SEPOL_ERR;
   1126 		goto exit;
   1127 		break;
   1128 	}
   1129 	fprintf(cil_out, "(%s %s)\n", CIL_KEY_MLS, val);
   1130 exit:
   1131 	return rc;
   1132 }
   1133 
   1134 static int __cil_write_first_child_helper(struct cil_tree_node *node, void *extra_args)
   1135 {
   1136 	int rc = SEPOL_ERR;
   1137 	struct cil_args_write *args = (struct cil_args_write *) extra_args;
   1138 	FILE *cil_out = NULL;
   1139 
   1140 	if (node == NULL || extra_args == NULL) {
   1141 		goto exit;
   1142 	}
   1143 
   1144 	cil_out = args->cil_out;
   1145 
   1146 	if (node->parent && node->parent->flavor != CIL_ROOT && node->parent->flavor != CIL_SRC_INFO)
   1147 		fprintf(cil_out,"(");
   1148 	rc = SEPOL_OK;
   1149 exit:
   1150 	return rc;
   1151 }
   1152 
   1153 static int __cil_write_node_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args)
   1154 {
   1155 	int rc = SEPOL_OK;
   1156 	struct cil_args_write *args = NULL;
   1157 	FILE *cil_out = NULL;
   1158 
   1159 	if (node == NULL || extra_args == NULL) {
   1160 		goto exit;
   1161 	}
   1162 
   1163 	args = extra_args;
   1164 	cil_out = args->cil_out;
   1165 
   1166 	switch (node->flavor) {
   1167 	case CIL_BLOCK:
   1168 		rc = cil_write_unsupported("CIL_BLOCK");
   1169 		break;
   1170 	case CIL_BLOCKABSTRACT:
   1171 		rc = cil_write_unsupported("CIL_BLOCKABSTRACT");
   1172 		break;
   1173 	case CIL_BLOCKINHERIT:
   1174 		rc = cil_write_unsupported("CIL_BLOCKINHERIT");
   1175 		break;
   1176 	case CIL_IN:
   1177 		rc = cil_write_unsupported("CIL_IN");
   1178 		break;
   1179 	case CIL_POLICYCAP:
   1180 		cil_write_policycap(node, cil_out);
   1181 		break;
   1182 	case CIL_PERM:
   1183 		rc = cil_write_perm(node, cil_out);
   1184 		break;
   1185 	case CIL_MAP_PERM:
   1186 		rc = cil_write_unsupported("CIL_MAP_PERM");
   1187 		break;
   1188 	case CIL_CLASSMAPPING:
   1189 		rc = cil_write_unsupported("CIL_CLASSMAPPING");
   1190 		break;
   1191 	case CIL_CLASS:
   1192 		rc = cil_write_class(node, finished, extra_args);
   1193 		break;
   1194 	case CIL_COMMON:
   1195 		rc = cil_write_class(node, finished, extra_args);
   1196 		break;
   1197 	case CIL_MAP_CLASS:
   1198 		rc = cil_write_unsupported("CIL_MAP_CLASS");
   1199 		break;
   1200 	case CIL_CLASSORDER:
   1201 		rc = cil_write_classorder(node, cil_out);
   1202 		break;
   1203 	case CIL_CLASSPERMISSION:
   1204 		rc = cil_write_unsupported("CIL_CLASSPERMISSION");
   1205 		break;
   1206 	case CIL_CLASSPERMISSIONSET:
   1207 		rc = cil_write_unsupported("CIL_CLASSPERMISSIONSET");
   1208 		break;
   1209 	case CIL_CLASSCOMMON:
   1210 		rc = cil_write_classcommon(node, cil_out);
   1211 		break;
   1212 	case CIL_SID:
   1213 		rc = cil_write_sid(node, cil_out);
   1214 		break;
   1215 	case CIL_SIDCONTEXT:
   1216 		rc = cil_write_sidcontext(node, cil_out);
   1217 		break;
   1218 	case CIL_SIDORDER:
   1219 		rc = cil_write_sidorder(node, cil_out);
   1220 		break;
   1221 	case CIL_USER:
   1222 		rc = cil_write_user(node, cil_out);
   1223 		break;
   1224 	case CIL_USERATTRIBUTE:
   1225 		rc = cil_write_unsupported("CIL_USERATTRIBUTE");
   1226 		break;
   1227 	case CIL_USERATTRIBUTESET:
   1228 		rc = cil_write_unsupported("CIL_USERATTRIBUTESET");
   1229 		break;
   1230 	case CIL_USERROLE:
   1231 		rc = cil_write_userrole(node, cil_out);
   1232 		break;
   1233 	case CIL_USERLEVEL:
   1234 		rc = cil_write_userlevel(node, cil_out);
   1235 		break;
   1236 	case CIL_USERRANGE:
   1237 		rc = cil_write_userrange(node, cil_out);
   1238 		break;
   1239 	case CIL_USERBOUNDS:
   1240 		rc = cil_write_unsupported("CIL_USERBOUNDS");
   1241 		break;
   1242 	case CIL_USERPREFIX:
   1243 		rc = cil_write_unsupported("CIL_USERPREFIX");
   1244 		break;
   1245 	case CIL_ROLE:
   1246 		rc = cil_write_role(node, cil_out);
   1247 		break;
   1248 	case CIL_ROLETYPE:
   1249 		rc = cil_write_roletype(node, cil_out);
   1250 		break;
   1251 	case CIL_ROLEBOUNDS:
   1252 		rc = cil_write_unsupported("CIL_ROLEBOUNDS");
   1253 		break;
   1254 	case CIL_ROLEATTRIBUTE:
   1255 		cil_write_roleattribute(node, cil_out);
   1256 		break;
   1257 	case CIL_ROLEATTRIBUTESET:
   1258 		rc = cil_write_unsupported("CIL_ROLEATTRIBUTESET");
   1259 		break;
   1260 	case CIL_ROLEALLOW:
   1261 		rc = cil_write_unsupported("CIL_ROLEALLOW");
   1262 		break;
   1263 	case CIL_TYPE:
   1264 		rc = cil_write_type(node, cil_out);
   1265 		break;
   1266 	case CIL_TYPEBOUNDS:
   1267 		rc = cil_write_unsupported("CIL_TYPEBOUNDS");
   1268 		break;
   1269 	case CIL_TYPEPERMISSIVE:
   1270 		rc = cil_write_typepermissive(node, cil_out);
   1271 		break;
   1272 	case CIL_TYPEATTRIBUTE:
   1273 		rc = cil_write_typeattribute(node, cil_out);
   1274 		break;
   1275 	case CIL_TYPEATTRIBUTESET:
   1276 		rc = cil_write_typeattributeset(node, cil_out);
   1277 		break;
   1278     case CIL_EXPANDTYPEATTRIBUTE:
   1279         rc = cil_write_expandtypeattribute(node, cil_out);
   1280         break;
   1281 	case CIL_TYPEALIAS:
   1282 		rc = cil_write_alias(node, cil_out);
   1283 		break;
   1284 	case CIL_TYPEALIASACTUAL:
   1285 		rc = cil_write_aliasactual(node, cil_out);
   1286 		break;
   1287 	case CIL_ROLETRANSITION:
   1288 		rc = cil_write_unsupported("CIL_ROLETRANSITION");
   1289 		break;
   1290 	case CIL_NAMETYPETRANSITION:
   1291 		rc = cil_write_nametypetransition(node, cil_out);
   1292 		break;
   1293 	case CIL_RANGETRANSITION:
   1294 		rc = cil_write_unsupported("CIL_RANGETRANSITION");
   1295 		break;
   1296 	case CIL_TUNABLE:
   1297 		rc = cil_write_unsupported("CIL_TUNABLE");
   1298 		break;
   1299 	case CIL_BOOL:
   1300 		rc = cil_write_unsupported("CIL_BOOL");
   1301 		break;
   1302 	case CIL_AVRULE:
   1303 	case CIL_AVRULEX:
   1304 		rc = cil_write_avrule(node, cil_out);
   1305 		break;
   1306 	case CIL_PERMISSIONX:
   1307 		rc = cil_write_unsupported("CIL_PERMISSIONX");
   1308 		break;
   1309 	case CIL_TYPE_RULE:
   1310 		cil_write_type_rule(node, cil_out);
   1311 		break;
   1312 	case CIL_SENS:
   1313 		rc = cil_write_sens(node, cil_out);
   1314 		break;
   1315 	case CIL_SENSALIAS:
   1316 		rc = cil_write_alias(node, cil_out);
   1317 		break;
   1318 	case CIL_SENSALIASACTUAL:
   1319 		rc = cil_write_aliasactual(node, cil_out);
   1320 		break;
   1321 	case CIL_CAT:
   1322 		rc = cil_write_cat(node, cil_out);
   1323 		break;
   1324 	case CIL_CATALIAS:
   1325 		rc = cil_write_alias(node, cil_out);
   1326 		break;
   1327 	case CIL_CATALIASACTUAL:
   1328 		rc = cil_write_aliasactual(node, cil_out);
   1329 		break;
   1330 	case CIL_CATSET:
   1331 		rc = cil_write_unsupported("CIL_CATSET");
   1332 		break;
   1333 	case CIL_SENSCAT:
   1334 		rc = cil_write_senscat(node, cil_out);
   1335 		break;
   1336 	case CIL_CATORDER:
   1337 		rc = cil_write_catorder(node, cil_out);
   1338 		break;
   1339 	case CIL_SENSITIVITYORDER:
   1340 		rc = cil_write_sensorder(node, cil_out);
   1341 		break;
   1342 	case CIL_LEVEL:
   1343 		rc = cil_write_unsupported("CIL_LEVEL");
   1344 		break;
   1345 	case CIL_LEVELRANGE:
   1346 		rc = cil_write_unsupported("CIL_LEVELRANGE");
   1347 		break;
   1348 	case CIL_CONTEXT:
   1349 		rc = cil_write_unsupported("CIL_CONTEXT");
   1350 		break;
   1351 	case CIL_NETIFCON:
   1352 		rc = cil_write_unsupported("CIL_NETIFCON");
   1353 		break;
   1354 	case CIL_GENFSCON:
   1355 		 rc = cil_write_genfscon(node, cil_out);
   1356 		break;
   1357 	case CIL_FILECON:
   1358 		rc = cil_write_unsupported("CIL_FILECON");
   1359 		break;
   1360 	case CIL_NODECON:
   1361 		rc = cil_write_unsupported("CIL_NODECON");
   1362 		break;
   1363 	case CIL_PORTCON:
   1364 		rc = cil_write_unsupported("CIL_PORTCON");
   1365 		break;
   1366 	case CIL_PIRQCON:
   1367 		rc = cil_write_unsupported("CIL_PIRQCON");
   1368 		break;
   1369 	case CIL_IOMEMCON:
   1370 		rc = cil_write_unsupported("CIL_IOMEMCON");
   1371 		break;
   1372 	case CIL_IOPORTCON:
   1373 		rc = cil_write_unsupported("CIL_IOPORTCON");
   1374 		break;
   1375 	case CIL_PCIDEVICECON:
   1376 		rc = cil_write_unsupported("CIL_PCIDEVICECON");
   1377 		break;
   1378 	case CIL_DEVICETREECON:
   1379 		rc = cil_write_unsupported("CIL_DEVICETREECON");
   1380 		break;
   1381 	case CIL_FSUSE:
   1382 		rc = cil_write_fsuse(node, cil_out);
   1383 		break;
   1384 	case CIL_CONSTRAIN:
   1385 		rc = cil_write_unsupported("CIL_CONSTRAIN");
   1386 		break;
   1387 	case CIL_MLSCONSTRAIN:
   1388 		rc = cil_write_constrain(node, cil_out);
   1389 		break;
   1390 	case CIL_VALIDATETRANS:
   1391 		rc = cil_write_unsupported("CIL_VALIDATETRANS");
   1392 		break;
   1393 	case CIL_MLSVALIDATETRANS:
   1394 		rc = cil_write_unsupported("CIL_MLSVALIDATETRANS");
   1395 		break;
   1396 	case CIL_CALL:
   1397 		rc = cil_write_unsupported("CIL_CALL");
   1398 		break;
   1399 	case CIL_MACRO:
   1400 		rc = cil_write_unsupported("CIL_MACRO");
   1401 		break;
   1402 	case CIL_NODE:
   1403 		rc = cil_write_unsupported("CIL_NODE");
   1404 		break;
   1405 	case CIL_OPTIONAL:
   1406 		rc = cil_write_unsupported("CIL_OPTIONAL");
   1407 		break;
   1408 	case CIL_IPADDR:
   1409 		rc = cil_write_unsupported("CIL_IPADDR");
   1410 		break;
   1411 	case CIL_CONDBLOCK:
   1412 		rc = cil_write_unsupported("CIL_CONDBLOCK");
   1413 		break;
   1414 	case CIL_BOOLEANIF:
   1415 		rc = cil_write_unsupported("CIL_BOOLEANIF");
   1416 		break;
   1417 	case CIL_TUNABLEIF:
   1418 		rc = cil_write_unsupported("CIL_TUNABLEIF");
   1419 		break;
   1420 	case CIL_DEFAULTUSER:
   1421 		rc = cil_write_unsupported("CIL_DEFAULTUSER");
   1422 		break;
   1423 	case CIL_DEFAULTROLE:
   1424 		rc = cil_write_unsupported("CIL_DEFAULTROLE");
   1425 		break;
   1426 	case CIL_DEFAULTTYPE:
   1427 		rc = cil_write_unsupported("CIL_DEFAULTTYPE");
   1428 		break;
   1429 	case CIL_DEFAULTRANGE:
   1430 		rc = cil_write_unsupported("CIL_DEFAULTRANGE");
   1431 		break;
   1432 	case CIL_SELINUXUSER:
   1433 		rc = cil_write_unsupported("CIL_SELINUXUSER");
   1434 		break;
   1435 	case CIL_SELINUXUSERDEFAULT:
   1436 		rc = cil_write_unsupported("CIL_SELINUXUSERDEFAULT");
   1437 		break;
   1438 	case CIL_HANDLEUNKNOWN:
   1439 		rc = cil_write_handleunknown(node, cil_out);
   1440 		break;
   1441 	case CIL_MLS:
   1442 		rc = cil_write_mls(node, cil_out);
   1443 		break;
   1444 	case CIL_SRC_INFO:
   1445 		break;
   1446 	case CIL_NONE:
   1447 		// TODO: add proper removal support
   1448 		*finished = CIL_TREE_SKIP_HEAD;
   1449 		break;
   1450 	default:
   1451 		cil_log(CIL_ERR, "Unknown AST flavor: %d.\n", node->flavor);
   1452 		rc = SEPOL_ERR;
   1453 		goto exit;
   1454 		break;
   1455 	}
   1456 exit:
   1457 	return rc;
   1458 }
   1459 
   1460 static int __cil_write_last_child_helper(struct cil_tree_node *node, void *extra_args)
   1461 {
   1462 	int rc = SEPOL_ERR;
   1463 	struct cil_args_write *args = NULL;
   1464 	FILE *cil_out = NULL;
   1465 
   1466 	if (node == NULL || extra_args == NULL) {
   1467 		goto exit;
   1468 	}
   1469 
   1470 	args = extra_args;
   1471 	cil_out = args->cil_out;
   1472 
   1473 	if (node->parent && node->parent->flavor != CIL_ROOT && node->parent->flavor != CIL_SRC_INFO) {
   1474 		fprintf(cil_out,")");
   1475 	}
   1476 	rc = SEPOL_OK;
   1477 exit:
   1478 	return rc;
   1479 }
   1480 
   1481 /* main exported function */
   1482 int cil_write_ast(struct cil_db *db, const char* path) {
   1483 	int rc = SEPOL_ERR;
   1484 	struct cil_args_write extra_args;
   1485 	FILE *cil_out = NULL;
   1486 
   1487 	cil_out = fopen(path, "we");
   1488 	if (cil_out == NULL) {
   1489 		cil_log(CIL_ERR, "Failure opening output file for writing AST\n");
   1490 		rc = SEPOL_ERR;
   1491 		goto exit;
   1492 	}
   1493 
   1494 	extra_args.cil_out = cil_out;
   1495 	extra_args.db = db;
   1496 	rc = cil_tree_walk(db->ast->root, __cil_write_node_helper,
   1497 			   __cil_write_first_child_helper,
   1498 			   __cil_write_last_child_helper,
   1499 			   &extra_args);
   1500 	if (rc != SEPOL_OK) {
   1501 		cil_log(CIL_INFO, "cil_tree_walk failed, rc: %d\n", rc);
   1502 		goto exit;
   1503 	}
   1504 
   1505 exit:
   1506 	fclose(cil_out);
   1507 	cil_out = NULL;
   1508 	return rc;
   1509 }
   1510