]> git.ozlabs.org Git - ccan/blob - ccan/tdb/io.c
tdb2: limit coalescing based on how successful we are.
[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
307 /* expand the database at least size bytes by expanding the underlying
308    file and doing the mmap again if necessary */
309 int tdb_expand(struct tdb_context *tdb, tdb_off_t size)
310 {
311         struct tdb_record rec;
312         tdb_off_t offset, new_size;     
313
314         if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
315                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n"));
316                 return -1;
317         }
318
319         /* must know about any previous expansions by another process */
320         tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
321
322         /* always make room for at least 100 more records, and at
323            least 25% more space. Round the database up to a multiple
324            of the page size */
325         new_size = MAX(tdb->map_size + size*100, tdb->map_size * 1.25);
326         size = TDB_ALIGN(new_size, tdb->page_size) - tdb->map_size;
327
328         if (!(tdb->flags & TDB_INTERNAL))
329                 tdb_munmap(tdb);
330
331         /*
332          * We must ensure the file is unmapped before doing this
333          * to ensure consistency with systems like OpenBSD where
334          * writes and mmaps are not consistent.
335          */
336
337         /* expand the file itself */
338         if (!(tdb->flags & TDB_INTERNAL)) {
339                 if (tdb->methods->tdb_expand_file(tdb, tdb->map_size, size) != 0)
340                         goto fail;
341         }
342
343         tdb->map_size += size;
344
345         if (tdb->flags & TDB_INTERNAL) {
346                 char *new_map_ptr = (char *)realloc(tdb->map_ptr,
347                                                     tdb->map_size);
348                 if (!new_map_ptr) {
349                         tdb->map_size -= size;
350                         goto fail;
351                 }
352                 tdb->map_ptr = new_map_ptr;
353         } else {
354                 /*
355                  * We must ensure the file is remapped before adding the space
356                  * to ensure consistency with systems like OpenBSD where
357                  * writes and mmaps are not consistent.
358                  */
359
360                 /* We're ok if the mmap fails as we'll fallback to read/write */
361                 tdb_mmap(tdb);
362         }
363
364         /* form a new freelist record */
365         memset(&rec,'\0',sizeof(rec));
366         rec.rec_len = size - sizeof(rec);
367
368         /* link it into the free list */
369         offset = tdb->map_size - size;
370         if (tdb_free(tdb, offset, &rec) == -1)
371                 goto fail;
372
373         tdb_unlock(tdb, -1, F_WRLCK);
374         return 0;
375  fail:
376         tdb_unlock(tdb, -1, F_WRLCK);
377         return -1;
378 }
379
380 /* read/write a tdb_off_t */
381 int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d)
382 {
383         return tdb->methods->tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV());
384 }
385
386 int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d)
387 {
388         tdb_off_t off = *d;
389         return tdb->methods->tdb_write(tdb, offset, CONVERT(off), sizeof(*d));
390 }
391
392
393 /* read a lump of data, allocating the space for it */
394 unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
395 {
396         unsigned char *buf;
397
398         /* some systems don't like zero length malloc */
399
400         if (!(buf = (unsigned char *)malloc(len ? len : 1))) {
401                 /* Ensure ecode is set for log fn. */
402                 tdb->ecode = TDB_ERR_OOM;
403                 TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_alloc_read malloc failed len=%d (%s)\n",
404                            len, strerror(errno)));
405                 return NULL;
406         }
407         if (tdb->methods->tdb_read(tdb, offset, buf, len, 0) == -1) {
408                 SAFE_FREE(buf);
409                 return NULL;
410         }
411         return buf;
412 }
413
414 /* Give a piece of tdb data to a parser */
415
416 int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key,
417                    tdb_off_t offset, tdb_len_t len,
418                    int (*parser)(TDB_DATA key, TDB_DATA data,
419                                  void *private_data),
420                    void *private_data)
421 {
422         TDB_DATA data;
423         int result;
424
425         data.dsize = len;
426
427         if ((tdb->transaction == NULL) && (tdb->map_ptr != NULL)) {
428                 /*
429                  * Optimize by avoiding the malloc/memcpy/free, point the
430                  * parser directly at the mmap area.
431                  */
432                 if (tdb->methods->tdb_oob(tdb, offset+len, 0) != 0) {
433                         return -1;
434                 }
435                 data.dptr = offset + (unsigned char *)tdb->map_ptr;
436                 return parser(key, data, private_data);
437         }
438
439         if (!(data.dptr = tdb_alloc_read(tdb, offset, len))) {
440                 return -1;
441         }
442
443         result = parser(key, data, private_data);
444         free(data.dptr);
445         return result;
446 }
447
448 /* read/write a record */
449 int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
450 {
451         if (tdb->methods->tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
452                 return -1;
453         if (TDB_BAD_MAGIC(rec)) {
454                 /* Ensure ecode is set for log fn. */
455                 tdb->ecode = TDB_ERR_CORRUPT;
456                 TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
457                 return -1;
458         }
459         return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0);
460 }
461
462 int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
463 {
464         struct tdb_record r = *rec;
465         return tdb->methods->tdb_write(tdb, offset, CONVERT(r), sizeof(r));
466 }
467
468 static const struct tdb_methods io_methods = {
469         tdb_read,
470         tdb_write,
471         tdb_next_hash_chain,
472         tdb_oob,
473         tdb_expand_file,
474 };
475
476 /*
477   initialise the default methods table
478 */
479 void tdb_io_init(struct tdb_context *tdb)
480 {
481         tdb->methods = &io_methods;
482 }