Exemplo n.º 1
0
/* expand a file.  we prefer to use ftruncate, as that is what posix
  says to use for mmap expansion */
static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition)
{
	char buf[1024];

	if (tdb->read_only || tdb->traverse_read) {
		tdb->ecode = TDB_ERR_RDONLY;
		return -1;
	}

	if (ftruncate(tdb->fd, size+addition) == -1) {
		char b = 0;
		if (pwrite(tdb->fd,  &b, 1, (size+addition) - 1) != 1) {
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n", 
				 size+addition, strerror(errno)));
			return -1;
		}
	}

	/* now fill the file with something. This ensures that the
	   file isn't sparse, which would be very bad if we ran out of
	   disk. This must be done with write, not via mmap */
	memset(buf, TDB_PAD_BYTE, sizeof(buf));
	while (addition) {
		int n = addition>sizeof(buf)?sizeof(buf):addition;
		int ret = pwrite(tdb->fd, buf, n, size);
		if (ret != n) {
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of %d failed (%s)\n", 
				   n, strerror(errno)));
			return -1;
		}
		addition -= n;
		size += n;
	}
	return 0;
}
Exemplo n.º 2
0
/* read a freelist record and check for simple errors */
int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct tdb_record *rec)
{
	if (tdb->methods->tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1)
		return -1;

	if (rec->magic == TDB_MAGIC) {
		/* this happens when a app is showdown while deleting a record - we should
		   not completely fail when this happens */
		TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read non-free magic 0x%x at offset=%d - fixing\n", 
			 rec->magic, off));
		rec->magic = TDB_FREE_MAGIC;
		if (tdb->methods->tdb_write(tdb, off, rec, sizeof(*rec)) == -1)
			return -1;
	}

	if (rec->magic != TDB_FREE_MAGIC) {
		/* Ensure ecode is set for log fn. */
		tdb->ecode = TDB_ERR_CORRUPT;
		TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read bad magic 0x%x at offset=%d\n", 
			   rec->magic, off));
		return -1;
	}
	if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0)
		return -1;
	return 0;
}
Exemplo n.º 3
0
/* expand a file.  we prefer to use ftruncate, as that is what posix
  says to use for mmap expansion */
static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition)
{
	char buf[8192];

	if (tdb->read_only || tdb->traverse_read) {
		tdb->ecode = TDB_ERR_RDONLY;
		return -1;
	}

	if (ftruncate(tdb->fd, size+addition) == -1) {
		char b = 0;
		ssize_t written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
		if (written == 0) {
			/* try once more, potentially revealing errno */
			written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
		}
		if (written == 0) {
			/* again - give up, guessing errno */
			errno = ENOSPC;
		}
		if (written != 1) {
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n", 
				 size+addition, strerror(errno)));
			return -1;
		}
	}

	/* now fill the file with something. This ensures that the
	   file isn't sparse, which would be very bad if we ran out of
	   disk. This must be done with write, not via mmap */
	memset(buf, TDB_PAD_BYTE, sizeof(buf));
	while (addition) {
		size_t n = addition>sizeof(buf)?sizeof(buf):addition;
		ssize_t written = pwrite(tdb->fd, buf, n, size);
		if (written == 0) {
			/* prevent infinite loops: try _once_ more */
			written = pwrite(tdb->fd, buf, n, size);
		}
		if (written == 0) {
			/* give up, trying to provide a useful errno */
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write "
				"returned 0 twice: giving up!\n"));
			errno = ENOSPC;
			return -1;
		} else if (written == -1) {
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of "
				 "%d bytes failed (%s)\n", (int)n,
				 strerror(errno)));
			return -1;
		} else if (written != n) {
			TDB_LOG((tdb, TDB_DEBUG_WARNING, "expand_file: wrote "
				 "only %d of %d bytes - retrying\n", (int)written,
				 (int)n));
		}
		addition -= written;
		size += written;
	}
	return 0;
}
Exemplo n.º 4
0
/* traverse the entire database - calling fn(tdb, key, data) on each element.
   return -1 on error or the record count traversed
   if fn is NULL then it is not called
   a non-zero return value from fn() indicates that the traversal should stop
  */
static int tdb_traverse_internal(struct tdb_context *tdb, 
				 tdb_traverse_func fn, void *private_data,
				 struct tdb_traverse_lock *tl)
{
	TDB_DATA key, dbuf;
	struct list_struct rec;
	int ret, count = 0;

