8ab0768883709467876939127a3dc6dddfeb4822
[ira/wip.git] / source / 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    note that "len" is the minimum length needed for the db
35 */
36 static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
37 {
38         struct stat st;
39         if (len <= tdb->map_size)
40                 return 0;
41         if (tdb->flags & TDB_INTERNAL) {
42                 if (!probe) {
43                         /* Ensure ecode is set for log fn. */
44                         tdb->ecode = TDB_ERR_IO;
45                         TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond internal malloc size %d\n",
46                                  (int)len, (int)tdb->map_size));
47                 }
48                 return TDB_ERRCODE(TDB_ERR_IO, -1);
49         }
50
51         if (fstat(tdb->fd, &st) == -1) {
52                 return TDB_ERRCODE(TDB_ERR_IO, -1);
53         }
54
55         if (st.st_size < (size_t)len) {
56                 if (!probe) {
57                         /* Ensure ecode is set for log fn. */
58                         tdb->ecode = TDB_ERR_IO;
59                         TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond eof at %d\n",
60                                  (int)len, (int)st.st_size));
61                 }
62                 return TDB_ERRCODE(TDB_ERR_IO, -1);
63         }
64
65         /* Unmap, update size, remap */
66         if (tdb_munmap(tdb) == -1)
67                 return TDB_ERRCODE(TDB_ERR_IO, -1);
68         tdb->map_size = st.st_size;
69         tdb_mmap(tdb);
70         return 0;
71 }
72
73 /* write a lump of data at a specified offset */
74 static int tdb_write(struct tdb_context *tdb, tdb_off_t off, 
75                      const void *buf, tdb_len_t len)
76 {
77         if (len == 0) {
78                 return 0;
79         }
80
81         if (tdb->read_only || tdb->traverse_read) {
82                 tdb->ecode = TDB_ERR_RDONLY;
83                 return -1;
84         }
85
86         if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0)
87                 return -1;
88
89         if (tdb->map_ptr) {
90                 memcpy(off + (char *)tdb->map_ptr, buf, len);
91         } else {
92                 ssize_t written = pwrite(tdb->fd, buf, len, off);
93                 if ((written != (ssize_t)len) && (written != -1)) {
94                         /* try once more */
95                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: wrote only "
96                                  "%d of %d bytes at %d, trying once more\n",
97                                  (int)written, len, off));
98                         errno = ENOSPC;
99                         written = pwrite(tdb->fd, (const void *)((const char *)buf+written),
100                                          len-written,
101                                          off+written);
102                 }
103                 if (written == -1) {
104                         /* Ensure ecode is set for log fn. */
105                         tdb->ecode = TDB_ERR_IO;
106                         TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d "
107                                  "len=%d (%s)\n", off, len, strerror(errno)));
108                         return TDB_ERRCODE(TDB_ERR_IO, -1);
109                 } else if (written != (ssize_t)len) {
110                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: failed to "
111                                  "write %d bytes at %d in two attempts\n",
112                                  len, off));
113                         errno = ENOSPC;
114                         return TDB_ERRCODE(TDB_ERR_IO, -1);
115                 }
116         }
117         return 0;
118 }
119
120 /* Endian conversion: we only ever deal with 4 byte quantities */
121 void *tdb_convert(void *buf, uint32_t size)
122 {
123         uint32_t i, *p = (uint32_t *)buf;
124         for (i = 0; i < size / 4; i++)
125                 p[i] = TDB_BYTEREV(p[i]);
126         return buf;
127 }
128
129
130 /* read a lump of data at a specified offset, maybe convert */
131 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf, 
132                     tdb_len_t len, int cv)
133 {
134         if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0) {
135                 return -1;
136         }
137
138         if (tdb->map_ptr) {
139                 memcpy(buf, off + (char *)tdb->map_ptr, len);
140         } else {
141                 ssize_t ret = pread(tdb->fd, buf, len, off);
142                 if (ret != (ssize_t)len) {
143                         /* Ensure ecode is set for log fn. */
144                         tdb->ecode = TDB_ERR_IO;
145                         TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %d "
146                                  "len=%d ret=%d (%s) map_size=%d\n",
147                                  (int)off, (int)len, (int)ret, strerror(errno),
148                                  (int)tdb->map_size));
149                         return TDB_ERRCODE(TDB_ERR_IO, -1);
150                 }
151         }
152         if (cv) {
153                 tdb_convert(buf, len);
154         }
155         return 0;
156 }
157
158
159
160 /*
161   do an unlocked scan of the hash table heads to find the next non-zero head. The value
162   will then be confirmed with the lock held
163 */              
164 static void tdb_next_hash_chain(struct tdb_context *tdb, uint32_t *chain)
165 {
166         uint32_t h = *chain;
167         if (tdb->map_ptr) {
168                 for (;h < tdb->header.hash_size;h++) {
169                         if (0 != *(uint32_t *)(TDB_HASH_TOP(h) + (unsigned char *)tdb->map_ptr)) {
170                                 break;
171                         }
172                 }
173         } else {
174                 uint32_t off=0;
175                 for (;h < tdb->header.hash_size;h++) {
176                         if (tdb_ofs_read(tdb, TDB_HASH_TOP(h), &off) != 0 || off != 0) {
177                                 break;
178                         }
179                 }
180         }
181         (*chain) = h;
182 }
183
184
185 int tdb_munmap(struct tdb_context *tdb)
186 {
187         if (tdb->flags & TDB_INTERNAL)
188                 return 0;
189
190 #ifdef HAVE_MMAP
191         if (tdb->map_ptr) {
192                 int ret = munmap(tdb->map_ptr, tdb->map_size);
193                 if (ret != 0)
194                         return ret;
195         }
196 #endif
197         tdb->map_ptr = NULL;
198         return 0;
199 }
200
201 void tdb_mmap(struct tdb_context *tdb)
202 {
203         if (tdb->flags & TDB_INTERNAL)
204                 return;
205
206 #ifdef HAVE_MMAP
207         if (!(tdb->flags & TDB_NOMMAP)) {
208                 tdb->map_ptr = mmap(NULL, tdb->map_size, 
209                                     PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
210                                     MAP_SHARED|MAP_FILE, tdb->fd, 0);
211
212                 /*
213                  * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
214                  */
215
216                 if (tdb->map_ptr == MAP_FAILED) {
217                         tdb->map_ptr = NULL;
218                         TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %d (%s)\n", 
219                                  tdb->map_size, strerror(errno)));
220                 }
221         } else {
222                 tdb->map_ptr = NULL;
223         }
224 #else
225         tdb->map_ptr = NULL;
226 #endif
227 }
228
229 /* expand a file.  we prefer to use ftruncate, as that is what posix
230   says to use for mmap expansion */
231 static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition)
232 {
233         char buf[1024];
234
235         if (tdb->read_only || tdb->traverse_read) {
236                 tdb->ecode = TDB_ERR_RDONLY;
237                 return -1;
238         }
239
240         if (ftruncate(tdb->fd, size+addition) == -1) {
241                 char b = 0;
242                 ssize_t written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
243                 if (written == 0) {
244                         /* try once more, potentially revealing errno */
245                         written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
246                 }
247                 if (written == 0) {
248                         /* again - give up, guessing errno */
249                         errno = ENOSPC;
250                 }
251                 if (written != 1) {
252                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n", 
253                                  size+addition, strerror(errno)));
254                         return -1;
255                 }
256         }
257
258         /* now fill the file with something. This ensures that the
259            file isn't sparse, which would be very bad if we ran out of
260            disk. This must be done with write, not via mmap */
261         memset(buf, TDB_PAD_BYTE, sizeof(buf));
262         while (addition) {
263                 size_t n = addition>sizeof(buf)?sizeof(buf):addition;
264                 ssize_t written = pwrite(tdb->fd, buf, n, size);
265                 if (written == 0) {
266                         /* prevent infinite loops: try _once_ more */
267                         written = pwrite(tdb->fd, buf, n, size);
268                 }
269                 if (written == 0) {
270                         /* give up, trying to provide a useful errno */
271                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write "
272                                 "returned 0 twice: giving up!\n"));
273                         errno = ENOSPC;
274                         return -1;
275                 } else if (written == -1) {
276                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of "
277                                  "%d bytes failed (%s)\n", (int)n,
278                                  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", (int)written,
283                                  (int)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 unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
378 {
379         unsigned char *buf;
380
381         /* some systems don't like zero length malloc */
382         if (len == 0) {
383                 len = 1;
384         }
385
386         if (!(buf = (unsigned 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 + (unsigned 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 }