s3:dbwrap: export logic of dbwrap_change_int32_atomic into an action function
[abartlet/samba.git/.git] / source3 / lib / dbwrap_util.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Utility functions for the dbwrap API
4    Copyright (C) Volker Lendecke 2007
5    Copyright (C) Michael Adam 2009
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 int32_t dbwrap_fetch_int32(struct db_context *db, const char *keystr)
25 {
26         TDB_DATA dbuf;
27         int32 ret;
28
29         if (db->fetch(db, NULL, string_term_tdb_data(keystr), &dbuf) != 0) {
30                 return -1;
31         }
32
33         if ((dbuf.dptr == NULL) || (dbuf.dsize != sizeof(int32_t))) {
34                 TALLOC_FREE(dbuf.dptr);
35                 return -1;
36         }
37
38         ret = IVAL(dbuf.dptr, 0);
39         TALLOC_FREE(dbuf.dptr);
40         return ret;
41 }
42
43 int dbwrap_store_int32(struct db_context *db, const char *keystr, int32_t v)
44 {
45         struct db_record *rec;
46         int32 v_store;
47         NTSTATUS status;
48
49         rec = db->fetch_locked(db, NULL, string_term_tdb_data(keystr));
50         if (rec == NULL) {
51                 return -1;
52         }
53
54         SIVAL(&v_store, 0, v);
55
56         status = rec->store(rec, make_tdb_data((const uint8 *)&v_store,
57                                                sizeof(v_store)),
58                             TDB_REPLACE);
59         TALLOC_FREE(rec);
60         return NT_STATUS_IS_OK(status) ? 0 : -1;
61 }
62
63 bool dbwrap_fetch_uint32(struct db_context *db, const char *keystr,
64                          uint32_t *val)
65 {
66         TDB_DATA dbuf;
67
68         if (db->fetch(db, NULL, string_term_tdb_data(keystr), &dbuf) != 0) {
69                 return false;
70         }
71
72         if ((dbuf.dptr == NULL) || (dbuf.dsize != sizeof(uint32_t))) {
73                 TALLOC_FREE(dbuf.dptr);
74                 return false;
75         }
76
77         *val = IVAL(dbuf.dptr, 0);
78         TALLOC_FREE(dbuf.dptr);
79         return true;
80 }
81
82 int dbwrap_store_uint32(struct db_context *db, const char *keystr, uint32_t v)
83 {
84         struct db_record *rec;
85         uint32 v_store;
86         NTSTATUS status;
87
88         rec = db->fetch_locked(db, NULL, string_term_tdb_data(keystr));
89         if (rec == NULL) {
90                 return -1;
91         }
92
93         SIVAL(&v_store, 0, v);
94
95         status = rec->store(rec, make_tdb_data((const uint8 *)&v_store,
96                                                sizeof(v_store)),
97                             TDB_REPLACE);
98         TALLOC_FREE(rec);
99         return NT_STATUS_IS_OK(status) ? 0 : -1;
100 }
101
102 /**
103  * Atomic unsigned integer change (addition):
104  *
105  * if value does not exist yet in the db, use *oldval as initial old value.
106  * return old value in *oldval.
107  * store *oldval + change_val to db.
108  */
109
110 struct dbwrap_change_uint32_atomic_context {
111         const char *keystr;
112         uint32_t *oldval;
113         uint32_t change_val;
114 };
115
116 static NTSTATUS dbwrap_change_uint32_atomic_action(struct db_context *db,
117                                                    void *private_data)
118 {
119         struct db_record *rec;
120         uint32 val = -1;
121         TDB_DATA data;
122         NTSTATUS ret;
123         struct dbwrap_change_uint32_atomic_context *state;
124
125         state = (struct dbwrap_change_uint32_atomic_context *)private_data;
126
127         rec = db->fetch_locked(db, NULL, string_term_tdb_data(state->keystr));
128         if (!rec) {
129                 return NT_STATUS_UNSUCCESSFUL;
130         }
131
132         if (rec->value.dptr == NULL) {
133                 val = *(state->oldval);
134         } else if (rec->value.dsize == sizeof(val)) {
135                 val = IVAL(rec->value.dptr, 0);
136                 *(state->oldval) = val;
137         } else {
138                 ret = NT_STATUS_UNSUCCESSFUL;
139                 goto done;
140         }
141
142         val += state->change_val;
143
144         data.dsize = sizeof(val);
145         data.dptr = (uint8 *)&val;
146
147         ret = rec->store(rec, data, TDB_REPLACE);
148
149 done:
150         TALLOC_FREE(rec);
151         return ret;
152 }
153
154 NTSTATUS dbwrap_change_uint32_atomic(struct db_context *db, const char *keystr,
155                                      uint32_t *oldval, uint32_t change_val)
156 {
157         NTSTATUS ret;
158         struct dbwrap_change_uint32_atomic_context state;
159
160         state.keystr = keystr;
161         state.oldval = oldval;
162         state.change_val = change_val;
163
164         ret = dbwrap_change_uint32_atomic_action(db, &state);
165
166         return ret;
167 }
168
169 NTSTATUS dbwrap_trans_change_uint32_atomic(struct db_context *db,
170                                            const char *keystr,
171                                            uint32_t *oldval,
172                                            uint32_t change_val)
173 {
174         NTSTATUS ret;
175         struct dbwrap_change_uint32_atomic_context state;
176
177         state.keystr = keystr;
178         state.oldval = oldval;
179         state.change_val = change_val;
180
181         ret = dbwrap_trans_do(db, dbwrap_change_uint32_atomic_action, &state);
182
183         return ret;
184 }
185
186 /**
187  * Atomic integer change (addition):
188  *
189  * if value does not exist yet in the db, use *oldval as initial old value.
190  * return old value in *oldval.
191  * store *oldval + change_val to db.
192  */
193
194 struct dbwrap_change_int32_atomic_context {
195         const char *keystr;
196         int32_t *oldval;
197         int32_t change_val;
198 };
199
200 static NTSTATUS dbwrap_change_int32_atomic_action(struct db_context *db,
201                                                   void *private_data)
202 {
203         struct db_record *rec;
204         int32_t val = -1;
205         TDB_DATA data;
206         NTSTATUS ret;
207         struct dbwrap_change_int32_atomic_context *state;
208
209         state = (struct dbwrap_change_int32_atomic_context *)private_data;
210
211         rec = db->fetch_locked(db, NULL, string_term_tdb_data(state->keystr));
212         if (!rec) {
213                 return NT_STATUS_UNSUCCESSFUL;
214         }
215
216         if (rec->value.dptr == NULL) {
217                 val = *(state->oldval);
218         } else if (rec->value.dsize == sizeof(val)) {
219                 val = IVAL(rec->value.dptr, 0);
220                 *(state->oldval) = val;
221         } else {
222                 ret = NT_STATUS_UNSUCCESSFUL;
223                 goto done;
224         }
225
226         val += state->change_val;
227
228         data.dsize = sizeof(val);
229         data.dptr = (uint8 *)&val;
230
231         ret = rec->store(rec, data, TDB_REPLACE);
232
233 done:
234         TALLOC_FREE(rec);
235         return ret;
236 }
237
238 NTSTATUS dbwrap_change_int32_atomic(struct db_context *db, const char *keystr,
239                                     int32_t *oldval, int32_t change_val)
240 {
241         NTSTATUS ret;
242         struct dbwrap_change_int32_atomic_context state;
243
244         state.keystr = keystr;
245         state.oldval = oldval;
246         state.change_val = change_val;
247
248         ret = dbwrap_change_int32_atomic_action(db, &state);
249
250         return ret;
251 }
252
253 struct dbwrap_store_context {
254         TDB_DATA *key;
255         TDB_DATA *dbuf;
256         int flag;
257 };
258
259 static NTSTATUS dbwrap_store_action(struct db_context *db, void *private_data)
260 {
261         struct db_record *rec = NULL;
262         NTSTATUS status;
263         struct dbwrap_store_context *store_ctx;
264
265         store_ctx = (struct dbwrap_store_context *)private_data;
266
267         rec = db->fetch_locked(db, talloc_tos(), *(store_ctx->key));
268         if (rec == NULL) {
269                 DEBUG(5, ("fetch_locked failed\n"));
270                 return NT_STATUS_NO_MEMORY;
271         }
272
273         status = rec->store(rec, *(store_ctx->dbuf), store_ctx->flag);
274         if (!NT_STATUS_IS_OK(status)) {
275                 DEBUG(5, ("store returned %s\n", nt_errstr(status)));
276         }
277
278         TALLOC_FREE(rec);
279         return status;
280 }
281
282 NTSTATUS dbwrap_trans_store(struct db_context *db, TDB_DATA key, TDB_DATA dbuf,
283                             int flag)
284 {
285         NTSTATUS status;
286         struct dbwrap_store_context store_ctx;
287
288         store_ctx.key = &key;
289         store_ctx.dbuf = &dbuf;
290         store_ctx.flag = flag;
291
292         status = dbwrap_trans_do(db, dbwrap_store_action, &store_ctx);
293
294         return status;
295 }
296
297 static NTSTATUS dbwrap_delete_action(struct db_context * db, void *private_data)
298 {
299         NTSTATUS status;
300         struct db_record *rec;
301         TDB_DATA *key = (TDB_DATA *)private_data;
302
303         rec = db->fetch_locked(db, talloc_tos(), *key);
304         if (rec == NULL) {
305                 DEBUG(5, ("fetch_locked failed\n"));
306                 return NT_STATUS_NO_MEMORY;
307         }
308
309         status = rec->delete_rec(rec);
310         if (!NT_STATUS_IS_OK(status)) {
311                 DEBUG(5, ("delete_rec returned %s\n", nt_errstr(status)));
312         }
313
314         talloc_free(rec);
315         return  status;
316 }
317
318 NTSTATUS dbwrap_trans_delete(struct db_context *db, TDB_DATA key)
319 {
320         NTSTATUS status;
321
322         status = dbwrap_trans_do(db, dbwrap_delete_action, &key);
323
324         return status;
325 }
326
327 NTSTATUS dbwrap_trans_store_int32(struct db_context *db, const char *keystr,
328                                   int32_t v)
329 {
330         int32 v_store;
331
332         SIVAL(&v_store, 0, v);
333
334         return dbwrap_trans_store(db, string_term_tdb_data(keystr),
335                                   make_tdb_data((const uint8 *)&v_store,
336                                                 sizeof(v_store)),
337                                   TDB_REPLACE);
338 }
339
340 NTSTATUS dbwrap_trans_store_uint32(struct db_context *db, const char *keystr,
341                                    uint32_t v)
342 {
343         uint32 v_store;
344
345         SIVAL(&v_store, 0, v);
346
347         return dbwrap_trans_store(db, string_term_tdb_data(keystr),
348                                   make_tdb_data((const uint8 *)&v_store,
349                                                 sizeof(v_store)),
350                                   TDB_REPLACE);
351 }
352
353 NTSTATUS dbwrap_trans_store_bystring(struct db_context *db, const char *key,
354                                      TDB_DATA data, int flags)
355 {
356         return dbwrap_trans_store(db, string_term_tdb_data(key), data, flags);
357 }
358
359 NTSTATUS dbwrap_trans_delete_bystring(struct db_context *db, const char *key)
360 {
361         return dbwrap_trans_delete(db, string_term_tdb_data(key));
362 }
363
364 /**
365  * Wrap db action(s) into a transaction.
366  */
367 NTSTATUS dbwrap_trans_do(struct db_context *db,
368                          NTSTATUS (*action)(struct db_context *, void *),
369                          void *private_data)
370 {
371         int res;
372         NTSTATUS status;
373
374         res = db->transaction_start(db);
375         if (res != 0) {
376                 DEBUG(5, ("transaction_start failed\n"));
377                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
378         }
379
380         status = action(db, private_data);
381         if (!NT_STATUS_IS_OK(status)) {
382                 if (db->transaction_cancel(db) != 0) {
383                         smb_panic("Cancelling transaction failed");
384                 }
385                 return status;
386         }
387
388         res = db->transaction_commit(db);
389         if (res == 0) {
390                 return NT_STATUS_OK;
391         }
392
393         DEBUG(2, ("transaction_commit failed\n"));
394         return NT_STATUS_INTERNAL_DB_CORRUPTION;
395 }
396
397 NTSTATUS dbwrap_delete_bystring_upper(struct db_context *db, const char *key)
398 {
399         char *key_upper;
400         NTSTATUS status;
401
402         key_upper = talloc_strdup_upper(talloc_tos(), key);
403         if (key_upper == NULL) {
404                 return NT_STATUS_NO_MEMORY;
405         }
406
407         status = dbwrap_delete_bystring(db, key_upper);
408
409         talloc_free(key_upper);
410         return status;
411 }
412
413 NTSTATUS dbwrap_store_bystring_upper(struct db_context *db, const char *key,
414                                      TDB_DATA data, int flags)
415 {
416         char *key_upper;
417         NTSTATUS status;
418
419         key_upper = talloc_strdup_upper(talloc_tos(), key);
420         if (key_upper == NULL) {
421                 return NT_STATUS_NO_MEMORY;
422         }
423
424         status = dbwrap_store_bystring(db, key_upper, data, flags);
425
426         talloc_free(key_upper);
427         return status;
428 }
429
430 TDB_DATA dbwrap_fetch_bystring_upper(struct db_context *db, TALLOC_CTX *mem_ctx,
431                                      const char *key)
432 {
433         char *key_upper;
434         TDB_DATA result;
435
436         key_upper = talloc_strdup_upper(talloc_tos(), key);
437         if (key_upper == NULL) {
438                 return make_tdb_data(NULL, 0);
439         }
440
441         result = dbwrap_fetch_bystring(db, mem_ctx, key_upper);
442
443         talloc_free(key_upper);
444         return result;
445 }