	/* This was in the initializaton, above, but the IRIX compiler
	 * did not like it.  crh
	 */
	tl->next = tdb->travlocks.next;

	/* fcntl locks don't stack: beware traverse inside traverse */
	tdb->travlocks.next = tl;

	/* tdb_next_lock places locks on the record returned, and its chain */
	while ((ret = tdb_next_lock(tdb, tl, &rec)) > 0) {
		count++;
		/* now read the full record */
		key.dptr = tdb_alloc_read(tdb, tl->off + sizeof(rec), 
					  rec.key_len + rec.data_len);
		if (!key.dptr) {
			ret = -1;
			if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0)
				goto out;
			if (tdb_unlock_record(tdb, tl->off) != 0)
				TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n"));
			goto out;
		}
		key.dsize = rec.key_len;
		dbuf.dptr = key.dptr + rec.key_len;
		dbuf.dsize = rec.data_len;

		/* Drop chain lock, call out */
		if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0) {
			ret = -1;
			SAFE_FREE(key.dptr);
			goto out;
		}
		if (fn && fn(tdb, key, dbuf, private_data)) {
			/* They want us to terminate traversal */
			ret = count;
			if (tdb_unlock_record(tdb, tl->off) != 0) {
				TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: unlock_record failed!\n"));;
				ret = -1;
			}
			SAFE_FREE(key.dptr);
			goto out;
		}
		SAFE_FREE(key.dptr);
	}
