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
8struct dbase_file;
9typedef 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 */
26struct 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
40static 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 */
114static 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
160int 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 */
188void 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 */
196dbase_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