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