out:
	tdb->travlocks.next = tl->next;
	if (ret < 0)
		return -1;
	else
		return count;
}
Exemplo n.º 5
0
/* find the next entry in the database, returning its key */
TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey)
{
	u32 oldhash;
	TDB_DATA key = tdb_null;
	struct list_struct rec;
	char *k = NULL;

	/* Is locked key the old key?  If so, traverse will be reliable. */
	if (tdb->travlocks.off) {
		if (tdb_lock(tdb,tdb->travlocks.hash,tdb->travlocks.lock_rw))
			return tdb_null;
		if (tdb_rec_read(tdb, tdb->travlocks.off, &rec) == -1
		    || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),
					    rec.key_len))
		    || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) {
			/* No, it wasn't: unlock it and start from scratch */
			if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0) {
				SAFE_FREE(k);
				return tdb_null;
			}
			if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) {
				SAFE_FREE(k);
				return tdb_null;
			}
			tdb->travlocks.off = 0;
		}

		SAFE_FREE(k);
	}

	if (!tdb->travlocks.off) {
		/* No previous element: do normal find, and lock record */
		tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), tdb->travlocks.lock_rw, &rec);
		if (!tdb->travlocks.off)
			return tdb_null;
		tdb->travlocks.hash = BUCKET(rec.full_hash);
		if (tdb_lock_record(tdb, tdb->travlocks.off) != 0) {
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno)));
			return tdb_null;
		}
	}
	oldhash = tdb->travlocks.hash;

	/* Grab next record: locks chain and returned record,
	   unlocks old record */
	if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) {
		key.dsize = rec.key_len;
		key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec),
					  key.dsize);
		/* Unlock the chain of this new record */
		if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0)
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
	}
	/* Unlock the chain of old record */
	if (tdb_unlock(tdb, BUCKET(oldhash), tdb->travlocks.lock_rw) != 0)
		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
	return key;
}
Exemplo n.º 6
0
/* Check that an in-use record is valid. */
static bool tdb_check_used_record(struct tdb_context *tdb,
				  tdb_off_t off,
				  const struct tdb_record *rec,
				  unsigned char **hashes,
				  int (*check)(TDB_DATA, TDB_DATA, void *),
				  void *private_data)
{
	TDB_DATA key, data;

	if (!tdb_check_record(tdb, off, rec))
		return false;

	/* key + data + tailer must fit in record */
	if (rec->key_len + rec->data_len + sizeof(tdb_off_t) > rec->rec_len) {
		TDB_LOG((tdb, TDB_DEBUG_ERROR,
			 "Record offset %d too short for contents\n", off));
		return false;
	}

	key = get_bytes(tdb, off + sizeof(*rec), rec->key_len);
	if (!key.dptr)
		return false;

	if (tdb->hash_fn(&key) != rec->full_hash) {
		TDB_LOG((tdb, TDB_DEBUG_ERROR,
			 "Record offset %d has incorrect hash\n", off));
		goto fail_put_key;
	}

	/* Mark this offset as a known value for this hash bucket. */
	record_offset(hashes[BUCKET(rec->full_hash)+1], off);
	/* And similarly if the next pointer is valid. */
	if (rec->next)
		record_offset(hashes[BUCKET(rec->full_hash)+1], rec->next);

	/* If they supply a check function and this record isn't dead,
	   get data and feed it. */
	if (check && rec->magic != TDB_DEAD_MAGIC) {
		data = get_bytes(tdb, off + sizeof(*rec) + rec->key_len,
				 rec->data_len);
		if (!data.dptr)
			goto fail_put_key;

		if (check(key, data, private_data) == -1)
			goto fail_put_data;
		put_bytes(tdb, data);
	}

	put_bytes(tdb, key);
	return true;

fail_put_data:
	put_bytes(tdb, data);
fail_put_key:
	put_bytes(tdb, key);
	return false;
}
Exemplo n.º 7
0
Arquivo: lock.c Projeto: hef/samba
int tdb_nest_unlock(struct tdb_context *tdb, uint32_t offset, int ltype,
		    bool mark_lock)
{
	int ret = -1;
	struct tdb_lock_type *lck;

	if (tdb->flags & TDB_NOLOCK)
		return 0;

	/* Sanity checks */
	if (offset >= lock_offset(tdb->hash_size)) {
		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: offset %u invalid (%d)\n", offset, tdb->hash_size));
		return ret;
	}

	lck = find_nestlock(tdb, offset);
	if ((lck == NULL) || (lck->count == 0)) {
		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n"));
		return -1;
	}

	if (lck->count > 1) {
		lck->count--;
		return 0;
	}

	/*
	 * This lock has count==1 left, so we need to unlock it in the
	 * kernel. We don't bother with decrementing the in-memory array
	 * element, we're about to overwrite it with the last array element
	 * anyway.
	 */

	if (mark_lock) {
		ret = 0;
	} else {
		ret = tdb_brunlock(tdb, ltype, offset, 1);
	}

	/*
	 * Shrink the array by overwriting the element just unlocked with the
	 * last array element.
	 */
	*lck = tdb->lockrecs[--tdb->num_lockrecs];

	/*
	 * We don't bother with realloc when the array shrinks, but if we have
	 * a completely idle tdb we should get rid of the locked array.
	 */

	if (ret)
		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n"));
	return ret;
}
Exemplo n.º 8
0
Arquivo: mutex.c Projeto: hef/samba
int tdb_mutex_allrecord_upgrade(struct tdb_context *tdb)
{
	struct tdb_mutexes *m = tdb->mutexes;
	int ret;
	uint32_t i;

	if (tdb->flags & TDB_NOLOCK) {
		return 0;
	}

	/*
	 * Our only caller tdb_allrecord_upgrade()
	 * garantees that we already own the allrecord lock.
	 *
	 * Which means m->allrecord_mutex is still locked by us.
	 */

	if (m->allrecord_lock != F_RDLCK) {
		tdb->ecode = TDB_ERR_LOCK;
		TDB_LOG((tdb, TDB_DEBUG_FATAL, "allrecord_lock == %d\n",
			 (int)m->allrecord_lock));
		return -1;
	}

	m->allrecord_lock = F_WRLCK;

	for (i=0; i<tdb->hash_size; i++) {

		/* ignore hashchains[0], the freelist */
		pthread_mutex_t *chain = &m->hashchains[i+1];

		ret = chain_mutex_lock(chain, true);
		if (ret != 0) {
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "pthread_mutex_lock"
				 "(chainlock) failed: %s\n", strerror(ret)));
			goto fail_unroll_allrecord_lock;
		}

		ret = pthread_mutex_unlock(chain);
		if (ret != 0) {
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "pthread_mutex_unlock"
				 "(chainlock) failed: %s\n", strerror(ret)));
			goto fail_unroll_allrecord_lock;
		}
	}

	return 0;

fail_unroll_allrecord_lock:
	m->allrecord_lock = F_RDLCK;
	tdb->ecode = TDB_ERR_LOCK;
	return -1;
}
Exemplo n.º 9
0
void tdb_mmap(struct tdb_context *tdb)
{
	if (tdb->flags & TDB_INTERNAL)
		return;

#ifdef HAVE_MMAP
	if (!(tdb->flags & TDB_NOMMAP)) {
		tdb->map_ptr = mmap(NULL, tdb->map_size, 
				    PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
				    MAP_SHARED|MAP_FILE, tdb->fd, 0);

		/*
		 * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
		 */

		if (tdb->map_ptr == MAP_FAILED) {
			tdb->map_ptr = NULL;
			TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %d (%s)\n", 
				 tdb->map_size, strerror(errno)));
		}
	} else {
		tdb->map_ptr = NULL;
	}
