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