xref: /titanic_51/usr/src/cmd/svc/configd/file_object.c (revision e37d48e735b8e55f327dfa35a2d0006049ea5e58)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 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  * file_object.c - enter objects into and load them from the backend
31  *
32  * The primary entry points in this layer are object_create(),
33  * object_create_pg(), object_delete(), and object_fill_children().  They each
34  * take an rc_node_t and use the functions in the object_info_t info array for
35  * the node's type.
36  */
37 
38 #include <assert.h>
39 #include <pthread.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <strings.h>
44 
45 #include "configd.h"
46 #include "repcache_protocol.h"
47 
48 typedef struct child_info {
49 	rc_node_t	*ci_parent;
50 	backend_tx_t	*ci_tx;			/* only for properties */
51 	rc_node_lookup_t ci_base_nl;
52 } child_info_t;
53 
54 typedef struct delete_ent delete_ent_t;
55 typedef struct delete_stack delete_stack_t;
56 typedef struct delete_info delete_info_t;
57 
58 typedef int	delete_cb_func(delete_info_t *, const delete_ent_t *);
59 
60 struct delete_ent {
61 	delete_cb_func	*de_cb;		/* callback */
62 	uint32_t	de_backend;
63 	uint32_t	de_id;
64 	uint32_t	de_gen;		/* only for property groups */
65 };
66 
67 struct delete_stack {
68 	struct delete_stack *ds_next;
69 	uint32_t	ds_size;	/* number of elements */
70 	uint32_t	ds_cur;		/* current offset */
71 	delete_ent_t	ds_buf[1];	/* actually ds_size */
72 };
73 #define	DELETE_STACK_SIZE(x)	offsetof(delete_stack_t, ds_buf[(x)])
74 
75 struct delete_info {
76 	backend_tx_t	*di_tx;
77 	backend_tx_t	*di_np_tx;
78 	delete_stack_t	*di_stack;
79 	delete_stack_t	*di_free;
80 };
81 
82 typedef struct object_info {
83 	uint32_t	obj_type;
84 	enum id_space	obj_id_space;
85 
86 	int (*obj_fill_children)(rc_node_t *);
87 	int (*obj_setup_child_info)(rc_node_t *, uint32_t, child_info_t *);
88 	int (*obj_query_child)(backend_query_t *, rc_node_lookup_t *,
89 	    const char *);
90 	int (*obj_insert_child)(backend_tx_t *, rc_node_lookup_t *,
91 	    const char *);
92 	int (*obj_insert_pg_child)(backend_tx_t *, rc_node_lookup_t *,
93 	    const char *, const char *, uint32_t, uint32_t);
94 	int (*obj_delete_start)(rc_node_t *, delete_info_t *);
95 } object_info_t;
96 
97 static void
98 string_to_id(const char *str, uint32_t *output, const char *fieldname)
99 {
100 	if (uu_strtouint(str, output, sizeof (*output), 0, 0, 0) == -1)
101 		backend_panic("invalid integer \"%s\" in field \"%s\"",
102 		    str, fieldname);
103 }
104 
105 #define	NUM_NEEDED	50
106 
107 static int
108 delete_stack_push(delete_info_t *dip, uint32_t be, delete_cb_func *cb,
109     uint32_t id, uint32_t gen)
110 {
111 	delete_stack_t *cur = dip->di_stack;
112 	delete_ent_t *ent;
113 
114 	if (cur == NULL || cur->ds_cur == cur->ds_size) {
115 		delete_stack_t *new = dip->di_free;
116 		dip->di_free = NULL;
117 		if (new == NULL) {
118 			new = uu_zalloc(DELETE_STACK_SIZE(NUM_NEEDED));
119 			if (new == NULL)
120 				return (REP_PROTOCOL_FAIL_NO_RESOURCES);
121 			new->ds_size = NUM_NEEDED;
122 		}
123 		new->ds_cur = 0;
124 		new->ds_next = dip->di_stack;
125 		dip->di_stack = new;
126 		cur = new;
127 	}
128 	assert(cur->ds_cur < cur->ds_size);
129 	ent = &cur->ds_buf[cur->ds_cur++];
130 
131 	ent->de_backend = be;
132 	ent->de_cb = cb;
133 	ent->de_id = id;
134 	ent->de_gen = gen;
135 
136 	return (REP_PROTOCOL_SUCCESS);
137 }
138 
139 static int
140 delete_stack_pop(delete_info_t *dip, delete_ent_t *out)
141 {
142 	delete_stack_t *cur = dip->di_stack;
143 	delete_ent_t *ent;
144 
145 	if (cur == NULL)
146 		return (0);
147 	assert(cur->ds_cur > 0 && cur->ds_cur <= cur->ds_size);
148 	ent = &cur->ds_buf[--cur->ds_cur];
149 	if (cur->ds_cur == 0) {
150 		dip->di_stack = cur->ds_next;
151 		cur->ds_next = NULL;
152 
153 		if (dip->di_free != NULL)
154 			uu_free(dip->di_free);
155 		dip->di_free = cur;
156 	}
157 	if (ent == NULL)
158 		return (0);
159 
160 	*out = *ent;
161 	return (1);
162 }
163 
164 static void
165 delete_stack_cleanup(delete_info_t *dip)
166 {
167 	delete_stack_t *cur;
168 	while ((cur = dip->di_stack) != NULL) {
169 		dip->di_stack = cur->ds_next;
170 
171 		uu_free(cur);
172 	}
173 
174 	if ((cur = dip->di_free) != NULL) {
175 		assert(cur->ds_next == NULL);	/* should only be one */
176 		uu_free(cur);
177 		dip->di_free = NULL;
178 	}
179 }
180 
181 struct delete_cb_info {
182 	delete_info_t	*dci_dip;
183 	uint32_t	dci_be;
184 	delete_cb_func	*dci_cb;
185 	int		dci_result;
186 };
187 
188 /*ARGSUSED*/
189 static int
190 push_delete_callback(void *data, int columns, char **vals, char **names)
191 {
192 	struct delete_cb_info *info = data;
193 
194 	const char *id_str = *vals++;
195 	const char *gen_str = *vals++;
196 
197 	uint32_t id;
198 	uint32_t gen;
199 
200 	assert(columns == 2);
201 
202 	string_to_id(id_str, &id, "id");
203 	string_to_id(gen_str, &gen, "gen_id");
204 
205 	info->dci_result = delete_stack_push(info->dci_dip, info->dci_be,
206 	    info->dci_cb, id, gen);
207 
208 	if (info->dci_result != REP_PROTOCOL_SUCCESS)
209 		return (BACKEND_CALLBACK_ABORT);
210 	return (BACKEND_CALLBACK_CONTINUE);
211 }
212 
213 static int
214 value_delete(delete_info_t *dip, const delete_ent_t *ent)
215 {
216 	uint32_t be = ent->de_backend;
217 	int r;
218 
219 	backend_query_t *q;
220 
221 	backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
222 	    dip->di_np_tx;
223 
224 	q = backend_query_alloc();
225 
226 	backend_query_add(q,
227 	    "SELECT 1 FROM prop_lnk_tbl WHERE (lnk_val_id = %d); "
228 	    "DELETE FROM value_tbl WHERE (value_id = %d); ",
229 	    ent->de_id, ent->de_id);
230 	r = backend_tx_run(tx, q, backend_fail_if_seen, NULL);
231 	backend_query_free(q);
232 	if (r == REP_PROTOCOL_DONE)
233 		return (REP_PROTOCOL_SUCCESS);		/* still in use */
234 	return (r);
235 }
236 
237 static int
238 pg_lnk_tbl_delete(delete_info_t *dip, const delete_ent_t *ent)
239 {
240 	struct delete_cb_info info;
241 	uint32_t be = ent->de_backend;
242 	int r;
243 
244 	backend_query_t *q;
245 
246 	backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
247 	    dip->di_np_tx;
248 
249 	/*
250 	 * For non-persistent backends, we could only have one parent, and
251 	 * he's already been deleted.
252 	 *
253 	 * For normal backends, we need to check to see if we're in
254 	 * a snapshot or are the active generation for the property
255 	 * group.  If we are, there's nothing to be done.
256 	 */
257 	if (be == BACKEND_TYPE_NORMAL) {
258 		q = backend_query_alloc();
259 		backend_query_add(q,
260 		    "SELECT 1 "
261 		    "FROM pg_tbl "
262 		    "WHERE (pg_id = %d AND pg_gen_id = %d); "
263 		    "SELECT 1 "
264 		    "FROM snaplevel_lnk_tbl "
265 		    "WHERE (snaplvl_pg_id = %d AND snaplvl_gen_id = %d);",
266 		    ent->de_id, ent->de_gen,
267 		    ent->de_id, ent->de_gen);
268 		r = backend_tx_run(tx, q, backend_fail_if_seen, NULL);
269 		backend_query_free(q);
270 
271 		if (r == REP_PROTOCOL_DONE)
272 			return (REP_PROTOCOL_SUCCESS);	/* still in use */
273 	}
274 
275 	info.dci_dip = dip;
276 	info.dci_be =  be;
277 	info.dci_cb = &value_delete;
278 	info.dci_result = REP_PROTOCOL_SUCCESS;
279 
280 	q = backend_query_alloc();
281 	backend_query_add(q,
282 	    "SELECT DISTINCT lnk_val_id, 0 FROM prop_lnk_tbl "
283 	    "WHERE "
284 	    "    (lnk_pg_id = %d AND lnk_gen_id = %d AND lnk_val_id NOTNULL); "
285 	    "DELETE FROM prop_lnk_tbl "
286 	    "WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
287 	    ent->de_id, ent->de_gen, ent->de_id, ent->de_gen);
288 
289 	r = backend_tx_run(tx, q, push_delete_callback, &info);
290 	backend_query_free(q);
291 
292 	if (r == REP_PROTOCOL_DONE) {
293 		assert(info.dci_result != REP_PROTOCOL_SUCCESS);
294 		return (info.dci_result);
295 	}
296 	return (r);
297 }
298 
299 static int
300 propertygrp_delete(delete_info_t *dip, const delete_ent_t *ent)
301 {
302 	uint32_t be = ent->de_backend;
303 	backend_query_t *q;
304 	uint32_t gen;
305 
306 	int r;
307 
308 	backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
309 	    dip->di_np_tx;
310 
311 	q = backend_query_alloc();
312 	backend_query_add(q,
313 	    "SELECT pg_gen_id FROM pg_tbl WHERE pg_id = %d; "
314 	    "DELETE FROM pg_tbl WHERE pg_id = %d",
315 	    ent->de_id, ent->de_id);
316 	r = backend_tx_run_single_int(tx, q, &gen);
317 	backend_query_free(q);
318 
319 	if (r != REP_PROTOCOL_SUCCESS)
320 		return (r);
321 
322 	return (delete_stack_push(dip, be, &pg_lnk_tbl_delete,
323 	    ent->de_id, gen));
324 }
325 
326 static int
327 snaplevel_lnk_delete(delete_info_t *dip, const delete_ent_t *ent)
328 {
329 	uint32_t be = ent->de_backend;
330 	backend_query_t *q;
331 	struct delete_cb_info info;
332 
333 	int r;
334 
335 	backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
336 	    dip->di_np_tx;
337 
338 	info.dci_dip = dip;
339 	info.dci_be = be;
340 	info.dci_cb = &pg_lnk_tbl_delete;
341 	info.dci_result = REP_PROTOCOL_SUCCESS;
342 
343 	q = backend_query_alloc();
344 	backend_query_add(q,
345 	    "SELECT snaplvl_pg_id, snaplvl_gen_id "
346 	    "    FROM snaplevel_lnk_tbl "
347 	    "    WHERE snaplvl_level_id = %d; "
348 	    "DELETE FROM snaplevel_lnk_tbl WHERE snaplvl_level_id = %d",
349 	    ent->de_id, ent->de_id);
350 	r = backend_tx_run(tx, q, push_delete_callback, &info);
351 	backend_query_free(q);
352 
353 	if (r == REP_PROTOCOL_DONE) {
354 		assert(info.dci_result != REP_PROTOCOL_SUCCESS);
355 		return (info.dci_result);
356 	}
357 	return (r);
358 }
359 
360 static int
361 snaplevel_tbl_delete(delete_info_t *dip, const delete_ent_t *ent)
362 {
363 	uint32_t be = ent->de_backend;
364 	backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
365 	    dip->di_np_tx;
366 
367 	struct delete_cb_info info;
368 	backend_query_t *q;
369 	int r;
370 
371 	assert(be == BACKEND_TYPE_NORMAL);
372 
373 	q = backend_query_alloc();
374 	backend_query_add(q,
375 	    "SELECT 1 FROM snapshot_lnk_tbl WHERE lnk_snap_id = %d",
376 	    ent->de_id);
377 	r = backend_tx_run(tx, q, backend_fail_if_seen, NULL);
378 	backend_query_free(q);
379 
380 	if (r == REP_PROTOCOL_DONE)
381 		return (REP_PROTOCOL_SUCCESS);		/* still in use */
382 
383 	info.dci_dip = dip;
384 	info.dci_be = be;
385 	info.dci_cb = &snaplevel_lnk_delete;
386 	info.dci_result = REP_PROTOCOL_SUCCESS;
387 
388 	q = backend_query_alloc();
389 	backend_query_add(q,
390 	    "SELECT snap_level_id, 0 FROM snaplevel_tbl WHERE snap_id = %d;"
391 	    "DELETE FROM snaplevel_tbl WHERE snap_id = %d",
392 	    ent->de_id, ent->de_id);
393 	r = backend_tx_run(tx, q, push_delete_callback, &info);
394 	backend_query_free(q);
395 
396 	if (r == REP_PROTOCOL_DONE) {
397 		assert(info.dci_result != REP_PROTOCOL_SUCCESS);
398 		return (info.dci_result);
399 	}
400 	return (r);
401 }
402 
403 static int
404 snapshot_lnk_delete(delete_info_t *dip, const delete_ent_t *ent)
405 {
406 	uint32_t be = ent->de_backend;
407 	backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
408 	    dip->di_np_tx;
409 
410 	backend_query_t *q;
411 	uint32_t snapid;
412 	int r;
413 
414 	assert(be == BACKEND_TYPE_NORMAL);
415 
416 	q = backend_query_alloc();
417 	backend_query_add(q,
418 	    "SELECT lnk_snap_id FROM snapshot_lnk_tbl WHERE lnk_id = %d; "
419 	    "DELETE FROM snapshot_lnk_tbl WHERE lnk_id = %d",
420 	    ent->de_id, ent->de_id);
421 	r = backend_tx_run_single_int(tx, q, &snapid);
422 	backend_query_free(q);
423 
424 	if (r != REP_PROTOCOL_SUCCESS)
425 		return (r);
426 
427 	return (delete_stack_push(dip, be, &snaplevel_tbl_delete, snapid, 0));
428 }
429 
430 static int
431 pgparent_delete_add_pgs(delete_info_t *dip, uint32_t parent_id)
432 {
433 	struct delete_cb_info info;
434 	backend_query_t *q;
435 	int r;
436 
437 	info.dci_dip = dip;
438 	info.dci_be = BACKEND_TYPE_NORMAL;
439 	info.dci_cb = &propertygrp_delete;
440 	info.dci_result = REP_PROTOCOL_SUCCESS;
441 
442 	q = backend_query_alloc();
443 	backend_query_add(q,
444 	    "SELECT pg_id, 0 FROM pg_tbl WHERE pg_parent_id = %d",
445 	    parent_id);
446 
447 	r = backend_tx_run(dip->di_tx, q, push_delete_callback, &info);
448 
449 	if (r == REP_PROTOCOL_DONE) {
450 		assert(info.dci_result != REP_PROTOCOL_SUCCESS);
451 		backend_query_free(q);
452 		return (info.dci_result);
453 	}
454 	if (r != REP_PROTOCOL_SUCCESS) {
455 		backend_query_free(q);
456 		return (r);
457 	}
458 
459 	if (dip->di_np_tx != NULL) {
460 		info.dci_be = BACKEND_TYPE_NONPERSIST;
461 
462 		r = backend_tx_run(dip->di_np_tx, q, push_delete_callback,
463 		    &info);
464 
465 		if (r == REP_PROTOCOL_DONE) {
466 			assert(info.dci_result != REP_PROTOCOL_SUCCESS);
467 			backend_query_free(q);
468 			return (info.dci_result);
469 		}
470 		if (r != REP_PROTOCOL_SUCCESS) {
471 			backend_query_free(q);
472 			return (r);
473 		}
474 	}
475 	backend_query_free(q);
476 	return (REP_PROTOCOL_SUCCESS);
477 }
478 
479 static int
480 service_delete(delete_info_t *dip, const delete_ent_t *ent)
481 {
482 	int r;
483 
484 	r = backend_tx_run_update_changed(dip->di_tx,
485 	    "DELETE FROM service_tbl WHERE svc_id = %d", ent->de_id);
486 	if (r != REP_PROTOCOL_SUCCESS)
487 		return (r);
488 
489 	return (pgparent_delete_add_pgs(dip, ent->de_id));
490 }
491 
492 static int
493 instance_delete(delete_info_t *dip, const delete_ent_t *ent)
494 {
495 	struct delete_cb_info info;
496 	int r;
497 	backend_query_t *q;
498 
499 	r = backend_tx_run_update_changed(dip->di_tx,
500 	    "DELETE FROM instance_tbl WHERE instance_id = %d", ent->de_id);
501 	if (r != REP_PROTOCOL_SUCCESS)
502 		return (r);
503 
504 	r = pgparent_delete_add_pgs(dip, ent->de_id);
505 	if (r != REP_PROTOCOL_SUCCESS)
506 		return (r);
507 
508 	info.dci_dip = dip;
509 	info.dci_be = BACKEND_TYPE_NORMAL;
510 	info.dci_cb = &snapshot_lnk_delete;
511 	info.dci_result = REP_PROTOCOL_SUCCESS;
512 
513 	q = backend_query_alloc();
514 	backend_query_add(q,
515 	    "SELECT lnk_id, 0 FROM snapshot_lnk_tbl WHERE lnk_inst_id = %d",
516 	    ent->de_id);
517 	r = backend_tx_run(dip->di_tx, q, push_delete_callback, &info);
518 	backend_query_free(q);
519 
520 	if (r == REP_PROTOCOL_DONE) {
521 		assert(info.dci_result != REP_PROTOCOL_SUCCESS);
522 		return (info.dci_result);
523 	}
524 	return (r);
525 }
526 
527 /*ARGSUSED*/
528 static int
529 fill_child_callback(void *data, int columns, char **vals, char **names)
530 {
531 	child_info_t *cp = data;
532 	rc_node_t *np;
533 	uint32_t main_id;
534 	const char *name;
535 	const char *cur;
536 	rc_node_lookup_t *lp = &cp->ci_base_nl;
537 
538 	assert(columns == 2);
539 
540 	name = *vals++;
541 	columns--;
542 
543 	cur = *vals++;
544 	columns--;
545 	string_to_id(cur, &main_id, "id");
546 
547 	lp->rl_main_id = main_id;
548 
549 	if ((np = rc_node_alloc()) == NULL)
550 		return (BACKEND_CALLBACK_ABORT);
551 
552 	np = rc_node_setup(np, lp, name, cp->ci_parent);
553 	rc_node_rele(np);
554 
555 	return (BACKEND_CALLBACK_CONTINUE);
556 }
557 
558 /*ARGSUSED*/
559 static int
560 fill_snapshot_callback(void *data, int columns, char **vals, char **names)
561 {
562 	child_info_t *cp = data;
563 	rc_node_t *np;
564 	uint32_t main_id;
565 	uint32_t snap_id;
566 	const char *name;
567 	const char *cur;
568 	const char *snap;
569 	rc_node_lookup_t *lp = &cp->ci_base_nl;
570 
571 	assert(columns == 3);
572 
573 	name = *vals++;
574 	columns--;
575 
576 	cur = *vals++;
577 	columns--;
578 	snap = *vals++;
579 	columns--;
580 
581 	string_to_id(cur, &main_id, "lnk_id");
582 	string_to_id(snap, &snap_id, "lnk_snap_id");
583 
584 	lp->rl_main_id = main_id;
585 
586 	if ((np = rc_node_alloc()) == NULL)
587 		return (BACKEND_CALLBACK_ABORT);
588 
589 	np = rc_node_setup_snapshot(np, lp, name, snap_id, cp->ci_parent);
590 	rc_node_rele(np);
591 
592 	return (BACKEND_CALLBACK_CONTINUE);
593 }
594 
595 /*ARGSUSED*/
596 static int
597 fill_pg_callback(void *data, int columns, char **vals, char **names)
598 {
599 	child_info_t *cip = data;
600 	const char *name;
601 	const char *type;
602 	const char *cur;
603 	uint32_t main_id;
604 	uint32_t flags;
605 	uint32_t gen_id;
606 
607 	rc_node_lookup_t *lp = &cip->ci_base_nl;
608 	rc_node_t *newnode, *pg;
609 
610 	assert(columns == 5);
611 
612 	name = *vals++;		/* pg_name */
613 	columns--;
614 
615 	cur = *vals++;		/* pg_id */
616 	columns--;
617 	string_to_id(cur, &main_id, "pg_id");
618 
619 	lp->rl_main_id = main_id;
620 
621 	cur = *vals++;		/* pg_gen_id */
622 	columns--;
623 	string_to_id(cur, &gen_id, "pg_gen_id");
624 
625 	type = *vals++;		/* pg_type */
626 	columns--;
627 
628 	cur = *vals++;		/* pg_flags */
629 	columns--;
630 	string_to_id(cur, &flags, "pg_flags");
631 
632 	if ((newnode = rc_node_alloc()) == NULL)
633 		return (BACKEND_CALLBACK_ABORT);
634 
635 	pg = rc_node_setup_pg(newnode, lp, name, type, flags, gen_id,
636 	    cip->ci_parent);
637 	if (pg == NULL) {
638 		rc_node_destroy(newnode);
639 		return (BACKEND_CALLBACK_ABORT);
640 	}
641 
642 	rc_node_rele(pg);
643 
644 	return (BACKEND_CALLBACK_CONTINUE);
645 }
646 
647 struct property_value_info {
648 	char		*pvi_base;
649 	size_t		pvi_pos;
650 	size_t		pvi_size;
651 	size_t		pvi_count;
652 };
653 
654 /*ARGSUSED*/
655 static int
656 property_value_size_cb(void *data, int columns, char **vals, char **names)
657 {
658 	struct property_value_info *info = data;
659 	assert(columns == 1);
660 
661 	info->pvi_size += strlen(vals[0]) + 1;		/* count the '\0' */
662 
663 	return (BACKEND_CALLBACK_CONTINUE);
664 }
665 
666 /*ARGSUSED*/
667 static int
668 property_value_cb(void *data, int columns, char **vals, char **names)
669 {
670 	struct property_value_info *info = data;
671 	size_t pos, left, len;
672 
673 	assert(columns == 1);
674 	pos = info->pvi_pos;
675 	left = info->pvi_size - pos;
676 
677 	pos = info->pvi_pos;
678 	left = info->pvi_size - pos;
679 
680 	if ((len = strlcpy(&info->pvi_base[pos], vals[0], left)) >= left) {
681 		/*
682 		 * since we preallocated, above, this shouldn't happen
683 		 */
684 		backend_panic("unexpected database change");
685 	}
686 
687 	len += 1;	/* count the '\0' */
688 
689 	info->pvi_pos += len;
690 	info->pvi_count++;
691 
692 	return (BACKEND_CALLBACK_CONTINUE);
693 }
694 
695 /*ARGSUSED*/
696 void
697 object_free_values(const char *vals, uint32_t type, size_t count, size_t size)
698 {
699 	if (vals != NULL)
700 		uu_free((void *)vals);
701 }
702 
703 /*ARGSUSED*/
704 static int
705 fill_property_callback(void *data, int columns, char **vals, char **names)
706 {
707 	child_info_t *cp = data;
708 	backend_tx_t *tx = cp->ci_tx;
709 	uint32_t main_id;
710 	const char *name;
711 	const char *cur;
712 	rep_protocol_value_type_t type;
713 	rc_node_lookup_t *lp = &cp->ci_base_nl;
714 	struct property_value_info info;
715 	int rc;
716 
717 	assert(columns == 4);
718 	assert(tx != NULL);
719 
720 	info.pvi_base = NULL;
721 	info.pvi_pos = 0;
722 	info.pvi_size = 0;
723 	info.pvi_count = 0;
724 
725 	name = *vals++;
726 
727 	cur = *vals++;
728 	string_to_id(cur, &main_id, "lnk_prop_id");
729 
730 	cur = *vals++;
731 	assert(('a' <= cur[0] && 'z' >= cur[0]) ||
732 	    ('A' <= cur[0] && 'Z' >= cur[0]) &&
733 	    (cur[1] == 0 || ('a' <= cur[1] && 'z' >= cur[1]) ||
734 	    ('A' <= cur[1] && 'Z' >= cur[1])));
735 	type = cur[0] | (cur[1] << 8);
736 
737 	lp->rl_main_id = main_id;
738 
739 	/*
740 	 * fill in the values, if any
741 	 */
742 	if ((cur = *vals++) != NULL) {
743 		rep_protocol_responseid_t r;
744 		backend_query_t *q = backend_query_alloc();
745 
746 		backend_query_add(q,
747 		    "SELECT value_value FROM value_tbl "
748 		    "WHERE (value_id = '%q')", cur);
749 
750 		switch (r = backend_tx_run(tx, q, property_value_size_cb,
751 		    &info)) {
752 		case REP_PROTOCOL_SUCCESS:
753 			break;
754 
755 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
756 			backend_query_free(q);
757 			return (BACKEND_CALLBACK_ABORT);
758 
759 		case REP_PROTOCOL_DONE:
760 		default:
761 			backend_panic("backend_tx_run() returned %d", r);
762 		}
763 		if (info.pvi_size > 0) {
764 			info.pvi_base = uu_zalloc(info.pvi_size);
765 			if (info.pvi_base == NULL) {
766 				backend_query_free(q);
767 				return (BACKEND_CALLBACK_ABORT);
768 			}
769 			switch (r = backend_tx_run(tx, q, property_value_cb,
770 			    &info)) {
771 			case REP_PROTOCOL_SUCCESS:
772 				break;
773 
774 			case REP_PROTOCOL_FAIL_NO_RESOURCES:
775 				uu_free(info.pvi_base);
776 				backend_query_free(q);
777 				return (BACKEND_CALLBACK_ABORT);
778 
779 			case REP_PROTOCOL_DONE:
780 			default:
781 				backend_panic("backend_tx_run() returned %d",
782 				    r);
783 			}
784 		}
785 		backend_query_free(q);
786 	}
787 
788 	rc = rc_node_create_property(cp->ci_parent, lp, name, type,
789 	    info.pvi_base, info.pvi_count, info.pvi_size);
790 	if (rc != REP_PROTOCOL_SUCCESS) {
791 		assert(rc == REP_PROTOCOL_FAIL_NO_RESOURCES);
792 		return (BACKEND_CALLBACK_ABORT);
793 	}
794 
795 	return (BACKEND_CALLBACK_CONTINUE);
796 }
797 
798 /*
799  * The *_setup_child_info() functions fill in a child_info_t structure with the
800  * information for the children of np with type type.
801  *
802  * They fail with
803  *   _TYPE_MISMATCH - object cannot have children of type type
804  */
805 
806 static int
807 scope_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
808 {
809 	if (type != REP_PROTOCOL_ENTITY_SERVICE)
810 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
811 
812 	bzero(cip, sizeof (*cip));
813 	cip->ci_parent = np;
814 	cip->ci_base_nl.rl_type = type;
815 	cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
816 	return (REP_PROTOCOL_SUCCESS);
817 }
818 
819 static int
820 service_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
821 {
822 	switch (type) {
823 	case REP_PROTOCOL_ENTITY_INSTANCE:
824 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
825 		break;
826 	default:
827 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
828 	}
829 
830 	bzero(cip, sizeof (*cip));
831 	cip->ci_parent = np;
832 	cip->ci_base_nl.rl_type = type;
833 	cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
834 	cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_main_id;
835 
836 	return (REP_PROTOCOL_SUCCESS);
837 }
838 
839 static int
840 instance_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
841 {
842 	switch (type) {
843 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
844 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
845 		break;
846 	default:
847 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
848 	}
849 
850 	bzero(cip, sizeof (*cip));
851 	cip->ci_parent = np;
852 	cip->ci_base_nl.rl_type = type;
853 	cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
854 	cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
855 	cip->ci_base_nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_main_id;
856 
857 	return (REP_PROTOCOL_SUCCESS);
858 }
859 
860 static int
861 snaplevel_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
862 {
863 	if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
864 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
865 
866 	bzero(cip, sizeof (*cip));
867 	cip->ci_parent = np;
868 	cip->ci_base_nl.rl_type = type;
869 	cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
870 	cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
871 	cip->ci_base_nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_ids[ID_INSTANCE];
872 	cip->ci_base_nl.rl_ids[ID_NAME] = np->rn_id.rl_ids[ID_NAME];
873 	cip->ci_base_nl.rl_ids[ID_SNAPSHOT] = np->rn_id.rl_ids[ID_SNAPSHOT];
874 	cip->ci_base_nl.rl_ids[ID_LEVEL] = np->rn_id.rl_main_id;
875 
876 	return (REP_PROTOCOL_SUCCESS);
877 }
878 
879 static int
880 propertygrp_setup_child_info(rc_node_t *pg, uint32_t type, child_info_t *cip)
881 {
882 	if (type != REP_PROTOCOL_ENTITY_PROPERTY)
883 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
884 
885 	bzero(cip, sizeof (*cip));
886 	cip->ci_parent = pg;
887 	cip->ci_base_nl.rl_type = type;
888 	cip->ci_base_nl.rl_backend = pg->rn_id.rl_backend;
889 	cip->ci_base_nl.rl_ids[ID_SERVICE] = pg->rn_id.rl_ids[ID_SERVICE];
890 	cip->ci_base_nl.rl_ids[ID_INSTANCE] = pg->rn_id.rl_ids[ID_INSTANCE];
891 	cip->ci_base_nl.rl_ids[ID_PG] = pg->rn_id.rl_main_id;
892 	cip->ci_base_nl.rl_ids[ID_GEN] = pg->rn_gen_id;
893 	cip->ci_base_nl.rl_ids[ID_NAME] = pg->rn_id.rl_ids[ID_NAME];
894 	cip->ci_base_nl.rl_ids[ID_SNAPSHOT] = pg->rn_id.rl_ids[ID_SNAPSHOT];
895 	cip->ci_base_nl.rl_ids[ID_LEVEL] = pg->rn_id.rl_ids[ID_LEVEL];
896 
897 	return (REP_PROTOCOL_SUCCESS);
898 }
899 
900 /*
901  * The *_fill_children() functions populate the children of the given rc_node_t
902  * by querying the database and calling rc_node_setup_*() functions (usually
903  * via a fill_*_callback()).
904  *
905  * They fail with
906  *   _NO_RESOURCES
907  */
908 
909 /*
910  * Returns
911  *   _NO_RESOURCES
912  *   _SUCCESS
913  */
914 static int
915 scope_fill_children(rc_node_t *np)
916 {
917 	backend_query_t *q;
918 	child_info_t ci;
919 	int res;
920 
921 	(void) scope_setup_child_info(np, REP_PROTOCOL_ENTITY_SERVICE, &ci);
922 
923 	q = backend_query_alloc();
924 	backend_query_append(q, "SELECT svc_name, svc_id FROM service_tbl");
925 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_child_callback, &ci);
926 	backend_query_free(q);
927 
928 	if (res == REP_PROTOCOL_DONE)
929 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
930 	return (res);
931 }
932 
933 /*
934  * Returns
935  *   _NO_RESOURCES
936  *   _SUCCESS
937  */
938 static int
939 service_fill_children(rc_node_t *np)
940 {
941 	backend_query_t *q;
942 	child_info_t ci;
943 	int res;
944 
945 	assert(np->rn_id.rl_backend == BACKEND_TYPE_NORMAL);
946 
947 	(void) service_setup_child_info(np, REP_PROTOCOL_ENTITY_INSTANCE, &ci);
948 
949 	q = backend_query_alloc();
950 	backend_query_add(q,
951 	    "SELECT instance_name, instance_id FROM instance_tbl"
952 	    "    WHERE (instance_svc = %d)",
953 	    np->rn_id.rl_main_id);
954 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_child_callback, &ci);
955 	backend_query_free(q);
956 
957 	if (res == REP_PROTOCOL_DONE)
958 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
959 	if (res != REP_PROTOCOL_SUCCESS)
960 		return (res);
961 
962 	(void) service_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
963 	    &ci);
964 
965 	q = backend_query_alloc();
966 	backend_query_add(q,
967 	    "SELECT pg_name, pg_id, pg_gen_id, pg_type, pg_flags FROM pg_tbl"
968 	    "    WHERE (pg_parent_id = %d)",
969 	    np->rn_id.rl_main_id);
970 
971 	ci.ci_base_nl.rl_backend = BACKEND_TYPE_NORMAL;
972 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
973 	if (res == REP_PROTOCOL_SUCCESS) {
974 		ci.ci_base_nl.rl_backend = BACKEND_TYPE_NONPERSIST;
975 		res = backend_run(BACKEND_TYPE_NONPERSIST, q,
976 		    fill_pg_callback, &ci);
977 		/* nonpersistant database may not exist */
978 		if (res == REP_PROTOCOL_FAIL_BACKEND_ACCESS)
979 			res = REP_PROTOCOL_SUCCESS;
980 	}
981 	if (res == REP_PROTOCOL_DONE)
982 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
983 	backend_query_free(q);
984 
985 	return (res);
986 }
987 
988 /*
989  * Returns
990  *   _NO_RESOURCES
991  *   _SUCCESS
992  */
993 static int
994 instance_fill_children(rc_node_t *np)
995 {
996 	backend_query_t *q;
997 	child_info_t ci;
998 	int res;
999 
1000 	assert(np->rn_id.rl_backend == BACKEND_TYPE_NORMAL);
1001 
1002 	/* Get child property groups */
1003 	(void) instance_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
1004 	    &ci);
1005 
1006 	q = backend_query_alloc();
1007 	backend_query_add(q,
1008 	    "SELECT pg_name, pg_id, pg_gen_id, pg_type, pg_flags FROM pg_tbl"
1009 	    "    WHERE (pg_parent_id = %d)",
1010 	    np->rn_id.rl_main_id);
1011 	ci.ci_base_nl.rl_backend = BACKEND_TYPE_NORMAL;
1012 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
1013 	if (res == REP_PROTOCOL_SUCCESS) {
1014 		ci.ci_base_nl.rl_backend = BACKEND_TYPE_NONPERSIST;
1015 		res = backend_run(BACKEND_TYPE_NONPERSIST, q,
1016 		    fill_pg_callback, &ci);
1017 		/* nonpersistant database may not exist */
1018 		if (res == REP_PROTOCOL_FAIL_BACKEND_ACCESS)
1019 			res = REP_PROTOCOL_SUCCESS;
1020 	}
1021 	if (res == REP_PROTOCOL_DONE)
1022 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1023 	backend_query_free(q);
1024 
1025 	if (res != REP_PROTOCOL_SUCCESS)
1026 		return (res);
1027 
1028 	/* Get child snapshots */
1029 	(void) instance_setup_child_info(np, REP_PROTOCOL_ENTITY_SNAPSHOT,
1030 	    &ci);
1031 
1032 	q = backend_query_alloc();
1033 	backend_query_add(q,
1034 	    "SELECT lnk_snap_name, lnk_id, lnk_snap_id FROM snapshot_lnk_tbl"
1035 	    "    WHERE (lnk_inst_id = %d)",
1036 	    np->rn_id.rl_main_id);
1037 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_snapshot_callback, &ci);
1038 	if (res == REP_PROTOCOL_DONE)
1039 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1040 	backend_query_free(q);
1041 
1042 	return (res);
1043 }
1044 
1045 /*
1046  * Returns
1047  *   _NO_RESOURCES
1048  *   _SUCCESS
1049  */
1050 static int
1051 snapshot_fill_children(rc_node_t *np)
1052 {
1053 	rc_node_t *nnp;
1054 	rc_snapshot_t *sp, *oldsp;
1055 	rc_snaplevel_t *lvl;
1056 	rc_node_lookup_t nl;
1057 	int r;
1058 
1059 	/* Get the rc_snapshot_t (& its rc_snaplevel_t's). */
1060 	(void) pthread_mutex_lock(&np->rn_lock);
1061 	sp = np->rn_snapshot;
1062 	(void) pthread_mutex_unlock(&np->rn_lock);
1063 	if (sp == NULL) {
1064 		r = rc_snapshot_get(np->rn_snapshot_id, &sp);
1065 		if (r != REP_PROTOCOL_SUCCESS) {
1066 			assert(r == REP_PROTOCOL_FAIL_NO_RESOURCES);
1067 			return (r);
1068 		}
1069 		(void) pthread_mutex_lock(&np->rn_lock);
1070 		oldsp = np->rn_snapshot;
1071 		assert(oldsp == NULL || oldsp == sp);
1072 		np->rn_snapshot = sp;
1073 		(void) pthread_mutex_unlock(&np->rn_lock);
1074 		if (oldsp != NULL)
1075 			rc_snapshot_rele(oldsp);
1076 	}
1077 
1078 	bzero(&nl, sizeof (nl));
1079 	nl.rl_type = REP_PROTOCOL_ENTITY_SNAPLEVEL;
1080 	nl.rl_backend = np->rn_id.rl_backend;
1081 	nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
1082 	nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_ids[ID_INSTANCE];
1083 	nl.rl_ids[ID_NAME] = np->rn_id.rl_main_id;
1084 	nl.rl_ids[ID_SNAPSHOT] = np->rn_snapshot_id;
1085 
1086 	/* Create rc_node_t's for the snapshot's rc_snaplevel_t's. */
1087 	for (lvl = sp->rs_levels; lvl != NULL; lvl = lvl->rsl_next) {
1088 		nnp = rc_node_alloc();
1089 		assert(nnp != NULL);
1090 		nl.rl_main_id = lvl->rsl_level_id;
1091 		nnp = rc_node_setup_snaplevel(nnp, &nl, lvl, np);
1092 		rc_node_rele(nnp);
1093 	}
1094 
1095 	return (REP_PROTOCOL_SUCCESS);
1096 }
1097 
1098 /*
1099  * Returns
1100  *   _NO_RESOURCES
1101  *   _SUCCESS
1102  */
1103 static int
1104 snaplevel_fill_children(rc_node_t *np)
1105 {
1106 	rc_snaplevel_t *lvl = np->rn_snaplevel;
1107 	child_info_t ci;
1108 	int res;
1109 	backend_query_t *q;
1110 
1111 	(void) snaplevel_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
1112 	    &ci);
1113 
1114 	q = backend_query_alloc();
1115 	backend_query_add(q,
1116 	    "SELECT snaplvl_pg_name, snaplvl_pg_id, snaplvl_gen_id, "
1117 	    "    snaplvl_pg_type, snaplvl_pg_flags "
1118 	    "    FROM snaplevel_lnk_tbl "
1119 	    "    WHERE (snaplvl_level_id = %d)",
1120 	    lvl->rsl_level_id);
1121 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
1122 	if (res == REP_PROTOCOL_DONE)
1123 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1124 	backend_query_free(q);
1125 
1126 	return (res);
1127 }
1128 
1129 /*
1130  * Returns
1131  *   _NO_RESOURCES
1132  *   _SUCCESS
1133  */
1134 static int
1135 propertygrp_fill_children(rc_node_t *np)
1136 {
1137 	backend_query_t *q;
1138 	child_info_t ci;
1139 	int res;
1140 	backend_tx_t *tx;
1141 
1142 	backend_type_t backend = np->rn_id.rl_backend;
1143 
1144 	(void) propertygrp_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTY,
1145 	    &ci);
1146 
1147 	res = backend_tx_begin_ro(backend, &tx);
1148 	if (res != REP_PROTOCOL_SUCCESS) {
1149 		/*
1150 		 * If the backend didn't exist, we wouldn't have got this
1151 		 * property group.
1152 		 */
1153 		assert(res != REP_PROTOCOL_FAIL_BACKEND_ACCESS);
1154 		return (res);
1155 	}
1156 
1157 	ci.ci_tx = tx;
1158 
1159 	q = backend_query_alloc();
1160 	backend_query_add(q,
1161 	    "SELECT lnk_prop_name, lnk_prop_id, lnk_prop_type, lnk_val_id "
1162 	    "FROM prop_lnk_tbl "
1163 	    "WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
1164 	    np->rn_id.rl_main_id, np->rn_gen_id);
1165 	res = backend_tx_run(tx, q, fill_property_callback, &ci);
1166 	if (res == REP_PROTOCOL_DONE)
1167 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1168 	backend_query_free(q);
1169 	backend_tx_end_ro(tx);
1170 
1171 	return (res);
1172 }
1173 
1174 /*
1175  * Fails with
1176  *   _TYPE_MISMATCH - lp is not for a service
1177  *   _INVALID_TYPE - lp has invalid type
1178  *   _BAD_REQUEST - name is invalid
1179  */
1180 static int
1181 scope_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
1182 {
1183 	uint32_t type = lp->rl_type;
1184 	int rc;
1185 
1186 	if (type != REP_PROTOCOL_ENTITY_SERVICE)
1187 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1188 
1189 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
1190 		return (rc);
1191 
1192 	backend_query_add(q,
1193 	    "SELECT svc_id FROM service_tbl "
1194 	    "WHERE svc_name = '%q'",
1195 	    name);
1196 
1197 	return (REP_PROTOCOL_SUCCESS);
1198 }
1199 
1200 /*
1201  * Fails with
1202  *   _NO_RESOURCES - out of memory
1203  */
1204 static int
1205 scope_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
1206 {
1207 	return (backend_tx_run_update(tx,
1208 	    "INSERT INTO service_tbl (svc_id, svc_name) "
1209 	    "VALUES (%d, '%q')",
1210 	    lp->rl_main_id, name));
1211 }
1212 
1213 /*
1214  * Fails with
1215  *   _TYPE_MISMATCH - lp is not for an instance or property group
1216  *   _INVALID_TYPE - lp has invalid type
1217  *   _BAD_REQUEST - name is invalid
1218  */
1219 static int
1220 service_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
1221 {
1222 	uint32_t type = lp->rl_type;
1223 	int rc;
1224 
1225 	if (type != REP_PROTOCOL_ENTITY_INSTANCE &&
1226 	    type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
1227 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1228 
1229 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
1230 		return (rc);
1231 
1232 	switch (type) {
1233 	case REP_PROTOCOL_ENTITY_INSTANCE:
1234 		backend_query_add(q,
1235 		    "SELECT instance_id FROM instance_tbl "
1236 		    "WHERE instance_name = '%q' AND instance_svc = %d",
1237 		    name, lp->rl_ids[ID_SERVICE]);
1238 		break;
1239 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
1240 		backend_query_add(q,
1241 		    "SELECT pg_id FROM pg_tbl "
1242 		    "    WHERE pg_name = '%q' AND pg_parent_id = %d",
1243 		    name, lp->rl_ids[ID_SERVICE]);
1244 		break;
1245 	default:
1246 		assert(0);
1247 		abort();
1248 	}
1249 
1250 	return (REP_PROTOCOL_SUCCESS);
1251 }
1252 
1253 /*
1254  * Fails with
1255  *   _NO_RESOURCES - out of memory
1256  */
1257 static int
1258 service_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
1259 {
1260 	return (backend_tx_run_update(tx,
1261 	    "INSERT INTO instance_tbl "
1262 	    "    (instance_id, instance_name, instance_svc) "
1263 	    "VALUES (%d, '%q', %d)",
1264 	    lp->rl_main_id, name, lp->rl_ids[ID_SERVICE]));
1265 }
1266 
1267 /*
1268  * Fails with
1269  *   _NO_RESOURCES - out of memory
1270  */
1271 static int
1272 instance_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
1273 {
1274 	return (backend_tx_run_update(tx,
1275 	    "INSERT INTO snapshot_lnk_tbl "
1276 	    "    (lnk_id, lnk_inst_id, lnk_snap_name, lnk_snap_id) "
1277 	    "VALUES (%d, %d, '%q', 0)",
1278 	    lp->rl_main_id, lp->rl_ids[ID_INSTANCE], name));
1279 }
1280 
1281 /*
1282  * Fails with
1283  *   _TYPE_MISMATCH - lp is not for a property group or snapshot
1284  *   _INVALID_TYPE - lp has invalid type
1285  *   _BAD_REQUEST - name is invalid
1286  */
1287 static int
1288 instance_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
1289 {
1290 	uint32_t type = lp->rl_type;
1291 	int rc;
1292 
1293 	if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP &&
1294 	    type != REP_PROTOCOL_ENTITY_SNAPSHOT)
1295 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1296 
1297 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
1298 		return (rc);
1299 
1300 	switch (type) {
1301 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
1302 		backend_query_add(q,
1303 		    "SELECT pg_id FROM pg_tbl "
1304 		    "    WHERE pg_name = '%q' AND pg_parent_id = %d",
1305 		    name, lp->rl_ids[ID_INSTANCE]);
1306 		break;
1307 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
1308 		backend_query_add(q,
1309 		    "SELECT lnk_id FROM snapshot_lnk_tbl "
1310 		    "    WHERE lnk_snap_name = '%q' AND lnk_inst_id = %d",
1311 		    name, lp->rl_ids[ID_INSTANCE]);
1312 		break;
1313 	default:
1314 		assert(0);
1315 		abort();
1316 	}
1317 
1318 	return (REP_PROTOCOL_SUCCESS);
1319 }
1320 
1321 static int
1322 generic_insert_pg_child(backend_tx_t *tx, rc_node_lookup_t *lp,
1323     const char *name, const char *pgtype, uint32_t flags, uint32_t gen)
1324 {
1325 	int parent_id = (lp->rl_ids[ID_INSTANCE] != 0)?
1326 	    lp->rl_ids[ID_INSTANCE] : lp->rl_ids[ID_SERVICE];
1327 	return (backend_tx_run_update(tx,
1328 	    "INSERT INTO pg_tbl "
1329 	    "    (pg_id, pg_name, pg_parent_id, pg_type, pg_flags, pg_gen_id) "
1330 	    "VALUES (%d, '%q', %d, '%q', %d, %d)",
1331 	    lp->rl_main_id, name, parent_id, pgtype, flags, gen));
1332 }
1333 
1334 static int
1335 service_delete_start(rc_node_t *np, delete_info_t *dip)
1336 {
1337 	int r;
1338 	backend_query_t *q = backend_query_alloc();
1339 
1340 	/*
1341 	 * Check for child instances, and refuse to delete if they exist.
1342 	 */
1343 	backend_query_add(q,
1344 	    "SELECT 1 FROM instance_tbl WHERE instance_svc = %d",
1345 	    np->rn_id.rl_main_id);
1346 
1347 	r = backend_tx_run(dip->di_tx, q, backend_fail_if_seen, NULL);
1348 	backend_query_free(q);
1349 
1350 	if (r == REP_PROTOCOL_DONE)
1351 		return (REP_PROTOCOL_FAIL_EXISTS);	/* instances exist */
1352 
1353 	return (delete_stack_push(dip, BACKEND_TYPE_NORMAL, &service_delete,
1354 	    np->rn_id.rl_main_id, 0));
1355 }
1356 
1357 static int
1358 instance_delete_start(rc_node_t *np, delete_info_t *dip)
1359 {
1360 	return (delete_stack_push(dip, BACKEND_TYPE_NORMAL, &instance_delete,
1361 	    np->rn_id.rl_main_id, 0));
1362 }
1363 
1364 static int
1365 snapshot_delete_start(rc_node_t *np, delete_info_t *dip)
1366 {
1367 	return (delete_stack_push(dip, BACKEND_TYPE_NORMAL,
1368 	    &snapshot_lnk_delete, np->rn_id.rl_main_id, 0));
1369 }
1370 
1371 static int
1372 propertygrp_delete_start(rc_node_t *np, delete_info_t *dip)
1373 {
1374 	return (delete_stack_push(dip, np->rn_id.rl_backend,
1375 	    &propertygrp_delete, np->rn_id.rl_main_id, 0));
1376 }
1377 
1378 static object_info_t info[] = {
1379 	{REP_PROTOCOL_ENTITY_NONE},
1380 	{REP_PROTOCOL_ENTITY_SCOPE,
1381 		BACKEND_ID_INVALID,
1382 		scope_fill_children,
1383 		scope_setup_child_info,
1384 		scope_query_child,
1385 		scope_insert_child,
1386 		NULL,
1387 		NULL,
1388 	},
1389 	{REP_PROTOCOL_ENTITY_SERVICE,
1390 		BACKEND_ID_SERVICE_INSTANCE,
1391 		service_fill_children,
1392 		service_setup_child_info,
1393 		service_query_child,
1394 		service_insert_child,
1395 		generic_insert_pg_child,
1396 		service_delete_start,
1397 	},
1398 	{REP_PROTOCOL_ENTITY_INSTANCE,
1399 		BACKEND_ID_SERVICE_INSTANCE,
1400 		instance_fill_children,
1401 		instance_setup_child_info,
1402 		instance_query_child,
1403 		instance_insert_child,
1404 		generic_insert_pg_child,
1405 		instance_delete_start,
1406 	},
1407 	{REP_PROTOCOL_ENTITY_SNAPSHOT,
1408 		BACKEND_ID_SNAPNAME,
1409 		snapshot_fill_children,
1410 		NULL,
1411 		NULL,
1412 		NULL,
1413 		NULL,
1414 		snapshot_delete_start,
1415 	},
1416 	{REP_PROTOCOL_ENTITY_SNAPLEVEL,
1417 		BACKEND_ID_SNAPLEVEL,
1418 		snaplevel_fill_children,
1419 		snaplevel_setup_child_info,
1420 	},
1421 	{REP_PROTOCOL_ENTITY_PROPERTYGRP,
1422 		BACKEND_ID_PROPERTYGRP,
1423 		propertygrp_fill_children,
1424 		NULL,
1425 		NULL,
1426 		NULL,
1427 		NULL,
1428 		propertygrp_delete_start,
1429 	},
1430 	{REP_PROTOCOL_ENTITY_PROPERTY},
1431 	{-1UL}
1432 };
1433 #define	NUM_INFO (sizeof (info) / sizeof (*info))
1434 
1435 /*
1436  * object_fill_children() populates the child list of an rc_node_t by calling
1437  * the appropriate <type>_fill_children() which runs backend queries that
1438  * call an appropriate fill_*_callback() which takes a row of results,
1439  * decodes them, and calls an rc_node_setup*() function in rc_node.c to create
1440  * a child.
1441  *
1442  * Fails with
1443  *   _NO_RESOURCES
1444  */
1445 int
1446 object_fill_children(rc_node_t *pp)
1447 {
1448 	uint32_t type = pp->rn_id.rl_type;
1449 	assert(type > 0 && type < NUM_INFO);
1450 
1451 	return ((*info[type].obj_fill_children)(pp));
1452 }
1453 
1454 int
1455 object_delete(rc_node_t *pp)
1456 {
1457 	int rc;
1458 
1459 	delete_info_t dip;
1460 	delete_ent_t de;
1461 
1462 	uint32_t type = pp->rn_id.rl_type;
1463 	assert(type > 0 && type < NUM_INFO);
1464 
1465 	if (info[type].obj_delete_start == NULL)
1466 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1467 
1468 	(void) memset(&dip, '\0', sizeof (dip));
1469 	rc = backend_tx_begin(BACKEND_TYPE_NORMAL, &dip.di_tx);
1470 	if (rc != REP_PROTOCOL_SUCCESS)
1471 		return (rc);
1472 
1473 	rc = backend_tx_begin(BACKEND_TYPE_NONPERSIST, &dip.di_np_tx);
1474 	if (rc == REP_PROTOCOL_FAIL_BACKEND_ACCESS ||
1475 	    rc == REP_PROTOCOL_FAIL_BACKEND_READONLY)
1476 		dip.di_np_tx = NULL;
1477 	else if (rc != REP_PROTOCOL_SUCCESS) {
1478 		backend_tx_rollback(dip.di_tx);
1479 		return (rc);
1480 	}
1481 
1482 	if ((rc = (*info[type].obj_delete_start)(pp, &dip)) !=
1483 	    REP_PROTOCOL_SUCCESS) {
1484 		goto fail;
1485 	}
1486 
1487 	while (delete_stack_pop(&dip, &de)) {
1488 		rc = (*de.de_cb)(&dip, &de);
1489 		if (rc != REP_PROTOCOL_SUCCESS)
1490 			goto fail;
1491 	}
1492 
1493 	rc = backend_tx_commit(dip.di_tx);
1494 	if (rc != REP_PROTOCOL_SUCCESS)
1495 		backend_tx_rollback(dip.di_np_tx);
1496 	else if (dip.di_np_tx)
1497 		(void) backend_tx_commit(dip.di_np_tx);
1498 
1499 	delete_stack_cleanup(&dip);
1500 
1501 	return (rc);
1502 
1503 fail:
1504 	backend_tx_rollback(dip.di_tx);
1505 	backend_tx_rollback(dip.di_np_tx);
1506 	delete_stack_cleanup(&dip);
1507 	return (rc);
1508 }
1509 
1510 int
1511 object_do_create(backend_tx_t *tx, child_info_t *cip, rc_node_t *pp,
1512     uint32_t type, const char *name, rc_node_t **cpp)
1513 {
1514 	uint32_t ptype = pp->rn_id.rl_type;
1515 
1516 	backend_query_t *q;
1517 	uint32_t id;
1518 	rc_node_t *np = NULL;
1519 	int rc;
1520 	object_info_t *ip;
1521 
1522 	rc_node_lookup_t *lp = &cip->ci_base_nl;
1523 
1524 	assert(ptype > 0 && ptype < NUM_INFO);
1525 
1526 	ip = &info[ptype];
1527 
1528 	if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP)
1529 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
1530 
1531 	if (ip->obj_setup_child_info == NULL ||
1532 	    ip->obj_query_child == NULL ||
1533 	    ip->obj_insert_child == NULL)
1534 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1535 
1536 	if ((rc = (*ip->obj_setup_child_info)(pp, type, cip)) !=
1537 	    REP_PROTOCOL_SUCCESS)
1538 		return (rc);
1539 
1540 	q = backend_query_alloc();
1541 	if ((rc = (*ip->obj_query_child)(q, lp, name)) !=
1542 	    REP_PROTOCOL_SUCCESS) {
1543 		assert(rc == REP_PROTOCOL_FAIL_BAD_REQUEST);
1544 		backend_query_free(q);
1545 		return (rc);
1546 	}
1547 
1548 	rc = backend_tx_run_single_int(tx, q, &id);
1549 	backend_query_free(q);
1550 
1551 	if (rc == REP_PROTOCOL_SUCCESS)
1552 		return (REP_PROTOCOL_FAIL_EXISTS);
1553 	else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND)
1554 		return (rc);
1555 
1556 	if ((lp->rl_main_id = backend_new_id(tx,
1557 	    info[type].obj_id_space)) == 0) {
1558 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1559 	}
1560 
1561 	if ((np = rc_node_alloc()) == NULL)
1562 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1563 
1564 	if ((rc = (*ip->obj_insert_child)(tx, lp, name)) !=
1565 	    REP_PROTOCOL_SUCCESS) {
1566 		rc_node_destroy(np);
1567 		return (rc);
1568 	}
1569 
1570 	*cpp = np;
1571 	return (REP_PROTOCOL_SUCCESS);
1572 }
1573 
1574 /*
1575  * Fails with
1576  *   _NOT_APPLICABLE - type is _PROPERTYGRP
1577  *   _BAD_REQUEST - cannot create children for this type of node
1578  *		    name is invalid
1579  *   _TYPE_MISMATCH - object cannot have children of type type
1580  *   _NO_RESOURCES - out of memory, or could not allocate new id
1581  *   _BACKEND_READONLY
1582  *   _BACKEND_ACCESS
1583  *   _EXISTS - child already exists
1584  */
1585 int
1586 object_create(rc_node_t *pp, uint32_t type, const char *name, rc_node_t **cpp)
1587 {
1588 	backend_tx_t *tx;
1589 	rc_node_t *np = NULL;
1590 	child_info_t ci;
1591 	int rc;
1592 
1593 	if ((rc = backend_tx_begin(pp->rn_id.rl_backend, &tx)) !=
1594 	    REP_PROTOCOL_SUCCESS) {
1595 		return (rc);
1596 	}
1597 
1598 	if ((rc = object_do_create(tx, &ci, pp, type, name, &np)) !=
1599 	    REP_PROTOCOL_SUCCESS) {
1600 		backend_tx_rollback(tx);
1601 		return (rc);
1602 	}
1603 
1604 	rc = backend_tx_commit(tx);
1605 	if (rc != REP_PROTOCOL_SUCCESS) {
1606 		rc_node_destroy(np);
1607 		return (rc);
1608 	}
1609 
1610 	*cpp = rc_node_setup(np, &ci.ci_base_nl, name, ci.ci_parent);
1611 
1612 	return (REP_PROTOCOL_SUCCESS);
1613 }
1614 
1615 /*ARGSUSED*/
1616 int
1617 object_create_pg(rc_node_t *pp, uint32_t type, const char *name,
1618     const char *pgtype, uint32_t flags, rc_node_t **cpp)
1619 {
1620 	uint32_t ptype = pp->rn_id.rl_type;
1621 	backend_tx_t *tx_ro, *tx_wr;
1622 	backend_query_t *q;
1623 	uint32_t id;
1624 	uint32_t gen = 0;
1625 	rc_node_t *np = NULL;
1626 	int rc;
1627 	int rc_wr;
1628 	int rc_ro;
1629 	object_info_t *ip;
1630 
1631 	int nonpersist = (flags & SCF_PG_FLAG_NONPERSISTENT);
1632 
1633 	child_info_t ci;
1634 	rc_node_lookup_t *lp = &ci.ci_base_nl;
1635 
1636 	assert(ptype > 0 && ptype < NUM_INFO);
1637 
1638 	if (ptype != REP_PROTOCOL_ENTITY_SERVICE &&
1639 	    ptype != REP_PROTOCOL_ENTITY_INSTANCE)
1640 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1641 
1642 	ip = &info[ptype];
1643 
1644 	assert(ip->obj_setup_child_info != NULL &&
1645 	    ip->obj_query_child != NULL &&
1646 	    ip->obj_insert_pg_child != NULL);
1647 
1648 	if ((rc = (*ip->obj_setup_child_info)(pp, type, &ci)) !=
1649 	    REP_PROTOCOL_SUCCESS)
1650 		return (rc);
1651 
1652 	q = backend_query_alloc();
1653 	if ((rc = (*ip->obj_query_child)(q, lp, name)) !=
1654 	    REP_PROTOCOL_SUCCESS) {
1655 		backend_query_free(q);
1656 		return (rc);
1657 	}
1658 
1659 	if (!nonpersist) {
1660 		lp->rl_backend = BACKEND_TYPE_NORMAL;
1661 		rc_wr = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx_wr);
1662 		rc_ro = backend_tx_begin_ro(BACKEND_TYPE_NONPERSIST, &tx_ro);
1663 	} else {
1664 		lp->rl_backend = BACKEND_TYPE_NONPERSIST;
1665 		rc_ro = backend_tx_begin_ro(BACKEND_TYPE_NORMAL, &tx_ro);
1666 		rc_wr = backend_tx_begin(BACKEND_TYPE_NONPERSIST, &tx_wr);
1667 	}
1668 
1669 	if (rc_wr != REP_PROTOCOL_SUCCESS) {
1670 		rc = rc_wr;
1671 		goto fail;
1672 	}
1673 	if (rc_ro != REP_PROTOCOL_SUCCESS &&
1674 	    rc_ro != REP_PROTOCOL_FAIL_BACKEND_ACCESS) {
1675 		rc = rc_ro;
1676 		goto fail;
1677 	}
1678 
1679 	if (tx_ro != NULL) {
1680 		rc = backend_tx_run_single_int(tx_ro, q, &id);
1681 
1682 		if (rc == REP_PROTOCOL_SUCCESS) {
1683 			backend_query_free(q);
1684 			rc = REP_PROTOCOL_FAIL_EXISTS;
1685 			goto fail;
1686 		} else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND) {
1687 			backend_query_free(q);
1688 			goto fail;
1689 		}
1690 	}
1691 
1692 	rc = backend_tx_run_single_int(tx_wr, q, &id);
1693 	backend_query_free(q);
1694 
1695 	if (rc == REP_PROTOCOL_SUCCESS) {
1696 		rc = REP_PROTOCOL_FAIL_EXISTS;
1697 		goto fail;
1698 	} else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND) {
1699 		goto fail;
1700 	}
1701 
1702 	if (tx_ro != NULL)
1703 		backend_tx_end_ro(tx_ro);
1704 	tx_ro = NULL;
1705 
1706 	if ((lp->rl_main_id = backend_new_id(tx_wr,
1707 	    info[type].obj_id_space)) == 0) {
1708 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1709 		goto fail;
1710 	}
1711 
1712 	if ((np = rc_node_alloc()) == NULL) {
1713 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1714 		goto fail;
1715 	}
1716 
1717 	if ((rc = (*ip->obj_insert_pg_child)(tx_wr, lp, name, pgtype, flags,
1718 	    gen)) != REP_PROTOCOL_SUCCESS) {
1719 		rc_node_destroy(np);
1720 		goto fail;
1721 	}
1722 
1723 	rc = backend_tx_commit(tx_wr);
1724 	if (rc != REP_PROTOCOL_SUCCESS) {
1725 		rc_node_destroy(np);
1726 		return (rc);
1727 	}
1728 
1729 	*cpp = rc_node_setup_pg(np, lp, name, pgtype, flags, gen, ci.ci_parent);
1730 
1731 	return (REP_PROTOCOL_SUCCESS);
1732 
1733 fail:
1734 	if (tx_ro != NULL)
1735 		backend_tx_end_ro(tx_ro);
1736 	if (tx_wr != NULL)
1737 		backend_tx_rollback(tx_wr);
1738 	return (rc);
1739 }
1740 
1741 /*
1742  * Given a row of snaplevel number, snaplevel id, service id, service name,
1743  * instance id, & instance name, create a rc_snaplevel_t & prepend it onto the
1744  * rs_levels list of the rc_snapshot_t passed in as data.
1745  * Returns _CONTINUE on success or _ABORT if any allocations fail.
1746  */
1747 /*ARGSUSED*/
1748 static int
1749 fill_snapshot_cb(void *data, int columns, char **vals, char **names)
1750 {
1751 	rc_snapshot_t *sp = data;
1752 	rc_snaplevel_t *lvl;
1753 	char *num = vals[0];
1754 	char *id = vals[1];
1755 	char *service_id = vals[2];
1756 	char *service = vals[3];
1757 	char *instance_id = vals[4];
1758 	char *instance = vals[5];
1759 	assert(columns == 6);
1760 
1761 	lvl = uu_zalloc(sizeof (*lvl));
1762 	if (lvl == NULL)
1763 		return (BACKEND_CALLBACK_ABORT);
1764 	lvl->rsl_parent = sp;
1765 	lvl->rsl_next = sp->rs_levels;
1766 	sp->rs_levels = lvl;
1767 
1768 	string_to_id(num, &lvl->rsl_level_num, "snap_level_num");
1769 	string_to_id(id, &lvl->rsl_level_id, "snap_level_id");
1770 	string_to_id(service_id, &lvl->rsl_service_id, "snap_level_service_id");
1771 	if (instance_id != NULL)
1772 		string_to_id(instance_id, &lvl->rsl_instance_id,
1773 		    "snap_level_instance_id");
1774 
1775 	lvl->rsl_scope = (const char *)"localhost";
1776 	lvl->rsl_service = strdup(service);
1777 	if (lvl->rsl_service == NULL) {
1778 		uu_free(lvl);
1779 		return (BACKEND_CALLBACK_ABORT);
1780 	}
1781 	if (instance) {
1782 		assert(lvl->rsl_instance_id != 0);
1783 		lvl->rsl_instance = strdup(instance);
1784 		if (lvl->rsl_instance == NULL) {
1785 			free((void *)lvl->rsl_instance);
1786 			uu_free(lvl);
1787 			return (BACKEND_CALLBACK_ABORT);
1788 		}
1789 	} else {
1790 		assert(lvl->rsl_instance_id == 0);
1791 	}
1792 
1793 	return (BACKEND_CALLBACK_CONTINUE);
1794 }
1795 
1796 /*
1797  * Populate sp's rs_levels list from the snaplevel_tbl table.
1798  * Fails with
1799  *   _NO_RESOURCES
1800  */
1801 int
1802 object_fill_snapshot(rc_snapshot_t *sp)
1803 {
1804 	backend_query_t *q;
1805 	rc_snaplevel_t *sl;
1806 	int result;
1807 	int i;
1808 
1809 	q = backend_query_alloc();
1810 	backend_query_add(q,
1811 	    "SELECT snap_level_num, snap_level_id, "
1812 	    "    snap_level_service_id, snap_level_service, "
1813 	    "    snap_level_instance_id, snap_level_instance "
1814 	    "FROM snaplevel_tbl "
1815 	    "WHERE snap_id = %d "
1816 	    "ORDER BY snap_level_id DESC",
1817 	    sp->rs_snap_id);
1818 
1819 	result = backend_run(BACKEND_TYPE_NORMAL, q, fill_snapshot_cb, sp);
1820 	if (result == REP_PROTOCOL_DONE)
1821 		result = REP_PROTOCOL_FAIL_NO_RESOURCES;
1822 	backend_query_free(q);
1823 
1824 	if (result == REP_PROTOCOL_SUCCESS) {
1825 		i = 0;
1826 		for (sl = sp->rs_levels; sl != NULL; sl = sl->rsl_next) {
1827 			if (sl->rsl_level_num != ++i) {
1828 				backend_panic("snaplevels corrupt; expected "
1829 				    "level %d, got %d", i, sl->rsl_level_num);
1830 			}
1831 		}
1832 	}
1833 	return (result);
1834 }
1835 
1836 /*
1837  * This represents a property group in a snapshot.
1838  */
1839 typedef struct check_snapshot_elem {
1840 	uint32_t cse_parent;
1841 	uint32_t cse_pg_id;
1842 	uint32_t cse_pg_gen;
1843 	char	cse_seen;
1844 } check_snapshot_elem_t;
1845 
1846 #define	CSI_MAX_PARENTS		COMPOSITION_DEPTH
1847 typedef struct check_snapshot_info {
1848 	size_t			csi_count;
1849 	size_t			csi_array_size;
1850 	check_snapshot_elem_t	*csi_array;
1851 	size_t			csi_nparents;
1852 	uint32_t		csi_parent_ids[CSI_MAX_PARENTS];
1853 } check_snapshot_info_t;
1854 
1855 /*ARGSUSED*/
1856 static int
1857 check_snapshot_fill_cb(void *data, int columns, char **vals, char **names)
1858 {
1859 	check_snapshot_info_t *csip = data;
1860 	check_snapshot_elem_t *cur;
1861 	const char *parent;
1862 	const char *pg_id;
1863 	const char *pg_gen_id;
1864 
1865 	if (columns == 1) {
1866 		uint32_t *target;
1867 
1868 		if (csip->csi_nparents >= CSI_MAX_PARENTS)
1869 			backend_panic("snaplevel table has too many elements");
1870 
1871 		target = &csip->csi_parent_ids[csip->csi_nparents++];
1872 		string_to_id(vals[0], target, "snap_level_*_id");
1873 
1874 		return (BACKEND_CALLBACK_CONTINUE);
1875 	}
1876 
1877 	assert(columns == 3);
1878 
1879 	parent = vals[0];
1880 	pg_id = vals[1];
1881 	pg_gen_id = vals[2];
1882 
1883 	if (csip->csi_count == csip->csi_array_size) {
1884 		size_t newsz = (csip->csi_array_size > 0) ?
1885 		    csip->csi_array_size * 2 : 8;
1886 		check_snapshot_elem_t *new = uu_zalloc(newsz * sizeof (*new));
1887 
1888 		if (new == NULL)
1889 			return (BACKEND_CALLBACK_ABORT);
1890 
1891 		(void) memcpy(new, csip->csi_array,
1892 		    sizeof (*new) * csip->csi_array_size);
1893 		uu_free(csip->csi_array);
1894 		csip->csi_array = new;
1895 		csip->csi_array_size = newsz;
1896 	}
1897 
1898 	cur = &csip->csi_array[csip->csi_count++];
1899 
1900 	string_to_id(parent, &cur->cse_parent, "snap_level_*_id");
1901 	string_to_id(pg_id, &cur->cse_pg_id, "snaplvl_pg_id");
1902 	string_to_id(pg_gen_id, &cur->cse_pg_gen, "snaplvl_gen_id");
1903 	cur->cse_seen = 0;
1904 
1905 	return (BACKEND_CALLBACK_CONTINUE);
1906 }
1907 
1908 static int
1909 check_snapshot_elem_cmp(const void *lhs_arg, const void *rhs_arg)
1910 {
1911 	const check_snapshot_elem_t *lhs = lhs_arg;
1912 	const check_snapshot_elem_t *rhs = rhs_arg;
1913 
1914 	if (lhs->cse_parent < rhs->cse_parent)
1915 		return (-1);
1916 	if (lhs->cse_parent > rhs->cse_parent)
1917 		return (1);
1918 
1919 	if (lhs->cse_pg_id < rhs->cse_pg_id)
1920 		return (-1);
1921 	if (lhs->cse_pg_id > rhs->cse_pg_id)
1922 		return (1);
1923 
1924 	if (lhs->cse_pg_gen < rhs->cse_pg_gen)
1925 		return (-1);
1926 	if (lhs->cse_pg_gen > rhs->cse_pg_gen)
1927 		return (1);
1928 
1929 	return (0);
1930 }
1931 
1932 /*ARGSUSED*/
1933 static int
1934 check_snapshot_check_cb(void *data, int columns, char **vals, char **names)
1935 {
1936 	check_snapshot_info_t *csip = data;
1937 	check_snapshot_elem_t elem;
1938 	check_snapshot_elem_t *cur;
1939 
1940 	const char *parent = vals[0];
1941 	const char *pg_id = vals[1];
1942 	const char *pg_gen_id = vals[2];
1943 
1944 	assert(columns == 3);
1945 
1946 	string_to_id(parent, &elem.cse_parent, "snap_level_*_id");
1947 	string_to_id(pg_id, &elem.cse_pg_id, "snaplvl_pg_id");
1948 	string_to_id(pg_gen_id, &elem.cse_pg_gen, "snaplvl_gen_id");
1949 
1950 	if ((cur = bsearch(&elem, csip->csi_array, csip->csi_count,
1951 	    sizeof (*csip->csi_array), check_snapshot_elem_cmp)) == NULL)
1952 		return (BACKEND_CALLBACK_ABORT);
1953 
1954 	if (cur->cse_seen)
1955 		backend_panic("duplicate property group reported");
1956 	cur->cse_seen = 1;
1957 	return (BACKEND_CALLBACK_CONTINUE);
1958 }
1959 
1960 /*
1961  * Check that a snapshot matches up with the latest in the repository.
1962  * Returns:
1963  *	REP_PROTOCOL_SUCCESS		if it is up-to-date,
1964  *	REP_PROTOCOL_DONE		if it is out-of-date, or
1965  *	REP_PROTOCOL_FAIL_NO_RESOURCES	if we ran out of memory.
1966  */
1967 static int
1968 object_check_snapshot(uint32_t snap_id)
1969 {
1970 	check_snapshot_info_t csi;
1971 	backend_query_t *q;
1972 	int result;
1973 	size_t idx;
1974 
1975 	/* if the snapshot has never been taken, it must be out of date. */
1976 	if (snap_id == 0)
1977 		return (REP_PROTOCOL_DONE);
1978 
1979 	(void) memset(&csi, '\0', sizeof (csi));
1980 
1981 	q = backend_query_alloc();
1982 	backend_query_add(q,
1983 	    "SELECT\n"
1984 	    "    CASE snap_level_instance_id\n"
1985 	    "        WHEN 0 THEN snap_level_service_id\n"
1986 	    "        ELSE snap_level_instance_id\n"
1987 	    "    END\n"
1988 	    "FROM snaplevel_tbl\n"
1989 	    "WHERE snap_id = %d;\n"
1990 	    "\n"
1991 	    "SELECT\n"
1992 	    "    CASE snap_level_instance_id\n"
1993 	    "        WHEN 0 THEN snap_level_service_id\n"
1994 	    "        ELSE snap_level_instance_id\n"
1995 	    "    END,\n"
1996 	    "    snaplvl_pg_id,\n"
1997 	    "    snaplvl_gen_id\n"
1998 	    "FROM snaplevel_tbl, snaplevel_lnk_tbl\n"
1999 	    "WHERE\n"
2000 	    "    (snaplvl_level_id = snap_level_id AND\n"
2001 	    "    snap_id = %d);",
2002 	    snap_id, snap_id);
2003 
2004 	result = backend_run(BACKEND_TYPE_NORMAL, q, check_snapshot_fill_cb,
2005 	    &csi);
2006 	if (result == REP_PROTOCOL_DONE)
2007 		result = REP_PROTOCOL_FAIL_NO_RESOURCES;
2008 	backend_query_free(q);
2009 
2010 	if (result != REP_PROTOCOL_SUCCESS)
2011 		goto fail;
2012 
2013 	if (csi.csi_count > 0) {
2014 		qsort(csi.csi_array, csi.csi_count, sizeof (*csi.csi_array),
2015 		    check_snapshot_elem_cmp);
2016 	}
2017 
2018 #if COMPOSITION_DEPTH == 2
2019 	if (csi.csi_nparents != COMPOSITION_DEPTH) {
2020 		result = REP_PROTOCOL_DONE;
2021 		goto fail;
2022 	}
2023 
2024 	q = backend_query_alloc();
2025 	backend_query_add(q,
2026 	    "SELECT "
2027 	    "    pg_parent_id, pg_id, pg_gen_id "
2028 	    "FROM "
2029 	    "    pg_tbl "
2030 	    "WHERE (pg_parent_id = %d OR pg_parent_id = %d)",
2031 	    csi.csi_parent_ids[0], csi.csi_parent_ids[1]);
2032 
2033 	result = backend_run(BACKEND_TYPE_NORMAL, q, check_snapshot_check_cb,
2034 	    &csi);
2035 #else
2036 #error This code must be updated
2037 #endif
2038 	/*
2039 	 * To succeed, the callback must not have aborted, and we must have
2040 	 * found all of the items.
2041 	 */
2042 	if (result == REP_PROTOCOL_SUCCESS) {
2043 		for (idx = 0; idx < csi.csi_count; idx++) {
2044 			if (csi.csi_array[idx].cse_seen == 0) {
2045 				result = REP_PROTOCOL_DONE;
2046 				goto fail;
2047 			}
2048 		}
2049 	}
2050 
2051 fail:
2052 	uu_free(csi.csi_array);
2053 	return (result);
2054 }
2055 
2056 /*ARGSUSED*/
2057 static int
2058 object_copy_string(void *data_arg, int columns, char **vals, char **names)
2059 {
2060 	char **data = data_arg;
2061 
2062 	assert(columns == 1);
2063 
2064 	if (*data != NULL)
2065 		free(*data);
2066 	*data = NULL;
2067 
2068 	if (vals[0] != NULL) {
2069 		if ((*data = strdup(vals[0])) == NULL)
2070 			return (BACKEND_CALLBACK_ABORT);
2071 	}
2072 
2073 	return (BACKEND_CALLBACK_CONTINUE);
2074 }
2075 
2076 struct snaplevel_add_info {
2077 	backend_query_t *sai_q;
2078 	uint32_t	sai_level_id;
2079 	int		sai_used;		/* sai_q has been used */
2080 };
2081 
2082 /*ARGSUSED*/
2083 static int
2084 object_snaplevel_process_pg(void *data_arg, int columns, char **vals,
2085     char **names)
2086 {
2087 	struct snaplevel_add_info *data = data_arg;
2088 
2089 	assert(columns == 5);
2090 
2091 	backend_query_add(data->sai_q,
2092 	    "INSERT INTO snaplevel_lnk_tbl "
2093 	    "    (snaplvl_level_id, snaplvl_pg_id, snaplvl_pg_name, "
2094 	    "    snaplvl_pg_type, snaplvl_pg_flags, snaplvl_gen_id)"
2095 	    "VALUES (%d, %s, '%q', '%q', %s, %s);",
2096 	    data->sai_level_id, vals[0], vals[1], vals[2], vals[3], vals[4]);
2097 
2098 	data->sai_used = 1;
2099 
2100 	return (BACKEND_CALLBACK_CONTINUE);
2101 }
2102 
2103 /*ARGSUSED*/
2104 static int
2105 object_snapshot_add_level(backend_tx_t *tx, uint32_t snap_id,
2106     uint32_t snap_level_num, uint32_t svc_id, const char *svc_name,
2107     uint32_t inst_id, const char *inst_name)
2108 {
2109 	struct snaplevel_add_info data;
2110 	backend_query_t *q;
2111 	int result;
2112 
2113 	assert((snap_level_num == 1 && inst_name != NULL) ||
2114 	    snap_level_num == 2 && inst_name == NULL);
2115 
2116 	data.sai_level_id = backend_new_id(tx, BACKEND_ID_SNAPLEVEL);
2117 	if (data.sai_level_id == 0) {
2118 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
2119 	}
2120 
2121 	result = backend_tx_run_update(tx,
2122 	    "INSERT INTO snaplevel_tbl "
2123 	    "    (snap_id, snap_level_num, snap_level_id, "
2124 	    "    snap_level_service_id, snap_level_service, "
2125 	    "    snap_level_instance_id, snap_level_instance) "
2126 	    "VALUES (%d, %d, %d, %d, %Q, %d, %Q);",
2127 	    snap_id, snap_level_num, data.sai_level_id, svc_id, svc_name,
2128 	    inst_id, inst_name);
2129 
2130 	q = backend_query_alloc();
2131 	backend_query_add(q,
2132 	    "SELECT pg_id, pg_name, pg_type, pg_flags, pg_gen_id FROM pg_tbl "
2133 	    "WHERE (pg_parent_id = %d);",
2134 	    (inst_name != NULL)? inst_id : svc_id);
2135 
2136 	data.sai_q = backend_query_alloc();
2137 	data.sai_used = 0;
2138 	result = backend_tx_run(tx, q, object_snaplevel_process_pg,
2139 	    &data);
2140 	backend_query_free(q);
2141 
2142 	if (result == REP_PROTOCOL_SUCCESS && data.sai_used != 0)
2143 		result = backend_tx_run(tx, data.sai_q, NULL, NULL);
2144 	backend_query_free(data.sai_q);
2145 
2146 	return (result);
2147 }
2148 
2149 /*
2150  * Fails with:
2151  *	_NO_RESOURCES - no new id or out of disk space
2152  *	_BACKEND_READONLY - persistent backend is read-only
2153  */
2154 static int
2155 object_snapshot_do_take(uint32_t instid, const char *inst_name,
2156     uint32_t svcid, const char *svc_name,
2157     backend_tx_t **tx_out, uint32_t *snapid_out)
2158 {
2159 	backend_tx_t *tx;
2160 	backend_query_t *q;
2161 	int result;
2162 
2163 	char *svc_name_alloc = NULL;
2164 	char *inst_name_alloc = NULL;
2165 	uint32_t snapid;
2166 
2167 	result = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx);
2168 	if (result != REP_PROTOCOL_SUCCESS)
2169 		return (result);
2170 
2171 	snapid = backend_new_id(tx, BACKEND_ID_SNAPSHOT);
2172 	if (snapid == 0) {
2173 		result = REP_PROTOCOL_FAIL_NO_RESOURCES;
2174 		goto fail;
2175 	}
2176 
2177 	if (svc_name == NULL) {
2178 		q = backend_query_alloc();
2179 		backend_query_add(q,
2180 		    "SELECT svc_name FROM service_tbl "
2181 		    "WHERE (svc_id = %d)", svcid);
2182 		result = backend_tx_run(tx, q, object_copy_string,
2183 		    &svc_name_alloc);
2184 		backend_query_free(q);
2185 
2186 		svc_name = svc_name_alloc;
2187 
2188 		if (result == REP_PROTOCOL_DONE) {
2189 			result = REP_PROTOCOL_FAIL_NO_RESOURCES;
2190 			goto fail;
2191 		}
2192 		if (result == REP_PROTOCOL_SUCCESS && svc_name == NULL)
2193 			backend_panic("unable to find name for svc id %d\n",
2194 			    svcid);
2195 
2196 		if (result != REP_PROTOCOL_SUCCESS)
2197 			goto fail;
2198 	}
2199 
2200 	if (inst_name == NULL) {
2201 		q = backend_query_alloc();
2202 		backend_query_add(q,
2203 		    "SELECT instance_name FROM instance_tbl "
2204 		    "WHERE (instance_id = %d)", instid);
2205 		result = backend_tx_run(tx, q, object_copy_string,
2206 		    &inst_name_alloc);
2207 		backend_query_free(q);
2208 
2209 		inst_name = inst_name_alloc;
2210 
2211 		if (result == REP_PROTOCOL_DONE) {
2212 			result = REP_PROTOCOL_FAIL_NO_RESOURCES;
2213 			goto fail;
2214 		}
2215 
2216 		if (result == REP_PROTOCOL_SUCCESS && inst_name == NULL)
2217 			backend_panic(
2218 			    "unable to find name for instance id %d\n", instid);
2219 
2220 		if (result != REP_PROTOCOL_SUCCESS)
2221 			goto fail;
2222 	}
2223 
2224 	result = object_snapshot_add_level(tx, snapid, 1,
2225 	    svcid, svc_name, instid, inst_name);
2226 
2227 	if (result != REP_PROTOCOL_SUCCESS)
2228 		goto fail;
2229 
2230 	result = object_snapshot_add_level(tx, snapid, 2,
2231 	    svcid, svc_name, 0, NULL);
2232 
2233 	if (result != REP_PROTOCOL_SUCCESS)
2234 		goto fail;
2235 
2236 	*snapid_out = snapid;
2237 	*tx_out = tx;
2238 
2239 	free(svc_name_alloc);
2240 	free(inst_name_alloc);
2241 
2242 	return (REP_PROTOCOL_SUCCESS);
2243 
2244 fail:
2245 	backend_tx_rollback(tx);
2246 	free(svc_name_alloc);
2247 	free(inst_name_alloc);
2248 	return (result);
2249 }
2250 
2251 /*
2252  * Fails with:
2253  *	_TYPE_MISMATCH - pp is not an instance
2254  *	_NO_RESOURCES - no new id or out of disk space
2255  *	_BACKEND_READONLY - persistent backend is read-only
2256  */
2257 int
2258 object_snapshot_take_new(rc_node_t *pp,
2259     const char *svc_name, const char *inst_name,
2260     const char *name, rc_node_t **outp)
2261 {
2262 	rc_node_lookup_t *insti = &pp->rn_id;
2263 
2264 	uint32_t instid = insti->rl_main_id;
2265 	uint32_t svcid = insti->rl_ids[ID_SERVICE];
2266 	uint32_t snapid = 0;
2267 	backend_tx_t *tx = NULL;
2268 	child_info_t ci;
2269 	rc_node_t *np;
2270 	int result;
2271 
2272 	if (insti->rl_type != REP_PROTOCOL_ENTITY_INSTANCE)
2273 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2274 
2275 	result = object_snapshot_do_take(instid, inst_name, svcid, svc_name,
2276 	    &tx, &snapid);
2277 	if (result != REP_PROTOCOL_SUCCESS)
2278 		return (result);
2279 
2280 	if ((result = object_do_create(tx, &ci, pp,
2281 	    REP_PROTOCOL_ENTITY_SNAPSHOT, name, &np)) != REP_PROTOCOL_SUCCESS) {
2282 		backend_tx_rollback(tx);
2283 		return (result);
2284 	}
2285 
2286 	/*
2287 	 * link the new object to the new snapshot.
2288 	 */
2289 	np->rn_snapshot_id = snapid;
2290 
2291 	result = backend_tx_run_update(tx,
2292 	    "UPDATE snapshot_lnk_tbl SET lnk_snap_id = %d WHERE lnk_id = %d;",
2293 	    snapid, ci.ci_base_nl.rl_main_id);
2294 	if (result != REP_PROTOCOL_SUCCESS) {
2295 		backend_tx_rollback(tx);
2296 		rc_node_destroy(np);
2297 		return (result);
2298 	}
2299 	result = backend_tx_commit(tx);
2300 	if (result != REP_PROTOCOL_SUCCESS) {
2301 		rc_node_destroy(np);
2302 		return (result);
2303 	}
2304 
2305 	*outp = rc_node_setup(np, &ci.ci_base_nl, name, ci.ci_parent);
2306 	return (REP_PROTOCOL_SUCCESS);
2307 }
2308 
2309 /*
2310  * Fails with:
2311  *	_TYPE_MISMATCH - pp is not an instance
2312  *	_NO_RESOURCES - no new id or out of disk space
2313  *	_BACKEND_READONLY - persistent backend is read-only
2314  */
2315 int
2316 object_snapshot_attach(rc_node_lookup_t *snapi, uint32_t *snapid_ptr,
2317     int takesnap)
2318 {
2319 	uint32_t svcid = snapi->rl_ids[ID_SERVICE];
2320 	uint32_t instid = snapi->rl_ids[ID_INSTANCE];
2321 	uint32_t snapid = *snapid_ptr;
2322 	uint32_t oldsnapid = 0;
2323 	backend_tx_t *tx = NULL;
2324 	backend_query_t *q;
2325 	int result;
2326 
2327 	delete_info_t dip;
2328 	delete_ent_t de;
2329 
2330 	if (snapi->rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT)
2331 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2332 
2333 	if (takesnap) {
2334 		/* first, check that we're actually out of date */
2335 		if (object_check_snapshot(snapid) == REP_PROTOCOL_SUCCESS)
2336 			return (REP_PROTOCOL_SUCCESS);
2337 
2338 		result = object_snapshot_do_take(instid, NULL,
2339 		    svcid, NULL, &tx, &snapid);
2340 		if (result != REP_PROTOCOL_SUCCESS)
2341 			return (result);
2342 	} else {
2343 		result = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx);
2344 		if (result != REP_PROTOCOL_SUCCESS)
2345 			return (result);
2346 	}
2347 
2348 	q = backend_query_alloc();
2349 	backend_query_add(q,
2350 	    "SELECT lnk_snap_id FROM snapshot_lnk_tbl WHERE lnk_id = %d; "
2351 	    "UPDATE snapshot_lnk_tbl SET lnk_snap_id = %d WHERE lnk_id = %d;",
2352 	    snapi->rl_main_id, snapid, snapi->rl_main_id);
2353 	result = backend_tx_run_single_int(tx, q, &oldsnapid);
2354 	backend_query_free(q);
2355 
2356 	if (result == REP_PROTOCOL_FAIL_NOT_FOUND) {
2357 		backend_tx_rollback(tx);
2358 		backend_panic("unable to find snapshot id %d",
2359 		    snapi->rl_main_id);
2360 	}
2361 	if (result != REP_PROTOCOL_SUCCESS)
2362 		goto fail;
2363 
2364 	/*
2365 	 * Now we use the delete stack to handle the possible unreferencing
2366 	 * of oldsnapid.
2367 	 */
2368 	(void) memset(&dip, 0, sizeof (dip));
2369 	dip.di_tx = tx;
2370 	dip.di_np_tx = NULL;	/* no need for non-persistant backend */
2371 
2372 	if ((result = delete_stack_push(&dip, BACKEND_TYPE_NORMAL,
2373 	    &snaplevel_tbl_delete, oldsnapid, 0)) != REP_PROTOCOL_SUCCESS)
2374 		goto fail;
2375 
2376 	while (delete_stack_pop(&dip, &de)) {
2377 		result = (*de.de_cb)(&dip, &de);
2378 		if (result != REP_PROTOCOL_SUCCESS)
2379 			goto fail;
2380 	}
2381 
2382 	result = backend_tx_commit(tx);
2383 	if (result != REP_PROTOCOL_SUCCESS)
2384 		goto fail;
2385 
2386 	delete_stack_cleanup(&dip);
2387 	*snapid_ptr = snapid;
2388 	return (REP_PROTOCOL_SUCCESS);
2389 
2390 fail:
2391 	backend_tx_rollback(tx);
2392 	delete_stack_cleanup(&dip);
2393 	return (result);
2394 }
2395