#else
	tdb->map_ptr = NULL;
#endif
}
Exemplo n.º 10
0
/* read a lump of data at a specified offset, maybe convert */
static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf, 
		    tdb_len_t len, int cv)
{
	if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0) {
		return -1;
	}

	if (tdb->map_ptr) {
		memcpy(buf, off + (char *)tdb->map_ptr, len);
	} else {
		ssize_t ret = pread(tdb->fd, buf, len, off);
		if (ret != (ssize_t)len) {
			/* Ensure ecode is set for log fn. */
			tdb->ecode = TDB_ERR_IO;
			TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %d "
				 "len=%d ret=%d (%s) map_size=%d\n",
				 (int)off, (int)len, (int)ret, strerror(errno),
				 (int)tdb->map_size));
			return -1;
		}
	}
	if (cv) {
		tdb_convert(buf, len);
	}
	return 0;
}
Exemplo n.º 11
0
/* Since we opened it, these shouldn't fail unless it's recent corruption. */
static bool tdb_check_header(struct tdb_context *tdb, tdb_off_t *recovery)
{
	struct tdb_header hdr;

	if (tdb->methods->tdb_read(tdb, 0, &hdr, sizeof(hdr), DOCONV()) == -1)
		return false;
	if (strcmp(hdr.magic_food, TDB_MAGIC_FOOD) != 0)
		goto corrupt;

	CONVERT(hdr);
	if (hdr.version != TDB_VERSION)
		goto corrupt;

	if (hdr.rwlocks != 0)
		goto corrupt;

	if (hdr.hash_size == 0)
		goto corrupt;

	if (hdr.hash_size != tdb->header.hash_size)
		goto corrupt;

	if (hdr.recovery_start != 0 &&
	    hdr.recovery_start < TDB_DATA_START(tdb->header.hash_size))
		goto corrupt;

	*recovery = hdr.recovery_start;
	return true;

corrupt:
	tdb->ecode = TDB_ERR_CORRUPT;
	TDB_LOG((tdb, TDB_DEBUG_ERROR, "Header is corrupt\n"));
	return false;
}
Exemplo n.º 12
0
/* lock/unlock entire database */
static int _tdb_lockall(struct tdb_context *tdb, int ltype)
{
	/* There are no locks on read-only dbs */
	if (tdb->read_only || tdb->traverse_read)
		return TDB_ERRCODE(TDB_ERR_LOCK, -1);

	if (tdb->global_lock.count && tdb->global_lock.ltype == ltype) {
		tdb->global_lock.count++;
		return 0;
	}

	if (tdb->global_lock.count) {
		/* a global lock of a different type exists */
		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
	}
	
	if (tdb->num_locks != 0) {
		/* can't combine global and chain locks */
		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
	}

	if (tdb->methods->tdb_brlock(tdb, FREELIST_TOP, ltype, F_SETLKW, 
				     0, 4*tdb->header.hash_size)) {
		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lockall failed (%s)\n", strerror(errno)));
		return -1;
	}

	tdb->global_lock.count = 1;
	tdb->global_lock.ltype = ltype;

	return 0;
}
Exemplo n.º 13
0
/* write a lump of data at a specified offset */
static int tdb_write(struct tdb_context *tdb, tdb_off_t off, 
		     const void *buf, tdb_len_t len)
{
	if (len == 0) {
		return 0;
	}

	if (tdb->read_only || tdb->traverse_read) {
		tdb->ecode = TDB_ERR_RDONLY;
		return -1;
	}

	if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0)
		return -1;

	if (tdb->map_ptr) {
		memcpy(off + (char *)tdb->map_ptr, buf, len);
	} else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) {
		/* Ensure ecode is set for log fn. */
		tdb->ecode = TDB_ERR_IO;
		TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d len=%d (%s)\n",
			   off, len, strerror(errno)));
		return TDB_ERRCODE(TDB_ERR_IO, -1);
	}
	return 0;
}
Exemplo n.º 14
0
/* find the first entry in the database and return its key */
_PUBLIC_ TDB_DATA tdb_firstkey(struct tdb_context *tdb)
{
	TDB_DATA key;
	struct tdb_record rec;
	tdb_off_t off;

	/* release any old lock */
	if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0)
		return tdb_null;
	tdb->travlocks.off = tdb->travlocks.list = 0;
	tdb->travlocks.lock_rw = F_RDLCK;

	/* Grab first record: locks chain and returned record. */
	off = tdb_next_lock(tdb, &tdb->travlocks, &rec);
	if (off == 0 || off == TDB_NEXT_LOCK_ERR) {
		tdb_trace_retrec(tdb, "tdb_firstkey", tdb_null);
		return tdb_null;
	}
	/* now read the key */
	key.dsize = rec.key_len;
	key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize);

	tdb_trace_retrec(tdb, "tdb_firstkey", key);

	/* Unlock the hash chain of the record we just read. */
	if (tdb_unlock(tdb, tdb->travlocks.list, tdb->travlocks.lock_rw) != 0)
		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_firstkey: error occurred while tdb_unlocking!\n"));
	return key;
}
Exemplo n.º 15
0
Arquivo: tdb.c Projeto: GSam/samba
/* Returns 0 on fail.  On success, return offset of record, and fills
   in rec */
