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