]> git.ozlabs.org Git - ccan/blob - ccan/tdb2/io.c
tdb2: initial commit (doesn't work, still writing tests)
[ccan] / ccan / tdb2 / io.c
1  /* 
2    Unix SMB/CIFS implementation.
3
4    trivial database library
5
6    Copyright (C) Andrew Tridgell              1999-2005
7    Copyright (C) Paul `Rusty' Russell              2000
8    Copyright (C) Jeremy Allison                    2000-2003
9    Copyright (C) Rusty Russell                     2010
10
11      ** NOTE! The following LGPL license applies to the tdb
12      ** library. This does NOT imply that all of Samba is released
13      ** under the LGPL
14
15    This library is free software; you can redistribute it and/or
16    modify it under the terms of the GNU Lesser General Public
17    License as published by the Free Software Foundation; either
18    version 3 of the License, or (at your option) any later version.
19
20    This library is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23    Lesser General Public License for more details.
24
25    You should have received a copy of the GNU Lesser General Public
26    License along with this library; if not, see <http://www.gnu.org/licenses/>.
27 */
28 #include "private.h"
29 #include <ccan/likely/likely.h>
30
31 void tdb_munmap(struct tdb_context *tdb)
32 {
33         if (tdb->flags & TDB_INTERNAL)
34                 return;
35
36         if (tdb->map_ptr) {
37                 munmap(tdb->map_ptr, tdb->map_size);
38                 tdb->map_ptr = NULL;
39         }
40 }
41
42 void tdb_mmap(struct tdb_context *tdb)
43 {
44         if (tdb->flags & TDB_INTERNAL)
45                 return;
46
47         if (tdb->flags & TDB_NOMMAP)
48                 return;
49
50         tdb->map_ptr = mmap(NULL, tdb->map_size, 
51                             PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
52                             MAP_SHARED, tdb->fd, 0);
53
54         /*
55          * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
56          */
57         if (tdb->map_ptr == MAP_FAILED) {
58                 tdb->map_ptr = NULL;
59                 tdb->log(tdb, TDB_DEBUG_WARNING, tdb->log_priv,
60                          "tdb_mmap failed for size %lld (%s)\n", 
61                          (long long)tdb->map_size, strerror(errno));
62         }
63 }
64
65 /* check for an out of bounds access - if it is out of bounds then
66    see if the database has been expanded by someone else and expand
67    if necessary 
68    note that "len" is the minimum length needed for the db
69 */
70 static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, bool probe)
71 {
72         struct stat st;
73         if (len <= tdb->map_size)
74                 return 0;
75         if (tdb->flags & TDB_INTERNAL) {
76                 if (!probe) {
77                         /* Ensure ecode is set for log fn. */
78                         tdb->ecode = TDB_ERR_IO;
79                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
80                                  "tdb_oob len %lld beyond internal"
81                                  " malloc size %lld\n",
82                                  (long long)len,
83                                  (long long)tdb->map_size);
84                 }
85                 return -1;
86         }
87
88         if (fstat(tdb->fd, &st) == -1) {
89                 tdb->ecode = TDB_ERR_IO;
90                 return -1;
91         }
92
93         if (st.st_size < (size_t)len) {
94                 if (!probe) {
95                         /* Ensure ecode is set for log fn. */
96                         tdb->ecode = TDB_ERR_IO;
97                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
98                                  "tdb_oob len %lld beyond eof at %lld\n",
99                                  (long long)len, (long long)st.st_size);
100                 }
101                 return -1;
102         }
103
104         /* Unmap, update size, remap */
105         tdb_munmap(tdb);
106         tdb->map_size = st.st_size;
107         tdb_mmap(tdb);
108         return 0;
109 }
110
111 static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len)
112 {
113         if (unlikely(!tdb->map_ptr))
114                 return NULL;
115
116         /* FIXME: We can do a subset of this! */
117         if (tdb->transaction)
118                 return NULL;
119
120         if (unlikely(tdb_oob(tdb, off + len, true) == -1))
121                 return NULL;
122         return (char *)tdb->map_ptr + off;
123 }
124
125 /* Either make a copy into pad and return that, or return ptr into mmap. */
126 /* Note: pad has to be a real object, so we can't get here if len
127  * overflows size_t */
128 /* FIXME: Transaction */
129 void *tdb_get(struct tdb_context *tdb, tdb_off_t off, void *pad, size_t len)
130 {
131         ssize_t r;
132
133         if (likely(!(tdb->flags & TDB_CONVERT))) {
134                 void *ret = tdb_direct(tdb, off, len);
135                 if (ret)
136                         return ret;
137         }
138
139         if (unlikely(tdb_oob(tdb, off + len, false) == -1))
140                 return NULL;
141
142         r = pread(tdb->fd, pad, len, off);
143         if (r != (ssize_t)len) {
144                 /* Ensure ecode is set for log fn. */
145                 tdb->ecode = TDB_ERR_IO;
146                 tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
147                          "tdb_read failed at %llu "
148                          "len=%lld ret=%lld (%s) map_size=%lld\n",
149                          (long long)off, (long long)len,
150                          (long long)r, strerror(errno),
151                          (long long)tdb->map_size);
152                 return NULL;
153         }
154         return tdb_convert(tdb, pad, len);
155 }
156
157 /* Endian conversion: we only ever deal with 8 byte quantities */
158 void *tdb_convert(const struct tdb_context *tdb, void *buf, tdb_len_t size)
159 {
160         if (unlikely((tdb->flags & TDB_CONVERT))) {
161                 uint64_t i, *p = (uint64_t *)buf;
162                 for (i = 0; i < size / 8; i++)
163                         p[i] = bswap_64(p[i]);
164         }
165         return buf;
166 }
167
168 /* Return first non-zero offset in num offset array, or num. */
169 /* FIXME: Return the off? */
170 uint64_t tdb_find_nonzero_off(struct tdb_context *tdb, tdb_off_t off,
171                               uint64_t num)
172 {
173         uint64_t i, *val;
174         bool alloc = false;
175
176         val = tdb_direct(tdb, off, num * sizeof(tdb_off_t));
177         if (!unlikely(val)) {
178                 val = tdb_alloc_read(tdb, off, num * sizeof(tdb_off_t));
179                 if (!val)
180                         return num;
181                 alloc = true;
182         }
183
184         for (i = 0; i < num; i++) {
185                 if (val[i])
186                         break;
187         }
188         if (unlikely(alloc))
189                 free(val);
190         return i;
191 }
192
193 /* Return first zero offset in num offset array, or num. */
194 uint64_t tdb_find_zero_off(struct tdb_context *tdb, tdb_off_t off,
195                            uint64_t num)
196 {
197         uint64_t i, *val;
198         bool alloc = false;
199
200         val = tdb_direct(tdb, off, num * sizeof(tdb_off_t));
201         if (!unlikely(val)) {
202                 val = tdb_alloc_read(tdb, off, num * sizeof(tdb_off_t));
203                 if (!val)
204                         return num;
205                 alloc = true;
206         }
207
208         for (i = 0; i < num; i++) {
209                 if (!val[i])
210                         break;
211         }
212         if (unlikely(alloc))
213                 free(val);
214         return i;
215 }
216
217 static int fill(struct tdb_context *tdb,
218                 const void *buf, size_t size,
219                 tdb_off_t off, tdb_len_t len)
220 {
221         while (len) {
222                 size_t n = len > size ? size : len;
223
224                 if (!tdb_pwrite_all(tdb->fd, buf, n, off)) {
225                         tdb->ecode = TDB_ERR_IO;
226                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
227                                  "fill write failed: giving up!\n");
228                         return -1;
229                 }
230                 len -= n;
231                 off += n;
232         }
233         return 0;
234 }
235
236 int zero_out(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len)
237 {
238         void *p = tdb_direct(tdb, off, len);
239         if (p) {
240                 memset(p, 0, len);
241                 return 0;
242         } else {
243                 char buf[8192] = { 0 };
244                 return fill(tdb, buf, sizeof(buf), len, off);
245         }
246 }
247
248 tdb_off_t tdb_read_off(struct tdb_context *tdb, tdb_off_t off)
249 {
250         tdb_off_t pad, *ret;
251
252         ret = tdb_get(tdb, off, &pad, sizeof(ret));
253         if (!ret) {
254                 return TDB_OFF_ERR;
255         }
256         return *ret;
257 }
258
259 /* Even on files, we can get partial writes due to signals. */
260 bool tdb_pwrite_all(int fd, const void *buf, size_t len, tdb_off_t off)
261 {
262         while (len) {
263                 size_t ret;
264                 ret = pwrite(fd, buf, len, off);
265                 if (ret < 0)
266                         return false;
267                 if (ret == 0) {
268                         errno = ENOSPC;
269                         return false;
270                 }
271                 buf += ret;
272                 off += ret;
273                 len -= ret;
274         }
275         return true;
276 }
277
278 /* write a lump of data at a specified offset */
279 static int tdb_write(struct tdb_context *tdb, tdb_off_t off, 
280                      const void *buf, tdb_len_t len)
281 {
282         if (len == 0) {
283                 return 0;
284         }
285
286         if (tdb->read_only) {
287                 tdb->ecode = TDB_ERR_RDONLY;
288                 return -1;
289         }
290
291         if (tdb->methods->oob(tdb, off + len, 0) != 0)
292                 return -1;
293
294         if (tdb->map_ptr) {
295                 memcpy(off + (char *)tdb->map_ptr, buf, len);
296         } else {
297                 if (!tdb_pwrite_all(tdb->fd, buf, len, off)) {
298                         tdb->ecode = TDB_ERR_IO;
299                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
300                                  "tdb_write failed at %llu len=%llu (%s)\n",
301                                  off, len, strerror(errno));
302                         return -1;
303                 }
304         }
305         return 0;
306 }
307
308 /* read a lump of data at a specified offset */
309 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
310                     tdb_len_t len)
311 {
312         if (tdb->methods->oob(tdb, off + len, 0) != 0) {
313                 return -1;
314         }
315
316         if (tdb->map_ptr) {
317                 memcpy(buf, off + (char *)tdb->map_ptr, len);
318         } else {
319                 ssize_t ret = pread(tdb->fd, buf, len, off);
320                 if (ret != (ssize_t)len) {
321                         /* Ensure ecode is set for log fn. */
322                         tdb->ecode = TDB_ERR_IO;
323                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
324                                  "tdb_read failed at %lld "
325                                  "len=%lld ret=%lld (%s) map_size=%lld\n",
326                                  (long long)off, (long long)len,
327                                  (long long)ret, strerror(errno),
328                                  (long long)tdb->map_size);
329                         return -1;
330                 }
331         }
332         return 0;
333 }
334
335 int tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
336                       void *rec, size_t len)
337 {
338         return tdb->methods->write(tdb, off, tdb_convert(tdb, rec, len), len);
339 }
340
341 int tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
342                       void *rec, size_t len)
343 {
344         int ret = tdb->methods->read(tdb, off, rec, len);
345         tdb_convert(tdb, rec, len);
346         return ret;
347 }
348
349 int tdb_write_off(struct tdb_context *tdb, tdb_off_t off, tdb_off_t val)
350 {
351         return tdb_write_convert(tdb, off, &val, sizeof(val));
352 }
353
354 /* read a lump of data, allocating the space for it */
355 void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
356 {
357         void *buf;
358
359         /* some systems don't like zero length malloc */
360         buf = malloc(len ? len : 1);
361         if (unlikely(!buf)) {
362                 tdb->ecode = TDB_ERR_OOM;
363                 tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
364                          "tdb_alloc_read malloc failed len=%lld\n",
365                          (long long)len);
366         } else if (unlikely(tdb->methods->read(tdb, offset, buf, len))) {
367                 free(buf);
368                 buf = NULL;
369         }
370         return buf;
371 }
372
373 uint64_t hash_record(struct tdb_context *tdb, tdb_off_t off)
374 {
375         struct tdb_used_record pad, *r;
376         void *key;
377         uint64_t klen, hash;
378
379         r = tdb_get(tdb, off, &pad, sizeof(*r));
380         if (!r)
381                 /* FIXME */
382                 return 0;
383
384         klen = rec_key_length(r);
385         key = tdb_direct(tdb, off + sizeof(*r), klen);
386         if (likely(key))
387                 return tdb_hash(tdb, key, klen);
388
389         key = tdb_alloc_read(tdb, off + sizeof(*r), klen);
390         if (unlikely(!key))
391                 return 0;
392         hash = tdb_hash(tdb, key, klen);
393         free(key);
394         return hash;
395 }
396
397 /* Give a piece of tdb data to a parser */
398 int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key,
399                    tdb_off_t offset, tdb_len_t len,
400                    int (*parser)(TDB_DATA key, TDB_DATA data,
401                                  void *private_data),
402                    void *private_data)
403 {
404         TDB_DATA data;
405         int result;
406         bool allocated = false;
407
408         data.dsize = len;
409         data.dptr = tdb_direct(tdb, offset, len);
410         if (unlikely(!data.dptr)) {
411                 if (!(data.dptr = tdb_alloc_read(tdb, offset, len))) {
412                         return -1;
413                 }
414                 allocated = true;
415         }
416         result = parser(key, data, private_data);
417         if (unlikely(allocated))
418                 free(data.dptr);
419         return result;
420 }
421
422 /* expand a file.  we prefer to use ftruncate, as that is what posix
423   says to use for mmap expansion */
424 static int tdb_expand_file(struct tdb_context *tdb,
425                            tdb_len_t size, tdb_len_t addition)
426 {
427         char buf[8192];
428
429         if (tdb->read_only) {
430                 tdb->ecode = TDB_ERR_RDONLY;
431                 return -1;
432         }
433
434         /* If this fails, we try to fill anyway. */
435         if (ftruncate(tdb->fd, size+addition))
436                 ;
437
438         /* now fill the file with something. This ensures that the
439            file isn't sparse, which would be very bad if we ran out of
440            disk. This must be done with write, not via mmap */
441         memset(buf, 0x43, sizeof(buf));
442         return fill(tdb, buf, sizeof(buf), addition, size);
443 }
444
445 const void *tdb_access_read(struct tdb_context *tdb,
446                             tdb_off_t off, tdb_len_t len)
447 {
448         const void *ret = tdb_direct(tdb, off, len);
449
450         if (!ret)
451                 ret = tdb_alloc_read(tdb, off, len);
452         return ret;
453 }
454
455 void tdb_access_release(struct tdb_context *tdb, const void *p)
456 {
457         if (!tdb->map_ptr
458             || (char *)p < (char *)tdb->map_ptr
459             || (char *)p >= (char *)tdb->map_ptr + tdb->map_size)
460                 free((void *)p);
461 }
462
463 #if 0
464 /* write a lump of data at a specified offset */
465 static int tdb_write(struct tdb_context *tdb, tdb_off_t off, 
466                      const void *buf, tdb_len_t len)
467 {
468         if (len == 0) {
469                 return 0;
470         }
471
472         if (tdb->read_only || tdb->traverse_read) {
473                 tdb->ecode = TDB_ERR_RDONLY;
474                 return -1;
475         }
476
477         if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0)
478                 return -1;
479
480         if (tdb->map_ptr) {
481                 memcpy(off + (char *)tdb->map_ptr, buf, len);
482         } else {
483                 ssize_t written = pwrite(tdb->fd, buf, len, off);
484                 if ((written != (ssize_t)len) && (written != -1)) {
485                         /* try once more */
486                         tdb->ecode = TDB_ERR_IO;
487                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: wrote only "
488                                  "%d of %d bytes at %d, trying once more\n",
489                                  (int)written, len, off));
490                         written = pwrite(tdb->fd, (const char *)buf+written,
491                                          len-written,
492                                          off+written);
493                 }
494                 if (written == -1) {
495                         /* Ensure ecode is set for log fn. */
496                         tdb->ecode = TDB_ERR_IO;
497                         TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d "
498                                  "len=%d (%s)\n", off, len, strerror(errno)));
499                         return -1;
500                 } else if (written != (ssize_t)len) {
501                         tdb->ecode = TDB_ERR_IO;
502                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: failed to "
503                                  "write %d bytes at %d in two attempts\n",
504                                  len, off));
505                         return -1;
506                 }
507         }
508         return 0;
509 }
510
511
512
513 /*
514   do an unlocked scan of the hash table heads to find the next non-zero head. The value
515   will then be confirmed with the lock held
516 */              
517 static void tdb_next_hash_chain(struct tdb_context *tdb, uint32_t *chain)
518 {
519         uint32_t h = *chain;
520         if (tdb->map_ptr) {
521                 for (;h < tdb->header.hash_size;h++) {
522                         if (0 != *(uint32_t *)(TDB_HASH_TOP(h) + (unsigned char *)tdb->map_ptr)) {
523                                 break;
524                         }
525                 }
526         } else {
527                 uint32_t off=0;
528                 for (;h < tdb->header.hash_size;h++) {
529                         if (tdb_ofs_read(tdb, TDB_HASH_TOP(h), &off) != 0 || off != 0) {
530                                 break;
531                         }
532                 }
533         }
534         (*chain) = h;
535 }
536
537
538 /* expand the database by expanding the underlying file and doing the
539    mmap again if necessary */
540 int tdb_expand(struct tdb_context *tdb)
541 {
542         struct tdb_record rec;
543         tdb_off_t offset, new_size;     
544
545         /* We have to lock every hash bucket and every free list. */
546         do {
547                 
548
549         if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
550                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n"));
551                 return -1;
552         }
553
554         /* must know about any previous expansions by another process */
555         tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
556
557         /* always make room for at least 100 more records, and at
558            least 25% more space. Round the database up to a multiple
559            of the page size */
560         new_size = MAX(tdb->map_size + size*100, tdb->map_size * 1.25);
561         size = TDB_ALIGN(new_size, tdb->page_size) - tdb->map_size;
562
563         if (!(tdb->flags & TDB_INTERNAL))
564                 tdb_munmap(tdb);
565
566         /*
567          * We must ensure the file is unmapped before doing this
568          * to ensure consistency with systems like OpenBSD where
569          * writes and mmaps are not consistent.
570          */
571
572         /* expand the file itself */
573         if (!(tdb->flags & TDB_INTERNAL)) {
574                 if (tdb->methods->tdb_expand_file(tdb, tdb->map_size, size) != 0)
575                         goto fail;
576         }
577
578         tdb->map_size += size;
579
580         if (tdb->flags & TDB_INTERNAL) {
581                 char *new_map_ptr = (char *)realloc(tdb->map_ptr,
582                                                     tdb->map_size);
583                 if (!new_map_ptr) {
584                         tdb->map_size -= size;
585                         goto fail;
586                 }
587                 tdb->map_ptr = new_map_ptr;
588         } else {
589                 /*
590                  * We must ensure the file is remapped before adding the space
591                  * to ensure consistency with systems like OpenBSD where
592                  * writes and mmaps are not consistent.
593                  */
594
595                 /* We're ok if the mmap fails as we'll fallback to read/write */
596                 tdb_mmap(tdb);
597         }
598
599         /* form a new freelist record */
600         memset(&rec,'\0',sizeof(rec));
601         rec.rec_len = size - sizeof(rec);
602
603         /* link it into the free list */
604         offset = tdb->map_size - size;
605         if (tdb_free(tdb, offset, &rec) == -1)
606                 goto fail;
607
608         tdb_unlock(tdb, -1, F_WRLCK);
609         return 0;
610  fail:
611         tdb_unlock(tdb, -1, F_WRLCK);
612         return -1;
613 }
614
615 /* read/write a tdb_off_t */
616 int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d)
617 {
618         return tdb->methods->tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV());
619 }
620
621 int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d)
622 {
623         tdb_off_t off = *d;
624         return tdb->methods->tdb_write(tdb, offset, CONVERT(off), sizeof(*d));
625 }
626
627
628 /* read/write a record */
629 int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
630 {
631         if (tdb->methods->tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
632                 return -1;
633         if (TDB_BAD_MAGIC(rec)) {
634                 /* Ensure ecode is set for log fn. */
635                 tdb->ecode = TDB_ERR_CORRUPT;
636                 TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
637                 return -1;
638         }
639         return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0);
640 }
641
642 int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
643 {
644         struct tdb_record r = *rec;
645         return tdb->methods->tdb_write(tdb, offset, CONVERT(r), sizeof(r));
646 }
647 #endif
648
649 static const struct tdb_methods io_methods = {
650         tdb_read,
651         tdb_write,
652         tdb_oob,
653         tdb_expand_file,
654 };
655
656 /*
657   initialise the default methods table
658 */
659 void tdb_io_init(struct tdb_context *tdb)
660 {
661         tdb->methods = &io_methods;
662 }