static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash,
			struct tdb_record *r)
{
	tdb_off_t rec_ptr;

	/* read in the hash top */
	if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
		return 0;

	/* keep looking until we find the right record */
	while (rec_ptr) {
		if (tdb_rec_read(tdb, rec_ptr, r) == -1)
			return 0;

		if (!TDB_DEAD(r) && hash==r->full_hash
		    && key.dsize==r->key_len
		    && tdb_parse_data(tdb, key, rec_ptr + sizeof(*r),
				      r->key_len, tdb_key_compare,
				      NULL) == 0) {
			return rec_ptr;
		}
		/* detect tight infinite loop */
		if (rec_ptr == r->next) {
			tdb->ecode = TDB_ERR_CORRUPT;
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_find: loop detected.\n"));
			return 0;
		}
		rec_ptr = r->next;
	}
	tdb->ecode = TDB_ERR_NOEXIST;
	return 0;
}
Exemplo n.º 16
0
static int tdb_expand_file_sparse(struct tdb_context *tdb,
				  tdb_off_t size,
				  tdb_off_t addition)
{
	if (tdb->read_only || tdb->traverse_read) {
		tdb->ecode = TDB_ERR_RDONLY;
		return -1;
	}

	if (ftruncate(tdb->fd, size+addition) == -1) {
		char b = 0;
		ssize_t written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
		if (written == 0) {
			/* try once more, potentially revealing errno */
			written = pwrite(tdb->fd,  &b, 1, (size+addition) - 1);
		}
		if (written == 0) {
			/* again - give up, guessing errno */
			errno = ENOSPC;
		}
		if (written != 1) {
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n",
				 size+addition, strerror(errno)));
			return -1;
		}
	}

	return 0;
}
Exemplo n.º 17
0
Arquivo: lock.c Projeto: gojdic/samba
/* unlock entire db */
static int _tdb_unlockall(struct tdb_context *tdb, int ltype)
{
	bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);

	ltype &= ~TDB_MARK_LOCK;

	/* There are no locks on read-only dbs */
	if (tdb->read_only || tdb->traverse_read) {
		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
	}

	if (tdb->global_lock.ltype != ltype || tdb->global_lock.count == 0) {
		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
	}

	if (tdb->global_lock.count > 1) {
		tdb->global_lock.count--;
		return 0;
	}

	if (!mark_lock &&
	    tdb->methods->tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 
				     0, 4*tdb->header.hash_size)) {
		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlockall failed (%s)\n", strerror(errno)));
		return -1;
	}

	tdb->global_lock.count = 0;
	tdb->global_lock.ltype = 0;

	return 0;
}
Exemplo n.º 18
0
Arquivo: open.c Projeto: hef/samba
_PUBLIC_ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
				int open_flags, mode_t mode,
				const struct tdb_logging_context *log_ctx,
				tdb_hash_func hash_fn)
{
	int orig_errno = errno;
	struct tdb_header header;
	struct tdb_context *tdb;
	struct stat st;
	int rev = 0, locked = 0;
	unsigned char *vp;
	uint32_t vertest;
	unsigned v;
	const char *hash_alg;
	uint32_t magic1, magic2;
	int ret;

	ZERO_STRUCT(header);

	if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) {
		/* Can't log this */
		errno = ENOMEM;
		goto fail;
	}
	tdb_io_init(tdb);

	if (tdb_flags & TDB_INTERNAL) {
		tdb_flags |= TDB_INCOMPATIBLE_HASH;
	}
	if (tdb_flags & TDB_MUTEX_LOCKING) {
		tdb_flags |= TDB_INCOMPATIBLE_HASH;
	}

	tdb->fd = -1;
