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