]> git.ozlabs.org Git - ccan/blob - ccan/tdb2/tdb1_io.c
tdb2: include tdb1 source in tests.
[ccan] / ccan / tdb2 / tdb1_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
10      ** NOTE! The following LGPL license applies to the tdb
11      ** library. This does NOT imply that all of Samba is released
12      ** under the LGPL
13
14    This library is free software; you can redistribute it and/or
15    modify it under the terms of the GNU Lesser General Public
16    License as published by the Free Software Foundation; either
17    version 3 of the License, or (at your option) any later version.
18
19    This library is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22    Lesser General Public License for more details.
23
24    You should have received a copy of the GNU Lesser General Public
25    License along with this library; if not, see <http://www.gnu.org/licenses/>.
26 */
27
28
29 #include "tdb1_private.h"
30 #ifndef MAX
31 #define MAX(a,b) ((a) > (b) ? (a) : (b))
32 #endif
33
34 /* check for an out of bounds access - if it is out of bounds then
35    see if the database has been expanded by someone else and expand
36    if necessary
37    note that "len" is the minimum length needed for the db
38 */
39 static int tdb1_oob(struct tdb1_context *tdb, tdb1_off_t len, int probe)
40 {
41         struct stat st;
42         if (len <= tdb->map_size)
43                 return 0;
44         if (tdb->flags & TDB1_INTERNAL) {
45                 if (!probe) {
46                         /* Ensure ecode is set for log fn. */
47                         tdb->ecode = TDB1_ERR_IO;
48                         TDB1_LOG((tdb, TDB1_DEBUG_FATAL,"tdb1_oob len %d beyond internal malloc size %d\n",
49                                  (int)len, (int)tdb->map_size));
50                 }
51                 return -1;
52         }
53
54         if (fstat(tdb->fd, &st) == -1) {
55                 tdb->ecode = TDB1_ERR_IO;
56                 return -1;
57         }
58
59         if (st.st_size < (size_t)len) {
60                 if (!probe) {
61                         /* Ensure ecode is set for log fn. */
62                         tdb->ecode = TDB1_ERR_IO;
63                         TDB1_LOG((tdb, TDB1_DEBUG_FATAL,"tdb1_oob len %d beyond eof at %d\n",
64                                  (int)len, (int)st.st_size));
65                 }
66                 return -1;
67         }
68
69         /* Unmap, update size, remap */
70         if (tdb1_munmap(tdb) == -1) {
71                 tdb->ecode = TDB1_ERR_IO;
72                 return -1;
73         }
74         tdb->map_size = st.st_size;
75         tdb1_mmap(tdb);
76         return 0;
77 }
78
79 /* write a lump of data at a specified offset */
80 static int tdb1_write(struct tdb1_context *tdb, tdb1_off_t off,
81                      const void *buf, tdb1_len_t len)
82 {
83         if (len == 0) {
84                 return 0;
85         }
86
87         if (tdb->read_only || tdb->traverse_read) {
88                 tdb->ecode = TDB1_ERR_RDONLY;
89                 return -1;
90         }
91
92         if (tdb->methods->tdb1_oob(tdb, off + len, 0) != 0)
93                 return -1;
94
95         if (tdb->map_ptr) {
96                 memcpy(off + (char *)tdb->map_ptr, buf, len);
97         } else {
98                 ssize_t written = pwrite(tdb->fd, buf, len, off);
99                 if ((written != (ssize_t)len) && (written != -1)) {
100                         /* try once more */
101                         tdb->ecode = TDB1_ERR_IO;
102                         TDB1_LOG((tdb, TDB1_DEBUG_FATAL, "tdb1_write: wrote only "
103                                  "%d of %d bytes at %d, trying once more\n",
104                                  (int)written, len, off));
105                         written = pwrite(tdb->fd, (const char *)buf+written,
106                                          len-written,
107                                          off+written);
108                 }
109                 if (written == -1) {
110                         /* Ensure ecode is set for log fn. */
111                         tdb->ecode = TDB1_ERR_IO;
112                         TDB1_LOG((tdb, TDB1_DEBUG_FATAL,"tdb1_write failed at %d "
113                                  "len=%d (%s)\n", off, len, strerror(errno)));
114                         return -1;
115                 } else if (written != (ssize_t)len) {
116                         tdb->ecode = TDB1_ERR_IO;
117                         TDB1_LOG((tdb, TDB1_DEBUG_FATAL, "tdb1_write: failed to "
118                                  "write %d bytes at %d in two attempts\n",
119                                  len, off));
120                         return -1;
121                 }
122         }
123         return 0;
124 }
125
126 /* Endian conversion: we only ever deal with 4 byte quantities */
127 void *tdb1_convert(void *buf, uint32_t size)
128 {
129         uint32_t i, *p = (uint32_t *)buf;
130         for (i = 0; i < size / 4; i++)
131                 p[i] = TDB1_BYTEREV(p[i]);
132         return buf;
133 }
134
135
136 /* read a lump of data at a specified offset, maybe convert */
137 static int tdb1_read(struct tdb1_context *tdb, tdb1_off_t off, void *buf,
138                     tdb1_len_t len, int cv)
139 {
140         if (tdb->methods->tdb1_oob(tdb, off + len, 0) != 0) {
141                 return -1;
142         }
143
144         if (tdb->map_ptr) {
145                 memcpy(buf, off + (char *)tdb->map_ptr, len);
146         } else {
147                 ssize_t ret = pread(tdb->fd, buf, len, off);
148                 if (ret != (ssize_t)len) {
149                         /* Ensure ecode is set for log fn. */
150                         tdb->ecode = TDB1_ERR_IO;
151                         TDB1_LOG((tdb, TDB1_DEBUG_FATAL,"tdb1_read failed at %d "
152                                  "len=%d ret=%d (%s) map_size=%d\n",
153                                  (int)off, (int)len, (int)ret, strerror(errno),
154                                  (int)tdb->map_size));
155                         return -1;
156                 }
157         }
158         if (cv) {
159                 tdb1_convert(buf, len);
160         }
161         return 0;
162 }
163
164
165
166 /*
167   do an unlocked scan of the hash table heads to find the next non-zero head. The value
168   will then be confirmed with the lock held
169 */
170 static void tdb1_next_hash_chain(struct tdb1_context *tdb, uint32_t *chain)
171 {
172         uint32_t h = *chain;
173         if (tdb->map_ptr) {
174                 for (;h < tdb->header.hash_size;h++) {
175                         if (0 != *(uint32_t *)(TDB1_HASH_TOP(h) + (unsigned char *)tdb->map_ptr)) {
176                                 break;
177                         }
178                 }
179         } else {
180                 uint32_t off=0;
181                 for (;h < tdb->header.hash_size;h++) {
182                         if (tdb1_ofs_read(tdb, TDB1_HASH_TOP(h), &off) != 0 || off != 0) {
183                                 break;
184                         }
185                 }
186         }
187         (*chain) = h;
188 }
189
190
191 int tdb1_munmap(struct tdb1_context *tdb)
192 {
193         if (tdb->flags & TDB1_INTERNAL)
194                 return 0;
195
196 #if HAVE_MMAP
197         if (tdb->map_ptr) {
198                 int ret;
199
200                 ret = munmap(tdb->map_ptr, tdb->map_size);
201                 if (ret != 0)
202                         return ret;
203         }
204 #endif
205         tdb->map_ptr = NULL;
206         return 0;
207 }
208
209 void tdb1_mmap(struct tdb1_context *tdb)
210 {
211         if (tdb->flags & TDB1_INTERNAL)
212                 return;
213
214 #if HAVE_MMAP
215         if (!(tdb->flags & TDB1_NOMMAP)) {
216                 tdb->map_ptr = mmap(NULL, tdb->map_size,
217                                     PROT_READ|(tdb->read_only? 0:PROT_WRITE),
218                                     MAP_SHARED|MAP_FILE, tdb->fd, 0);
219
220                 /*
221                  * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
222                  */
223
224                 if (tdb->map_ptr == MAP_FAILED) {
225                         tdb->map_ptr = NULL;
226                         TDB1_LOG((tdb, TDB1_DEBUG_WARNING, "tdb1_mmap failed for size %d (%s)\n",
227                                  tdb->map_size, strerror(errno)));
228                 }
229         } else {
230                 tdb->map_ptr = NULL;
231         }
232 #else
233         tdb->map_ptr = NULL;
234 #endif
235 }
236
237 /* expand a file.  we prefer to use ftruncate, as that is what posix
238   says to use for mmap expansion */
239 static int tdb1_expand_file(struct tdb1_context *tdb, tdb1_off_t size, tdb1_off_t addition)
240 {
241         char buf[8192];
242
243         if (tdb->read_only || tdb->traverse_read) {
244                 tdb->ecode = TDB1_ERR_RDONLY;
245                 return -1;
246         }
247
248         if (ftruncate(tdb->fd, size+addition) == -1) {
249                 char b = 0;
250                 ssize_t written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
251                 if (written == 0) {
252                         /* try once more, potentially revealing errno */
253                         written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
254                 }
255                 if (written == 0) {
256                         /* again - give up, guessing errno */
257                         errno = ENOSPC;
258                 }
259                 if (written != 1) {
260                         TDB1_LOG((tdb, TDB1_DEBUG_FATAL, "expand_file to %d failed (%s)\n",
261                                  size+addition, strerror(errno)));
262                         return -1;
263                 }
264         }
265
266         /* now fill the file with something. This ensures that the
267            file isn't sparse, which would be very bad if we ran out of
268            disk. This must be done with write, not via mmap */
269         memset(buf, TDB1_PAD_BYTE, sizeof(buf));
270         while (addition) {
271                 size_t n = addition>sizeof(buf)?sizeof(buf):addition;
272                 ssize_t written = pwrite(tdb->fd, buf, n, size);
273                 if (written == 0) {
274                         /* prevent infinite loops: try _once_ more */
275                         written = pwrite(tdb->fd, buf, n, size);
276                 }
277                 if (written == 0) {
278                         /* give up, trying to provide a useful errno */
279                         TDB1_LOG((tdb, TDB1_DEBUG_FATAL, "expand_file write "
280                                 "returned 0 twice: giving up!\n"));
281                         errno = ENOSPC;
282                         return -1;
283                 } else if (written == -1) {
284                         TDB1_LOG((tdb, TDB1_DEBUG_FATAL, "expand_file write of "
285                                  "%d bytes failed (%s)\n", (int)n,
286                                  strerror(errno)));
287                         return -1;
288                 } else if (written != n) {
289                         TDB1_LOG((tdb, TDB1_DEBUG_WARNING, "expand_file: wrote "
290                                  "only %d of %d bytes - retrying\n", (int)written,
291                                  (int)n));
292                 }
293                 addition -= written;
294                 size += written;
295         }
296         return 0;
297 }
298
299
300 /* expand the database at least size bytes by expanding the underlying
301    file and doing the mmap again if necessary */
302 int tdb1_expand(struct tdb1_context *tdb, tdb1_off_t size)
303 {
304         struct tdb1_record rec;
305         tdb1_off_t offset, new_size, top_size, map_size;
306
307         if (tdb1_lock(tdb, -1, F_WRLCK) == -1) {
308                 TDB1_LOG((tdb, TDB1_DEBUG_ERROR, "lock failed in tdb1_expand\n"));
309                 return -1;
310         }
311
312         /* must know about any previous expansions by another process */
313         tdb->methods->tdb1_oob(tdb, tdb->map_size + 1, 1);
314
315         /* limit size in order to avoid using up huge amounts of memory for
316          * in memory tdbs if an oddball huge record creeps in */
317         if (size > 100 * 1024) {
318                 top_size = tdb->map_size + size * 2;
319         } else {
320                 top_size = tdb->map_size + size * 100;
321         }
322
323         /* always make room for at least top_size more records, and at
324            least 25% more space. if the DB is smaller than 100MiB,
325            otherwise grow it by 10% only. */
326         if (tdb->map_size > 100 * 1024 * 1024) {
327                 map_size = tdb->map_size * 1.10;
328         } else {
329                 map_size = tdb->map_size * 1.25;
330         }
331
332         /* Round the database up to a multiple of the page size */
333         new_size = MAX(top_size, map_size);
334         size = TDB1_ALIGN(new_size, tdb->page_size) - tdb->map_size;
335
336         if (!(tdb->flags & TDB1_INTERNAL))
337                 tdb1_munmap(tdb);
338
339         /*
340          * We must ensure the file is unmapped before doing this
341          * to ensure consistency with systems like OpenBSD where
342          * writes and mmaps are not consistent.
343          */
344
345         /* expand the file itself */
346         if (!(tdb->flags & TDB1_INTERNAL)) {
347                 if (tdb->methods->tdb1_expand_file(tdb, tdb->map_size, size) != 0)
348                         goto fail;
349         }
350
351         tdb->map_size += size;
352
353         if (tdb->flags & TDB1_INTERNAL) {
354                 char *new_map_ptr = (char *)realloc(tdb->map_ptr,
355                                                     tdb->map_size);
356                 if (!new_map_ptr) {
357                         tdb->map_size -= size;
358                         goto fail;
359                 }
360                 tdb->map_ptr = new_map_ptr;
361         } else {
362                 /*
363                  * We must ensure the file is remapped before adding the space
364                  * to ensure consistency with systems like OpenBSD where
365                  * writes and mmaps are not consistent.
366                  */
367
368                 /* We're ok if the mmap fails as we'll fallback to read/write */
369                 tdb1_mmap(tdb);
370         }
371
372         /* form a new freelist record */
373         memset(&rec,'\0',sizeof(rec));
374         rec.rec_len = size - sizeof(rec);
375
376         /* link it into the free list */
377         offset = tdb->map_size - size;
378         if (tdb1_free(tdb, offset, &rec) == -1)
379                 goto fail;
380
381         tdb1_unlock(tdb, -1, F_WRLCK);
382         return 0;
383  fail:
384         tdb1_unlock(tdb, -1, F_WRLCK);
385         return -1;
386 }
387
388 /* read/write a tdb1_off_t */
389 int tdb1_ofs_read(struct tdb1_context *tdb, tdb1_off_t offset, tdb1_off_t *d)
390 {
391         return tdb->methods->tdb1_read(tdb, offset, (char*)d, sizeof(*d), TDB1_DOCONV());
392 }
393
394 int tdb1_ofs_write(struct tdb1_context *tdb, tdb1_off_t offset, tdb1_off_t *d)
395 {
396         tdb1_off_t off = *d;
397         return tdb->methods->tdb1_write(tdb, offset, TDB1_CONV(off), sizeof(*d));
398 }
399
400
401 /* read a lump of data, allocating the space for it */
402 unsigned char *tdb1_alloc_read(struct tdb1_context *tdb, tdb1_off_t offset, tdb1_len_t len)
403 {
404         unsigned char *buf;
405
406         /* some systems don't like zero length malloc */
407
408         if (!(buf = (unsigned char *)malloc(len ? len : 1))) {
409                 /* Ensure ecode is set for log fn. */
410                 tdb->ecode = TDB1_ERR_OOM;
411                 TDB1_LOG((tdb, TDB1_DEBUG_ERROR,"tdb1_alloc_read malloc failed len=%d (%s)\n",
412                            len, strerror(errno)));
413                 return NULL;
414         }
415         if (tdb->methods->tdb1_read(tdb, offset, buf, len, 0) == -1) {
416                 SAFE_FREE(buf);
417                 return NULL;
418         }
419         return buf;
420 }
421
422 /* Give a piece of tdb data to a parser */
423
424 int tdb1_parse_data(struct tdb1_context *tdb, TDB1_DATA key,
425                    tdb1_off_t offset, tdb1_len_t len,
426                    int (*parser)(TDB1_DATA key, TDB1_DATA data,
427                                  void *private_data),
428                    void *private_data)
429 {
430         TDB1_DATA data;
431         int result;
432
433         data.dsize = len;
434
435         if ((tdb->transaction == NULL) && (tdb->map_ptr != NULL)) {
436                 /*
437                  * Optimize by avoiding the malloc/memcpy/free, point the
438                  * parser directly at the mmap area.
439                  */
440                 if (tdb->methods->tdb1_oob(tdb, offset+len, 0) != 0) {
441                         return -1;
442                 }
443                 data.dptr = offset + (unsigned char *)tdb->map_ptr;
444                 return parser(key, data, private_data);
445         }
446
447         if (!(data.dptr = tdb1_alloc_read(tdb, offset, len))) {
448                 return -1;
449         }
450
451         result = parser(key, data, private_data);
452         free(data.dptr);
453         return result;
454 }
455
456 /* read/write a record */
457 int tdb1_rec_read(struct tdb1_context *tdb, tdb1_off_t offset, struct tdb1_record *rec)
458 {
459         if (tdb->methods->tdb1_read(tdb, offset, rec, sizeof(*rec),TDB1_DOCONV()) == -1)
460                 return -1;
461         if (TDB1_BAD_MAGIC(rec)) {
462                 /* Ensure ecode is set for log fn. */
463                 tdb->ecode = TDB1_ERR_CORRUPT;
464                 TDB1_LOG((tdb, TDB1_DEBUG_FATAL,"tdb1_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
465                 return -1;
466         }
467         return tdb->methods->tdb1_oob(tdb, rec->next+sizeof(*rec), 0);
468 }
469
470 int tdb1_rec_write(struct tdb1_context *tdb, tdb1_off_t offset, struct tdb1_record *rec)
471 {
472         struct tdb1_record r = *rec;
473         return tdb->methods->tdb1_write(tdb, offset, TDB1_CONV(r), sizeof(r));
474 }
475
476 static const struct tdb1_methods io1_methods = {
477         tdb1_read,
478         tdb1_write,
479         tdb1_next_hash_chain,
480         tdb1_oob,
481         tdb1_expand_file,
482 };
483
484 /*
485   initialise the default methods table
486 */
487 void tdb1_io_init(struct tdb1_context *tdb)
488 {
489         tdb->methods = &io1_methods;
490 }