#ifdef TDB_TRACE
	tdb->tracefd = -1;
#endif
	tdb->name = NULL;
	tdb->map_ptr = NULL;
	tdb->flags = tdb_flags;
	tdb->open_flags = open_flags;
	if (log_ctx) {
		tdb->log = *log_ctx;
	} else {
		tdb->log.log_fn = null_log_fn;
		tdb->log.log_private = NULL;
	}

	if (name == NULL && (tdb_flags & TDB_INTERNAL)) {
		name = "__TDB_INTERNAL__";
	}

	if (name == NULL) {
		tdb->name = discard_const_p(char, "__NULL__");
		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: called with name == NULL\n"));
		tdb->name = NULL;
		errno = EINVAL;
		goto fail;
	}
Exemplo n.º 19
0
/* write a lump of data at a specified offset */
static int tdb_write(struct tdb_context *tdb, tdb_off_t off, 
		     const void *buf, tdb_len_t len)
{
	if (len == 0) {
		return 0;
	}

	if (tdb->read_only || tdb->traverse_read) {
		tdb->ecode = TDB_ERR_RDONLY;
		return -1;
	}

	if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0)
		return -1;

	if (tdb->map_ptr) {
		memcpy(off + (char *)tdb->map_ptr, buf, len);
	} else {
		ssize_t written = pwrite(tdb->fd, buf, len, off);
		if ((written != (ssize_t)len) && (written != -1)) {
			/* try once more */
			tdb->ecode = TDB_ERR_IO;
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: wrote only "
				 "%d of %d bytes at %d, trying once more\n",
				 (int)written, len, off));
			written = pwrite(tdb->fd, (const char *)buf+written,
					 len-written,
					 off+written);
		}
		if (written == -1) {
			/* Ensure ecode is set for log fn. */
			tdb->ecode = TDB_ERR_IO;
			TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d "
				 "len=%d (%s)\n", off, len, strerror(errno)));
			return -1;
		} else if (written != (ssize_t)len) {
			tdb->ecode = TDB_ERR_IO;
			TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: failed to "
				 "write %d bytes at %d in two attempts\n",
				 len, off));
			return -1;
		}
	}
	return 0;
}
Exemplo n.º 20
0
Arquivo: tdb.c Projeto: gojdic/samba
/*
  add a region of the file to the freelist. Length is the size of the region in bytes, 
  which includes the free list header that needs to be added
 */
