Use -O not -O3: reduces ccan/tdb test time from 24 to 18 seconds.
[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, *p = (uint32_t *)buf;
129         for (i = 0; i < size / 4; i++)
130                 p[i] = TDB_BYTEREV(p[i]);
131         return buf;
132 }
133
134
135 /* read a lump of data at a specified offset, maybe convert */
136 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf, 
137                     tdb_len_t len, int cv)
138 {
139         if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0) {
140                 return -1;
141         }
142
143         if (tdb->map_ptr) {
144                 memcpy(buf, off + (char *)tdb->map_ptr, len);
145         } else {
146                 ssize_t ret = pread(tdb->fd, buf, len, off);
147                 if (ret != (ssize_t)len) {
148                         /* Ensure ecode is set for log fn. */
149                         tdb->ecode = TDB_ERR_IO;
150                         TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %d "
151                                  "len=%d ret=%d (%s) map_size=%d\n",
152                                  (int)off, (int)len, (int)ret, strerror(errno),
153                                  (int)tdb->map_size));
154                         return -1;
155                 }
156         }
157         if (cv) {
158                 tdb_convert(buf, len);
159         }
160         return 0;
161 }
162
163
164
165 /*
166   do an unlocked scan of the hash table heads to find the next non-zero head. The value
167   will then be confirmed with the lock held
168 */              
169 static void tdb_next_hash_chain(struct tdb_context *tdb, uint32_t *chain)
170 {
171         uint32_t h = *chain;
172         if (tdb->map_ptr) {
173                 for (;h < tdb->header.hash_size;h++) {
174                         if (0 != *(uint32_t *)(TDB_HASH_TOP(h) + (unsigned char *)tdb->map_ptr)) {
175                                 break;
176                         }
177                 }
178         } else {
179                 uint32_t off=0;
180                 for (;h < tdb->header.hash_size;h++) {
181                         if (tdb_ofs_read(tdb, TDB_HASH_TOP(h), &off) != 0 || off != 0) {
182                                 break;
183                         }
184                 }
185         }
186         (*chain) = h;
187 }
188
189
190 int tdb_munmap(struct tdb_context *tdb)
191 {
192         if (tdb->flags & TDB_INTERNAL)
193                 return 0;
194
195 #if HAVE_MMAP
196         if (tdb->map_ptr) {
197                 int ret = munmap(tdb->map_ptr, tdb->map_size);
198                 if (ret != 0)
199                         return ret;
200         }
201 #endif
202         tdb->map_ptr = NULL;
203         return 0;
204 }
205
206 void tdb_mmap(struct tdb_context *tdb)
207 {
208         if (tdb->flags & TDB_INTERNAL)
209                 return;
210
211 #if HAVE_MMAP
212         if (!(tdb->flags & TDB_NOMMAP)) {
213                 tdb->map_ptr = mmap(NULL, tdb->map_size, 
214                                     PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
215                                     MAP_SHARED, tdb->fd, 0);
216
217                 /*
218                  * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
219                  */
220
221                 if (tdb->map_ptr == MAP_FAILED) {
222                         tdb->map_ptr = NULL;
223                         TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %d (%s)\n", 
224                                  tdb->map_size, strerror(errno)));
225                 }
226         } else {
227                 tdb->map_ptr = NULL;
228         }
229 #else
230         tdb->map_ptr = NULL;
231 #endif
232 }
233
234 /* expand a file.  we prefer to use ftruncate, as that is what posix
235   says to use for mmap expansion */
236 static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition)
237 {
238         char buf[8192];
239
240         if (tdb->read_only || tdb->traverse_read) {
241                 tdb->ecode = TDB_ERR_RDONLY;
242                 return -1;
243         }
244
245         if (ftruncate(tdb->fd, size+addition) == -1) {
246                 char b = 0;
247                 ssize_t written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
248                 if (written == 0) {
249                         /* try once more, potentially revealing errno */
250                         written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
251                 }
252                 if (written == 0) {
253                         /* again - give up, guessing errno */
254                         errno = ENOSPC;
255                 }
256                 if (written != 1) {
257                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n", 
258                                  size+addition, strerror(errno)));
259                         return -1;
260                 }
261         }
262
263         /* now fill the file with something. This ensures that the
264            file isn't sparse, which would be very bad if we ran out of
265            disk. This must be done with write, not via mmap */
266         memset(buf, TDB_PAD_BYTE, sizeof(buf));
267         while (addition) {
268                 size_t n = addition>sizeof(buf)?sizeof(buf):addition;
269                 ssize_t written = pwrite(tdb->fd, buf, n, size);
270                 if (written == 0) {
271                         /* prevent infinite loops: try _once_ more */
272                         written = pwrite(tdb->fd, buf, n, size);
273                 }
274                 if (written == 0) {
275                         /* give up, trying to provide a useful errno */
276                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write "
277                                 "returned 0 twice: giving up!\n"));
278                         errno = ENOSPC;
279                         return -1;
280                 } else if (written == -1) {
281                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of "
282                                  "%d bytes failed (%s)\n", (int)n,
283                                  strerror(errno)));
284                         return -1;
285                 } else if (written != n) {
286                         TDB_LOG((tdb, TDB_DEBUG_WARNING, "expand_file: wrote "
287                                  "only %d of %d bytes - retrying\n", (int)written,
288                                  (int)n));
289                 }
290                 addition -= written;
291                 size += written;
292         }
293         return 0;
294 }
295
296
297 /* expand the database at least size bytes by expanding the underlying
298    file and doing the mmap again if necessary */
299 int tdb_expand(struct tdb_context *tdb, tdb_off_t size)
300 {
301         struct list_struct rec;
302         tdb_off_t offset, new_size;     
303
304         if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
305                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n"));
306                 return -1;
307         }
308
309         /* must know about any previous expansions by another process */
310         tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
311
312         /* always make room for at least 100 more records, and at
313            least 25% more space. Round the database up to a multiple
314            of the page size */
315         new_size = MAX(tdb->map_size + size*100, tdb->map_size * 1.25);
316         size = TDB_ALIGN(new_size, tdb->page_size) - tdb->map_size;
317
318         if (!(tdb->flags & TDB_INTERNAL))
319                 tdb_munmap(tdb);
320
321         /*
322          * We must ensure the file is unmapped before doing this
323          * to ensure consistency with systems like OpenBSD where
324          * writes and mmaps are not consistent.
325          */
326
327         /* expand the file itself */
328         if (!(tdb->flags & TDB_INTERNAL)) {
329                 if (tdb->methods->tdb_expand_file(tdb, tdb->map_size, size) != 0)
330                         goto fail;
331         }
332
333         tdb->map_size += size;
334
335         if (tdb->flags & TDB_INTERNAL) {
336                 char *new_map_ptr = (char *)realloc(tdb->map_ptr,
337                                                     tdb->map_size);
338                 if (!new_map_ptr) {
339                         tdb->map_size -= size;
340                         goto fail;
341                 }
342                 tdb->map_ptr = new_map_ptr;
343         } else {
344                 /*
345                  * We must ensure the file is remapped before adding the space
346                  * to ensure consistency with systems like OpenBSD where
347                  * writes and mmaps are not consistent.
348                  */
349
350                 /* We're ok if the mmap fails as we'll fallback to read/write */
351                 tdb_mmap(tdb);
352         }
353
354         /* form a new freelist record */
355         memset(&rec,'\0',sizeof(rec));
356         rec.rec_len = size - sizeof(rec);
357
358         /* link it into the free list */
359         offset = tdb->map_size - size;
360         if (tdb_free(tdb, offset, &rec) == -1)
361                 goto fail;
362
363         tdb_unlock(tdb, -1, F_WRLCK);
364         return 0;
365  fail:
366         tdb_unlock(tdb, -1, F_WRLCK);
367         return -1;
368 }
369
370 /* read/write a tdb_off_t */
371 int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d)
372 {
373         return tdb->methods->tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV());
374 }
375
376 int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d)
377 {
378         tdb_off_t off = *d;
379         return tdb->methods->tdb_write(tdb, offset, CONVERT(off), sizeof(*d));
380 }
381
382
383 /* read a lump of data, allocating the space for it */
384 unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
385 {
386         unsigned char *buf;
387
388         /* some systems don't like zero length malloc */
389         if (!(buf = (unsigned char *)malloc(len ? len : 1))) {
390                 /* Ensure ecode is set for log fn. */
391                 tdb->ecode = TDB_ERR_OOM;
392                 TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_alloc_read malloc failed len=%d (%s)\n",
393                            len, strerror(errno)));
394                 return NULL;
395         }
396         if (tdb->methods->tdb_read(tdb, offset, buf, len, 0) == -1) {
397                 SAFE_FREE(buf);
398                 return NULL;
399         }
400         return buf;
401 }
402
403 /* Give a piece of tdb data to a parser */
404
405 int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key,
406                    tdb_off_t offset, tdb_len_t len,
407                    int (*parser)(TDB_DATA key, TDB_DATA data,
408                                  void *private_data),
409                    void *private_data)
410 {
411         TDB_DATA data;
412         int result;
413
414         data.dsize = len;
415
416         if ((tdb->transaction == NULL) && (tdb->map_ptr != NULL)) {
417                 /*
418                  * Optimize by avoiding the malloc/memcpy/free, point the
419                  * parser directly at the mmap area.
420                  */
421                 if (tdb->methods->tdb_oob(tdb, offset+len, 0) != 0) {
422                         return -1;
423                 }
424                 data.dptr = offset + (unsigned char *)tdb->map_ptr;
425                 return parser(key, data, private_data);
426         }
427
428         if (!(data.dptr = tdb_alloc_read(tdb, offset, len))) {
429                 return -1;
430         }
431
432         result = parser(key, data, private_data);
433         free(data.dptr);
434         return result;
435 }
436
437 /* read/write a record */
438 int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec)
439 {
440         if (tdb->methods->tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
441                 return -1;
442         if (TDB_BAD_MAGIC(rec)) {
443                 /* Ensure ecode is set for log fn. */
444                 tdb->ecode = TDB_ERR_CORRUPT;
445                 TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
446                 return -1;
447         }
448         return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0);
449 }
450
451 int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec)
452 {
453         struct list_struct r = *rec;
454         return tdb->methods->tdb_write(tdb, offset, CONVERT(r), sizeof(r));
455 }
456
457 static const struct tdb_methods io_methods = {
458         tdb_read,
459         tdb_write,
460         tdb_next_hash_chain,
461         tdb_oob,
462         tdb_expand_file,
463         tdb_brlock,
464         tdb_brunlock
465 };
466
467 /*
468   initialise the default methods table
469 */
470 void tdb_io_init(struct tdb_context *tdb)
471 {
472         tdb->methods = &io_methods;
473 }