Ejemplo n.º 1
0
Archivo: lock.c Proyecto: hef/samba
static int tdb_lock_list(struct tdb_context *tdb, int list, int ltype,
			 enum tdb_lock_flags waitflag)
{
	int ret;
	bool check = false;

	if (tdb->allrecord_lock.count) {
		return tdb_lock_covered_by_allrecord_lock(tdb, ltype);
	}

	/*
	 * Check for recoveries: Someone might have kill -9'ed a process
	 * during a commit.
	 */
	check = !have_data_locks(tdb);
	ret = tdb_nest_lock(tdb, lock_offset(list), ltype, waitflag);

	if (ret == 0 && check && tdb_needs_recovery(tdb)) {
		tdb_nest_unlock(tdb, lock_offset(list), ltype, false);

		if (tdb_lock_and_recover(tdb) == -1) {
			return -1;
		}
		return tdb_lock_list(tdb, list, ltype, waitflag);
	}
	return ret;
}
Ejemplo n.º 2
0
Archivo: lock.c Proyecto: hef/samba
/* lock/unlock entire database.  It can only be upgradable if you have some
 * other way of guaranteeing exclusivity (ie. transaction write lock).
 * We do the locking gradually to avoid being starved by smaller locks. */
int tdb_allrecord_lock(struct tdb_context *tdb, int ltype,
		       enum tdb_lock_flags flags, bool upgradable)
{
	int ret;

	switch (tdb_allrecord_check(tdb, ltype, flags, upgradable)) {
	case -1:
		return -1;
	case 0:
		return 0;
	}

	/* We cover two kinds of locks:
	 * 1) Normal chain locks.  Taken for almost all operations.
	 * 2) Individual records locks.  Taken after normal or free
	 *    chain locks.
	 *
	 * It is (1) which cause the starvation problem, so we're only
	 * gradual for that. */

	if (tdb_have_mutexes(tdb)) {
		ret = tdb_mutex_allrecord_lock(tdb, ltype, flags);
	} else {
		ret = tdb_chainlock_gradual(tdb, ltype, flags, FREELIST_TOP,
					    tdb->hash_size * 4);
	}

	if (ret == -1) {
		return -1;
	}

	/* Grab individual record locks. */
	if (tdb_brlock(tdb, ltype, lock_offset(tdb->hash_size), 0,
		       flags) == -1) {
		if (tdb_have_mutexes(tdb)) {
			tdb_mutex_allrecord_unlock(tdb);
		} else {
			tdb_brunlock(tdb, ltype, FREELIST_TOP,
				     tdb->hash_size * 4);
		}
		return -1;
	}

	tdb->allrecord_lock.count = 1;
	/* If it's upgradable, it's actually exclusive so we can treat
	 * it as a write lock. */
	tdb->allrecord_lock.ltype = upgradable ? F_WRLCK : ltype;
	tdb->allrecord_lock.off = upgradable;

	if (tdb_needs_recovery(tdb)) {
		bool mark = flags & TDB_LOCK_MARK_ONLY;
		tdb_allrecord_unlock(tdb, ltype, mark);
		if (mark) {
			tdb->ecode = TDB_ERR_LOCK;
			TDB_LOG((tdb, TDB_DEBUG_ERROR,
				 "tdb_lockall_mark cannot do recovery\n"));
			return -1;
		}
		if (tdb_lock_and_recover(tdb) == -1) {
			return -1;
		}
		return tdb_allrecord_lock(tdb, ltype, flags, upgradable);
	}

	return 0;
}
Ejemplo n.º 3
0
static enum agent_return do_operation(enum operation op, const char *name)
{
	TDB_DATA k;
	enum agent_return ret;
	TDB_DATA data;

	if (op != OPEN && op != OPEN_WITH_CLEAR_IF_FIRST && !tdb) {
		diag("external: No tdb open!");
		return OTHER_FAILURE;
	}

	k.dptr = (void *)name;
	k.dsize = strlen(name);

	locking_would_block = 0;
	switch (op) {
	case OPEN:
		if (tdb) {
			diag("Already have tdb %s open", tdb_name(tdb));
			return OTHER_FAILURE;
		}
		tdb = tdb_open_ex(name, 0, TDB_DEFAULT, O_RDWR, 0,
				  &taplogctx, NULL);
		if (!tdb) {
			if (!locking_would_block)
				diag("Opening tdb gave %s", strerror(errno));
			ret = OTHER_FAILURE;
		} else
			ret = SUCCESS;
		break;
	case OPEN_WITH_CLEAR_IF_FIRST:
		if (tdb)
			return OTHER_FAILURE;
		tdb = tdb_open_ex(name, 0, TDB_CLEAR_IF_FIRST, O_RDWR, 0,
				  &taplogctx, NULL);
		ret = tdb ? SUCCESS : OTHER_FAILURE;
		break;
	case TRANSACTION_START:
		ret = tdb_transaction_start(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
		break;
	case FETCH:
		data = tdb_fetch(tdb, k);
		if (data.dptr == NULL) {
			if (tdb_error(tdb) == TDB_ERR_NOEXIST)
				ret = FAILED;
			else
				ret = OTHER_FAILURE;
		} else if (data.dsize != k.dsize
			   || memcmp(data.dptr, k.dptr, k.dsize) != 0) {
			ret = OTHER_FAILURE;
		} else {
			ret = SUCCESS;
		}
		free(data.dptr);
		break;
	case STORE:
		ret = tdb_store(tdb, k, k, 0) == 0 ? SUCCESS : OTHER_FAILURE;
		break;
	case TRANSACTION_COMMIT:
		ret = tdb_transaction_commit(tdb)==0 ? SUCCESS : OTHER_FAILURE;
		break;
	case CHECK:
		ret = tdb_check(tdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE;
		break;
	case NEEDS_RECOVERY:
		ret = tdb_needs_recovery(tdb) ? SUCCESS : FAILED;
		break;
	case CLOSE:
		ret = tdb_close(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
		tdb = NULL;
		break;
	default:
		ret = OTHER_FAILURE;
	}

	if (locking_would_block)
		ret = WOULD_HAVE_BLOCKED;

	return ret;
}