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