]> git.ozlabs.org Git - ccan/blob - ccan/tdb2/io.c
cdee88aa1fd57321220d121a5bfe381332dc9269
[ccan] / ccan / tdb2 / 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    Copyright (C) Rusty Russell                     2010
10
11      ** NOTE! The following LGPL license applies to the tdb
12      ** library. This does NOT imply that all of Samba is released
13      ** under the LGPL
14
15    This library is free software; you can redistribute it and/or
16    modify it under the terms of the GNU Lesser General Public
17    License as published by the Free Software Foundation; either
18    version 3 of the License, or (at your option) any later version.
19
20    This library is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23    Lesser General Public License for more details.
24
25    You should have received a copy of the GNU Lesser General Public
26    License along with this library; if not, see <http://www.gnu.org/licenses/>.
27 */
28 #include "private.h"
29 #include <ccan/likely/likely.h>
30
31 void tdb_munmap(struct tdb_context *tdb)
32 {
33         if (tdb->flags & TDB_INTERNAL)
34                 return;
35
36         if (tdb->map_ptr) {
37                 munmap(tdb->map_ptr, tdb->map_size);
38                 tdb->map_ptr = NULL;
39         }
40 }
41
42 void tdb_mmap(struct tdb_context *tdb)
43 {
44         if (tdb->flags & TDB_INTERNAL)
45                 return;
46
47         if (tdb->flags & TDB_NOMMAP)
48                 return;
49
50         tdb->map_ptr = mmap(NULL, tdb->map_size, 
51                             PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
52                             MAP_SHARED, tdb->fd, 0);
53
54         /*
55          * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
56          */
57         if (tdb->map_ptr == MAP_FAILED) {
58                 tdb->map_ptr = NULL;
59                 tdb->log(tdb, TDB_DEBUG_WARNING, tdb->log_priv,
60                          "tdb_mmap failed for size %lld (%s)\n", 
61                          (long long)tdb->map_size, strerror(errno));
62         }
63 }
64
65 /* check for an out of bounds access - if it is out of bounds then
66    see if the database has been expanded by someone else and expand
67    if necessary 
68    note that "len" is the minimum length needed for the db
69 */
70 static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, bool probe)
71 {
72         struct stat st;
73         if (len <= tdb->map_size)
74                 return 0;
75         if (tdb->flags & TDB_INTERNAL) {
76                 if (!probe) {
77                         /* Ensure ecode is set for log fn. */
78                         tdb->ecode = TDB_ERR_IO;
79                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
80                                  "tdb_oob len %lld beyond internal"
81                                  " malloc size %lld\n",
82                                  (long long)len,
83                                  (long long)tdb->map_size);
84                 }
85                 return -1;
86         }
87
88         if (fstat(tdb->fd, &st) == -1) {
89                 tdb->ecode = TDB_ERR_IO;
90                 return -1;
91         }
92
93         if (st.st_size < (size_t)len) {
94                 if (!probe) {
95                         /* Ensure ecode is set for log fn. */
96                         tdb->ecode = TDB_ERR_IO;
97                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
98                                  "tdb_oob len %lld beyond eof at %lld\n",
99                                  (long long)len, (long long)st.st_size);
100                 }
101                 return -1;
102         }
103
104         /* Unmap, update size, remap */
105         tdb_munmap(tdb);
106         tdb->map_size = st.st_size;
107         tdb_mmap(tdb);
108         return 0;
109 }
110
111 static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len)
112 {
113         if (unlikely(!tdb->map_ptr))
114                 return NULL;
115
116         /* FIXME: We can do a subset of this! */
117         if (tdb->transaction)
118                 return NULL;
119
120         if (unlikely(tdb_oob(tdb, off + len, true) == -1))
121                 return NULL;
122         return (char *)tdb->map_ptr + off;
123 }
124
125 /* Either make a copy into pad and return that, or return ptr into mmap. */
126 /* Note: pad has to be a real object, so we can't get here if len
127  * overflows size_t */
128 void *tdb_get(struct tdb_context *tdb, tdb_off_t off, void *pad, size_t len)
129 {
130         if (likely(!(tdb->flags & TDB_CONVERT))) {
131                 void *ret = tdb_direct(tdb, off, len);
132                 if (ret)
133                         return ret;
134         }
135
136         if (unlikely(tdb_oob(tdb, off + len, false) == -1))
137                 return NULL;
138
139         if (tdb->methods->read(tdb, off, pad, len) == -1)
140                 return NULL;
141         return tdb_convert(tdb, pad, len);
142 }
143
144 /* Endian conversion: we only ever deal with 8 byte quantities */
145 void *tdb_convert(const struct tdb_context *tdb, void *buf, tdb_len_t size)
146 {
147         if (unlikely((tdb->flags & TDB_CONVERT))) {
148                 uint64_t i, *p = (uint64_t *)buf;
149                 for (i = 0; i < size / 8; i++)
150                         p[i] = bswap_64(p[i]);
151         }
152         return buf;
153 }
154
155 /* Return first non-zero offset in num offset array, or num. */
156 /* FIXME: Return the off? */
157 uint64_t tdb_find_nonzero_off(struct tdb_context *tdb, tdb_off_t off,
158                               uint64_t num)
159 {
160         uint64_t i, *val;
161         bool alloc = false;
162
163         val = tdb_direct(tdb, off, num * sizeof(tdb_off_t));
164         if (!unlikely(val)) {
165                 val = tdb_alloc_read(tdb, off, num * sizeof(tdb_off_t));
166                 if (!val)
167                         return num;
168                 alloc = true;
169         }
170
171         for (i = 0; i < num; i++) {
172                 if (val[i])
173                         break;
174         }
175         if (unlikely(alloc))
176                 free(val);
177         return i;
178 }
179
180 /* Return first zero offset in num offset array, or num. */
181 uint64_t tdb_find_zero_off(struct tdb_context *tdb, tdb_off_t off,
182                            uint64_t num)
183 {
184         uint64_t i, *val;
185         bool alloc = false;
186
187         val = tdb_direct(tdb, off, num * sizeof(tdb_off_t));
188         if (!unlikely(val)) {
189                 val = tdb_alloc_read(tdb, off, num * sizeof(tdb_off_t));
190                 if (!val)
191                         return num;
192                 alloc = true;
193         }
194
195         for (i = 0; i < num; i++) {
196                 if (!val[i])
197                         break;
198         }
199         if (unlikely(alloc))
200                 free(val);
201         return i;
202 }
203
204 static int fill(struct tdb_context *tdb,
205                 const void *buf, size_t size,
206                 tdb_off_t off, tdb_len_t len)
207 {
208         while (len) {
209                 size_t n = len > size ? size : len;
210
211                 if (!tdb_pwrite_all(tdb->fd, buf, n, off)) {
212                         tdb->ecode = TDB_ERR_IO;
213                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
214                                  "fill write failed: giving up!\n");
215                         return -1;
216                 }
217                 len -= n;
218                 off += n;
219         }
220         return 0;
221 }
222
223 int zero_out(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len)
224 {
225         void *p = tdb_direct(tdb, off, len);
226         if (p) {
227                 memset(p, 0, len);
228                 return 0;
229         } else {
230                 char buf[8192] = { 0 };
231                 return fill(tdb, buf, sizeof(buf), off, len);
232         }
233 }
234
235 tdb_off_t tdb_read_off(struct tdb_context *tdb, tdb_off_t off)
236 {
237         tdb_off_t pad, *ret;
238
239         ret = tdb_get(tdb, off, &pad, sizeof(pad));
240         if (!ret) {
241                 return TDB_OFF_ERR;
242         }
243         return *ret;
244 }
245
246 /* Even on files, we can get partial writes due to signals. */
247 bool tdb_pwrite_all(int fd, const void *buf, size_t len, tdb_off_t off)
248 {
249         while (len) {
250                 ssize_t ret;
251                 ret = pwrite(fd, buf, len, off);
252                 if (ret < 0)
253                         return false;
254                 if (ret == 0) {
255                         errno = ENOSPC;
256                         return false;
257                 }
258                 buf = (char *)buf + ret;
259                 off += ret;
260                 len -= ret;
261         }
262         return true;
263 }
264
265 /* Even on files, we can get partial reads due to signals. */
266 bool tdb_pread_all(int fd, void *buf, size_t len, tdb_off_t off)
267 {
268         while (len) {
269                 ssize_t ret;
270                 ret = pread(fd, buf, len, off);
271                 if (ret < 0)
272                         return false;
273                 if (ret == 0) {
274                         /* ETOOSHORT? */
275                         errno = EWOULDBLOCK;
276                         return false;
277                 }
278                 buf = (char *)buf + ret;
279                 off += ret;
280                 len -= ret;
281         }
282         return true;
283 }
284
285 bool tdb_read_all(int fd, void *buf, size_t len)
286 {
287         while (len) {
288                 ssize_t ret;
289                 ret = read(fd, buf, len);
290                 if (ret < 0)
291                         return false;
292                 if (ret == 0) {
293                         /* ETOOSHORT? */
294                         errno = EWOULDBLOCK;
295                         return false;
296                 }
297                 buf = (char *)buf + ret;
298                 len -= ret;
299         }
300         return true;
301 }
302
303 /* write a lump of data at a specified offset */
304 static int tdb_write(struct tdb_context *tdb, tdb_off_t off, 
305                      const void *buf, tdb_len_t len)
306 {
307         if (len == 0) {
308                 return 0;
309         }
310
311         if (tdb->read_only) {
312                 tdb->ecode = TDB_ERR_RDONLY;
313                 return -1;
314         }
315
316         if (tdb->methods->oob(tdb, off + len, 0) != 0)
317                 return -1;
318
319         if (tdb->map_ptr) {
320                 memcpy(off + (char *)tdb->map_ptr, buf, len);
321         } else {
322                 if (!tdb_pwrite_all(tdb->fd, buf, len, off)) {
323                         tdb->ecode = TDB_ERR_IO;
324                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
325                                  "tdb_write failed at %llu len=%llu (%s)\n",
326                                  off, len, strerror(errno));
327                         return -1;
328                 }
329         }
330         return 0;
331 }
332
333 /* read a lump of data at a specified offset */
334 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
335                     tdb_len_t len)
336 {
337         if (tdb->methods->oob(tdb, off + len, 0) != 0) {
338                 return -1;
339         }
340
341         if (tdb->map_ptr) {
342                 memcpy(buf, off + (char *)tdb->map_ptr, len);
343         } else {
344                 if (!tdb_pread_all(tdb->fd, buf, len, off)) {
345                         /* Ensure ecode is set for log fn. */
346                         tdb->ecode = TDB_ERR_IO;
347                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
348                                  "tdb_read failed at %lld "
349                                  "len=%lld (%s) map_size=%lld\n",
350                                  (long long)off, (long long)len,
351                                  strerror(errno),
352                                  (long long)tdb->map_size);
353                         return -1;
354                 }
355         }
356         return 0;
357 }
358
359 int tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
360                       void *rec, size_t len)
361 {
362         return tdb->methods->write(tdb, off, tdb_convert(tdb, rec, len), len);
363 }
364
365 int tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
366                       void *rec, size_t len)
367 {
368         int ret = tdb->methods->read(tdb, off, rec, len);
369         tdb_convert(tdb, rec, len);
370         return ret;
371 }
372
373 int tdb_write_off(struct tdb_context *tdb, tdb_off_t off, tdb_off_t val)
374 {
375         return tdb_write_convert(tdb, off, &val, sizeof(val));
376 }
377
378 /* read a lump of data, allocating the space for it */
379 void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
380 {
381         void *buf;
382
383         /* some systems don't like zero length malloc */
384         buf = malloc(len ? len : 1);
385         if (unlikely(!buf)) {
386                 tdb->ecode = TDB_ERR_OOM;
387                 tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
388                          "tdb_alloc_read malloc failed len=%lld\n",
389                          (long long)len);
390         } else if (unlikely(tdb->methods->read(tdb, offset, buf, len))) {
391                 free(buf);
392                 buf = NULL;
393         }
394         return buf;
395 }
396
397 uint64_t hash_record(struct tdb_context *tdb, tdb_off_t off)
398 {
399         struct tdb_used_record pad, *r;
400         void *key;
401         uint64_t klen, hash;
402
403         r = tdb_get(tdb, off, &pad, sizeof(pad));
404         if (!r)
405                 /* FIXME */
406                 return 0;
407
408         klen = rec_key_length(r);
409         key = tdb_direct(tdb, off + sizeof(pad), klen);
410         if (likely(key))
411                 return tdb_hash(tdb, key, klen);
412
413         key = tdb_alloc_read(tdb, off + sizeof(pad), klen);
414         if (unlikely(!key))
415                 return 0;
416         hash = tdb_hash(tdb, key, klen);
417         free(key);
418         return hash;
419 }
420
421 /* Give a piece of tdb data to a parser */
422 int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key,
423                    tdb_off_t offset, tdb_len_t len,
424                    int (*parser)(TDB_DATA key, TDB_DATA data,
425                                  void *private_data),
426                    void *private_data)
427 {
428         TDB_DATA data;
429         int result;
430         bool allocated = false;
431
432         data.dsize = len;
433         data.dptr = tdb_direct(tdb, offset, len);
434         if (unlikely(!data.dptr)) {
435                 if (!(data.dptr = tdb_alloc_read(tdb, offset, len))) {
436                         return -1;
437                 }
438                 allocated = true;
439         }
440         result = parser(key, data, private_data);
441         if (unlikely(allocated))
442                 free(data.dptr);
443         return result;
444 }
445
446 /* expand a file.  we prefer to use ftruncate, as that is what posix
447   says to use for mmap expansion */
448 static int tdb_expand_file(struct tdb_context *tdb, tdb_len_t addition)
449 {
450         char buf[8192];
451
452         if (tdb->read_only) {
453                 tdb->ecode = TDB_ERR_RDONLY;
454                 return -1;
455         }
456
457         if (tdb->flags & TDB_INTERNAL) {
458                 char *new = realloc(tdb->map_ptr, tdb->map_size + addition);
459                 if (!new) {
460                         tdb->ecode = TDB_ERR_OOM;
461                         return -1;
462                 }
463                 tdb->map_ptr = new;
464                 tdb->map_size += addition;
465         } else {
466                 /* Unmap before trying to write; old TDB claimed OpenBSD had
467                  * problem with this otherwise. */
468                 tdb_munmap(tdb);
469
470                 /* If this fails, we try to fill anyway. */
471                 if (ftruncate(tdb->fd, tdb->map_size + addition))
472                         ;
473
474                 /* now fill the file with something. This ensures that the
475                    file isn't sparse, which would be very bad if we ran out of
476                    disk. This must be done with write, not via mmap */
477                 memset(buf, 0x43, sizeof(buf));
478                 if (fill(tdb, buf, sizeof(buf), tdb->map_size, addition) == -1)
479                         return -1;
480                 tdb->map_size += addition;
481                 tdb_mmap(tdb);
482         }
483         return 0;
484 }
485
486 const void *tdb_access_read(struct tdb_context *tdb,
487                             tdb_off_t off, tdb_len_t len)
488 {
489         const void *ret = tdb_direct(tdb, off, len);
490
491         if (!ret)
492                 ret = tdb_alloc_read(tdb, off, len);
493         return ret;
494 }
495
496 void tdb_access_release(struct tdb_context *tdb, const void *p)
497 {
498         if (!tdb->map_ptr
499             || (char *)p < (char *)tdb->map_ptr
500             || (char *)p >= (char *)tdb->map_ptr + tdb->map_size)
501                 free((void *)p);
502 }
503
504 #if 0
505 /* write a lump of data at a specified offset */
506 static int tdb_write(struct tdb_context *tdb, tdb_off_t off, 
507                      const void *buf, tdb_len_t len)
508 {
509         if (len == 0) {
510                 return 0;
511         }
512
513         if (tdb->read_only || tdb->traverse_read) {
514                 tdb->ecode = TDB_ERR_RDONLY;
515                 return -1;
516         }
517
518         if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0)
519                 return -1;
520
521         if (tdb->map_ptr) {
522                 memcpy(off + (char *)tdb->map_ptr, buf, len);
523         } else {
524                 ssize_t written = pwrite(tdb->fd, buf, len, off);
525                 if ((written != (ssize_t)len) && (written != -1)) {
526                         /* try once more */
527                         tdb->ecode = TDB_ERR_IO;
528                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: wrote only "
529                                  "%d of %d bytes at %d, trying once more\n",
530                                  (int)written, len, off));
531                         written = pwrite(tdb->fd, (const char *)buf+written,
532                                          len-written,
533                                          off+written);
534                 }
535                 if (written == -1) {
536                         /* Ensure ecode is set for log fn. */
537                         tdb->ecode = TDB_ERR_IO;
538                         TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d "
539                                  "len=%d (%s)\n", off, len, strerror(errno)));
540                         return -1;
541                 } else if (written != (ssize_t)len) {
542                         tdb->ecode = TDB_ERR_IO;
543                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: failed to "
544                                  "write %d bytes at %d in two attempts\n",
545                                  len, off));
546                         return -1;
547                 }
548         }
549         return 0;
550 }
551
552
553
554 /*
555   do an unlocked scan of the hash table heads to find the next non-zero head. The value
556   will then be confirmed with the lock held
557 */              
558 static void tdb_next_hash_chain(struct tdb_context *tdb, uint32_t *chain)
559 {
560         uint32_t h = *chain;
561         if (tdb->map_ptr) {
562                 for (;h < tdb->header.hash_size;h++) {
563                         if (0 != *(uint32_t *)(TDB_HASH_TOP(h) + (unsigned char *)tdb->map_ptr)) {
564                                 break;
565                         }
566                 }
567         } else {
568                 uint32_t off=0;
569                 for (;h < tdb->header.hash_size;h++) {
570                         if (tdb_ofs_read(tdb, TDB_HASH_TOP(h), &off) != 0 || off != 0) {
571                                 break;
572                         }
573                 }
574         }
575         (*chain) = h;
576 }
577
578 /* read/write a tdb_off_t */
579 int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d)
580 {
581         return tdb->methods->tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV());
582 }
583
584 int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d)
585 {
586         tdb_off_t off = *d;
587         return tdb->methods->tdb_write(tdb, offset, CONVERT(off), sizeof(*d));
588 }
589
590
591 /* read/write a record */
592 int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
593 {
594         if (tdb->methods->tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
595                 return -1;
596         if (TDB_BAD_MAGIC(rec)) {
597                 /* Ensure ecode is set for log fn. */
598                 tdb->ecode = TDB_ERR_CORRUPT;
599                 TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
600                 return -1;
601         }
602         return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0);
603 }
604
605 int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
606 {
607         struct tdb_record r = *rec;
608         return tdb->methods->tdb_write(tdb, offset, CONVERT(r), sizeof(r));
609 }
610 #endif
611
612 static const struct tdb_methods io_methods = {
613         tdb_read,
614         tdb_write,
615         tdb_oob,
616         tdb_expand_file,
617 };
618
619 /*
620   initialise the default methods table
621 */
622 void tdb_io_init(struct tdb_context *tdb)
623 {
624         tdb->methods = &io_methods;
625 }