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