return false;
}
+static bool read_all(int fd, void *buf, size_t len)
+{
+ while (len) {
+ ssize_t ret;
+ ret = read(fd, buf, len);
+ if (ret < 0)
+ return false;
+ if (ret == 0) {
+ /* ETOOSHORT? */
+ errno = EWOULDBLOCK;
+ return false;
+ }
+ buf = (char *)buf + ret;
+ len -= ret;
+ }
+ return true;
+}
+
static uint64_t random_number(struct tdb_context *tdb)
{
int fd;
fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0) {
- if (tdb_read_all(fd, &ret, sizeof(ret))) {
- tdb_logerr(tdb, TDB_SUCCESS, TDB_DEBUG_TRACE,
- "tdb_open: random from /dev/urandom");
+ if (read_all(fd, &ret, sizeof(ret))) {
close(fd);
return ret;
}
char reply[1 + sizeof(uint64_t)];
int r = read(fd, reply, sizeof(reply));
if (r > 1) {
- tdb_logerr(tdb, TDB_SUCCESS, TDB_DEBUG_TRACE,
- "tdb_open: %u random bytes from"
- " /dev/egd-pool", r-1);
/* Copy at least some bytes. */
memcpy(&ret, reply+1, r - 1);
if (reply[0] == sizeof(uint64_t)
/* Fallback: pid and time. */
gettimeofday(&now, NULL);
ret = getpid() * 100132289ULL + now.tv_sec * 1000000ULL + now.tv_usec;
- tdb_logerr(tdb, TDB_SUCCESS, TDB_DEBUG_TRACE,
+ tdb_logerr(tdb, TDB_SUCCESS, TDB_LOG_WARNING,
"tdb_open: random from getpid and time");
return ret;
}
/* We make it up in memory, then write it out if not internal */
struct new_database newdb;
unsigned int magic_len;
+ ssize_t rlen;
/* Fill in the header */
newdb.hdr.version = TDB_VERSION;
tdb->map_size = sizeof(newdb);
tdb->map_ptr = malloc(tdb->map_size);
if (!tdb->map_ptr) {
- tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL,
+ tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
"tdb_new_database: failed to allocate");
return -1;
}
if (ftruncate(tdb->fd, 0) == -1)
return -1;
- if (!tdb_pwrite_all(tdb->fd, &newdb, sizeof(newdb), 0)) {
- tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
- "tdb_new_database: failed to write: %s",
- strerror(errno));
+ rlen = write(tdb->fd, &newdb, sizeof(newdb));
+ if (rlen != sizeof(newdb)) {
+ if (rlen >= 0)
+ errno = ENOSPC;
+ tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
+ "tdb_new_database: %zi writing header: %s",
+ rlen, strerror(errno));
return -1;
}
return 0;
int saved_errno = 0;
uint64_t hash_test;
unsigned v;
+ ssize_t rlen;
struct tdb_header hdr;
struct tdb_attribute_seed *seed = NULL;
+ enum TDB_ERROR ecode;
tdb = malloc(sizeof(*tdb));
if (!tdb) {
tdb->stats->size = sizeof(attr->stats);
break;
default:
- tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_DEBUG_ERROR,
+ tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
"tdb_open: unknown attribute type %u",
attr->base.attr);
goto fail;
}
if ((open_flags & O_ACCMODE) == O_WRONLY) {
- tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_DEBUG_ERROR,
+ tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
"tdb_open: can't open tdb %s write-only", name);
goto fail;
}
if ((tdb->fd = open(name, open_flags, mode)) == -1) {
/* errno set by open(2) */
saved_errno = errno;
- tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_ERROR,
+ tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
"tdb_open: could not open file %s: %s",
name, strerror(errno));
goto fail;
fcntl(tdb->fd, F_SETFD, v | FD_CLOEXEC);
/* ensure there is only one process initialising at once */
- if (tdb_lock_open(tdb, TDB_LOCK_WAIT|TDB_LOCK_NOCHECK) == -1) {
- /* errno set by tdb_brlock */
- saved_errno = errno;
+ tdb->ecode = tdb_lock_open(tdb, TDB_LOCK_WAIT|TDB_LOCK_NOCHECK);
+ if (tdb->ecode != TDB_SUCCESS) {
goto fail;
}
- if (!tdb_pread_all(tdb->fd, &hdr, sizeof(hdr), 0)
- || strcmp(hdr.magic_food, TDB_MAGIC_FOOD) != 0) {
- if (!(open_flags & O_CREAT)) {
- tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_ERROR,
- "tdb_open: %s is not a tdb file", name);
- goto fail;
- }
+ /* If they used O_TRUNC, read will return 0. */
+ rlen = read(tdb->fd, &hdr, sizeof(hdr));
+ if (rlen == 0 && (open_flags & O_CREAT)) {
if (tdb_new_database(tdb, seed, &hdr) == -1) {
goto fail;
}
- } else if (hdr.version != TDB_VERSION) {
+ } else if (rlen < 0) {
+ tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
+ "tdb_open: error %s reading %s",
+ strerror(errno), name);
+ goto fail;
+ } else if (rlen < sizeof(hdr)
+ || strcmp(hdr.magic_food, TDB_MAGIC_FOOD) != 0) {
+ tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
+ "tdb_open: %s is not a tdb file", name);
+ goto fail;
+ }
+
+ if (hdr.version != TDB_VERSION) {
if (hdr.version == bswap_64(TDB_VERSION))
tdb->flags |= TDB_CONVERT;
else {
/* wrong version */
- tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_ERROR,
+ tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
"tdb_open: %s is unknown version 0x%llx",
name, (long long)hdr.version);
goto fail;
hash_test = tdb_hash(tdb, &hash_test, sizeof(hash_test));
if (hdr.hash_test != hash_test) {
/* wrong hash variant */
- tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_ERROR,
+ tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
"tdb_open: %s uses a different hash function",
name);
goto fail;
if (fstat(tdb->fd, &st) == -1) {
saved_errno = errno;
- tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_ERROR,
+ tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
"tdb_open: could not stat open %s: %s",
name, strerror(errno));
goto fail;
/* Is it already in the open list? If so, fail. */
if (tdb_already_open(st.st_dev, st.st_ino)) {
/* FIXME */
- tdb_logerr(tdb, TDB_ERR_NESTING, TDB_DEBUG_ERROR,
+ tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_USE_ERROR,
"tdb_open: %s (%d,%d) is already open in this"
" process",
name, (int)st.st_dev, (int)st.st_ino);
tdb->name = strdup(name);
if (!tdb->name) {
- tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_ERROR,
+ tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
"tdb_open: failed to allocate name");
goto fail;
}
tdb->methods->oob(tdb, tdb->map_size + 1, true);
/* Now it's fully formed, recover if necessary. */
- if (tdb_needs_recovery(tdb) && tdb_lock_and_recover(tdb) == -1) {
- goto fail;
+ if (tdb_needs_recovery(tdb)) {
+ ecode = tdb_lock_and_recover(tdb);
+ if (ecode != TDB_SUCCESS) {
+ tdb->ecode = ecode;
+ goto fail;
+ }
}
if (tdb_ftable_init(tdb) == -1)
case TDB_ERR_EINVAL:
saved_errno = EINVAL;
break;
- case TDB_ERR_NESTING:
- saved_errno = EBUSY;
- break;
default:
saved_errno = EINVAL;
break;
} else
tdb_munmap(tdb);
}
+ free(tdb->lockrecs);
free((char *)tdb->name);
if (tdb->fd != -1)
if (close(tdb->fd) != 0)
- tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_ERROR,
+ tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
"tdb_open: failed to close tdb->fd"
- " on error!");
+ " on error: %s", strerror(errno));
free(tdb);
errno = saved_errno;
return NULL;
uint64_t h)
{
uint64_t dataroom = rec_data_length(rec) + rec_extra_padding(rec);
+ enum TDB_ERROR ecode;
if (set_header(tdb, rec, TDB_USED_MAGIC, keylen, datalen,
keylen + dataroom, h))
return -1;
- return tdb_write_convert(tdb, off, rec, sizeof(*rec));
+ ecode = tdb_write_convert(tdb, off, rec, sizeof(*rec));
+ if (ecode != TDB_SUCCESS) {
+ tdb->ecode = ecode;
+ return -1;
+ }
+ return 0;
}
/* Returns -1 on error, 0 on OK */
bool growing)
{
tdb_off_t new_off;
+ enum TDB_ERROR ecode;
/* Allocate a new record. */
new_off = alloc(tdb, key.dsize, dbuf.dsize, h->h, TDB_USED_MAGIC,
}
new_off += sizeof(struct tdb_used_record);
- if (tdb->methods->write(tdb, new_off, key.dptr, key.dsize) == -1)
+ ecode = tdb->methods->twrite(tdb, new_off, key.dptr, key.dsize);
+ if (ecode != TDB_SUCCESS) {
+ tdb->ecode = ecode;
return -1;
+ }
new_off += key.dsize;
- if (tdb->methods->write(tdb, new_off, dbuf.dptr, dbuf.dsize) == -1)
+ ecode = tdb->methods->twrite(tdb, new_off, dbuf.dptr, dbuf.dsize);
+ if (ecode != TDB_SUCCESS) {
+ tdb->ecode = ecode;
return -1;
+ }
/* FIXME: tdb_increment_seqnum(tdb); */
return 0;
tdb_len_t old_room = 0;
struct tdb_used_record rec;
int ret;
+ enum TDB_ERROR ecode;
off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
if (unlikely(off == TDB_OFF_ERR))
key.dsize, dbuf.dsize,
&rec, h.h))
goto fail;
- if (tdb->methods->write(tdb, off + sizeof(rec)
- + key.dsize,
- dbuf.dptr, dbuf.dsize))
+ ecode = tdb->methods->twrite(tdb,
+ off + sizeof(rec)
+ + key.dsize,
+ dbuf.dptr,
+ dbuf.dsize);
+ if (ecode != TDB_SUCCESS) {
+ tdb->ecode = ecode;
goto fail;
+ }
tdb_unlock_hashes(tdb, h.hlock_start,
h.hlock_range, F_WRLCK);
return 0;
tdb_len_t old_room = 0, old_dlen;
unsigned char *newdata;
struct tdb_data new_dbuf;
+ enum TDB_ERROR ecode;
int ret;
off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
goto fail;
off += sizeof(rec) + key.dsize + old_dlen;
- if (tdb->methods->write(tdb, off, dbuf.dptr,
- dbuf.dsize) == -1)
+ ecode = tdb->methods->twrite(tdb, off, dbuf.dptr,
+ dbuf.dsize);
+ if (ecode != TDB_SUCCESS) {
+ tdb->ecode = ecode;
goto fail;
+ }
/* FIXME: tdb_increment_seqnum(tdb); */
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range,
/* Slow path. */
newdata = malloc(key.dsize + old_dlen + dbuf.dsize);
if (!newdata) {
- tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL,
+ tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
"tdb_append: failed to allocate %zu bytes",
(size_t)(key.dsize+old_dlen+dbuf.dsize));
goto fail;
}
- if (tdb->methods->read(tdb, off + sizeof(rec) + key.dsize,
- newdata, old_dlen) != 0) {
+ ecode = tdb->methods->tread(tdb, off + sizeof(rec) + key.dsize,
+ newdata, old_dlen);
+ if (ecode != TDB_SUCCESS) {
+ tdb->ecode = ecode;
free(newdata);
goto fail;
}
case TDB_ERR_LOCK: return "Locking error";
case TDB_ERR_OOM: return "Out of memory";
case TDB_ERR_EXISTS: return "Record exists";
- case TDB_ERR_NESTING: return "Transaction already started";
case TDB_ERR_EINVAL: return "Invalid parameter";
case TDB_ERR_NOEXIST: return "Record does not exist";
case TDB_ERR_RDONLY: return "write not permitted";
return "Invalid error code";
}
-void COLD tdb_logerr(struct tdb_context *tdb,
- enum TDB_ERROR ecode,
- enum tdb_debug_level level,
- const char *fmt, ...)
+enum TDB_ERROR COLD tdb_logerr(struct tdb_context *tdb,
+ enum TDB_ERROR ecode,
+ enum tdb_log_level level,
+ const char *fmt, ...)
{
char *message;
va_list ap;
tdb->ecode = ecode;
if (!tdb->logfn)
- return;
+ return ecode;
/* FIXME: Doesn't assume asprintf. */
va_start(ap, fmt);
message = malloc(len + 1);
if (!message) {
- tdb->logfn(tdb, level, tdb->log_private,
- "out of memory formatting message");
- return;
+ tdb->logfn(tdb, TDB_LOG_ERROR, tdb->log_private,
+ "out of memory formatting message:");
+ tdb->logfn(tdb, level, tdb->log_private, fmt);
+ return ecode;
}
va_start(ap, fmt);
len = vsprintf(message, fmt, ap);
tdb->logfn(tdb, level, tdb->log_private, message);
free(message);
errno = saved_errno;
+ return ecode;
}