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