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