static int tdb_free_region(struct tdb_context *tdb, tdb_off_t offset, ssize_t length)
{
	struct list_struct rec;
	if (length <= sizeof(rec)) {
		/* the region is not worth adding */
		return 0;
	}
	if (length + offset > tdb->map_size) {
		TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_free_region: adding region beyond end of file\n"));
		return -1;		
	}
	memset(&rec,'\0',sizeof(rec));
	rec.rec_len = length - sizeof(rec);
	if (tdb_free(tdb, offset, &rec) == -1) {
		TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_free_region: failed to add free record\n"));
		return -1;
	}
	return 0;
}
Exemplo n.º 21
0
Arquivo: lock.c Projeto: gojdic/samba
/* lock a list in the database. list -1 is the alloc list */
int tdb_lock(struct tdb_context *tdb, int list, int ltype)
{
	int ret;
	ret = _tdb_lock(tdb, list, ltype, F_SETLKW);
	if (ret) {
		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d "
			 "ltype=%d (%s)\n",  list, ltype, strerror(errno)));
	}
	return ret;
}
Exemplo n.º 22
0
Arquivo: lock.c Projeto: hef/samba
/*
  upgrade a read lock to a write lock.
*/
int tdb_allrecord_upgrade(struct tdb_context *tdb)
{
	int ret;

	if (tdb->allrecord_lock.count != 1) {
		TDB_LOG((tdb, TDB_DEBUG_ERROR,
			 "tdb_allrecord_upgrade failed: count %u too high\n",
			 tdb->allrecord_lock.count));
		return -1;
	}

	if (tdb->allrecord_lock.off != 1) {
		TDB_LOG((tdb, TDB_DEBUG_ERROR,
			 "tdb_allrecord_upgrade failed: already upgraded?\n"));
		return -1;
	}

	if (tdb_have_mutexes(tdb)) {
		ret = tdb_mutex_allrecord_upgrade(tdb);
		if (ret == -1) {
			goto fail;
		}
		ret = tdb_brlock_retry(tdb, F_WRLCK, lock_offset(tdb->hash_size),
				       0, TDB_LOCK_WAIT|TDB_LOCK_PROBE);
		if (ret == -1) {
			tdb_mutex_allrecord_downgrade(tdb);
		}
	} else {
		ret = tdb_brlock_retry(tdb, F_WRLCK, FREELIST_TOP, 0,
				       TDB_LOCK_WAIT|TDB_LOCK_PROBE);
	}

	if (ret == 0) {
		tdb->allrecord_lock.ltype = F_WRLCK;
		tdb->allrecord_lock.off = 0;
		return 0;
	}
fail:
	TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_allrecord_upgrade failed\n"));
	return -1;
}
Exemplo n.º 23
0
/* read/write a record */
int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
{
	if (tdb->methods->tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
		return -1;
	if (TDB_BAD_MAGIC(rec)) {
		/* Ensure ecode is set for log fn. */
		tdb->ecode = TDB_ERR_CORRUPT;
		TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
		return -1;
	}
	return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0);
}
Exemplo n.º 24
0
static void
__do_close_table(TDB *db)
{
	/* Unmapping can be done from process context. */
	tdb_file_close(db);

	tdb_htrie_exit(db->hdr);

	TDB_LOG("Close table '%s'\n", db->tbl_name);

	kfree(db);
}
Exemplo n.º 25
0
/* check for an out of bounds access - if it is out of bounds then
   see if the database has been expanded by someone else and expand
   if necessary 
   note that "len" is the minimum length needed for the db
*/
static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
{
	struct stat st;
	if (len <= tdb->map_size)
		return 0;
	if (tdb->flags & TDB_INTERNAL) {
		if (!probe) {
			/* Ensure ecode is set for log fn. */
			tdb->ecode = TDB_ERR_IO;
			TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond internal malloc size %d\n",
				 (int)len, (int)tdb->map_size));
		}
		return -1;
	}

	if (fstat(tdb->fd, &st) == -1) {
		tdb->ecode = TDB_ERR_IO;
		return -1;
	}

	/* Unmap, update size, remap */
	if (tdb_munmap(tdb) == -1) {
		tdb->ecode = TDB_ERR_IO;
		return -1;
	}
	tdb->map_size = st.st_size;
	tdb_mmap(tdb);

	if (st.st_size < (size_t)len) {
		if (!probe) {
			/* Ensure ecode is set for log fn. */
			tdb->ecode = TDB_ERR_IO;
			TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond eof at %d\n",
				 (int)len, (int)st.st_size));
		}
		return -1;
	}

	return 0;
}
Exemplo n.º 26
0
/* reopen a tdb - this can be used after a fork to ensure that we have an independent
   seek pointer from our parent and to re-establish locks */
int tdb_reopen(struct tdb_context *tdb)
{
	struct stat st;

	if (tdb->flags & TDB_INTERNAL) {
		return 0; /* Nothing to do. */
	}

	if (tdb->num_locks != 0 || tdb->global_lock.count) {
		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed with locks held\n"));
		goto fail;
	}

	if (tdb->transaction != 0) {
		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed inside a transaction\n"));
		goto fail;
	}

	if (tdb_munmap(tdb) != 0) {
		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: munmap failed (%s)\n", strerror(errno)));
		goto fail;
	}
	if (close(tdb->fd) != 0)
		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: WARNING closing tdb->fd failed!\n"));
	tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0);
	if (tdb->fd == -1) {
		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: open failed (%s)\n", strerror(errno)));
		goto fail;
	}
	if ((tdb->flags & TDB_CLEAR_IF_FIRST) && 
	    (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1)) {
		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n"));
		goto fail;
	}
	if (fstat(tdb->fd, &st) != 0) {
		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: fstat failed (%s)\n", strerror(errno)));
		goto fail;
	}
	if (st.st_ino != tdb->inode || st.st_dev != tdb->device) {
		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: file dev/inode has changed!\n"));
		goto fail;
	}
	tdb_mmap(tdb);

	return 0;

