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 	mode_t mask;
123 
124 	if (!dbase_llist_is_modified(&dbase->llist))
125 		return STATUS_SUCCESS;
126 
127 	fname = dbase->path[handle->is_in_transaction];
128 
129 	mask = umask(0077);
130 	str = fopen(fname, "w");
131 	umask(mask);
132 	if (!str) {
133 		ERR(handle, "could not open %s for writing: %s",
134 		    fname, strerror(errno));
135 		goto err;
136 	}
137 	__fsetlocking(str, FSETLOCKING_BYCALLER);
138 
139 	if (fprintf(str, "# This file is auto-generated by libsemanage\n"
140 		    "# Do not edit directly.\n\n") < 0) {
141 
142 		ERR(handle, "could not write file header for %s", fname);
143 		goto err;
144 	}
145 
146 	for (ptr = dbase->llist.cache_tail; ptr != NULL; ptr = ptr->prev) {
147 		if (rftable->print(handle, ptr->data, str) < 0)
148 			goto err;
149 	}
150 
151 	dbase_llist_set_modified(&dbase->llist, 0);
152 	fclose(str);
153 	return STATUS_SUCCESS;
154 
155       err:
156 	if (str != NULL)
157 		fclose(str);
158 
159 	ERR(handle, "could not flush database to file");
160 	return STATUS_ERR;
161 }
162 
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)163 int dbase_file_init(semanage_handle_t * handle,
164 		    const char *path_ro,
165 		    const char *path_rw,
166 		    record_table_t * rtable,
167 		    record_file_table_t * rftable, dbase_file_t ** dbase)
168 {
169 
170 	dbase_file_t *tmp_dbase = (dbase_file_t *) malloc(sizeof(dbase_file_t));
171 
172 	if (!tmp_dbase)
173 		goto omem;
174 
175 	tmp_dbase->path[0] = path_ro;
176 	tmp_dbase->path[1] = path_rw;
177 	tmp_dbase->rftable = rftable;
178 	dbase_llist_init(&tmp_dbase->llist, rtable, &SEMANAGE_FILE_DTABLE);
179 
180 	*dbase = tmp_dbase;
181 
182 	return STATUS_SUCCESS;
183 
184       omem:
185 	ERR(handle, "out of memory, could not initialize file database");
186 	free(tmp_dbase);
187 	return STATUS_ERR;
188 }
189 
190 /* Release dbase resources */
dbase_file_release(dbase_file_t * dbase)191 void dbase_file_release(dbase_file_t * dbase)
192 {
193 
194 	dbase_llist_drop_cache(&dbase->llist);
195 	free(dbase);
196 }
197 
198 /* FILE dbase - method table implementation */
199 dbase_table_t SEMANAGE_FILE_DTABLE = {
200 
201 	/* Cache/Transactions */
202 	.cache = dbase_file_cache,
203 	.drop_cache = (void *)dbase_llist_drop_cache,
204 	.flush = dbase_file_flush,
205 	.is_modified = (void *)dbase_llist_is_modified,
206 
207 	/* Database API */
208 	.iterate = (void *)dbase_llist_iterate,
209 	.exists = (void *)dbase_llist_exists,
210 	.list = (void *)dbase_llist_list,
211 	.add = (void *)dbase_llist_add,
212 	.set = (void *)dbase_llist_set,
213 	.del = (void *)dbase_llist_del,
214 	.clear = (void *)dbase_llist_clear,
215 	.modify = (void *)dbase_llist_modify,
216 	.query = (void *)dbase_llist_query,
217 	.count = (void *)dbase_llist_count,
218 
219 	/* Polymorphism */
220 	.get_rtable = (void *)dbase_llist_get_rtable
221 };
222