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