X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Ftdb2%2Fio.c;h=aa79def1e38cc5b49b500d1b513eb9dc446fbbb7;hp=cdee88aa1fd57321220d121a5bfe381332dc9269;hb=1fe7f55b1efc5deefb20815a03b56c8ef0f6dc53;hpb=06e0037d97f5e1d83667ec40627cef862f3b7b85 diff --git a/ccan/tdb2/io.c b/ccan/tdb2/io.c index cdee88aa..aa79def1 100644 --- a/ccan/tdb2/io.c +++ b/ccan/tdb2/io.c @@ -26,6 +26,7 @@ License along with this library; if not, see . */ #include "private.h" +#include #include void tdb_munmap(struct tdb_context *tdb) @@ -47,8 +48,7 @@ void tdb_mmap(struct tdb_context *tdb) if (tdb->flags & TDB_NOMMAP) return; - tdb->map_ptr = mmap(NULL, tdb->map_size, - PROT_READ|(tdb->read_only? 0:PROT_WRITE), + tdb->map_ptr = mmap(NULL, tdb->map_size, tdb->mmap_flags, MAP_SHARED, tdb->fd, 0); /* @@ -70,6 +70,11 @@ void tdb_mmap(struct tdb_context *tdb) static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, bool probe) { struct stat st; + int ret; + + /* We can't hold pointers during this: we could unmap! */ + assert(!tdb->direct_access || tdb_has_expansion_lock(tdb)); + if (len <= tdb->map_size) return 0; if (tdb->flags & TDB_INTERNAL) { @@ -85,7 +90,14 @@ static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, bool probe) return -1; } - if (fstat(tdb->fd, &st) == -1) { + if (tdb_lock_expand(tdb, F_RDLCK) != 0) + return -1; + + ret = fstat(tdb->fd, &st); + + tdb_unlock_expand(tdb, F_RDLCK); + + if (ret == -1) { tdb->ecode = TDB_ERR_IO; return -1; } @@ -103,6 +115,7 @@ static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, bool probe) /* Unmap, update size, remap */ tdb_munmap(tdb); + tdb->map_size = st.st_size; tdb_mmap(tdb); return 0; @@ -132,19 +145,13 @@ void *tdb_get(struct tdb_context *tdb, tdb_off_t off, void *pad, size_t len) if (ret) return ret; } - - if (unlikely(tdb_oob(tdb, off + len, false) == -1)) - return NULL; - - if (tdb->methods->read(tdb, off, pad, len) == -1) - return NULL; - return tdb_convert(tdb, pad, len); + return tdb_read_convert(tdb, off, pad, len) == -1 ? NULL : pad; } /* Endian conversion: we only ever deal with 8 byte quantities */ void *tdb_convert(const struct tdb_context *tdb, void *buf, tdb_len_t size) { - if (unlikely((tdb->flags & TDB_CONVERT))) { + if (unlikely((tdb->flags & TDB_CONVERT)) && buf) { uint64_t i, *p = (uint64_t *)buf; for (i = 0; i < size / 8; i++) p[i] = bswap_64(p[i]); @@ -152,84 +159,63 @@ void *tdb_convert(const struct tdb_context *tdb, void *buf, tdb_len_t size) return buf; } -/* Return first non-zero offset in num offset array, or num. */ /* FIXME: Return the off? */ -uint64_t tdb_find_nonzero_off(struct tdb_context *tdb, tdb_off_t off, - uint64_t num) -{ - uint64_t i, *val; - bool alloc = false; - - val = tdb_direct(tdb, off, num * sizeof(tdb_off_t)); - if (!unlikely(val)) { - val = tdb_alloc_read(tdb, off, num * sizeof(tdb_off_t)); - if (!val) - return num; - alloc = true; - } +uint64_t tdb_find_nonzero_off(struct tdb_context *tdb, + tdb_off_t base, uint64_t start, uint64_t end) +{ + uint64_t i; + const uint64_t *val; - for (i = 0; i < num; i++) { + /* Zero vs non-zero is the same unconverted: minor optimization. */ + val = tdb_access_read(tdb, base + start * sizeof(tdb_off_t), + (end - start) * sizeof(tdb_off_t), false); + if (!val) + return end; + + for (i = 0; i < (end - start); i++) { if (val[i]) break; } - if (unlikely(alloc)) - free(val); - return i; + tdb_access_release(tdb, val); + return start + i; } /* Return first zero offset in num offset array, or num. */ uint64_t tdb_find_zero_off(struct tdb_context *tdb, tdb_off_t off, uint64_t num) { - uint64_t i, *val; - bool alloc = false; + uint64_t i; + const uint64_t *val; - val = tdb_direct(tdb, off, num * sizeof(tdb_off_t)); - if (!unlikely(val)) { - val = tdb_alloc_read(tdb, off, num * sizeof(tdb_off_t)); - if (!val) - return num; - alloc = true; - } + /* Zero vs non-zero is the same unconverted: minor optimization. */ + val = tdb_access_read(tdb, off, num * sizeof(tdb_off_t), false); + if (!val) + return num; for (i = 0; i < num; i++) { if (!val[i]) break; } - if (unlikely(alloc)) - free(val); + tdb_access_release(tdb, val); return i; } -static int fill(struct tdb_context *tdb, - const void *buf, size_t size, - tdb_off_t off, tdb_len_t len) -{ - while (len) { - size_t n = len > size ? size : len; - - if (!tdb_pwrite_all(tdb->fd, buf, n, off)) { - tdb->ecode = TDB_ERR_IO; - tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv, - "fill write failed: giving up!\n"); - return -1; - } - len -= n; - off += n; - } - return 0; -} - int zero_out(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len) { + char buf[8192] = { 0 }; void *p = tdb_direct(tdb, off, len); if (p) { memset(p, 0, len); return 0; - } else { - char buf[8192] = { 0 }; - return fill(tdb, buf, sizeof(buf), off, len); } + while (len) { + unsigned todo = len < sizeof(buf) ? len : sizeof(buf); + if (tdb->methods->write(tdb, off, buf, todo) == -1) + return -1; + len -= todo; + off += todo; + } + return 0; } tdb_off_t tdb_read_off(struct tdb_context *tdb, tdb_off_t off) @@ -357,9 +343,26 @@ static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf, } int tdb_write_convert(struct tdb_context *tdb, tdb_off_t off, - void *rec, size_t len) + const void *rec, size_t len) { - return tdb->methods->write(tdb, off, tdb_convert(tdb, rec, len), len); + int ret; + if (unlikely((tdb->flags & TDB_CONVERT))) { + void *conv = malloc(len); + if (!conv) { + tdb->ecode = TDB_ERR_OOM; + tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv, + "tdb_write: no memory converting %zu bytes\n", + len); + return -1; + } + memcpy(conv, rec, len); + ret = tdb->methods->write(tdb, off, + tdb_convert(tdb, conv, len), len); + free(conv); + } else + ret = tdb->methods->write(tdb, off, rec, len); + + return ret; } int tdb_read_convert(struct tdb_context *tdb, tdb_off_t off, @@ -375,72 +378,48 @@ int tdb_write_off(struct tdb_context *tdb, tdb_off_t off, tdb_off_t val) return tdb_write_convert(tdb, off, &val, sizeof(val)); } -/* read a lump of data, allocating the space for it */ -void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len) +static void *_tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, + tdb_len_t len, unsigned int prefix) { void *buf; /* some systems don't like zero length malloc */ - buf = malloc(len ? len : 1); + buf = malloc(prefix + len ? prefix + len : 1); if (unlikely(!buf)) { tdb->ecode = TDB_ERR_OOM; tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv, "tdb_alloc_read malloc failed len=%lld\n", - (long long)len); - } else if (unlikely(tdb->methods->read(tdb, offset, buf, len))) { + (long long)prefix + len); + } else if (unlikely(tdb->methods->read(tdb, offset, buf+prefix, len))) { free(buf); buf = NULL; } return buf; } -uint64_t hash_record(struct tdb_context *tdb, tdb_off_t off) +/* read a lump of data, allocating the space for it */ +void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len) { - struct tdb_used_record pad, *r; - void *key; - uint64_t klen, hash; - - r = tdb_get(tdb, off, &pad, sizeof(pad)); - if (!r) - /* FIXME */ - return 0; + return _tdb_alloc_read(tdb, offset, len, 0); +} - klen = rec_key_length(r); - key = tdb_direct(tdb, off + sizeof(pad), klen); - if (likely(key)) - return tdb_hash(tdb, key, klen); +static int fill(struct tdb_context *tdb, + const void *buf, size_t size, + tdb_off_t off, tdb_len_t len) +{ + while (len) { + size_t n = len > size ? size : len; - key = tdb_alloc_read(tdb, off + sizeof(pad), klen); - if (unlikely(!key)) - return 0; - hash = tdb_hash(tdb, key, klen); - free(key); - return hash; -} - -/* Give a piece of tdb data to a parser */ -int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key, - tdb_off_t offset, tdb_len_t len, - int (*parser)(TDB_DATA key, TDB_DATA data, - void *private_data), - void *private_data) -{ - TDB_DATA data; - int result; - bool allocated = false; - - data.dsize = len; - data.dptr = tdb_direct(tdb, offset, len); - if (unlikely(!data.dptr)) { - if (!(data.dptr = tdb_alloc_read(tdb, offset, len))) { + if (!tdb_pwrite_all(tdb->fd, buf, n, off)) { + tdb->ecode = TDB_ERR_IO; + tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv, + "fill write failed: giving up!\n"); return -1; } - allocated = true; + len -= n; + off += n; } - result = parser(key, data, private_data); - if (unlikely(allocated)) - free(data.dptr); - return result; + return 0; } /* expand a file. we prefer to use ftruncate, as that is what posix @@ -483,13 +462,57 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_len_t addition) return 0; } +/* This is only neded for tdb_access_commit, but used everywhere to simplify. */ +struct tdb_access_hdr { + tdb_off_t off; + tdb_len_t len; + bool convert; +}; + const void *tdb_access_read(struct tdb_context *tdb, - tdb_off_t off, tdb_len_t len) + tdb_off_t off, tdb_len_t len, bool convert) { - const void *ret = tdb_direct(tdb, off, len); + const void *ret = NULL; + + if (likely(!(tdb->flags & TDB_CONVERT))) + ret = tdb_direct(tdb, off, len); + + if (!ret) { + struct tdb_access_hdr *hdr; + hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr)); + if (hdr) { + ret = hdr + 1; + if (convert) + tdb_convert(tdb, (void *)ret, len); + } + } else + tdb->direct_access++; + + return ret; +} + +void *tdb_access_write(struct tdb_context *tdb, + tdb_off_t off, tdb_len_t len, bool convert) +{ + void *ret = NULL; + + if (likely(!(tdb->flags & TDB_CONVERT))) + ret = tdb_direct(tdb, off, len); + + if (!ret) { + struct tdb_access_hdr *hdr; + hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr)); + if (hdr) { + hdr->off = off; + hdr->len = len; + hdr->convert = convert; + ret = hdr + 1; + if (convert) + tdb_convert(tdb, (void *)ret, len); + } + } else + tdb->direct_access++; - if (!ret) - ret = tdb_alloc_read(tdb, off, len); return ret; } @@ -498,7 +521,30 @@ void tdb_access_release(struct tdb_context *tdb, const void *p) if (!tdb->map_ptr || (char *)p < (char *)tdb->map_ptr || (char *)p >= (char *)tdb->map_ptr + tdb->map_size) - free((void *)p); + free((struct tdb_access_hdr *)p - 1); + else + tdb->direct_access--; +} + +int tdb_access_commit(struct tdb_context *tdb, void *p) +{ + int ret = 0; + + if (!tdb->map_ptr + || (char *)p < (char *)tdb->map_ptr + || (char *)p >= (char *)tdb->map_ptr + tdb->map_size) { + struct tdb_access_hdr *hdr; + + hdr = (struct tdb_access_hdr *)p - 1; + if (hdr->convert) + ret = tdb_write_convert(tdb, hdr->off, p, hdr->len); + else + ret = tdb_write(tdb, hdr->off, p, hdr->len); + free(hdr); + } else + tdb->direct_access--; + + return ret; } #if 0