Coverage Report

Created: 2023-09-25 06:33

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