1 /* Copyright (C) 2005 Red Hat, Inc. */
2 
3 /* Object: dbase_file_t (File)
4  * Extends: dbase_llist_t (Linked List)
5  * Implements: dbase_t (Database)
6  */
7 
8 struct dbase_file;
9 typedef struct dbase_file dbase_t;
10 #define DBASE_DEFINED
11 
12 #include <stdlib.h>
13 #include <stddef.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <stdio.h>
17 #include <stdio_ext.h>
18 #include "debug.h"
19 #include "handle.h"
20 #include "parse_utils.h"
21 #include "database_file.h"
22 #include "database_llist.h"
23 #include "semanage_store.h"
24 
25 /* FILE dbase */
26 struct dbase_file {
27 
28 	/* Parent object - must always be
29 	 * the first field - here we are using
30 	 * a linked list to store the records */
31 	dbase_llist_t llist;
32 
33 	/* Backing path for read-only[0] and transaction[1] */
34 	const char *path[2];
35 
36 	/* FILE extension */
37 	record_file_table_t *rftable;
38 };
39 
dbase_file_cache(semanage_handle_t * handle,dbase_file_t * dbase)40 static int dbase_file_cache(semanage_handle_t * handle, dbase_file_t * dbase)
41 {
42 
43 	record_table_t *rtable = dbase_llist_get_rtable(&dbase->llist);
44 	record_file_table_t *rftable = dbase->rftable;
45 
46 	record_t *process_record = NULL;
47 	int pstatus = STATUS_SUCCESS;
48 
49 	parse_info_t *parse_info = NULL;
50 	const char *fname = NULL;
51 
52 	/* Already cached */
53 	if (!dbase_llist_needs_resync(handle, &dbase->llist))
54 		return STATUS_SUCCESS;
55 
56 	/* Update cache serial */
57 	dbase_llist_cache_init(&dbase->llist);
58 	if (dbase_llist_set_serial(handle, &dbase->llist) < 0)
59 		goto err;
60 
61 	fname = dbase->path[handle->is_in_transaction];
62 
63 	if (parse_init(handle, fname, NULL, &parse_info) < 0)
64 		goto err;
65 
66 	if (parse_open(handle, parse_info) < 0)
67 		goto err;
68 
69 	/* Main processing loop */
70 	do {
71 
72 		/* Create record */
73 		if (rtable->create(handle, &process_record) < 0)
74 			goto err;
75 
76 		/* Parse record */
77 		pstatus = rftable->parse(handle, parse_info, process_record);
78 
79 		/* Parse error */
80 		if (pstatus < 0)
81 			goto err;
82 
83 		/* End of file */
84 		else if (pstatus == STATUS_NODATA)
85 			break;
86 
87 		/* Prepend to cache */
88 		if (dbase_llist_cache_prepend(handle, &dbase->llist,
89 					      process_record) < 0)
90 			goto err;
91 
92 		rtable->free(process_record);
93 		process_record = NULL;
94 
95 	} while (pstatus != STATUS_NODATA);
96 
97 	rtable->free(process_record);
98 	parse_close(parse_info);
99 	parse_release(parse_info);
100 	return STATUS_SUCCESS;
101 
102       err:
103 	ERR(handle, "could not cache file database");
104 	rtable->free(process_record);
105 	if (parse_info) {
106 		parse_close(parse_info);
107 		parse_release(parse_info);
108 	}
109 	dbase_llist_drop_cache(&dbase->llist);
110 	return STATUS_ERR;
111 }
112 
113 /* Flush database to file */
dbase_file_flush(semanage_handle_t * handle,dbase_file_t * dbase)114 static int dbase_file_flush(semanage_handle_t * handle, dbase_file_t * dbase)
115 {
116 
117 	record_file_table_t *rftable = dbase->rftable;
118 
119 	cache_entry_t *ptr;
120 	const char *fname = NULL;
121 	FILE *str = NULL;
122 
123 	if (!dbase_llist_is_modified(&dbase->llist))
124 		return STATUS_SUCCESS;
125 
126 	fname = dbase->path[handle->is_in_transaction];
127 
128 	str = fopen(fname, "w");
129 	if (!str) {
130 		ERR(handle, "could not open %s for writing: %s",
131 		    fname, strerror(errno));
132 		goto err;
133 	}
134 	__fsetlocking(str, FSETLOCKING_BYCALLER);
135 
136 	if (fprintf(str, "# This file is auto-generated by libsemanage\n"
137 		    "# Do not edit directly.\n\n") < 0) {
138 
139 		ERR(handle, "could not write file header for %s", fname);
140 		goto err;
141 	}
142 
143 	for (ptr = dbase->llist.cache_tail; ptr != NULL; ptr = ptr->prev) {
144 		if (rftable->print(handle, ptr->data, str) < 0)
145 			goto err;
146 	}
147 
148 	dbase_llist_set_modified(&dbase->llist, 0);
149 	fclose(str);
150 	return STATUS_SUCCESS;
151 
152       err:
153 	if (str != NULL)
154 		fclose(str);
155 
156 	ERR(handle, "could not flush database to file");
157 	return STATUS_ERR;
158 }
159 
dbase_file_init(semanage_handle_t * handle,const char * path_ro,const char * path_rw,record_table_t * rtable,record_file_table_t * rftable,dbase_file_t ** dbase)160 int dbase_file_init(semanage_handle_t * handle,
161 		    const char *path_ro,
162 		    const char *path_rw,
163 		    record_table_t * rtable,
164 		    record_file_table_t * rftable, dbase_file_t ** dbase)
165 {
166 
167 	dbase_file_t *tmp_dbase = (dbase_file_t *) malloc(sizeof(dbase_file_t));
168 
169 	if (!tmp_dbase)
170 		goto omem;
171 
172 	tmp_dbase->path[0] = path_ro;
173 	tmp_dbase->path[1] = path_rw;
174 	tmp_dbase->rftable = rftable;
175 	dbase_llist_init(&tmp_dbase->llist, rtable, &SEMANAGE_FILE_DTABLE);
176 
177 	*dbase = tmp_dbase;
178 
179 	return STATUS_SUCCESS;
180 
181       omem:
182 	ERR(handle, "out of memory, could not initialize file database");
183 	free(tmp_dbase);
184 	return STATUS_ERR;
185 }
186 
187 /* Release dbase resources */
dbase_file_release(dbase_file_t * dbase)188 void dbase_file_release(dbase_file_t * dbase)
189 {
190 
191 	dbase_llist_drop_cache(&dbase->llist);
192 	free(dbase);
193 }
194 
195 /* FILE dbase - method table implementation */
196 dbase_table_t SEMANAGE_FILE_DTABLE = {
197 
198 	/* Cache/Transactions */
199 	.cache = dbase_file_cache,
200 	.drop_cache = (void *)dbase_llist_drop_cache,
201 	.flush = dbase_file_flush,
202 	.is_modified = (void *)dbase_llist_is_modified,
203 
204 	/* Database API */
205 	.iterate = (void *)dbase_llist_iterate,
206 	.exists = (void *)dbase_llist_exists,
207 	.list = (void *)dbase_llist_list,
208 	.add = (void *)dbase_llist_add,
209 	.set = (void *)dbase_llist_set,
210 	.del = (void *)dbase_llist_del,
211 	.clear = (void *)dbase_llist_clear,
212 	.modify = (void *)dbase_llist_modify,
213 	.query = (void *)dbase_llist_query,
214 	.count = (void *)dbase_llist_count,
215 
216 	/* Polymorphism */
217 	.get_rtable = (void *)dbase_llist_get_rtable
218 };
219