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