10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*5777Stw21770 * Common Development and Distribution License (the "License"). 6*5777Stw21770 * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 21*5777Stw21770 220Sstevel@tonic-gate /* 23*5777Stw21770 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate /* 300Sstevel@tonic-gate * This file only contains the transaction commit logic. 310Sstevel@tonic-gate */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate #include <assert.h> 340Sstevel@tonic-gate #include <alloca.h> 350Sstevel@tonic-gate #include <errno.h> 360Sstevel@tonic-gate #include <stdio.h> 370Sstevel@tonic-gate #include <stdlib.h> 380Sstevel@tonic-gate #include <strings.h> 390Sstevel@tonic-gate #include <sys/sysmacros.h> 400Sstevel@tonic-gate #include "configd.h" 410Sstevel@tonic-gate 420Sstevel@tonic-gate #define INVALID_OBJ_ID ((uint32_t)-1) 430Sstevel@tonic-gate #define INVALID_TYPE ((uint32_t)-1) 440Sstevel@tonic-gate 450Sstevel@tonic-gate struct tx_cmd { 460Sstevel@tonic-gate const struct rep_protocol_transaction_cmd *tx_cmd; 470Sstevel@tonic-gate const char *tx_prop; 480Sstevel@tonic-gate uint32_t *tx_values; 490Sstevel@tonic-gate uint32_t tx_nvalues; 500Sstevel@tonic-gate uint32_t tx_orig_value_id; 510Sstevel@tonic-gate char tx_found; 520Sstevel@tonic-gate char tx_processed; 530Sstevel@tonic-gate char tx_bad; 540Sstevel@tonic-gate }; 550Sstevel@tonic-gate 560Sstevel@tonic-gate static int 570Sstevel@tonic-gate tx_cmd_compare(const void *key, const void *elem_arg) 580Sstevel@tonic-gate { 590Sstevel@tonic-gate const struct tx_cmd *elem = elem_arg; 600Sstevel@tonic-gate 610Sstevel@tonic-gate return (strcmp((const char *)key, elem->tx_prop)); 620Sstevel@tonic-gate } 630Sstevel@tonic-gate 640Sstevel@tonic-gate struct tx_commit_data { 650Sstevel@tonic-gate uint32_t txc_pg_id; 660Sstevel@tonic-gate uint32_t txc_gen; 670Sstevel@tonic-gate uint32_t txc_oldgen; 680Sstevel@tonic-gate short txc_backend; 690Sstevel@tonic-gate backend_tx_t *txc_tx; 700Sstevel@tonic-gate backend_query_t *txc_inserts; 710Sstevel@tonic-gate size_t txc_count; 720Sstevel@tonic-gate rep_protocol_responseid_t txc_result; 730Sstevel@tonic-gate struct tx_cmd txc_cmds[1]; /* actually txc_count */ 740Sstevel@tonic-gate }; 750Sstevel@tonic-gate #define TX_COMMIT_DATA_SIZE(count) \ 760Sstevel@tonic-gate offsetof(struct tx_commit_data, txc_cmds[count]) 770Sstevel@tonic-gate 780Sstevel@tonic-gate /*ARGSUSED*/ 790Sstevel@tonic-gate static int 800Sstevel@tonic-gate tx_check_genid(void *data_arg, int columns, char **vals, char **names) 810Sstevel@tonic-gate { 82*5777Stw21770 tx_commit_data_t *data = data_arg; 830Sstevel@tonic-gate assert(columns == 1); 840Sstevel@tonic-gate if (atoi(vals[0]) != data->txc_oldgen) 850Sstevel@tonic-gate data->txc_result = REP_PROTOCOL_FAIL_NOT_LATEST; 860Sstevel@tonic-gate else 870Sstevel@tonic-gate data->txc_result = REP_PROTOCOL_SUCCESS; 880Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 890Sstevel@tonic-gate } 900Sstevel@tonic-gate 910Sstevel@tonic-gate /* 920Sstevel@tonic-gate * tx_process_property() is called once for each property in current 930Sstevel@tonic-gate * property group generation. Its purpose is threefold: 940Sstevel@tonic-gate * 950Sstevel@tonic-gate * 1. copy properties not mentioned in the transaction over unchanged. 960Sstevel@tonic-gate * 2. mark DELETEd properties as seen (they will be left out of the new 970Sstevel@tonic-gate * generation). 980Sstevel@tonic-gate * 3. consistancy-check NEW, CLEAR, and REPLACE commands. 990Sstevel@tonic-gate * 1000Sstevel@tonic-gate * Any consistancy problems set tx_bad, and seen properties are marked 1010Sstevel@tonic-gate * tx_found. These is used later, in tx_process_cmds(). 1020Sstevel@tonic-gate */ 1030Sstevel@tonic-gate /*ARGSUSED*/ 1040Sstevel@tonic-gate static int 1050Sstevel@tonic-gate tx_process_property(void *data_arg, int columns, char **vals, char **names) 1060Sstevel@tonic-gate { 107*5777Stw21770 tx_commit_data_t *data = data_arg; 1080Sstevel@tonic-gate struct tx_cmd *elem; 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate const char *prop_name = vals[0]; 1110Sstevel@tonic-gate const char *prop_type = vals[1]; 1120Sstevel@tonic-gate const char *lnk_val_id = vals[2]; 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate char *endptr; 1150Sstevel@tonic-gate 1160Sstevel@tonic-gate assert(columns == 3); 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate elem = bsearch(prop_name, data->txc_cmds, data->txc_count, 1190Sstevel@tonic-gate sizeof (*data->txc_cmds), tx_cmd_compare); 1200Sstevel@tonic-gate 1210Sstevel@tonic-gate if (elem == NULL) { 1220Sstevel@tonic-gate backend_query_add(data->txc_inserts, 1230Sstevel@tonic-gate "INSERT INTO prop_lnk_tbl" 1240Sstevel@tonic-gate " (lnk_pg_id, lnk_gen_id, lnk_prop_name, lnk_prop_type," 1250Sstevel@tonic-gate " lnk_val_id) " 1260Sstevel@tonic-gate "VALUES ( %d, %d, '%q', '%q', %Q );", 1270Sstevel@tonic-gate data->txc_pg_id, data->txc_gen, prop_name, prop_type, 1280Sstevel@tonic-gate lnk_val_id); 1290Sstevel@tonic-gate } else { 1300Sstevel@tonic-gate assert(!elem->tx_found); 1310Sstevel@tonic-gate elem->tx_found = 1; 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate if (lnk_val_id != NULL) { 1340Sstevel@tonic-gate errno = 0; 1350Sstevel@tonic-gate elem->tx_orig_value_id = 1360Sstevel@tonic-gate strtoul(lnk_val_id, &endptr, 10); 1370Sstevel@tonic-gate if (elem->tx_orig_value_id == 0 || *endptr != 0 || 1380Sstevel@tonic-gate errno != 0) { 1390Sstevel@tonic-gate return (BACKEND_CALLBACK_ABORT); 1400Sstevel@tonic-gate } 1410Sstevel@tonic-gate } else { 1420Sstevel@tonic-gate elem->tx_orig_value_id = 0; 1430Sstevel@tonic-gate } 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate switch (elem->tx_cmd->rptc_action) { 1460Sstevel@tonic-gate case REP_PROTOCOL_TX_ENTRY_NEW: 1470Sstevel@tonic-gate elem->tx_bad = 1; 1480Sstevel@tonic-gate data->txc_result = REP_PROTOCOL_FAIL_EXISTS; 1490Sstevel@tonic-gate break; 1500Sstevel@tonic-gate case REP_PROTOCOL_TX_ENTRY_CLEAR: 1510Sstevel@tonic-gate if (REP_PROTOCOL_BASE_TYPE(elem->tx_cmd->rptc_type) != 1520Sstevel@tonic-gate prop_type[0] && 1530Sstevel@tonic-gate REP_PROTOCOL_SUBTYPE(elem->tx_cmd->rptc_type) != 1540Sstevel@tonic-gate prop_type[1]) { 1550Sstevel@tonic-gate elem->tx_bad = 1; 1560Sstevel@tonic-gate data->txc_result = 1570Sstevel@tonic-gate REP_PROTOCOL_FAIL_TYPE_MISMATCH; 1580Sstevel@tonic-gate } 1590Sstevel@tonic-gate break; 1600Sstevel@tonic-gate case REP_PROTOCOL_TX_ENTRY_REPLACE: 1610Sstevel@tonic-gate break; 1620Sstevel@tonic-gate case REP_PROTOCOL_TX_ENTRY_DELETE: 1630Sstevel@tonic-gate elem->tx_processed = 1; 1640Sstevel@tonic-gate break; 1650Sstevel@tonic-gate default: 1660Sstevel@tonic-gate assert(0); 1670Sstevel@tonic-gate break; 1680Sstevel@tonic-gate } 1690Sstevel@tonic-gate } 1700Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 1710Sstevel@tonic-gate } 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate /* 1740Sstevel@tonic-gate * tx_process_cmds() finishes the job tx_process_property() started: 1750Sstevel@tonic-gate * 1760Sstevel@tonic-gate * 1. if tx_process_property() marked a command as bad, we skip it. 1770Sstevel@tonic-gate * 2. if a DELETE, REPLACE, or CLEAR operated on a non-existant property, 1780Sstevel@tonic-gate * we mark it as bad. 1790Sstevel@tonic-gate * 3. we complete the work of NEW, REPLACE, and CLEAR, by inserting the 1800Sstevel@tonic-gate * appropriate values into the database. 1810Sstevel@tonic-gate * 4. we delete all replaced data, if it is no longer referenced. 1820Sstevel@tonic-gate * 1830Sstevel@tonic-gate * Finally, we check all of the commands, and fail if anything was marked bad. 1840Sstevel@tonic-gate */ 1850Sstevel@tonic-gate static int 186*5777Stw21770 tx_process_cmds(tx_commit_data_t *data) 1870Sstevel@tonic-gate { 1880Sstevel@tonic-gate int idx; 1890Sstevel@tonic-gate int r; 1900Sstevel@tonic-gate int count = data->txc_count; 1910Sstevel@tonic-gate struct tx_cmd *elem; 1920Sstevel@tonic-gate uint32_t val_id = 0; 1930Sstevel@tonic-gate uint8_t type[3]; 1940Sstevel@tonic-gate 1950Sstevel@tonic-gate backend_query_t *q; 1960Sstevel@tonic-gate int do_delete; 197*5777Stw21770 uint32_t nvalues; 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate /* 2000Sstevel@tonic-gate * For persistent pgs, we use backend_fail_if_seen to abort the 2010Sstevel@tonic-gate * deletion if there is a snapshot using our current state. 2020Sstevel@tonic-gate * 2030Sstevel@tonic-gate * All of the deletions in this function are safe, since 2040Sstevel@tonic-gate * rc_tx_commit() guarantees that all the data is in-cache. 2050Sstevel@tonic-gate */ 2060Sstevel@tonic-gate q = backend_query_alloc(); 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate if (data->txc_backend != BACKEND_TYPE_NONPERSIST) { 2090Sstevel@tonic-gate backend_query_add(q, 2100Sstevel@tonic-gate "SELECT 1 FROM snaplevel_lnk_tbl " 2110Sstevel@tonic-gate " WHERE (snaplvl_pg_id = %d AND snaplvl_gen_id = %d); ", 2120Sstevel@tonic-gate data->txc_pg_id, data->txc_oldgen); 2130Sstevel@tonic-gate } 2140Sstevel@tonic-gate backend_query_add(q, 2150Sstevel@tonic-gate "DELETE FROM prop_lnk_tbl" 2160Sstevel@tonic-gate " WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)", 2170Sstevel@tonic-gate data->txc_pg_id, data->txc_oldgen); 2180Sstevel@tonic-gate r = backend_tx_run(data->txc_tx, q, backend_fail_if_seen, NULL); 2190Sstevel@tonic-gate backend_query_free(q); 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate if (r == REP_PROTOCOL_SUCCESS) 2220Sstevel@tonic-gate do_delete = 1; 2230Sstevel@tonic-gate else if (r == REP_PROTOCOL_DONE) 2240Sstevel@tonic-gate do_delete = 0; /* old gen_id is in use */ 2250Sstevel@tonic-gate else 2260Sstevel@tonic-gate return (r); 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate for (idx = 0; idx < count; idx++) { 2290Sstevel@tonic-gate elem = &data->txc_cmds[idx]; 2300Sstevel@tonic-gate 2310Sstevel@tonic-gate if (elem->tx_bad) 2320Sstevel@tonic-gate continue; 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate switch (elem->tx_cmd->rptc_action) { 2350Sstevel@tonic-gate case REP_PROTOCOL_TX_ENTRY_DELETE: 2360Sstevel@tonic-gate case REP_PROTOCOL_TX_ENTRY_REPLACE: 2370Sstevel@tonic-gate case REP_PROTOCOL_TX_ENTRY_CLEAR: 2380Sstevel@tonic-gate if (!elem->tx_found) { 2390Sstevel@tonic-gate elem->tx_bad = 1; 2400Sstevel@tonic-gate continue; 2410Sstevel@tonic-gate } 2420Sstevel@tonic-gate break; 2430Sstevel@tonic-gate case REP_PROTOCOL_TX_ENTRY_NEW: 2440Sstevel@tonic-gate break; 2450Sstevel@tonic-gate default: 2460Sstevel@tonic-gate assert(0); 2470Sstevel@tonic-gate break; 2480Sstevel@tonic-gate } 2490Sstevel@tonic-gate 2500Sstevel@tonic-gate if (do_delete && 2510Sstevel@tonic-gate elem->tx_cmd->rptc_action != REP_PROTOCOL_TX_ENTRY_NEW && 2520Sstevel@tonic-gate elem->tx_orig_value_id != 0) { 2530Sstevel@tonic-gate /* 2540Sstevel@tonic-gate * delete the old values, if they are not in use 2550Sstevel@tonic-gate */ 2560Sstevel@tonic-gate q = backend_query_alloc(); 2570Sstevel@tonic-gate backend_query_add(q, 2580Sstevel@tonic-gate "SELECT 1 FROM prop_lnk_tbl " 2590Sstevel@tonic-gate " WHERE (lnk_val_id = %d); " 2600Sstevel@tonic-gate "DELETE FROM value_tbl" 2610Sstevel@tonic-gate " WHERE (value_id = %d)", 2620Sstevel@tonic-gate elem->tx_orig_value_id, elem->tx_orig_value_id); 2630Sstevel@tonic-gate r = backend_tx_run(data->txc_tx, q, 2640Sstevel@tonic-gate backend_fail_if_seen, NULL); 2650Sstevel@tonic-gate backend_query_free(q); 2660Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS && r != REP_PROTOCOL_DONE) 2670Sstevel@tonic-gate return (r); 2680Sstevel@tonic-gate } 2690Sstevel@tonic-gate 2700Sstevel@tonic-gate if (elem->tx_cmd->rptc_action == REP_PROTOCOL_TX_ENTRY_DELETE) 2710Sstevel@tonic-gate continue; /* no further work to do */ 2720Sstevel@tonic-gate 2730Sstevel@tonic-gate type[0] = REP_PROTOCOL_BASE_TYPE(elem->tx_cmd->rptc_type); 2740Sstevel@tonic-gate type[1] = REP_PROTOCOL_SUBTYPE(elem->tx_cmd->rptc_type); 2750Sstevel@tonic-gate type[2] = 0; 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate if (elem->tx_nvalues == 0) { 2780Sstevel@tonic-gate r = backend_tx_run_update(data->txc_tx, 2790Sstevel@tonic-gate "INSERT INTO prop_lnk_tbl" 2800Sstevel@tonic-gate " (lnk_pg_id, lnk_gen_id, " 2810Sstevel@tonic-gate " lnk_prop_name, lnk_prop_type, lnk_val_id) " 2820Sstevel@tonic-gate "VALUES ( %d, %d, '%q', '%q', NULL );", 2830Sstevel@tonic-gate data->txc_pg_id, data->txc_gen, elem->tx_prop, 2840Sstevel@tonic-gate type); 2850Sstevel@tonic-gate } else { 2860Sstevel@tonic-gate uint32_t *v; 2870Sstevel@tonic-gate const char *str; 2880Sstevel@tonic-gate 2890Sstevel@tonic-gate val_id = backend_new_id(data->txc_tx, BACKEND_ID_VALUE); 2900Sstevel@tonic-gate if (val_id == 0) 2910Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 2920Sstevel@tonic-gate r = backend_tx_run_update(data->txc_tx, 2930Sstevel@tonic-gate "INSERT INTO prop_lnk_tbl " 2940Sstevel@tonic-gate " (lnk_pg_id, lnk_gen_id, " 2950Sstevel@tonic-gate " lnk_prop_name, lnk_prop_type, lnk_val_id) " 2960Sstevel@tonic-gate "VALUES ( %d, %d, '%q', '%q', %d );", 2970Sstevel@tonic-gate data->txc_pg_id, data->txc_gen, elem->tx_prop, 2980Sstevel@tonic-gate type, val_id); 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate v = elem->tx_values; 3010Sstevel@tonic-gate 302*5777Stw21770 nvalues = elem->tx_nvalues; 3030Sstevel@tonic-gate while (r == REP_PROTOCOL_SUCCESS && 304*5777Stw21770 nvalues--) { 3050Sstevel@tonic-gate str = (const char *)&v[1]; 3060Sstevel@tonic-gate 3070Sstevel@tonic-gate r = backend_tx_run_update(data->txc_tx, 3080Sstevel@tonic-gate "INSERT INTO value_tbl " 3090Sstevel@tonic-gate " (value_id, value_type, value_value) " 3100Sstevel@tonic-gate "VALUES (%d, '%c', '%q');\n", 3110Sstevel@tonic-gate val_id, elem->tx_cmd->rptc_type, str); 3120Sstevel@tonic-gate 3130Sstevel@tonic-gate /*LINTED alignment*/ 3140Sstevel@tonic-gate v = (uint32_t *)((caddr_t)str + TX_SIZE(*v)); 3150Sstevel@tonic-gate } 3160Sstevel@tonic-gate } 3170Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) 3180Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_UNKNOWN); 3190Sstevel@tonic-gate elem->tx_processed = 1; 3200Sstevel@tonic-gate } 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate for (idx = 0; idx < count; idx++) { 3230Sstevel@tonic-gate elem = &data->txc_cmds[idx]; 3240Sstevel@tonic-gate 3250Sstevel@tonic-gate if (elem->tx_bad) 3260Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_TX); 3270Sstevel@tonic-gate } 3280Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 3290Sstevel@tonic-gate } 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate static boolean_t 3320Sstevel@tonic-gate check_string(uintptr_t loc, uint32_t len, uint32_t sz) 3330Sstevel@tonic-gate { 3340Sstevel@tonic-gate const char *ptr = (const char *)loc; 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate if (len == 0 || len > sz || ptr[len - 1] != 0 || strlen(ptr) != len - 1) 3370Sstevel@tonic-gate return (0); 3380Sstevel@tonic-gate return (1); 3390Sstevel@tonic-gate } 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate static int 342*5777Stw21770 tx_check_and_setup(tx_commit_data_t *data, const void *cmds_arg, 3430Sstevel@tonic-gate uint32_t count) 3440Sstevel@tonic-gate { 3450Sstevel@tonic-gate const struct rep_protocol_transaction_cmd *cmds; 3460Sstevel@tonic-gate struct tx_cmd *cur; 3470Sstevel@tonic-gate struct tx_cmd *prev = NULL; 3480Sstevel@tonic-gate 3490Sstevel@tonic-gate uintptr_t loc; 3500Sstevel@tonic-gate uint32_t sz, len; 3510Sstevel@tonic-gate int idx; 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate loc = (uintptr_t)cmds_arg; 3540Sstevel@tonic-gate 3550Sstevel@tonic-gate for (idx = 0; idx < count; idx++) { 3560Sstevel@tonic-gate cur = &data->txc_cmds[idx]; 3570Sstevel@tonic-gate 3580Sstevel@tonic-gate cmds = (struct rep_protocol_transaction_cmd *)loc; 3590Sstevel@tonic-gate cur->tx_cmd = cmds; 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate sz = cmds->rptc_size; 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate loc += REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE; 3640Sstevel@tonic-gate sz -= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE; 3650Sstevel@tonic-gate 3660Sstevel@tonic-gate len = cmds->rptc_name_len; 3670Sstevel@tonic-gate if (len <= 1 || !check_string(loc, len, sz)) { 3680Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 3690Sstevel@tonic-gate } 3700Sstevel@tonic-gate cur->tx_prop = (const char *)loc; 3710Sstevel@tonic-gate 3720Sstevel@tonic-gate len = TX_SIZE(len); 3730Sstevel@tonic-gate loc += len; 3740Sstevel@tonic-gate sz -= len; 3750Sstevel@tonic-gate 3760Sstevel@tonic-gate cur->tx_nvalues = 0; 3770Sstevel@tonic-gate cur->tx_values = (uint32_t *)loc; 3780Sstevel@tonic-gate 3790Sstevel@tonic-gate while (sz > 0) { 3800Sstevel@tonic-gate if (sz < sizeof (uint32_t)) 3810Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 3820Sstevel@tonic-gate 3830Sstevel@tonic-gate cur->tx_nvalues++; 3840Sstevel@tonic-gate 3850Sstevel@tonic-gate len = *(uint32_t *)loc; 3860Sstevel@tonic-gate loc += sizeof (uint32_t); 3870Sstevel@tonic-gate sz -= sizeof (uint32_t); 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate if (!check_string(loc, len, sz)) 3900Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate /* 3930Sstevel@tonic-gate * XXX here, we should be checking that the values 3940Sstevel@tonic-gate * match the purported type 3950Sstevel@tonic-gate */ 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate len = TX_SIZE(len); 3980Sstevel@tonic-gate 3990Sstevel@tonic-gate if (len > sz) 4000Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate loc += len; 4030Sstevel@tonic-gate sz -= len; 4040Sstevel@tonic-gate } 4050Sstevel@tonic-gate 4060Sstevel@tonic-gate if (prev != NULL && strcmp(prev->tx_prop, cur->tx_prop) >= 0) 4070Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4080Sstevel@tonic-gate 4090Sstevel@tonic-gate prev = cur; 4100Sstevel@tonic-gate } 4110Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 4120Sstevel@tonic-gate } 4130Sstevel@tonic-gate 414*5777Stw21770 /* 415*5777Stw21770 * Free the memory associated with a tx_commit_data structure. 416*5777Stw21770 */ 417*5777Stw21770 void 418*5777Stw21770 tx_commit_data_free(tx_commit_data_t *tx_data) 419*5777Stw21770 { 420*5777Stw21770 uu_free(tx_data); 421*5777Stw21770 } 422*5777Stw21770 423*5777Stw21770 /* 424*5777Stw21770 * Parse the data of a REP_PROTOCOL_PROPERTYGRP_TX_COMMIT message into a 425*5777Stw21770 * more useful form. The data in the message will be represented by a 426*5777Stw21770 * tx_commit_data_t structure which is allocated by this function. The 427*5777Stw21770 * address of the allocated structure is returned to *tx_data and must be 428*5777Stw21770 * freed by calling tx_commit_data_free(). 429*5777Stw21770 * 430*5777Stw21770 * Parameters: 431*5777Stw21770 * cmds_arg Address of the commands in the 432*5777Stw21770 * REP_PROTOCOL_PROPERTYGRP_TX_COMMIT message. 433*5777Stw21770 * 434*5777Stw21770 * cmds_sz Number of message bytes at cmds_arg. 435*5777Stw21770 * 436*5777Stw21770 * tx_data Points to the place to receive the address of the 437*5777Stw21770 * allocated memory. 438*5777Stw21770 * 439*5777Stw21770 * Fails with 440*5777Stw21770 * _BAD_REQUEST 441*5777Stw21770 * _NO_RESOURCES 442*5777Stw21770 */ 4430Sstevel@tonic-gate int 444*5777Stw21770 tx_commit_data_new(const void *cmds_arg, size_t cmds_sz, 445*5777Stw21770 tx_commit_data_t **tx_data) 4460Sstevel@tonic-gate { 4470Sstevel@tonic-gate const struct rep_protocol_transaction_cmd *cmds; 448*5777Stw21770 tx_commit_data_t *data; 4490Sstevel@tonic-gate uintptr_t loc; 450*5777Stw21770 uint32_t count; 451*5777Stw21770 uint32_t sz; 4520Sstevel@tonic-gate int ret; 4530Sstevel@tonic-gate 4540Sstevel@tonic-gate /* 4550Sstevel@tonic-gate * First, verify that the reported sizes make sense, and count 4560Sstevel@tonic-gate * the number of commands. 4570Sstevel@tonic-gate */ 4580Sstevel@tonic-gate count = 0; 4590Sstevel@tonic-gate loc = (uintptr_t)cmds_arg; 4600Sstevel@tonic-gate 4610Sstevel@tonic-gate while (cmds_sz > 0) { 4620Sstevel@tonic-gate cmds = (struct rep_protocol_transaction_cmd *)loc; 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE) 4650Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate sz = cmds->rptc_size; 4680Sstevel@tonic-gate if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE) 4690Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate sz = TX_SIZE(sz); 4720Sstevel@tonic-gate if (sz > cmds_sz) 4730Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 4740Sstevel@tonic-gate 4750Sstevel@tonic-gate loc += sz; 4760Sstevel@tonic-gate cmds_sz -= sz; 4770Sstevel@tonic-gate count++; 4780Sstevel@tonic-gate } 4790Sstevel@tonic-gate 480*5777Stw21770 data = uu_zalloc(TX_COMMIT_DATA_SIZE(count)); 481*5777Stw21770 if (data == NULL) 482*5777Stw21770 return (REP_PROTOCOL_FAIL_NO_RESOURCES); 4830Sstevel@tonic-gate 4840Sstevel@tonic-gate /* 4850Sstevel@tonic-gate * verify that everything looks okay, and set up our command 4860Sstevel@tonic-gate * datastructures. 4870Sstevel@tonic-gate */ 488*5777Stw21770 data->txc_count = count; 4890Sstevel@tonic-gate ret = tx_check_and_setup(data, cmds_arg, count); 490*5777Stw21770 if (ret == REP_PROTOCOL_SUCCESS) { 491*5777Stw21770 *tx_data = data; 492*5777Stw21770 } else { 493*5777Stw21770 *tx_data = NULL; 494*5777Stw21770 uu_free(data); 495*5777Stw21770 } 496*5777Stw21770 return (ret); 497*5777Stw21770 } 498*5777Stw21770 499*5777Stw21770 /* 500*5777Stw21770 * The following are a set of accessor functions to retrieve data from a 501*5777Stw21770 * tx_commit_data_t that has been allocated by tx_commit_data_new(). 502*5777Stw21770 */ 503*5777Stw21770 504*5777Stw21770 /* 505*5777Stw21770 * Return the action of the transaction command whose command number is 506*5777Stw21770 * cmd_no. The action is placed at *action. 507*5777Stw21770 * 508*5777Stw21770 * Returns: 509*5777Stw21770 * _FAIL_BAD_REQUEST cmd_no is out of range. 510*5777Stw21770 */ 511*5777Stw21770 int 512*5777Stw21770 tx_cmd_action(tx_commit_data_t *tx_data, size_t cmd_no, 513*5777Stw21770 enum rep_protocol_transaction_action *action) 514*5777Stw21770 { 515*5777Stw21770 struct tx_cmd *cur; 516*5777Stw21770 517*5777Stw21770 assert(cmd_no < tx_data->txc_count); 518*5777Stw21770 if (cmd_no >= tx_data->txc_count) 519*5777Stw21770 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 520*5777Stw21770 521*5777Stw21770 cur = &tx_data->txc_cmds[cmd_no]; 522*5777Stw21770 *action = cur->tx_cmd->rptc_action; 523*5777Stw21770 return (REP_PROTOCOL_SUCCESS); 524*5777Stw21770 } 525*5777Stw21770 526*5777Stw21770 /* 527*5777Stw21770 * Return the number of transaction commands held in tx_data. 528*5777Stw21770 */ 529*5777Stw21770 size_t 530*5777Stw21770 tx_cmd_count(tx_commit_data_t *tx_data) 531*5777Stw21770 { 532*5777Stw21770 return (tx_data->txc_count); 533*5777Stw21770 } 534*5777Stw21770 535*5777Stw21770 /* 536*5777Stw21770 * Return the number of property values that are associated with the 537*5777Stw21770 * transaction command whose number is cmd_no. The number of values is 538*5777Stw21770 * returned to *nvalues. 539*5777Stw21770 * 540*5777Stw21770 * Returns: 541*5777Stw21770 * _FAIL_BAD_REQUEST cmd_no is out of range. 542*5777Stw21770 */ 543*5777Stw21770 int 544*5777Stw21770 tx_cmd_nvalues(tx_commit_data_t *tx_data, size_t cmd_no, uint32_t *nvalues) 545*5777Stw21770 { 546*5777Stw21770 struct tx_cmd *cur; 547*5777Stw21770 548*5777Stw21770 assert(cmd_no < tx_data->txc_count); 549*5777Stw21770 if (cmd_no >= tx_data->txc_count) 550*5777Stw21770 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 551*5777Stw21770 552*5777Stw21770 cur = &tx_data->txc_cmds[cmd_no]; 553*5777Stw21770 *nvalues = cur->tx_nvalues; 554*5777Stw21770 return (REP_PROTOCOL_SUCCESS); 555*5777Stw21770 } 556*5777Stw21770 557*5777Stw21770 /* 558*5777Stw21770 * Return a pointer to the property name of the command whose number is 559*5777Stw21770 * cmd_no. The property name pointer is returned to *pname. 560*5777Stw21770 * 561*5777Stw21770 * Returns: 562*5777Stw21770 * _FAIL_BAD_REQUEST cmd_no is out of range. 563*5777Stw21770 */ 564*5777Stw21770 int 565*5777Stw21770 tx_cmd_prop(tx_commit_data_t *tx_data, size_t cmd_no, const char **pname) 566*5777Stw21770 { 567*5777Stw21770 struct tx_cmd *cur; 568*5777Stw21770 569*5777Stw21770 assert(cmd_no < tx_data->txc_count); 570*5777Stw21770 if (cmd_no >= tx_data->txc_count) 571*5777Stw21770 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 572*5777Stw21770 573*5777Stw21770 cur = &tx_data->txc_cmds[cmd_no]; 574*5777Stw21770 *pname = cur->tx_prop; 575*5777Stw21770 return (REP_PROTOCOL_SUCCESS); 576*5777Stw21770 } 577*5777Stw21770 578*5777Stw21770 /* 579*5777Stw21770 * Return the property type of the property whose command number is 580*5777Stw21770 * cmd_no. The property type is returned to *ptype. 581*5777Stw21770 * 582*5777Stw21770 * Returns: 583*5777Stw21770 * _FAIL_BAD_REQUEST cmd_no is out of range. 584*5777Stw21770 */ 585*5777Stw21770 int 586*5777Stw21770 tx_cmd_prop_type(tx_commit_data_t *tx_data, size_t cmd_no, uint32_t *ptype) 587*5777Stw21770 { 588*5777Stw21770 struct tx_cmd *cur; 589*5777Stw21770 590*5777Stw21770 assert(cmd_no < tx_data->txc_count); 591*5777Stw21770 if (cmd_no >= tx_data->txc_count) 592*5777Stw21770 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 593*5777Stw21770 594*5777Stw21770 cur = &tx_data->txc_cmds[cmd_no]; 595*5777Stw21770 *ptype = cur->tx_cmd->rptc_type; 596*5777Stw21770 return (REP_PROTOCOL_SUCCESS); 597*5777Stw21770 } 598*5777Stw21770 599*5777Stw21770 /* 600*5777Stw21770 * This function is used to retrieve a property value from the transaction 601*5777Stw21770 * data. val_no specifies which value is to be retrieved from the 602*5777Stw21770 * transaction command whose number is cmd_no. A pointer to the specified 603*5777Stw21770 * value is placed in *val. 604*5777Stw21770 * 605*5777Stw21770 * Returns: 606*5777Stw21770 * _FAIL_BAD_REQUEST cmd_no or val_no is out of range. 607*5777Stw21770 */ 608*5777Stw21770 int 609*5777Stw21770 tx_cmd_value(tx_commit_data_t *tx_data, size_t cmd_no, uint32_t val_no, 610*5777Stw21770 const char **val) 611*5777Stw21770 { 612*5777Stw21770 const char *bp; 613*5777Stw21770 struct tx_cmd *cur; 614*5777Stw21770 uint32_t i; 615*5777Stw21770 uint32_t value_len; 616*5777Stw21770 617*5777Stw21770 assert(cmd_no < tx_data->txc_count); 618*5777Stw21770 if (cmd_no >= tx_data->txc_count) 619*5777Stw21770 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 620*5777Stw21770 621*5777Stw21770 cur = &tx_data->txc_cmds[cmd_no]; 622*5777Stw21770 assert(val_no < cur->tx_nvalues); 623*5777Stw21770 if (val_no >= cur->tx_nvalues) 624*5777Stw21770 return (REP_PROTOCOL_FAIL_BAD_REQUEST); 625*5777Stw21770 626*5777Stw21770 /* Find the correct value */ 627*5777Stw21770 bp = (char *)cur->tx_values; 628*5777Stw21770 for (i = 0; i < val_no; i++) { 629*5777Stw21770 /* LINTED alignment */ 630*5777Stw21770 value_len = *(uint32_t *)bp; 631*5777Stw21770 bp += sizeof (uint32_t) + TX_SIZE(value_len); 632*5777Stw21770 } 633*5777Stw21770 634*5777Stw21770 /* Bypass the count & return pointer to value. */ 635*5777Stw21770 bp += sizeof (uint32_t); 636*5777Stw21770 *val = bp; 637*5777Stw21770 return (REP_PROTOCOL_SUCCESS); 638*5777Stw21770 } 639*5777Stw21770 640*5777Stw21770 int 641*5777Stw21770 object_tx_commit(rc_node_lookup_t *lp, tx_commit_data_t *data, uint32_t *gen) 642*5777Stw21770 { 643*5777Stw21770 uint32_t new_gen; 644*5777Stw21770 int ret; 645*5777Stw21770 rep_protocol_responseid_t r; 646*5777Stw21770 backend_tx_t *tx; 647*5777Stw21770 backend_query_t *q; 648*5777Stw21770 int backend = lp->rl_backend; 6490Sstevel@tonic-gate 6500Sstevel@tonic-gate ret = backend_tx_begin(backend, &tx); 6510Sstevel@tonic-gate if (ret != REP_PROTOCOL_SUCCESS) 6520Sstevel@tonic-gate return (ret); 6530Sstevel@tonic-gate 6540Sstevel@tonic-gate /* Make sure the pg is up-to-date. */ 6550Sstevel@tonic-gate data->txc_oldgen = *gen; 6560Sstevel@tonic-gate data->txc_backend = backend; 6570Sstevel@tonic-gate data->txc_result = REP_PROTOCOL_FAIL_NOT_FOUND; 6580Sstevel@tonic-gate 6590Sstevel@tonic-gate q = backend_query_alloc(); 6600Sstevel@tonic-gate backend_query_add(q, "SELECT pg_gen_id FROM pg_tbl WHERE (pg_id = %d);", 6610Sstevel@tonic-gate lp->rl_main_id); 6620Sstevel@tonic-gate r = backend_tx_run(tx, q, tx_check_genid, data); 6630Sstevel@tonic-gate backend_query_free(q); 6640Sstevel@tonic-gate 6650Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS || 6660Sstevel@tonic-gate (r = data->txc_result) != REP_PROTOCOL_SUCCESS) { 6670Sstevel@tonic-gate backend_tx_rollback(tx); 6680Sstevel@tonic-gate goto end; 6690Sstevel@tonic-gate } 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate /* If the transaction is empty, cut out early. */ 672*5777Stw21770 if (data->txc_count == 0) { 6730Sstevel@tonic-gate backend_tx_rollback(tx); 6740Sstevel@tonic-gate r = REP_PROTOCOL_DONE; 6750Sstevel@tonic-gate goto end; 6760Sstevel@tonic-gate } 6770Sstevel@tonic-gate 6780Sstevel@tonic-gate new_gen = backend_new_id(tx, BACKEND_ID_GENERATION); 6790Sstevel@tonic-gate if (new_gen == 0) { 6800Sstevel@tonic-gate backend_tx_rollback(tx); 6810Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 6820Sstevel@tonic-gate } 6830Sstevel@tonic-gate 6840Sstevel@tonic-gate data->txc_pg_id = lp->rl_main_id; 6850Sstevel@tonic-gate data->txc_gen = new_gen; 6860Sstevel@tonic-gate data->txc_tx = tx; 6870Sstevel@tonic-gate 6880Sstevel@tonic-gate r = backend_tx_run_update(tx, 6890Sstevel@tonic-gate "UPDATE pg_tbl SET pg_gen_id = %d " 6900Sstevel@tonic-gate " WHERE (pg_id = %d AND pg_gen_id = %d);", 6910Sstevel@tonic-gate new_gen, lp->rl_main_id, *gen); 6920Sstevel@tonic-gate 6930Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 6940Sstevel@tonic-gate backend_tx_rollback(tx); 6950Sstevel@tonic-gate goto end; 6960Sstevel@tonic-gate } 6970Sstevel@tonic-gate 6980Sstevel@tonic-gate q = backend_query_alloc(); 6990Sstevel@tonic-gate 7000Sstevel@tonic-gate backend_query_add(q, 7010Sstevel@tonic-gate "SELECT lnk_prop_name, lnk_prop_type, lnk_val_id " 7020Sstevel@tonic-gate "FROM prop_lnk_tbl " 7030Sstevel@tonic-gate "WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)", 7040Sstevel@tonic-gate lp->rl_main_id, *gen); 7050Sstevel@tonic-gate 7060Sstevel@tonic-gate data->txc_inserts = backend_query_alloc(); 7070Sstevel@tonic-gate r = backend_tx_run(tx, q, tx_process_property, data); 7080Sstevel@tonic-gate backend_query_free(q); 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate if (r == REP_PROTOCOL_DONE) 7110Sstevel@tonic-gate r = REP_PROTOCOL_FAIL_UNKNOWN; /* corruption */ 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS || 7140Sstevel@tonic-gate (r = data->txc_result) != REP_PROTOCOL_SUCCESS) { 7150Sstevel@tonic-gate backend_query_free(data->txc_inserts); 7160Sstevel@tonic-gate backend_tx_rollback(tx); 7170Sstevel@tonic-gate goto end; 7180Sstevel@tonic-gate } 7190Sstevel@tonic-gate 7200Sstevel@tonic-gate r = backend_tx_run(tx, data->txc_inserts, NULL, NULL); 7210Sstevel@tonic-gate backend_query_free(data->txc_inserts); 7220Sstevel@tonic-gate 7230Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 7240Sstevel@tonic-gate backend_tx_rollback(tx); 7250Sstevel@tonic-gate goto end; 7260Sstevel@tonic-gate } 7270Sstevel@tonic-gate 7280Sstevel@tonic-gate r = tx_process_cmds(data); 7290Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 7300Sstevel@tonic-gate backend_tx_rollback(tx); 7310Sstevel@tonic-gate goto end; 7320Sstevel@tonic-gate } 7330Sstevel@tonic-gate r = backend_tx_commit(tx); 7340Sstevel@tonic-gate 7350Sstevel@tonic-gate if (r == REP_PROTOCOL_SUCCESS) 7360Sstevel@tonic-gate *gen = new_gen; 7370Sstevel@tonic-gate end: 7380Sstevel@tonic-gate return (r); 7390Sstevel@tonic-gate } 740