]> git.ozlabs.org Git - ccan/blob - ccan/tdb2/io.c
e264940954405dca6e7aea3c57b277e8b9a5b4bb
[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 <assert.h>
30 #include <ccan/likely/likely.h>
31
32 void tdb_munmap(struct tdb_context *tdb)
33 {
34         if (tdb->flags & TDB_INTERNAL)
35                 return;
36
37         if (tdb->map_ptr) {
38                 munmap(tdb->map_ptr, tdb->map_size);
39                 tdb->map_ptr = NULL;
40         }
41 }
42
43 void tdb_mmap(struct tdb_context *tdb)
44 {
45         if (tdb->flags & TDB_INTERNAL)
46                 return;
47
48         if (tdb->flags & TDB_NOMMAP)
49                 return;
50
51         tdb->map_ptr = mmap(NULL, tdb->map_size, tdb->mmap_flags,
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         int ret;
74
75         /* We can't hold pointers during this: we could unmap! */
76         assert(!tdb->direct_access
77                || (tdb->flags & TDB_NOLOCK)
78                || tdb_has_expansion_lock(tdb));
79
80         if (len <= tdb->map_size)
81                 return 0;
82         if (tdb->flags & TDB_INTERNAL) {
83                 if (!probe) {
84                         /* Ensure ecode is set for log fn. */
85                         tdb->ecode = TDB_ERR_IO;
86                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
87                                  "tdb_oob len %lld beyond internal"
88                                  " malloc size %lld\n",
89                                  (long long)len,
90                                  (long long)tdb->map_size);
91                 }
92                 return -1;
93         }
94
95         if (tdb_lock_expand(tdb, F_RDLCK) != 0)
96                 return -1;
97
98         ret = fstat(tdb->fd, &st);
99
100         tdb_unlock_expand(tdb, F_RDLCK);
101
102         if (ret == -1) {
103                 tdb->ecode = TDB_ERR_IO;
104                 return -1;
105         }
106
107         if (st.st_size < (size_t)len) {
108                 if (!probe) {
109                         /* Ensure ecode is set for log fn. */
110                         tdb->ecode = TDB_ERR_IO;
111                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
112                                  "tdb_oob len %lld beyond eof at %lld\n",
113                                  (long long)len, (long long)st.st_size);
114                 }
115                 return -1;
116         }
117
118         /* Unmap, update size, remap */
119         tdb_munmap(tdb);
120
121         tdb->map_size = st.st_size;
122         tdb_mmap(tdb);
123         return 0;
124 }
125
126 /* Either make a copy into pad and return that, or return ptr into mmap. */
127 /* Note: pad has to be a real object, so we can't get here if len
128  * overflows size_t */
129 void *tdb_get(struct tdb_context *tdb, tdb_off_t off, void *pad, size_t len)
130 {
131         if (likely(!(tdb->flags & TDB_CONVERT))) {
132                 void *ret = tdb->methods->direct(tdb, off, len);
133                 if (ret)
134                         return ret;
135         }
136         return tdb_read_convert(tdb, off, pad, len) == -1 ? NULL : pad;
137 }
138
139 /* Endian conversion: we only ever deal with 8 byte quantities */
140 void *tdb_convert(const struct tdb_context *tdb, void *buf, tdb_len_t size)
141 {
142         if (unlikely((tdb->flags & TDB_CONVERT)) && buf) {
143                 uint64_t i, *p = (uint64_t *)buf;
144                 for (i = 0; i < size / 8; i++)
145                         p[i] = bswap_64(p[i]);
146         }
147         return buf;
148 }
149
150 /* FIXME: Return the off? */
151 uint64_t tdb_find_nonzero_off(struct tdb_context *tdb,
152                               tdb_off_t base, uint64_t start, uint64_t end)
153 {
154         uint64_t i;
155         const uint64_t *val;
156
157         /* Zero vs non-zero is the same unconverted: minor optimization. */
158         val = tdb_access_read(tdb, base + start * sizeof(tdb_off_t),
159                               (end - start) * sizeof(tdb_off_t), false);
160         if (!val)
161                 return end;
162
163         for (i = 0; i < (end - start); i++) {
164                 if (val[i])
165                         break;
166         }
167         tdb_access_release(tdb, val);
168         return start + i;
169 }
170
171 /* Return first zero offset in num offset array, or num. */
172 uint64_t tdb_find_zero_off(struct tdb_context *tdb, tdb_off_t off,
173                            uint64_t num)
174 {
175         uint64_t i;
176         const uint64_t *val;
177
178         /* Zero vs non-zero is the same unconverted: minor optimization. */
179         val = tdb_access_read(tdb, off, num * sizeof(tdb_off_t), false);
180         if (!val)
181                 return num;
182
183         for (i = 0; i < num; i++) {
184                 if (!val[i])
185                         break;
186         }
187         tdb_access_release(tdb, val);
188         return i;
189 }
190
191 int zero_out(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len)
192 {
193         char buf[8192] = { 0 };
194         void *p = tdb->methods->direct(tdb, off, len);
195         if (p) {
196                 memset(p, 0, len);
197                 return 0;
198         }
199         while (len) {
200                 unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
201                 if (tdb->methods->write(tdb, off, buf, todo) == -1)
202                         return -1;
203                 len -= todo;
204                 off += todo;
205         }
206         return 0;
207 }
208
209 tdb_off_t tdb_read_off(struct tdb_context *tdb, tdb_off_t off)
210 {
211         tdb_off_t pad, *ret;
212
213         ret = tdb_get(tdb, off, &pad, sizeof(pad));
214         if (!ret) {
215                 return TDB_OFF_ERR;
216         }
217         return *ret;
218 }
219
220 /* Even on files, we can get partial writes due to signals. */
221 bool tdb_pwrite_all(int fd, const void *buf, size_t len, tdb_off_t off)
222 {
223         while (len) {
224                 ssize_t ret;
225                 ret = pwrite(fd, buf, len, off);
226                 if (ret < 0)
227                         return false;
228                 if (ret == 0) {
229                         errno = ENOSPC;
230                         return false;
231                 }
232                 buf = (char *)buf + ret;
233                 off += ret;
234                 len -= ret;
235         }
236         return true;
237 }
238
239 /* Even on files, we can get partial reads due to signals. */
240 bool tdb_pread_all(int fd, void *buf, size_t len, tdb_off_t off)
241 {
242         while (len) {
243                 ssize_t ret;
244                 ret = pread(fd, buf, len, off);
245                 if (ret < 0)
246                         return false;
247                 if (ret == 0) {
248                         /* ETOOSHORT? */
249                         errno = EWOULDBLOCK;
250                         return false;
251                 }
252                 buf = (char *)buf + ret;
253                 off += ret;
254                 len -= ret;
255         }
256         return true;
257 }
258
259 bool tdb_read_all(int fd, void *buf, size_t len)
260 {
261         while (len) {
262                 ssize_t ret;
263                 ret = read(fd, buf, len);
264                 if (ret < 0)
265                         return false;
266                 if (ret == 0) {
267                         /* ETOOSHORT? */
268                         errno = EWOULDBLOCK;
269                         return false;
270                 }
271                 buf = (char *)buf + ret;
272                 len -= ret;
273         }
274         return true;
275 }
276
277 /* write a lump of data at a specified offset */
278 static int tdb_write(struct tdb_context *tdb, tdb_off_t off, 
279                      const void *buf, tdb_len_t len)
280 {
281         if (len == 0) {
282                 return 0;
283         }
284
285         if (tdb->read_only) {
286                 tdb->ecode = TDB_ERR_RDONLY;
287                 return -1;
288         }
289
290         if (tdb->methods->oob(tdb, off + len, 0) != 0)
291                 return -1;
292
293         if (tdb->map_ptr) {
294                 memcpy(off + (char *)tdb->map_ptr, buf, len);
295         } else {
296                 if (!tdb_pwrite_all(tdb->fd, buf, len, off)) {
297                         tdb->ecode = TDB_ERR_IO;
298                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
299                                  "tdb_write failed at %llu len=%llu (%s)\n",
300                                  (long long)off, (long long)len,
301                                  strerror(errno));
302                         return -1;
303                 }
304         }
305         return 0;
306 }
307
308 /* read a lump of data at a specified offset */
309 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
310                     tdb_len_t len)
311 {
312         if (tdb->methods->oob(tdb, off + len, 0) != 0) {
313                 return -1;
314         }
315
316         if (tdb->map_ptr) {
317                 memcpy(buf, off + (char *)tdb->map_ptr, len);
318         } else {
319                 if (!tdb_pread_all(tdb->fd, buf, len, off)) {
320                         /* Ensure ecode is set for log fn. */
321                         tdb->ecode = TDB_ERR_IO;
322                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
323                                  "tdb_read failed at %lld "
324                                  "len=%lld (%s) map_size=%lld\n",
325                                  (long long)off, (long long)len,
326                                  strerror(errno),
327                                  (long long)tdb->map_size);
328                         return -1;
329                 }
330         }
331         return 0;
332 }
333
334 int tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
335                       const void *rec, size_t len)
336 {
337         int ret;
338         if (unlikely((tdb->flags & TDB_CONVERT))) {
339                 void *conv = malloc(len);
340                 if (!conv) {
341                         tdb->ecode = TDB_ERR_OOM;
342                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
343                                  "tdb_write: no memory converting %zu bytes\n",
344                                  len);
345                         return -1;
346                 }
347                 memcpy(conv, rec, len);
348                 ret = tdb->methods->write(tdb, off,
349                                           tdb_convert(tdb, conv, len), len);
350                 free(conv);
351         } else
352                 ret = tdb->methods->write(tdb, off, rec, len);
353
354         return ret;
355 }
356
357 int tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
358                       void *rec, size_t len)
359 {
360         int ret = tdb->methods->read(tdb, off, rec, len);
361         tdb_convert(tdb, rec, len);
362         return ret;
363 }
364
365 int tdb_write_off(struct tdb_context *tdb, tdb_off_t off, tdb_off_t val)
366 {
367         return tdb_write_convert(tdb, off, &val, sizeof(val));
368 }
369
370 static void *_tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset,
371                              tdb_len_t len, unsigned int prefix)
372 {
373         void *buf;
374
375         /* some systems don't like zero length malloc */
376         buf = malloc(prefix + len ? prefix + len : 1);
377         if (unlikely(!buf)) {
378                 tdb->ecode = TDB_ERR_OOM;
379                 tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
380                          "tdb_alloc_read malloc failed len=%lld\n",
381                          (long long)prefix + len);
382         } else if (unlikely(tdb->methods->read(tdb, offset, buf+prefix, len))) {
383                 free(buf);
384                 buf = NULL;
385         }
386         return buf;
387 }
388
389 /* read a lump of data, allocating the space for it */
390 void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
391 {
392         return _tdb_alloc_read(tdb, offset, len, 0);
393 }
394
395 static int fill(struct tdb_context *tdb,
396                 const void *buf, size_t size,
397                 tdb_off_t off, tdb_len_t len)
398 {
399         while (len) {
400                 size_t n = len > size ? size : len;
401
402                 if (!tdb_pwrite_all(tdb->fd, buf, n, off)) {
403                         tdb->ecode = TDB_ERR_IO;
404                         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
405                                  "fill write failed: giving up!\n");
406                         return -1;
407                 }
408                 len -= n;
409                 off += n;
410         }
411         return 0;
412 }
413
414 /* expand a file.  we prefer to use ftruncate, as that is what posix
415   says to use for mmap expansion */
416 static int tdb_expand_file(struct tdb_context *tdb, tdb_len_t addition)
417 {
418         char buf[8192];
419
420         if (tdb->read_only) {
421                 tdb->ecode = TDB_ERR_RDONLY;
422                 return -1;
423         }
424
425         if (tdb->flags & TDB_INTERNAL) {
426                 char *new = realloc(tdb->map_ptr, tdb->map_size + addition);
427                 if (!new) {
428                         tdb->ecode = TDB_ERR_OOM;
429                         return -1;
430                 }
431                 tdb->map_ptr = new;
432                 tdb->map_size += addition;
433         } else {
434                 /* Unmap before trying to write; old TDB claimed OpenBSD had
435                  * problem with this otherwise. */
436                 tdb_munmap(tdb);
437
438                 /* If this fails, we try to fill anyway. */
439                 if (ftruncate(tdb->fd, tdb->map_size + addition))
440                         ;
441
442                 /* now fill the file with something. This ensures that the
443                    file isn't sparse, which would be very bad if we ran out of
444                    disk. This must be done with write, not via mmap */
445                 memset(buf, 0x43, sizeof(buf));
446                 if (0 || fill(tdb, buf, sizeof(buf), tdb->map_size, addition) == -1)
447                         return -1;
448                 tdb->map_size += addition;
449                 tdb_mmap(tdb);
450         }
451         return 0;
452 }
453
454 /* This is only neded for tdb_access_commit, but used everywhere to simplify. */
455 struct tdb_access_hdr {
456         tdb_off_t off;
457         tdb_len_t len;
458         bool convert;
459 };
460
461 const void *tdb_access_read(struct tdb_context *tdb,
462                             tdb_off_t off, tdb_len_t len, bool convert)
463 {
464         const void *ret = NULL; 
465
466         if (likely(!(tdb->flags & TDB_CONVERT)))
467                 ret = tdb->methods->direct(tdb, off, len);
468
469         if (!ret) {
470                 struct tdb_access_hdr *hdr;
471                 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
472                 if (hdr) {
473                         ret = hdr + 1;
474                         if (convert)
475                                 tdb_convert(tdb, (void *)ret, len);
476                 }
477         } else
478                 tdb->direct_access++;
479
480         return ret;
481 }
482
483 void *tdb_access_write(struct tdb_context *tdb,
484                        tdb_off_t off, tdb_len_t len, bool convert)
485 {
486         void *ret = NULL;
487
488         if (likely(!(tdb->flags & TDB_CONVERT)))
489                 ret = tdb->methods->direct(tdb, off, len);
490
491         if (!ret) {
492                 struct tdb_access_hdr *hdr;
493                 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
494                 if (hdr) {
495                         hdr->off = off;
496                         hdr->len = len;
497                         hdr->convert = convert;
498                         ret = hdr + 1;
499                         if (convert)
500                                 tdb_convert(tdb, (void *)ret, len);
501                 }
502         } else
503                 tdb->direct_access++;
504
505         return ret;
506 }
507
508 void tdb_access_release(struct tdb_context *tdb, const void *p)
509 {
510         if (!tdb->map_ptr
511             || (char *)p < (char *)tdb->map_ptr
512             || (char *)p >= (char *)tdb->map_ptr + tdb->map_size)
513                 free((struct tdb_access_hdr *)p - 1);
514         else
515                 tdb->direct_access--;
516 }
517
518 int tdb_access_commit(struct tdb_context *tdb, void *p)
519 {
520         int ret = 0;
521
522         if (!tdb->map_ptr
523             || (char *)p < (char *)tdb->map_ptr
524             || (char *)p >= (char *)tdb->map_ptr + tdb->map_size) {
525                 struct tdb_access_hdr *hdr;
526
527                 hdr = (struct tdb_access_hdr *)p - 1;
528                 if (hdr->convert)
529                         ret = tdb_write_convert(tdb, hdr->off, p, hdr->len);
530                 else
531                         ret = tdb_write(tdb, hdr->off, p, hdr->len);
532                 free(hdr);
533         } else
534                 tdb->direct_access--;
535
536         return ret;
537 }
538
539 static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len)
540 {
541         if (unlikely(!tdb->map_ptr))
542                 return NULL;
543
544         if (unlikely(tdb_oob(tdb, off + len, true) == -1))
545                 return NULL;
546         return (char *)tdb->map_ptr + off;
547 }
548
549 void add_stat_(struct tdb_context *tdb, uint64_t *stat, size_t val)
550 {
551         if ((uintptr_t)stat < (uintptr_t)tdb->stats + tdb->stats->size)
552                 *stat += val;
553 }
554
555 static const struct tdb_methods io_methods = {
556         tdb_read,
557         tdb_write,
558         tdb_oob,
559         tdb_expand_file,
560         tdb_direct,
561 };
562
563 /*
564   initialise the default methods table
565 */
566 void tdb_io_init(struct tdb_context *tdb)
567 {
568         tdb->methods = &io_methods;
569 }