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 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 */ 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 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 */ 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