util_tdb: move timeout chainlock variants from source3/lib/util/util_tdb.c
[ddiss/samba.git] / lib / util / util_tdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    tdb utility functions
5
6    Copyright (C) Andrew Tridgell 1992-2006
7    Copyright (C) Volker Lendecke 2007-2011
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "system/filesys.h"
25 #include "../lib/tdb/include/tdb.h"
26 #include "../lib/util/util_tdb.h"
27
28 /* these are little tdb utility functions that are meant to make
29    dealing with a tdb database a little less cumbersome in Samba */
30
31 /***************************************************************
32  Make a TDB_DATA and keep the const warning in one place
33 ****************************************************************/
34
35 TDB_DATA make_tdb_data(const uint8_t *dptr, size_t dsize)
36 {
37         TDB_DATA ret;
38         ret.dptr = discard_const_p(uint8_t, dptr);
39         ret.dsize = dsize;
40         return ret;
41 }
42
43 bool tdb_data_equal(TDB_DATA t1, TDB_DATA t2)
44 {
45         if (t1.dsize != t2.dsize) {
46                 return false;
47         }
48         return (memcmp(t1.dptr, t2.dptr, t1.dsize) == 0);
49 }
50
51 bool tdb_data_is_empty(TDB_DATA d)
52 {
53         return (d.dsize == 0) || (d.dptr == NULL);
54 }
55
56 TDB_DATA string_tdb_data(const char *string)
57 {
58         return make_tdb_data((const uint8_t *)string, string ? strlen(string) : 0 );
59 }
60
61 TDB_DATA string_term_tdb_data(const char *string)
62 {
63         return make_tdb_data((const uint8_t *)string, string ? strlen(string) + 1 : 0);
64 }
65
66 /****************************************************************************
67  Lock a chain by string. Return non-zero if lock failed.
68 ****************************************************************************/
69
70 int tdb_lock_bystring(struct tdb_context *tdb, const char *keyval)
71 {
72         TDB_DATA key = string_term_tdb_data(keyval);
73         
74         return tdb_chainlock(tdb, key);
75 }
76
77 /****************************************************************************
78  Unlock a chain by string.
79 ****************************************************************************/
80
81 void tdb_unlock_bystring(struct tdb_context *tdb, const char *keyval)
82 {
83         TDB_DATA key = string_term_tdb_data(keyval);
84
85         tdb_chainunlock(tdb, key);
86 }
87
88 /****************************************************************************
89  Read lock a chain by string. Return non-zero if lock failed.
90 ****************************************************************************/
91
92 int tdb_read_lock_bystring(struct tdb_context *tdb, const char *keyval)
93 {
94         TDB_DATA key = string_term_tdb_data(keyval);
95         
96         return tdb_chainlock_read(tdb, key);
97 }
98
99 /****************************************************************************
100  Read unlock a chain by string.
101 ****************************************************************************/
102
103 void tdb_read_unlock_bystring(struct tdb_context *tdb, const char *keyval)
104 {
105         TDB_DATA key = string_term_tdb_data(keyval);
106         
107         tdb_chainunlock_read(tdb, key);
108 }
109
110
111 /****************************************************************************
112  Fetch a int32_t value by a arbitrary blob key, return -1 if not found.
113  Output is int32_t in native byte order.
114 ****************************************************************************/
115
116 int32_t tdb_fetch_int32_byblob(struct tdb_context *tdb, TDB_DATA key)
117 {
118         TDB_DATA data;
119         int32_t ret;
120
121         data = tdb_fetch(tdb, key);
122         if (!data.dptr || data.dsize != sizeof(int32_t)) {
123                 SAFE_FREE(data.dptr);
124                 return -1;
125         }
126
127         ret = IVAL(data.dptr,0);
128         SAFE_FREE(data.dptr);
129         return ret;
130 }
131
132 /****************************************************************************
133  Fetch a int32_t value by string key, return -1 if not found.
134  Output is int32_t in native byte order.
135 ****************************************************************************/
136
137 int32_t tdb_fetch_int32(struct tdb_context *tdb, const char *keystr)
138 {
139         return tdb_fetch_int32_byblob(tdb, string_term_tdb_data(keystr));
140 }
141
142 /****************************************************************************
143  Store a int32_t value by an arbitrary blob key, return 0 on success, -ve on failure.
144  Input is int32_t in native byte order. Output in tdb is in little-endian.
145 ****************************************************************************/
146
147 int tdb_store_int32_byblob(struct tdb_context *tdb, TDB_DATA key, int32_t v)
148 {
149         TDB_DATA data;
150         int32_t v_store;
151
152         SIVAL(&v_store,0,v);
153         data.dptr = (unsigned char *)&v_store;
154         data.dsize = sizeof(int32_t);
155
156         return tdb_store(tdb, key, data, TDB_REPLACE);
157 }
158
159 /****************************************************************************
160  Store a int32_t value by string key, return 0 on success, -ve on failure.
161  Input is int32_t in native byte order. Output in tdb is in little-endian.
162 ****************************************************************************/
163
164 int tdb_store_int32(struct tdb_context *tdb, const char *keystr, int32_t v)
165 {
166         return tdb_store_int32_byblob(tdb, string_term_tdb_data(keystr), v);
167 }
168
169 /****************************************************************************
170  Fetch a uint32_t value by a arbitrary blob key, return false if not found.
171  Output is uint32_t in native byte order.
172 ****************************************************************************/
173
174 bool tdb_fetch_uint32_byblob(struct tdb_context *tdb, TDB_DATA key, uint32_t *value)
175 {
176         TDB_DATA data;
177
178         data = tdb_fetch(tdb, key);
179         if (!data.dptr || data.dsize != sizeof(uint32_t)) {
180                 SAFE_FREE(data.dptr);
181                 return false;
182         }
183
184         *value = IVAL(data.dptr,0);
185         SAFE_FREE(data.dptr);
186         return true;
187 }
188
189 /****************************************************************************
190  Fetch a uint32_t value by string key, return false if not found.
191  Output is uint32_t in native byte order.
192 ****************************************************************************/
193
194 bool tdb_fetch_uint32(struct tdb_context *tdb, const char *keystr, uint32_t *value)
195 {
196         return tdb_fetch_uint32_byblob(tdb, string_term_tdb_data(keystr), value);
197 }
198
199 /****************************************************************************
200  Store a uint32_t value by an arbitrary blob key, return true on success, false on failure.
201  Input is uint32_t in native byte order. Output in tdb is in little-endian.
202 ****************************************************************************/
203
204 bool tdb_store_uint32_byblob(struct tdb_context *tdb, TDB_DATA key, uint32_t value)
205 {
206         TDB_DATA data;
207         uint32_t v_store;
208         bool ret = true;
209
210         SIVAL(&v_store, 0, value);
211         data.dptr = (unsigned char *)&v_store;
212         data.dsize = sizeof(uint32_t);
213
214         if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
215                 ret = false;
216
217         return ret;
218 }
219
220 /****************************************************************************
221  Store a uint32_t value by string key, return true on success, false on failure.
222  Input is uint32_t in native byte order. Output in tdb is in little-endian.
223 ****************************************************************************/
224
225 bool tdb_store_uint32(struct tdb_context *tdb, const char *keystr, uint32_t value)
226 {
227         return tdb_store_uint32_byblob(tdb, string_term_tdb_data(keystr), value);
228 }
229 /****************************************************************************
230  Store a buffer by a null terminated string key.  Return 0 on success, -ve
231  on failure.
232 ****************************************************************************/
233
234 int tdb_store_bystring(struct tdb_context *tdb, const char *keystr, TDB_DATA data, int flags)
235 {
236         TDB_DATA key = string_term_tdb_data(keystr);
237         
238         return tdb_store(tdb, key, data, flags);
239 }
240
241 /****************************************************************************
242  Fetch a buffer using a null terminated string key.  Don't forget to call
243  free() on the result dptr.
244 ****************************************************************************/
245
246 TDB_DATA tdb_fetch_bystring(struct tdb_context *tdb, const char *keystr)
247 {
248         TDB_DATA key = string_term_tdb_data(keystr);
249
250         return tdb_fetch(tdb, key);
251 }
252
253 /****************************************************************************
254  Delete an entry using a null terminated string key. 
255 ****************************************************************************/
256
257 int tdb_delete_bystring(struct tdb_context *tdb, const char *keystr)
258 {
259         TDB_DATA key = string_term_tdb_data(keystr);
260
261         return tdb_delete(tdb, key);
262 }
263
264 /****************************************************************************
265  Atomic integer change. Returns old value. To create, set initial value in *oldval. 
266 ****************************************************************************/
267
268 int32_t tdb_change_int32_atomic(struct tdb_context *tdb, const char *keystr, int32_t *oldval, int32_t change_val)
269 {
270         int32_t val;
271         int32_t ret = -1;
272
273         if (tdb_lock_bystring(tdb, keystr) != 0)
274                 return -1;
275
276         if ((val = tdb_fetch_int32(tdb, keystr)) == -1) {
277                 /* The lookup failed */
278                 if (tdb_error(tdb) != TDB_ERR_NOEXIST) {
279                         /* but not because it didn't exist */
280                         goto err_out;
281                 }
282                 
283                 /* Start with 'old' value */
284                 val = *oldval;
285
286         } else {
287                 /* It worked, set return value (oldval) to tdb data */
288                 *oldval = val;
289         }
290
291         /* Increment value for storage and return next time */
292         val += change_val;
293                 
294         if (tdb_store_int32(tdb, keystr, val) != 0)
295                 goto err_out;
296
297         ret = 0;
298
299   err_out:
300
301         tdb_unlock_bystring(tdb, keystr);
302         return ret;
303 }
304
305 static sig_atomic_t gotalarm;
306
307 /***************************************************************
308  Signal function to tell us we timed out.
309 ****************************************************************/
310
311 static void gotalarm_sig(int signum)
312 {
313         gotalarm = 1;
314 }
315
316 /****************************************************************************
317  Lock a chain with timeout (in seconds).
318 ****************************************************************************/
319
320 static int tdb_chainlock_with_timeout_internal( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout, int rw_type)
321 {
322         /* Allow tdb_chainlock to be interrupted by an alarm. */
323         int ret;
324         gotalarm = 0;
325
326         if (timeout) {
327                 CatchSignal(SIGALRM, gotalarm_sig);
328                 tdb_setalarm_sigptr(tdb, &gotalarm);
329                 alarm(timeout);
330         }
331
332         if (rw_type == F_RDLCK)
333                 ret = tdb_chainlock_read(tdb, key);
334         else
335                 ret = tdb_chainlock(tdb, key);
336
337         if (timeout) {
338                 alarm(0);
339                 tdb_setalarm_sigptr(tdb, NULL);
340                 CatchSignal(SIGALRM, SIG_IGN);
341                 if (gotalarm && (ret != 0)) {
342                         DEBUG(0,("tdb_chainlock_with_timeout_internal: alarm (%u) timed out for key %s in tdb %s\n",
343                                 timeout, key.dptr, tdb_name(tdb)));
344                         /* TODO: If we time out waiting for a lock, it might
345                          * be nice to use F_GETLK to get the pid of the
346                          * process currently holding the lock and print that
347                          * as part of the debugging message. -- mbp */
348                         return -1;
349                 }
350         }
351
352         return ret == 0 ? 0 : -1;
353 }
354
355 /****************************************************************************
356  Write lock a chain. Return non-zero if timeout or lock failed.
357 ****************************************************************************/
358
359 int tdb_chainlock_with_timeout( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout)
360 {
361         return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
362 }
363
364 int tdb_lock_bystring_with_timeout(TDB_CONTEXT *tdb, const char *keyval,
365                                    int timeout)
366 {
367         TDB_DATA key = string_term_tdb_data(keyval);
368
369         return tdb_chainlock_with_timeout(tdb, key, timeout);
370 }
371
372 /****************************************************************************
373  Read lock a chain by string. Return non-zero if timeout or lock failed.
374 ****************************************************************************/
375
376 int tdb_read_lock_bystring_with_timeout(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
377 {
378         TDB_DATA key = string_term_tdb_data(keyval);
379
380         return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_RDLCK);
381 }
382
383 /****************************************************************************
384  Atomic unsigned integer change. Returns old value. To create, set initial value in *oldval. 
385 ****************************************************************************/
386
387 bool tdb_change_uint32_atomic(struct tdb_context *tdb, const char *keystr, uint32_t *oldval, uint32_t change_val)
388 {
389         uint32_t val;
390         bool ret = false;
391
392         if (tdb_lock_bystring(tdb, keystr) != 0)
393                 return false;
394
395         if (!tdb_fetch_uint32(tdb, keystr, &val)) {
396                 /* It failed */
397                 if (tdb_error(tdb) != TDB_ERR_NOEXIST) { 
398                         /* and not because it didn't exist */
399                         goto err_out;
400                 }
401
402                 /* Start with 'old' value */
403                 val = *oldval;
404
405         } else {
406                 /* it worked, set return value (oldval) to tdb data */
407                 *oldval = val;
408
409         }
410
411         /* get a new value to store */
412         val += change_val;
413                 
414         if (!tdb_store_uint32(tdb, keystr, val))
415                 goto err_out;
416
417         ret = true;
418
419   err_out:
420
421         tdb_unlock_bystring(tdb, keystr);
422         return ret;
423 }
424
425 /****************************************************************************
426  Allow tdb_delete to be used as a tdb_traversal_fn.
427 ****************************************************************************/
428
429 int tdb_traverse_delete_fn(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf,
430                      void *state)
431 {
432     return tdb_delete(the_tdb, key);
433 }
434
435 /****************************************************************************
436  Return an NTSTATUS from a TDB_ERROR
437 ****************************************************************************/
438
439 NTSTATUS map_nt_error_from_tdb(enum TDB_ERROR err)
440 {
441         NTSTATUS result = NT_STATUS_INTERNAL_ERROR;
442
443         switch (err) {
444         case TDB_SUCCESS:
445                 result = NT_STATUS_OK;
446                 break;
447         case TDB_ERR_CORRUPT:
448                 result = NT_STATUS_INTERNAL_DB_CORRUPTION;
449                 break;
450         case TDB_ERR_IO:
451                 result = NT_STATUS_UNEXPECTED_IO_ERROR;
452                 break;
453         case TDB_ERR_OOM:
454                 result = NT_STATUS_NO_MEMORY;
455                 break;
456         case TDB_ERR_EXISTS:
457                 result = NT_STATUS_OBJECT_NAME_COLLISION;
458                 break;
459
460         case TDB_ERR_LOCK:
461                 /*
462                  * TDB_ERR_LOCK is very broad, we could for example
463                  * distinguish between fcntl locks and invalid lock
464                  * sequences. So NT_STATUS_FILE_LOCK_CONFLICT is a
465                  * compromise.
466                  */
467                 result = NT_STATUS_FILE_LOCK_CONFLICT;
468                 break;
469
470         case TDB_ERR_NOLOCK:
471         case TDB_ERR_LOCK_TIMEOUT:
472                 /*
473                  * These two ones in the enum are not actually used
474                  */
475                 result = NT_STATUS_FILE_LOCK_CONFLICT;
476                 break;
477         case TDB_ERR_NOEXIST:
478                 result = NT_STATUS_NOT_FOUND;
479                 break;
480         case TDB_ERR_EINVAL:
481                 result = NT_STATUS_INVALID_PARAMETER;
482                 break;
483         case TDB_ERR_RDONLY:
484                 result = NT_STATUS_ACCESS_DENIED;
485                 break;
486         case TDB_ERR_NESTING:
487                 result = NT_STATUS_INTERNAL_ERROR;
488                 break;
489         };
490         return result;
491 }