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