ctdb-common: Add traverse_update function to db_hash abstraction
[samba.git] / ctdb / common / db_hash.c
1 /*
2    Using tdb as a hash table
3
4    Copyright (C) Amitay Isaacs  2015
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "replace.h"
21 #include "system/filesys.h"
22
23 #include <talloc.h>
24 #include <tdb.h>
25
26 #include "common/db_hash.h"
27
28 struct db_hash_context {
29         struct tdb_context *db;
30 };
31
32
33 static int db_hash_destructor(struct db_hash_context *dh)
34 {
35         if (dh->db != NULL) {
36                 tdb_close(dh->db);
37                 dh->db = NULL;
38         }
39         return 0;
40 }
41
42 int db_hash_init(TALLOC_CTX *mem_ctx, const char *name, int hash_size,
43                  enum db_hash_type type, struct db_hash_context **result)
44 {
45         struct db_hash_context *dh;
46         int tdb_flags = TDB_INTERNAL | TDB_DISALLOW_NESTING;
47
48         dh = talloc_zero(mem_ctx, struct db_hash_context);
49         if (dh == NULL) {
50                 return ENOMEM;
51         }
52
53         if (type == DB_HASH_COMPLEX) {
54                 tdb_flags |= TDB_INCOMPATIBLE_HASH;
55         }
56
57         dh->db = tdb_open(name, hash_size, tdb_flags, O_RDWR|O_CREAT, 0);
58         if (dh->db == NULL) {
59                 talloc_free(dh);
60                 return ENOMEM;
61         }
62
63         talloc_set_destructor(dh, db_hash_destructor);
64         *result = dh;
65         return 0;
66 }
67
68 static int db_hash_map_tdb_error(struct db_hash_context *dh)
69 {
70         enum TDB_ERROR tdb_err;
71         int ret;
72
73         tdb_err = tdb_error(dh->db);
74         switch (tdb_err) {
75                 case TDB_SUCCESS:
76                         ret = 0; break;
77                 case TDB_ERR_OOM:
78                         ret = ENOMEM; break;
79                 case TDB_ERR_EXISTS:
80                         ret = EEXIST; break;
81                 case TDB_ERR_NOEXIST:
82                         ret = ENOENT; break;
83                 case TDB_ERR_EINVAL:
84                         ret = EINVAL; break;
85                 default:
86                         ret = EIO; break;
87         }
88         return ret;
89 }
90
91 int db_hash_insert(struct db_hash_context *dh, uint8_t *keybuf, size_t keylen,
92                    uint8_t *databuf, size_t datalen)
93 {
94         TDB_DATA key, data;
95         int ret;
96
97         if (dh == NULL) {
98                 return EINVAL;
99         }
100
101         key.dptr = keybuf;
102         key.dsize = keylen;
103
104         data.dptr = databuf;
105         data.dsize = datalen;
106
107         ret = tdb_store(dh->db, key, data, TDB_INSERT);
108         if (ret != 0) {
109                 ret = db_hash_map_tdb_error(dh);
110         }
111         return ret;
112 }
113
114 int db_hash_add(struct db_hash_context *dh, uint8_t *keybuf, size_t keylen,
115                 uint8_t *databuf, size_t datalen)
116 {
117         TDB_DATA key, data;
118         int ret;
119
120         if (dh == NULL) {
121                 return EINVAL;
122         }
123
124         key.dptr = keybuf;
125         key.dsize = keylen;
126
127         data.dptr = databuf;
128         data.dsize = datalen;
129
130         ret = tdb_store(dh->db, key, data, TDB_REPLACE);
131         if (ret != 0) {
132                 ret = db_hash_map_tdb_error(dh);
133         }
134         return ret;
135 }
136
137 int db_hash_delete(struct db_hash_context *dh, uint8_t *keybuf, size_t keylen)
138 {
139         TDB_DATA key;
140         int ret;
141
142         key.dptr = keybuf;
143         key.dsize = keylen;
144
145         if (dh == NULL) {
146                 return EINVAL;
147         }
148
149         ret = tdb_delete(dh->db, key);
150         if (ret != 0) {
151                 ret = db_hash_map_tdb_error(dh);
152         }
153         return ret;
154 }
155
156 struct db_hash_fetch_state {
157         db_hash_record_parser_fn parser;
158         void *private_data;
159 };
160
161 static int db_hash_fetch_parser(TDB_DATA key, TDB_DATA data, void *private_data)
162 {
163         struct db_hash_fetch_state *state =
164                 (struct db_hash_fetch_state *)private_data;
165         int ret;
166
167         ret = state->parser(key.dptr, key.dsize, data.dptr, data.dsize,
168                             state->private_data);
169         return ret;
170 }
171
172 int db_hash_fetch(struct db_hash_context *dh, uint8_t *keybuf, size_t keylen,
173                   db_hash_record_parser_fn parser, void *private_data)
174 {
175         struct db_hash_fetch_state state;
176         TDB_DATA key;
177         int ret;
178
179         if (dh == NULL || parser == NULL) {
180                 return EINVAL;
181         }
182
183         state.parser = parser;
184         state.private_data = private_data;
185
186         key.dptr = keybuf;
187         key.dsize = keylen;
188
189         ret = tdb_parse_record(dh->db, key, db_hash_fetch_parser, &state);
190         if (ret == -1) {
191                 return ENOENT;
192         }
193         return ret;
194 }
195
196 int db_hash_exists(struct db_hash_context *dh, uint8_t *keybuf, size_t keylen)
197 {
198         TDB_DATA key;
199         int ret;
200
201         if (dh == NULL) {
202                 return EINVAL;
203         }
204
205         key.dptr = keybuf;
206         key.dsize = keylen;
207
208         ret = tdb_exists(dh->db, key);
209         if (ret == 1) {
210                 /* Key found */
211                 ret = 0;
212         } else {
213                 ret = db_hash_map_tdb_error(dh);
214                 if (ret == 0) {
215                         ret = ENOENT;
216                 }
217         }
218         return ret;
219 }
220
221 struct db_hash_traverse_state {
222         db_hash_record_parser_fn parser;
223         void *private_data;
224 };
225
226 static int db_hash_traverse_parser(struct tdb_context *tdb,
227                                    TDB_DATA key, TDB_DATA data,
228                                    void *private_data)
229 {
230         struct db_hash_traverse_state *state =
231                 (struct db_hash_traverse_state *)private_data;
232
233         return state->parser(key.dptr, key.dsize, data.dptr, data.dsize,
234                              state->private_data);
235 }
236
237 int db_hash_traverse(struct db_hash_context *dh,
238                      db_hash_record_parser_fn parser, void *private_data,
239                      int *count)
240 {
241         struct db_hash_traverse_state state;
242         int ret;
243
244         if (dh == NULL) {
245                 return EINVAL;
246         }
247
248         /* Special case, for counting records */
249         if (parser == NULL) {
250                 ret = tdb_traverse_read(dh->db, NULL, NULL);
251         } else {
252                 state.parser = parser;
253                 state.private_data = private_data;
254
255                 ret = tdb_traverse_read(dh->db, db_hash_traverse_parser, &state);
256         }
257
258         if (ret == -1) {
259                 ret = db_hash_map_tdb_error(dh);
260         } else {
261                 if (count != NULL) {
262                         *count = ret;
263                 }
264                 ret = 0;
265         }
266
267         return ret;
268 }
269
270 int db_hash_traverse_update(struct db_hash_context *dh,
271                             db_hash_record_parser_fn parser,
272                             void *private_data, int *count)
273 {
274         struct db_hash_traverse_state state;
275         int ret;
276
277         if (dh == NULL || parser == NULL) {
278                 return EINVAL;
279         }
280
281         state.parser = parser;
282         state.private_data = private_data;
283
284         ret = tdb_traverse(dh->db, db_hash_traverse_parser, &state);
285         if (ret == -1) {
286                 ret = db_hash_map_tdb_error(dh);
287         } else {
288                 if (count != NULL) {
289                         *count = ret;
290                 }
291                 ret = 0;
292         }
293
294         return ret;
295 }