Home | History | Annotate | Download | only in configd
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 /*
     30  * This file only contains the transaction commit logic.
     31  */
     32 
     33 #include <assert.h>
     34 #include <alloca.h>
     35 #include <errno.h>
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <strings.h>
     39 #include <sys/sysmacros.h>
     40 #include "configd.h"
     41 
     42 #define	INVALID_OBJ_ID ((uint32_t)-1)
     43 #define	INVALID_TYPE ((uint32_t)-1)
     44 
     45 struct tx_cmd {
     46 	const struct rep_protocol_transaction_cmd *tx_cmd;
     47 	const char	*tx_prop;
     48 	uint32_t	*tx_values;
     49 	uint32_t	tx_nvalues;
     50 	uint32_t	tx_orig_value_id;
     51 	char		tx_found;
     52 	char		tx_processed;
     53 	char		tx_bad;
     54 };
     55 
     56 static int
     57 tx_cmd_compare(const void *key, const void *elem_arg)
     58 {
     59 	const struct tx_cmd *elem = elem_arg;
     60 
     61 	return (strcmp((const char *)key, elem->tx_prop));
     62 }
     63 
     64 struct tx_commit_data {
     65 	uint32_t	txc_pg_id;
     66 	uint32_t	txc_gen;
     67 	uint32_t	txc_oldgen;
     68 	short		txc_backend;
     69 	backend_tx_t	*txc_tx;
     70 	backend_query_t	*txc_inserts;
     71 	size_t		txc_count;
     72 	rep_protocol_responseid_t txc_result;
     73 	struct tx_cmd	txc_cmds[1];		/* actually txc_count */
     74 };
     75 #define	TX_COMMIT_DATA_SIZE(count) \
     76 	offsetof(struct tx_commit_data, txc_cmds[count])
     77 
     78 /*ARGSUSED*/
     79 static int
     80 tx_check_genid(void *data_arg, int columns, char **vals, char **names)
     81 {
     82 	tx_commit_data_t *data = data_arg;
     83 	assert(columns == 1);
     84 	if (atoi(vals[0]) != data->txc_oldgen)
     85 		data->txc_result = REP_PROTOCOL_FAIL_NOT_LATEST;
     86 	else
     87 		data->txc_result = REP_PROTOCOL_SUCCESS;
     88 	return (BACKEND_CALLBACK_CONTINUE);
     89 }
     90 
     91 /*
     92  * tx_process_property() is called once for each property in current
     93  * property group generation.  Its purpose is threefold:
     94  *
     95  *	1. copy properties not mentioned in the transaction over unchanged.
     96  *	2. mark DELETEd properties as seen (they will be left out of the new
     97  *	   generation).
     98  *	3. consistancy-check NEW, CLEAR, and REPLACE commands.
     99  *
    100  * Any consistancy problems set tx_bad, and seen properties are marked
    101  * tx_found.  These is used later, in tx_process_cmds().
    102  */
    103 /*ARGSUSED*/
    104 static int
    105 tx_process_property(void *data_arg, int columns, char **vals, char **names)
    106 {
    107 	tx_commit_data_t *data = data_arg;
    108 	struct tx_cmd *elem;
    109 
    110 	const char *prop_name = vals[0];
    111 	const char *prop_type = vals[1];
    112 	const char *lnk_val_id = vals[2];
    113 
    114 	char *endptr;
    115 
    116 	assert(columns == 3);
    117 
    118 	elem = bsearch(prop_name, data->txc_cmds, data->txc_count,
    119 	    sizeof (*data->txc_cmds), tx_cmd_compare);
    120 
    121 	if (elem == NULL) {
    122 		backend_query_add(data->txc_inserts,
    123 		    "INSERT INTO prop_lnk_tbl"
    124 		    "    (lnk_pg_id, lnk_gen_id, lnk_prop_name, lnk_prop_type,"
    125 		    "    lnk_val_id) "
    126 		    "VALUES ( %d, %d, '%q', '%q', %Q );",
    127 		    data->txc_pg_id, data->txc_gen, prop_name, prop_type,
    128 		    lnk_val_id);
    129 	} else {
    130 		assert(!elem->tx_found);
    131 		elem->tx_found = 1;
    132 
    133 		if (lnk_val_id != NULL) {
    134 			errno = 0;
    135 			elem->tx_orig_value_id =
    136 			    strtoul(lnk_val_id, &endptr, 10);
    137 			if (elem->tx_orig_value_id == 0 || *endptr != 0 ||
    138 			    errno != 0) {
    139 				return (BACKEND_CALLBACK_ABORT);
    140 			}
    141 		} else {
    142 			elem->tx_orig_value_id = 0;
    143 		}
    144 
    145 		switch (elem->tx_cmd->rptc_action) {
    146 		case REP_PROTOCOL_TX_ENTRY_NEW:
    147 			elem->tx_bad = 1;
    148 			data->txc_result = REP_PROTOCOL_FAIL_EXISTS;
    149 			break;
    150 		case REP_PROTOCOL_TX_ENTRY_CLEAR:
    151 			if (REP_PROTOCOL_BASE_TYPE(elem->tx_cmd->rptc_type) !=
    152 			    prop_type[0] &&
    153 			    REP_PROTOCOL_SUBTYPE(elem->tx_cmd->rptc_type) !=
    154 			    prop_type[1]) {
    155 				elem->tx_bad = 1;
    156 				data->txc_result =
    157 				    REP_PROTOCOL_FAIL_TYPE_MISMATCH;
    158 			}
    159 			break;
    160 		case REP_PROTOCOL_TX_ENTRY_REPLACE:
    161 			break;
    162 		case REP_PROTOCOL_TX_ENTRY_DELETE:
    163 			elem->tx_processed = 1;
    164 			break;
    165 		default:
    166 			assert(0);
    167 			break;
    168 		}
    169 	}
    170 	return (BACKEND_CALLBACK_CONTINUE);
    171 }
    172 
    173 /*
    174  * tx_process_cmds() finishes the job tx_process_property() started:
    175  *
    176  *	1. if tx_process_property() marked a command as bad, we skip it.
    177  *	2. if a DELETE, REPLACE, or CLEAR operated on a non-existant property,
    178  *	    we mark it as bad.
    179  *	3. we complete the work of NEW, REPLACE, and CLEAR, by inserting the
    180  *	    appropriate values into the database.
    181  *	4. we delete all replaced data, if it is no longer referenced.
    182  *
    183  * Finally, we check all of the commands, and fail if anything was marked bad.
    184  */
    185 static int
    186 tx_process_cmds(tx_commit_data_t *data)
    187 {
    188 	int idx;
    189 	int r;
    190 	int count = data->txc_count;
    191 	struct tx_cmd *elem;
    192 	uint32_t val_id = 0;
    193 	uint8_t type[3];
    194 
    195 	backend_query_t *q;
    196 	int do_delete;
    197 
    198 	/*
    199 	 * For persistent pgs, we use backend_fail_if_seen to abort the
    200 	 * deletion if there is a snapshot using our current state.
    201 	 *
    202 	 * All of the deletions in this function are safe, since
    203 	 * rc_tx_commit() guarantees that all the data is in-cache.
    204 	 */
    205 	q = backend_query_alloc();
    206 
    207 	if (data->txc_backend != BACKEND_TYPE_NONPERSIST) {
    208 		backend_query_add(q,
    209 		    "SELECT 1 FROM snaplevel_lnk_tbl "
    210 		    "    WHERE (snaplvl_pg_id = %d AND snaplvl_gen_id = %d); ",
    211 		    data->txc_pg_id, data->txc_oldgen);
    212 	}
    213 	backend_query_add(q,
    214 	    "DELETE FROM prop_lnk_tbl"
    215 	    "    WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
    216 	    data->txc_pg_id, data->txc_oldgen);
    217 	r = backend_tx_run(data->txc_tx, q, backend_fail_if_seen, NULL);
    218 	backend_query_free(q);
    219 
    220 	if (r == REP_PROTOCOL_SUCCESS)
    221 		do_delete = 1;
    222 	else if (r == REP_PROTOCOL_DONE)
    223 		do_delete = 0;		/* old gen_id is in use */
    224 	else
    225 		return (r);
    226 
    227 	for (idx = 0; idx < count; idx++) {
    228 		elem = &data->txc_cmds[idx];
    229 
    230 		if (elem->tx_bad)
    231 			continue;
    232 
    233 		switch (elem->tx_cmd->rptc_action) {
    234 		case REP_PROTOCOL_TX_ENTRY_DELETE:
    235 		case REP_PROTOCOL_TX_ENTRY_REPLACE:
    236 		case REP_PROTOCOL_TX_ENTRY_CLEAR:
    237 			if (!elem->tx_found) {
    238 				elem->tx_bad = 1;
    239 				continue;
    240 			}
    241 			break;
    242 		case REP_PROTOCOL_TX_ENTRY_NEW:
    243 			break;
    244 		default:
    245 			assert(0);
    246 			break;
    247 		}
    248 
    249 		if (do_delete &&
    250 		    elem->tx_cmd->rptc_action != REP_PROTOCOL_TX_ENTRY_NEW &&
    251 		    elem->tx_orig_value_id != 0) {
    252 			/*
    253 			 * delete the old values, if they are not in use
    254 			 */
    255 			q = backend_query_alloc();
    256 			backend_query_add(q,
    257 			    "SELECT 1 FROM prop_lnk_tbl "
    258 			    "    WHERE (lnk_val_id = %d); "
    259 			    "DELETE FROM value_tbl"
    260 			    "    WHERE (value_id = %d)",
    261 			    elem->tx_orig_value_id, elem->tx_orig_value_id);
    262 			r = backend_tx_run(data->txc_tx, q,
    263 			    backend_fail_if_seen, NULL);
    264 			backend_query_free(q);
    265 			if (r != REP_PROTOCOL_SUCCESS && r != REP_PROTOCOL_DONE)
    266 				return (r);
    267 		}
    268 
    269 		if (elem->tx_cmd->rptc_action == REP_PROTOCOL_TX_ENTRY_DELETE)
    270 			continue;		/* no further work to do */
    271 
    272 		type[0] = REP_PROTOCOL_BASE_TYPE(elem->tx_cmd->rptc_type);
    273 		type[1] = REP_PROTOCOL_SUBTYPE(elem->tx_cmd->rptc_type);
    274 		type[2] = 0;
    275 
    276 		if (elem->tx_nvalues == 0) {
    277 			r = backend_tx_run_update(data->txc_tx,
    278 			    "INSERT INTO prop_lnk_tbl"
    279 			    "    (lnk_pg_id, lnk_gen_id, "
    280 			    "    lnk_prop_name, lnk_prop_type, lnk_val_id) "
    281 			    "VALUES ( %d, %d, '%q', '%q', NULL );",
    282 			    data->txc_pg_id, data->txc_gen, elem->tx_prop,
    283 			    type);
    284 		} else {
    285 			uint32_t *v, i = 0;
    286 			const char *str;
    287 
    288 			val_id = backend_new_id(data->txc_tx, BACKEND_ID_VALUE);
    289 			if (val_id == 0)
    290 				return (REP_PROTOCOL_FAIL_NO_RESOURCES);
    291 			r = backend_tx_run_update(data->txc_tx,
    292 			    "INSERT INTO prop_lnk_tbl "
    293 			    "    (lnk_pg_id, lnk_gen_id, "
    294 			    "    lnk_prop_name, lnk_prop_type, lnk_val_id) "
    295 			    "VALUES ( %d, %d, '%q', '%q', %d );",
    296 			    data->txc_pg_id, data->txc_gen, elem->tx_prop,
    297 			    type, val_id);
    298 
    299 			v = elem->tx_values;
    300 
    301 			for (i = 0; i < elem->tx_nvalues; i++) {
    302 				str = (const char *)&v[1];
    303 
    304 				/*
    305 				 * Update values in backend,  imposing
    306 				 * ordering via the value_order column.
    307 				 * This ordering is then used in subseqent
    308 				 * value retrieval operations.  We can
    309 				 * safely assume that the repository schema
    310 				 * has been upgraded (and hence has the
    311 				 * value_order column in value_tbl),  since
    312 				 * it is upgraded as soon as the repository
    313 				 * is writable.
    314 				 */
    315 				r = backend_tx_run_update(data->txc_tx,
    316 				    "INSERT INTO value_tbl (value_id, "
    317 				    "value_type, value_value, "
    318 				    "value_order) VALUES (%d, '%c', "
    319 				    "'%q', '%d');\n",
    320 				    val_id, elem->tx_cmd->rptc_type,
    321 				    str, i);
    322 				if (r != REP_PROTOCOL_SUCCESS)
    323 					break;
    324 
    325 				/*LINTED alignment*/
    326 				v = (uint32_t *)((caddr_t)str + TX_SIZE(*v));
    327 			}
    328 		}
    329 		if (r != REP_PROTOCOL_SUCCESS)
    330 			return (REP_PROTOCOL_FAIL_UNKNOWN);
    331 		elem->tx_processed = 1;
    332 	}
    333 
    334 	for (idx = 0; idx < count; idx++) {
    335 		elem = &data->txc_cmds[idx];
    336 
    337 		if (elem->tx_bad)
    338 			return (REP_PROTOCOL_FAIL_BAD_TX);
    339 	}
    340 	return (REP_PROTOCOL_SUCCESS);
    341 }
    342 
    343 static boolean_t
    344 check_string(uintptr_t loc, uint32_t len, uint32_t sz)
    345 {
    346 	const char *ptr = (const char *)loc;
    347 
    348 	if (len == 0 || len > sz || ptr[len - 1] != 0 || strlen(ptr) != len - 1)
    349 		return (0);
    350 	return (1);
    351 }
    352 
    353 static int
    354 tx_check_and_setup(tx_commit_data_t *data, const void *cmds_arg,
    355     uint32_t count)
    356 {
    357 	const struct rep_protocol_transaction_cmd *cmds;
    358 	struct tx_cmd *cur;
    359 	struct tx_cmd *prev = NULL;
    360 
    361 	uintptr_t loc;
    362 	uint32_t sz, len;
    363 	int idx;
    364 
    365 	loc = (uintptr_t)cmds_arg;
    366 
    367 	for (idx = 0; idx < count; idx++) {
    368 		cur = &data->txc_cmds[idx];
    369 
    370 		cmds = (struct rep_protocol_transaction_cmd *)loc;
    371 		cur->tx_cmd = cmds;
    372 
    373 		sz = cmds->rptc_size;
    374 
    375 		loc += REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE;
    376 		sz -= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE;
    377 
    378 		len = cmds->rptc_name_len;
    379 		if (len <= 1 || !check_string(loc, len, sz)) {
    380 			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    381 		}
    382 		cur->tx_prop = (const char *)loc;
    383 
    384 		len = TX_SIZE(len);
    385 		loc += len;
    386 		sz -= len;
    387 
    388 		cur->tx_nvalues = 0;
    389 		cur->tx_values = (uint32_t *)loc;
    390 
    391 		while (sz > 0) {
    392 			if (sz < sizeof (uint32_t))
    393 				return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    394 
    395 			cur->tx_nvalues++;
    396 
    397 			len = *(uint32_t *)loc;
    398 			loc += sizeof (uint32_t);
    399 			sz -= sizeof (uint32_t);
    400 
    401 			if (!check_string(loc, len, sz))
    402 				return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    403 
    404 			/*
    405 			 * XXX here, we should be checking that the values
    406 			 * match the purported type
    407 			 */
    408 
    409 			len = TX_SIZE(len);
    410 
    411 			if (len > sz)
    412 				return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    413 
    414 			loc += len;
    415 			sz -= len;
    416 		}
    417 
    418 		if (prev != NULL && strcmp(prev->tx_prop, cur->tx_prop) >= 0)
    419 			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    420 
    421 		prev = cur;
    422 	}
    423 	return (REP_PROTOCOL_SUCCESS);
    424 }
    425 
    426 /*
    427  * Free the memory associated with a tx_commit_data structure.
    428  */
    429 void
    430 tx_commit_data_free(tx_commit_data_t *tx_data)
    431 {
    432 	uu_free(tx_data);
    433 }
    434 
    435 /*
    436  * Parse the data of a REP_PROTOCOL_PROPERTYGRP_TX_COMMIT message into a
    437  * more useful form.  The data in the message will be represented by a
    438  * tx_commit_data_t structure which is allocated by this function.  The
    439  * address of the allocated structure is returned to *tx_data and must be
    440  * freed by calling tx_commit_data_free().
    441  *
    442  * Parameters:
    443  *	cmds_arg	Address of the commands in the
    444  *			REP_PROTOCOL_PROPERTYGRP_TX_COMMIT message.
    445  *
    446  *	cmds_sz		Number of message bytes at cmds_arg.
    447  *
    448  *	tx_data		Points to the place to receive the address of the
    449  *			allocated memory.
    450  *
    451  * Fails with
    452  *	_BAD_REQUEST
    453  *	_NO_RESOURCES
    454  */
    455 int
    456 tx_commit_data_new(const void *cmds_arg, size_t cmds_sz,
    457     tx_commit_data_t **tx_data)
    458 {
    459 	const struct rep_protocol_transaction_cmd *cmds;
    460 	tx_commit_data_t *data;
    461 	uintptr_t loc;
    462 	uint32_t count;
    463 	uint32_t sz;
    464 	int ret;
    465 
    466 	/*
    467 	 * First, verify that the reported sizes make sense, and count
    468 	 * the number of commands.
    469 	 */
    470 	count = 0;
    471 	loc = (uintptr_t)cmds_arg;
    472 
    473 	while (cmds_sz > 0) {
    474 		cmds = (struct rep_protocol_transaction_cmd *)loc;
    475 
    476 		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
    477 			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    478 
    479 		sz = cmds->rptc_size;
    480 		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
    481 			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    482 
    483 		sz = TX_SIZE(sz);
    484 		if (sz > cmds_sz)
    485 			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    486 
    487 		loc += sz;
    488 		cmds_sz -= sz;
    489 		count++;
    490 	}
    491 
    492 	data = uu_zalloc(TX_COMMIT_DATA_SIZE(count));
    493 	if (data == NULL)
    494 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
    495 
    496 	/*
    497 	 * verify that everything looks okay, and set up our command
    498 	 * datastructures.
    499 	 */
    500 	data->txc_count = count;
    501 	ret = tx_check_and_setup(data, cmds_arg, count);
    502 	if (ret == REP_PROTOCOL_SUCCESS) {
    503 		*tx_data = data;
    504 	} else {
    505 		*tx_data = NULL;
    506 		uu_free(data);
    507 	}
    508 	return (ret);
    509 }
    510 
    511 /*
    512  * The following are a set of accessor functions to retrieve data from a
    513  * tx_commit_data_t that has been allocated by tx_commit_data_new().
    514  */
    515 
    516 /*
    517  * Return the action of the transaction command whose command number is
    518  * cmd_no.  The action is placed at *action.
    519  *
    520  * Returns:
    521  *	_FAIL_BAD_REQUEST	cmd_no is out of range.
    522  */
    523 int
    524 tx_cmd_action(tx_commit_data_t *tx_data, size_t cmd_no,
    525     enum rep_protocol_transaction_action *action)
    526 {
    527 	struct tx_cmd *cur;
    528 
    529 	assert(cmd_no < tx_data->txc_count);
    530 	if (cmd_no >= tx_data->txc_count)
    531 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    532 
    533 	cur = &tx_data->txc_cmds[cmd_no];
    534 	*action = cur->tx_cmd->rptc_action;
    535 	return (REP_PROTOCOL_SUCCESS);
    536 }
    537 
    538 /*
    539  * Return the number of transaction commands held in tx_data.
    540  */
    541 size_t
    542 tx_cmd_count(tx_commit_data_t *tx_data)
    543 {
    544 	return (tx_data->txc_count);
    545 }
    546 
    547 /*
    548  * Return the number of property values that are associated with the
    549  * transaction command whose number is cmd_no.  The number of values is
    550  * returned to *nvalues.
    551  *
    552  * Returns:
    553  *	_FAIL_BAD_REQUEST	cmd_no is out of range.
    554  */
    555 int
    556 tx_cmd_nvalues(tx_commit_data_t *tx_data, size_t cmd_no, uint32_t *nvalues)
    557 {
    558 	struct tx_cmd *cur;
    559 
    560 	assert(cmd_no < tx_data->txc_count);
    561 	if (cmd_no >= tx_data->txc_count)
    562 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    563 
    564 	cur = &tx_data->txc_cmds[cmd_no];
    565 	*nvalues = cur->tx_nvalues;
    566 	return (REP_PROTOCOL_SUCCESS);
    567 }
    568 
    569 /*
    570  * Return a pointer to the property name of the command whose number is
    571  * cmd_no.  The property name pointer is returned to *pname.
    572  *
    573  * Returns:
    574  *	_FAIL_BAD_REQUEST	cmd_no is out of range.
    575  */
    576 int
    577 tx_cmd_prop(tx_commit_data_t *tx_data, size_t cmd_no, const char **pname)
    578 {
    579 	struct tx_cmd *cur;
    580 
    581 	assert(cmd_no < tx_data->txc_count);
    582 	if (cmd_no >= tx_data->txc_count)
    583 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    584 
    585 	cur = &tx_data->txc_cmds[cmd_no];
    586 	*pname = cur->tx_prop;
    587 	return (REP_PROTOCOL_SUCCESS);
    588 }
    589 
    590 /*
    591  * Return the property type of the property whose command number is
    592  * cmd_no.  The property type is returned to *ptype.
    593  *
    594  * Returns:
    595  *	_FAIL_BAD_REQUEST	cmd_no is out of range.
    596  */
    597 int
    598 tx_cmd_prop_type(tx_commit_data_t *tx_data, size_t cmd_no, uint32_t *ptype)
    599 {
    600 	struct tx_cmd *cur;
    601 
    602 	assert(cmd_no < tx_data->txc_count);
    603 	if (cmd_no >= tx_data->txc_count)
    604 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    605 
    606 	cur = &tx_data->txc_cmds[cmd_no];
    607 	*ptype = cur->tx_cmd->rptc_type;
    608 	return (REP_PROTOCOL_SUCCESS);
    609 }
    610 
    611 /*
    612  * This function is used to retrieve a property value from the transaction
    613  * data.  val_no specifies which value is to be retrieved from the
    614  * transaction command whose number is cmd_no.  A pointer to the specified
    615  * value is placed in *val.
    616  *
    617  * Returns:
    618  *	_FAIL_BAD_REQUEST	cmd_no or val_no is out of range.
    619  */
    620 int
    621 tx_cmd_value(tx_commit_data_t *tx_data, size_t cmd_no, uint32_t val_no,
    622     const char **val)
    623 {
    624 	const char *bp;
    625 	struct tx_cmd *cur;
    626 	uint32_t i;
    627 	uint32_t value_len;
    628 
    629 	assert(cmd_no < tx_data->txc_count);
    630 	if (cmd_no >= tx_data->txc_count)
    631 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    632 
    633 	cur = &tx_data->txc_cmds[cmd_no];
    634 	assert(val_no < cur->tx_nvalues);
    635 	if (val_no >= cur->tx_nvalues)
    636 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
    637 
    638 	/* Find the correct value */
    639 	bp = (char *)cur->tx_values;
    640 	for (i = 0; i < val_no; i++) {
    641 		/* LINTED alignment */
    642 		value_len = *(uint32_t *)bp;
    643 		bp += sizeof (uint32_t) + TX_SIZE(value_len);
    644 	}
    645 
    646 	/* Bypass the count & return pointer to value. */
    647 	bp += sizeof (uint32_t);
    648 	*val = bp;
    649 	return (REP_PROTOCOL_SUCCESS);
    650 }
    651 
    652 int
    653 object_tx_commit(rc_node_lookup_t *lp, tx_commit_data_t *data, uint32_t *gen)
    654 {
    655 	uint32_t new_gen;
    656 	int ret;
    657 	rep_protocol_responseid_t r;
    658 	backend_tx_t *tx;
    659 	backend_query_t *q;
    660 	int backend = lp->rl_backend;
    661 
    662 	ret = backend_tx_begin(backend, &tx);
    663 	if (ret != REP_PROTOCOL_SUCCESS)
    664 		return (ret);
    665 
    666 	/* Make sure the pg is up-to-date. */
    667 	data->txc_oldgen = *gen;
    668 	data->txc_backend = backend;
    669 	data->txc_result = REP_PROTOCOL_FAIL_NOT_FOUND;
    670 
    671 	q = backend_query_alloc();
    672 	backend_query_add(q, "SELECT pg_gen_id FROM pg_tbl WHERE (pg_id = %d);",
    673 	    lp->rl_main_id);
    674 	r = backend_tx_run(tx, q, tx_check_genid, data);
    675 	backend_query_free(q);
    676 
    677 	if (r != REP_PROTOCOL_SUCCESS ||
    678 	    (r = data->txc_result) != REP_PROTOCOL_SUCCESS) {
    679 		backend_tx_rollback(tx);
    680 		goto end;
    681 	}
    682 
    683 	/* If the transaction is empty, cut out early. */
    684 	if (data->txc_count == 0) {
    685 		backend_tx_rollback(tx);
    686 		r = REP_PROTOCOL_DONE;
    687 		goto end;
    688 	}
    689 
    690 	new_gen = backend_new_id(tx, BACKEND_ID_GENERATION);
    691 	if (new_gen == 0) {
    692 		backend_tx_rollback(tx);
    693 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
    694 	}
    695 
    696 	data->txc_pg_id = lp->rl_main_id;
    697 	data->txc_gen = new_gen;
    698 	data->txc_tx = tx;
    699 
    700 	r = backend_tx_run_update(tx,
    701 	    "UPDATE pg_tbl SET pg_gen_id = %d "
    702 	    "    WHERE (pg_id = %d AND pg_gen_id = %d);",
    703 	    new_gen, lp->rl_main_id, *gen);
    704 
    705 	if (r != REP_PROTOCOL_SUCCESS) {
    706 		backend_tx_rollback(tx);
    707 		goto end;
    708 	}
    709 
    710 	q = backend_query_alloc();
    711 
    712 	backend_query_add(q,
    713 	    "SELECT lnk_prop_name, lnk_prop_type, lnk_val_id "
    714 	    "FROM prop_lnk_tbl "
    715 	    "WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
    716 	    lp->rl_main_id, *gen);
    717 
    718 	data->txc_inserts = backend_query_alloc();
    719 	r = backend_tx_run(tx, q, tx_process_property, data);
    720 	backend_query_free(q);
    721 
    722 	if (r == REP_PROTOCOL_DONE)
    723 		r = REP_PROTOCOL_FAIL_UNKNOWN;		/* corruption */
    724 
    725 	if (r != REP_PROTOCOL_SUCCESS ||
    726 	    (r = data->txc_result) != REP_PROTOCOL_SUCCESS) {
    727 		backend_query_free(data->txc_inserts);
    728 		backend_tx_rollback(tx);
    729 		goto end;
    730 	}
    731 
    732 	r = backend_tx_run(tx, data->txc_inserts, NULL, NULL);
    733 	backend_query_free(data->txc_inserts);
    734 
    735 	if (r != REP_PROTOCOL_SUCCESS) {
    736 		backend_tx_rollback(tx);
    737 		goto end;
    738 	}
    739 
    740 	r = tx_process_cmds(data);
    741 	if (r != REP_PROTOCOL_SUCCESS) {
    742 		backend_tx_rollback(tx);
    743 		goto end;
    744 	}
    745 	r = backend_tx_commit(tx);
    746 
    747 	if (r == REP_PROTOCOL_SUCCESS)
    748 		*gen = new_gen;
    749 end:
    750 	return (r);
    751 }
    752