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
24int32_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
43int 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
63bool 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
82int 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
110struct dbwrap_change_uint32_atomic_context {
111	const char *keystr;
112	uint32_t *oldval;
113	uint32_t change_val;
114};
115
116static 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	uint32_t v_store;
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	SIVAL(&v_store, 0, val);
145
146	ret = rec->store(rec,
147			 make_tdb_data((const uint8 *)&v_store,
148				       sizeof(v_store)),
149			 TDB_REPLACE);
150
151done:
152	TALLOC_FREE(rec);
153	return ret;
154}
155
156NTSTATUS dbwrap_change_uint32_atomic(struct db_context *db, const char *keystr,
157				     uint32_t *oldval, uint32_t change_val)
158{
159	NTSTATUS ret;
160	struct dbwrap_change_uint32_atomic_context state;
161
162	state.keystr = keystr;
163	state.oldval = oldval;
164	state.change_val = change_val;
165
166	ret = dbwrap_change_uint32_atomic_action(db, &state);
167
168	return ret;
169}
170
171NTSTATUS dbwrap_trans_change_uint32_atomic(struct db_context *db,
172					   const char *keystr,
173					   uint32_t *oldval,
174					   uint32_t change_val)
175{
176	NTSTATUS ret;
177	struct dbwrap_change_uint32_atomic_context state;
178
179	state.keystr = keystr;
180	state.oldval = oldval;
181	state.change_val = change_val;
182
183	ret = dbwrap_trans_do(db, dbwrap_change_uint32_atomic_action, &state);
184
185	return ret;
186}
187
188/**
189 * Atomic integer change (addition):
190 *
191 * if value does not exist yet in the db, use *oldval as initial old value.
192 * return old value in *oldval.
193 * store *oldval + change_val to db.
194 */
195
196struct dbwrap_change_int32_atomic_context {
197	const char *keystr;
198	int32_t *oldval;
199	int32_t change_val;
200};
201
202static NTSTATUS dbwrap_change_int32_atomic_action(struct db_context *db,
203						  void *private_data)
204{
205	struct db_record *rec;
206	int32_t val = -1;
207	int32_t v_store;
208	NTSTATUS ret;
209	struct dbwrap_change_int32_atomic_context *state;
210
211	state = (struct dbwrap_change_int32_atomic_context *)private_data;
212
213	rec = db->fetch_locked(db, NULL, string_term_tdb_data(state->keystr));
214	if (!rec) {
215		return NT_STATUS_UNSUCCESSFUL;
216	}
217
218	if (rec->value.dptr == NULL) {
219		val = *(state->oldval);
220	} else if (rec->value.dsize == sizeof(val)) {
221		val = IVAL(rec->value.dptr, 0);
222		*(state->oldval) = val;
223	} else {
224		ret = NT_STATUS_UNSUCCESSFUL;
225		goto done;
226	}
227
228	val += state->change_val;
229
230	SIVAL(&v_store, 0, val);
231
232	ret = rec->store(rec,
233			 make_tdb_data((const uint8_t *)&v_store,
234				       sizeof(v_store)),
235			 TDB_REPLACE);
236
237done:
238	TALLOC_FREE(rec);
239	return ret;
240}
241
242NTSTATUS dbwrap_change_int32_atomic(struct db_context *db, const char *keystr,
243				    int32_t *oldval, int32_t change_val)
244{
245	NTSTATUS ret;
246	struct dbwrap_change_int32_atomic_context state;
247
248	state.keystr = keystr;
249	state.oldval = oldval;
250	state.change_val = change_val;
251
252	ret = dbwrap_change_int32_atomic_action(db, &state);
253
254	return ret;
255}
256
257NTSTATUS dbwrap_trans_change_int32_atomic(struct db_context *db,
258					  const char *keystr,
259					  int32_t *oldval,
260					  int32_t change_val)
261{
262	NTSTATUS ret;
263	struct dbwrap_change_int32_atomic_context state;
264
265	state.keystr = keystr;
266	state.oldval = oldval;
267	state.change_val = change_val;
268
269	ret = dbwrap_trans_do(db, dbwrap_change_int32_atomic_action, &state);
270
271	return ret;
272}
273
274struct dbwrap_store_context {
275	TDB_DATA *key;
276	TDB_DATA *dbuf;
277	int flag;
278};
279
280static NTSTATUS dbwrap_store_action(struct db_context *db, void *private_data)
281{
282	struct db_record *rec = NULL;
283	NTSTATUS status;
284	struct dbwrap_store_context *store_ctx;
285
286	store_ctx = (struct dbwrap_store_context *)private_data;
287
288	rec = db->fetch_locked(db, talloc_tos(), *(store_ctx->key));
289	if (rec == NULL) {
290		DEBUG(5, ("fetch_locked failed\n"));
291		return NT_STATUS_NO_MEMORY;
292	}
293
294	status = rec->store(rec, *(store_ctx->dbuf), store_ctx->flag);
295	if (!NT_STATUS_IS_OK(status)) {
296		DEBUG(5, ("store returned %s\n", nt_errstr(status)));
297	}
298
299	TALLOC_FREE(rec);
300	return status;
301}
302
303NTSTATUS dbwrap_trans_store(struct db_context *db, TDB_DATA key, TDB_DATA dbuf,
304			    int flag)
305{
306	NTSTATUS status;
307	struct dbwrap_store_context store_ctx;
308
309	store_ctx.key = &key;
310	store_ctx.dbuf = &dbuf;
311	store_ctx.flag = flag;
312
313	status = dbwrap_trans_do(db, dbwrap_store_action, &store_ctx);
314
315	return status;
316}
317
318static NTSTATUS dbwrap_delete_action(struct db_context * db, void *private_data)
319{
320	NTSTATUS status;
321	struct db_record *rec;
322	TDB_DATA *key = (TDB_DATA *)private_data;
323
324	rec = db->fetch_locked(db, talloc_tos(), *key);
325	if (rec == NULL) {
326		DEBUG(5, ("fetch_locked failed\n"));
327		return NT_STATUS_NO_MEMORY;
328	}
329
330	status = rec->delete_rec(rec);
331	if (!NT_STATUS_IS_OK(status)) {
332		DEBUG(5, ("delete_rec returned %s\n", nt_errstr(status)));
333	}
334
335	talloc_free(rec);
336	return  status;
337}
338
339NTSTATUS dbwrap_trans_delete(struct db_context *db, TDB_DATA key)
340{
341	NTSTATUS status;
342
343	status = dbwrap_trans_do(db, dbwrap_delete_action, &key);
344
345	return status;
346}
347
348NTSTATUS dbwrap_trans_store_int32(struct db_context *db, const char *keystr,
349				  int32_t v)
350{
351	int32 v_store;
352
353	SIVAL(&v_store, 0, v);
354
355	return dbwrap_trans_store(db, string_term_tdb_data(keystr),
356				  make_tdb_data((const uint8 *)&v_store,
357						sizeof(v_store)),
358				  TDB_REPLACE);
359}
360
361NTSTATUS dbwrap_trans_store_uint32(struct db_context *db, const char *keystr,
362				   uint32_t v)
363{
364	uint32 v_store;
365
366	SIVAL(&v_store, 0, v);
367
368	return dbwrap_trans_store(db, string_term_tdb_data(keystr),
369				  make_tdb_data((const uint8 *)&v_store,
370						sizeof(v_store)),
371				  TDB_REPLACE);
372}
373
374NTSTATUS dbwrap_trans_store_bystring(struct db_context *db, const char *key,
375				     TDB_DATA data, int flags)
376{
377	return dbwrap_trans_store(db, string_term_tdb_data(key), data, flags);
378}
379
380NTSTATUS dbwrap_trans_delete_bystring(struct db_context *db, const char *key)
381{
382	return dbwrap_trans_delete(db, string_term_tdb_data(key));
383}
384
385/**
386 * Wrap db action(s) into a transaction.
387 */
388NTSTATUS dbwrap_trans_do(struct db_context *db,
389			 NTSTATUS (*action)(struct db_context *, void *),
390			 void *private_data)
391{
392	int res;
393	NTSTATUS status;
394
395	res = db->transaction_start(db);
396	if (res != 0) {
397		DEBUG(5, ("transaction_start failed\n"));
398		return NT_STATUS_INTERNAL_DB_CORRUPTION;
399	}
400
401	status = action(db, private_data);
402	if (!NT_STATUS_IS_OK(status)) {
403		if (db->transaction_cancel(db) != 0) {
404			smb_panic("Cancelling transaction failed");
405		}
406		return status;
407	}
408
409	res = db->transaction_commit(db);
410	if (res == 0) {
411		return NT_STATUS_OK;
412	}
413
414	DEBUG(2, ("transaction_commit failed\n"));
415	return NT_STATUS_INTERNAL_DB_CORRUPTION;
416}
417
418NTSTATUS dbwrap_delete_bystring_upper(struct db_context *db, const char *key)
419{
420	char *key_upper;
421	NTSTATUS status;
422
423	key_upper = talloc_strdup_upper(talloc_tos(), key);
424	if (key_upper == NULL) {
425		return NT_STATUS_NO_MEMORY;
426	}
427
428	status = dbwrap_delete_bystring(db, key_upper);
429
430	talloc_free(key_upper);
431	return status;
432}
433
434NTSTATUS dbwrap_store_bystring_upper(struct db_context *db, const char *key,
435				     TDB_DATA data, int flags)
436{
437	char *key_upper;
438	NTSTATUS status;
439
440	key_upper = talloc_strdup_upper(talloc_tos(), key);
441	if (key_upper == NULL) {
442		return NT_STATUS_NO_MEMORY;
443	}
444
445	status = dbwrap_store_bystring(db, key_upper, data, flags);
446
447	talloc_free(key_upper);
448	return status;
449}
450
451TDB_DATA dbwrap_fetch_bystring_upper(struct db_context *db, TALLOC_CTX *mem_ctx,
452				     const char *key)
453{
454	char *key_upper;
455	TDB_DATA result;
456
457	key_upper = talloc_strdup_upper(talloc_tos(), key);
458	if (key_upper == NULL) {
459		return make_tdb_data(NULL, 0);
460	}
461
462	result = dbwrap_fetch_bystring(db, mem_ctx, key_upper);
463
464	talloc_free(key_upper);
465	return result;
466}
467