fail:
	tdb_close(tdb);
	return -1;
}
Exemplo n.º 27
0
static void __exit
tdb_exit(void)
{
	TDB_LOG("Shutdown Tempesta DB\n");

	tdb_if_exit();

	/*
	 * There are no database users, so roughtly close all abandoned
	 * tables w/o refrence checking and so on.
	 */
	tdb_tbl_foreach(__do_close_table);
}
Exemplo n.º 28
0
Arquivo: lock.c Projeto: hef/samba
/* unlock entire db */
int tdb_allrecord_unlock(struct tdb_context *tdb, int ltype, bool mark_lock)
{
	/* There are no locks on read-only dbs */
	if (tdb->read_only || tdb->traverse_read) {
		tdb->ecode = TDB_ERR_LOCK;
		return -1;
	}

	if (tdb->allrecord_lock.count == 0) {
		tdb->ecode = TDB_ERR_LOCK;
		return -1;
	}

	/* Upgradable locks are marked as write locks. */
	if (tdb->allrecord_lock.ltype != ltype
	    && (!tdb->allrecord_lock.off || ltype != F_RDLCK)) {
		tdb->ecode = TDB_ERR_LOCK;
		return -1;
	}

	if (tdb->allrecord_lock.count > 1) {
		tdb->allrecord_lock.count--;
		return 0;
	}

	if (!mark_lock) {
		int ret;

		if (tdb_have_mutexes(tdb)) {
			ret = tdb_mutex_allrecord_unlock(tdb);
			if (ret == 0) {
				ret = tdb_brunlock(tdb, ltype,
						   lock_offset(tdb->hash_size),
						   0);
			}
		} else {
			ret = tdb_brunlock(tdb, ltype, FREELIST_TOP, 0);
		}

		if (ret != 0) {
			TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlockall failed "
				 "(%s)\n", strerror(errno)));
			return -1;
		}
	}

	tdb->allrecord_lock.count = 0;
	tdb->allrecord_lock.ltype = 0;

	return 0;
}
Exemplo n.º 29
0
/* delete an entry in the database given a key */
static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash)
{
	tdb_off_t rec_ptr;
	struct list_struct rec;
	int ret;

	if (tdb->max_dead_records != 0) {

		/*
		 * Allow for some dead records per hash chain, mainly for
		 * tdb's with a very high create/delete rate like locking.tdb.
		 */

		if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
			return -1;

		if (tdb_count_dead(tdb, hash) >= tdb->max_dead_records) {
			/*
			 * Don't let the per-chain freelist grow too large,
			 * delete all existing dead records
			 */
			tdb_purge_dead(tdb, hash);
		}

		if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) {
			tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
			return -1;
		}

		/*
		 * Just mark the record as dead.
		 */
		rec.magic = TDB_DEAD_MAGIC;
		ret = tdb_rec_write(tdb, rec_ptr, &rec);
	}
	else {
		if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK,
						   &rec)))
			return -1;

		ret = tdb_do_delete(tdb, rec_ptr, &rec);
	}

	if (ret == 0) {
		tdb_increment_seqnum(tdb);
	}

	if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0)
		TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_delete: WARNING tdb_unlock failed!\n"));
	return ret;
}
Exemplo n.º 30
0
Arquivo: lock.c Projeto: hef/samba
/* lock an offset in the database. */
int tdb_nest_lock(struct tdb_context *tdb, uint32_t offset, int ltype,
		  enum tdb_lock_flags flags)
{
	struct tdb_lock_type *new_lck;

	if (offset >= lock_offset(tdb->hash_size)) {
		tdb->ecode = TDB_ERR_LOCK;
		TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid offset %u for ltype=%d\n",
			 offset, ltype));
		return -1;
	}
	if (tdb->flags & TDB_NOLOCK)
		return 0;

	new_lck = find_nestlock(tdb, offset);
	if (new_lck) {
		/*
		 * Just increment the in-memory struct, posix locks
		 * don't stack.
		 */
		new_lck->count++;
		return 0;
	}

	if (tdb->num_lockrecs == tdb->lockrecs_array_length) {
		new_lck = (struct tdb_lock_type *)realloc(
			tdb->lockrecs,
			sizeof(*tdb->lockrecs) * (tdb->num_lockrecs+1));
		if (new_lck == NULL) {
			errno = ENOMEM;
			return -1;
		}
		tdb->lockrecs_array_length = tdb->num_lockrecs+1;
		tdb->lockrecs = new_lck;
	}

	/* Since fcntl locks don't nest, we do a lock for the first one,
	   and simply bump the count for future ones */
	if (tdb_brlock(tdb, ltype, offset, 1, flags)) {
		return -1;
	}

	new_lck = &tdb->lockrecs[tdb->num_lockrecs];

	new_lck->off = offset;
	new_lck->count = 1;
	new_lck->ltype = ltype;
	tdb->num_lockrecs++;

	return 0;
}