/src/qubes-os/qubes-core-qubesdb/daemon/db-core.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include <stdlib.h> |
2 | | #include <stdio.h> |
3 | | #include <string.h> |
4 | | #ifndef WIN32 |
5 | | #include <unistd.h> |
6 | | #else |
7 | | #define strdup _strdup |
8 | | #include <windows.h> |
9 | | #include <strsafe.h> |
10 | | #endif |
11 | | #include "buffer.h" |
12 | | |
13 | | #include <qubesdb.h> |
14 | | #include "qubesdb_internal.h" |
15 | | |
16 | 26 | struct qubesdb *qubesdb_init(send_watch_notify_t send_notify_func) { |
17 | 26 | struct qubesdb *db; |
18 | | |
19 | 26 | db = malloc(sizeof (*db)); |
20 | 26 | if (!db) { |
21 | 0 | return NULL; |
22 | 0 | } |
23 | | |
24 | 26 | db->entries = malloc(sizeof(*db->entries)); |
25 | 26 | if (!db->entries) { |
26 | 0 | free(db); |
27 | 0 | return NULL; |
28 | 0 | } |
29 | | |
30 | 26 | db->entries->prev = db->entries; |
31 | 26 | db->entries->next = db->entries; |
32 | 26 | db->entries->path[0] = '/'; |
33 | 26 | db->entries->path[1] = '\0'; |
34 | 26 | db->entries->value = strdup(""); |
35 | 26 | db->entries->value_len = 0; |
36 | | |
37 | 26 | db->watches = NULL; |
38 | | |
39 | 26 | db->send_watch_notify = send_notify_func; |
40 | | |
41 | 26 | return db; |
42 | 26 | } |
43 | | |
44 | 26 | void qubesdb_destroy(struct qubesdb *db) { |
45 | 26 | struct qubesdb_entry *entry; |
46 | 26 | struct qubesdb_entry *tmp_entry; |
47 | | |
48 | 26 | entry = db->entries->next; |
49 | | |
50 | 26 | while (entry != db->entries) { |
51 | 0 | tmp_entry = entry; |
52 | 0 | entry = entry->next; |
53 | |
|
54 | 0 | tmp_entry->prev->next = tmp_entry->next; |
55 | 0 | tmp_entry->next->prev = tmp_entry->prev; |
56 | 0 | buffer_secure_zero(tmp_entry->value, tmp_entry->value_len); |
57 | 0 | free(tmp_entry->value); |
58 | 0 | free(tmp_entry); |
59 | 0 | } |
60 | | |
61 | 26 | buffer_secure_zero(db->entries->value, db->entries->value_len); |
62 | 26 | free(db->entries->value); |
63 | 26 | free(db->entries); |
64 | | // TODO: free watches |
65 | 26 | free(db); |
66 | 26 | } |
67 | | |
68 | 0 | struct qubesdb_entry *qubesdb_search(struct qubesdb *db, char *path, int exact) { |
69 | 0 | struct qubesdb_entry *entry; |
70 | 0 | int diff; |
71 | |
|
72 | 0 | entry = db->entries->next; |
73 | |
|
74 | 0 | while (entry != db->entries) { |
75 | 0 | diff = strcmp(path, entry->path); |
76 | 0 | if (!diff) |
77 | 0 | return entry; |
78 | 0 | if (diff < 0) { |
79 | 0 | if (exact) |
80 | 0 | return NULL; |
81 | 0 | else |
82 | 0 | return entry; /* FIXME: entry->prev? */ |
83 | 0 | } |
84 | 0 | entry = entry->next; |
85 | 0 | } |
86 | 0 | if (exact) |
87 | 0 | return NULL; |
88 | 0 | else |
89 | 0 | return entry; |
90 | 0 | } |
91 | | |
92 | 0 | struct qubesdb_entry *qubesdb_insert(struct qubesdb *db, char *path) { |
93 | 0 | struct qubesdb_entry *entry, *new_entry; |
94 | |
|
95 | 0 | if (!path) |
96 | 0 | return NULL; |
97 | | /* path must begin with '/' |
98 | | * Note: this also guarantee strlen(path) > 0 */ |
99 | 0 | if (path[0] != '/') |
100 | 0 | return NULL; |
101 | | /* path cannot end with '/' */ |
102 | 0 | if (path[strlen(path)-1] == '/') |
103 | 0 | return NULL; |
104 | 0 | if (strlen(path) >= QDB_MAX_PATH) |
105 | 0 | return NULL; |
106 | 0 | entry = qubesdb_search(db, path, 0); |
107 | 0 | if (!entry) |
108 | | /* shouldn't happen */ |
109 | 0 | return NULL; |
110 | 0 | if (strcmp(entry->path, path)==0) { |
111 | | /* entry already exists */ |
112 | 0 | return entry; |
113 | 0 | } else { |
114 | 0 | new_entry = calloc(1,sizeof(*new_entry)); |
115 | 0 | if (!new_entry) |
116 | 0 | return NULL; |
117 | 0 | #ifndef WIN32 |
118 | 0 | strncpy(new_entry->path, path, QDB_MAX_PATH - 1); |
119 | | #else |
120 | | StringCbCopyA(new_entry->path, sizeof(new_entry->path), path); |
121 | | #endif |
122 | |
|
123 | 0 | new_entry->value = NULL; |
124 | 0 | new_entry->next = entry; |
125 | 0 | new_entry->prev = entry->prev; |
126 | 0 | entry->prev->next = new_entry; |
127 | 0 | entry->prev = new_entry; |
128 | 0 | return new_entry; |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | 0 | int qubesdb_write(struct qubesdb *db, char *path, char *data, int data_len) { |
133 | 0 | struct qubesdb_entry *db_entry; |
134 | |
|
135 | 0 | db_entry = qubesdb_insert(db, path); |
136 | 0 | if (!db_entry) { |
137 | 0 | return 0; |
138 | 0 | } |
139 | 0 | if (db_entry->value) { |
140 | 0 | buffer_secure_zero(db_entry->value, db_entry->value_len); |
141 | 0 | free(db_entry->value); |
142 | 0 | } |
143 | 0 | if (data_len) { |
144 | 0 | db_entry->value = malloc(data_len); |
145 | 0 | memcpy(db_entry->value, data, data_len); |
146 | 0 | } else { |
147 | 0 | db_entry->value = strdup(""); |
148 | 0 | } |
149 | 0 | if (!db_entry->value) { |
150 | 0 | fputs("malloc(3) failed\n", stderr); |
151 | 0 | _exit(1); |
152 | 0 | } |
153 | 0 | db_entry->value_len = data_len; |
154 | 0 | return 1; |
155 | 0 | } |
156 | | |
157 | 0 | int qubesdb_remove(struct qubesdb *db, char *path) { |
158 | 0 | struct qubesdb_entry *entry; |
159 | 0 | struct qubesdb_entry *tmp_entry; |
160 | 0 | int remove_dir, cmp_len; |
161 | 0 | int anything_removed = 0; |
162 | |
|
163 | 0 | cmp_len = (int)strlen(path); |
164 | | /* check if requested whole dir remove */ |
165 | 0 | if (path[cmp_len-1] == '/') { |
166 | 0 | remove_dir = 1; |
167 | 0 | } else { |
168 | | /* compare with trailing \0 for exact match */ |
169 | 0 | cmp_len++; |
170 | 0 | remove_dir = 0; |
171 | 0 | } |
172 | |
|
173 | 0 | entry = qubesdb_search(db, path, !remove_dir); |
174 | 0 | if (!entry) |
175 | 0 | return 0; |
176 | 0 | while (entry->next != entry && strncmp(entry->path, path, cmp_len) == 0) { |
177 | 0 | tmp_entry = entry; |
178 | 0 | entry = entry->next; |
179 | |
|
180 | 0 | tmp_entry->prev->next = tmp_entry->next; |
181 | 0 | tmp_entry->next->prev = tmp_entry->prev; |
182 | 0 | buffer_secure_zero(tmp_entry->value, tmp_entry->value_len); |
183 | 0 | free(tmp_entry->value); |
184 | 0 | free(tmp_entry); |
185 | 0 | anything_removed = 1; |
186 | 0 | } |
187 | 0 | return anything_removed; |
188 | 0 | } |
189 | | |
190 | | int qubesdb_add_watch(struct qubesdb *db, char *path, |
191 | 0 | struct client *client) { |
192 | 0 | struct qubesdb_watch *new_watch; |
193 | |
|
194 | 0 | new_watch = calloc(1,sizeof(*new_watch)); |
195 | 0 | if (!new_watch) { |
196 | 0 | return 0; |
197 | 0 | } |
198 | 0 | new_watch->next = db->watches; |
199 | 0 | db->watches = new_watch; |
200 | 0 | new_watch->client = client; |
201 | | /* path already verified by caller */ |
202 | 0 | #ifndef WIN32 |
203 | 0 | strncpy(new_watch->path, path, QDB_MAX_PATH - 1); |
204 | | #else |
205 | | StringCbCopyA(new_watch->path, sizeof(new_watch->path), path); |
206 | | #endif |
207 | 0 | new_watch->cmp_len = (int)strlen(path) + 1; |
208 | 0 | if (new_watch->cmp_len >= 2 && path[new_watch->cmp_len-2] == '/') |
209 | 0 | new_watch->cmp_len--; |
210 | 0 | return 1; |
211 | 0 | } |
212 | | |
213 | | int qubesdb_remove_watch(struct qubesdb *db, char *path, |
214 | 0 | struct client *client) { |
215 | 0 | struct qubesdb_watch *watch, *prev_watch, *tmp_watch; |
216 | 0 | int anything_removed = 0; |
217 | |
|
218 | 0 | watch = db->watches; |
219 | 0 | prev_watch = NULL; |
220 | 0 | while (watch) { |
221 | 0 | if (watch->client == client && |
222 | 0 | (!path || strcmp(watch->path, path) == 0)) { |
223 | 0 | if (prev_watch) |
224 | 0 | prev_watch->next = watch->next; |
225 | 0 | else |
226 | 0 | db->watches = watch->next; |
227 | 0 | tmp_watch = watch; |
228 | 0 | watch = watch->next; |
229 | 0 | free(tmp_watch); |
230 | 0 | anything_removed = 1; |
231 | 0 | if (path) |
232 | | /* remove only one matching entry */ |
233 | 0 | break; |
234 | 0 | else |
235 | | /* remove all entries for given client */ |
236 | 0 | continue; |
237 | 0 | } |
238 | 0 | prev_watch = watch; |
239 | 0 | watch = watch->next; |
240 | 0 | } |
241 | 0 | return anything_removed; |
242 | 0 | } |
243 | | |
244 | 0 | int qubesdb_fire_watches(struct qubesdb *db, char *path) { |
245 | 0 | struct qubesdb_watch *watch; |
246 | 0 | struct qdb_hdr hdr; |
247 | 0 | int fired_anything = 0; |
248 | |
|
249 | 0 | watch = db->watches; |
250 | 0 | while (watch) { |
251 | 0 | if (strncmp(watch->path, path, watch->cmp_len) == 0) { |
252 | 0 | hdr.type = QDB_RESP_WATCH; |
253 | 0 | #ifndef WIN32 |
254 | 0 | strncpy(hdr.path, path, sizeof(hdr.path) - 1); |
255 | 0 | hdr.path[sizeof(hdr.path) - 1] = '\0'; |
256 | | #else |
257 | | StringCbCopyA(hdr.path, sizeof(hdr.path), path); |
258 | | #endif |
259 | 0 | hdr.data_len = 0; |
260 | |
|
261 | 0 | #ifndef WIN32 |
262 | 0 | if (db->send_watch_notify && db->send_watch_notify(watch->client, (char*)&hdr, sizeof(hdr)) < 0) |
263 | 0 | fprintf(stderr, "Failed to fire watch on %s for client " CLIENT_SOCKET_FORMAT "\n", |
264 | 0 | path, watch->client->fd); |
265 | | #else |
266 | | if (db->send_watch_notify && db->send_watch_notify(watch->client, (char*)&hdr, sizeof(hdr), db->pipe_server) < 0) |
267 | | fprintf(stderr, "Failed to fire watch on %s for client " CLIENT_SOCKET_FORMAT "\n", |
268 | | path, watch->client->id); |
269 | | #endif |
270 | 0 | fired_anything = 1; |
271 | 0 | } |
272 | 0 | watch = watch->next; |
273 | 0 | } |
274 | 0 | return fired_anything; |
275 | 0 | } |