xref: /illumos-gate/usr/src/cmd/svc/configd/file_object.c (revision 30f1f754a88aa357235064d8c7ce78f2f546e0aa)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright (c) 2016 by Delphix. All rights reserved.
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 	 * it'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 		/*
747 		 * Ensure that select operation is reflective
748 		 * of repository schema.  If the repository has
749 		 * been upgraded,  make use of value ordering
750 		 * by retrieving values in order using the
751 		 * value_order column.  Otherwise, simply
752 		 * run the select with no order specified.
753 		 * The order-insensitive select is necessary
754 		 * as on first reboot post-upgrade,  the repository
755 		 * contents need to be read before the repository
756 		 * backend is writable (and upgrade is possible).
757 		 */
758 		if (backend_is_upgraded(tx)) {
759 			backend_query_add(q,
760 			    "SELECT value_value FROM value_tbl "
761 			    "WHERE (value_id = '%q') ORDER BY value_order",
762 			    cur);
763 		} else {
764 			backend_query_add(q,
765 			    "SELECT value_value FROM value_tbl "
766 			    "WHERE (value_id = '%q')",
767 			    cur);
768 		}
769 
770 		switch (r = backend_tx_run(tx, q, property_value_size_cb,
771 		    &info)) {
772 		case REP_PROTOCOL_SUCCESS:
773 			break;
774 
775 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
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", r);
782 		}
783 		if (info.pvi_size > 0) {
784 			info.pvi_base = uu_zalloc(info.pvi_size);
785 			if (info.pvi_base == NULL) {
786 				backend_query_free(q);
787 				return (BACKEND_CALLBACK_ABORT);
788 			}
789 			switch (r = backend_tx_run(tx, q, property_value_cb,
790 			    &info)) {
791 			case REP_PROTOCOL_SUCCESS:
792 				break;
793 
794 			case REP_PROTOCOL_FAIL_NO_RESOURCES:
795 				uu_free(info.pvi_base);
796 				backend_query_free(q);
797 				return (BACKEND_CALLBACK_ABORT);
798 
799 			case REP_PROTOCOL_DONE:
800 			default:
801 				backend_panic("backend_tx_run() returned %d",
802 				    r);
803 			}
804 		}
805 		backend_query_free(q);
806 	}
807 
808 	rc = rc_node_create_property(cp->ci_parent, lp, name, type,
809 	    info.pvi_base, info.pvi_count, info.pvi_size);
810 	if (rc != REP_PROTOCOL_SUCCESS) {
811 		assert(rc == REP_PROTOCOL_FAIL_NO_RESOURCES);
812 		return (BACKEND_CALLBACK_ABORT);
813 	}
814 
815 	return (BACKEND_CALLBACK_CONTINUE);
816 }
817 
818 /*
819  * The *_setup_child_info() functions fill in a child_info_t structure with the
820  * information for the children of np with type type.
821  *
822  * They fail with
823  *   _TYPE_MISMATCH - object cannot have children of type type
824  */
825 
826 static int
827 scope_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
828 {
829 	if (type != REP_PROTOCOL_ENTITY_SERVICE)
830 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
831 
832 	bzero(cip, sizeof (*cip));
833 	cip->ci_parent = np;
834 	cip->ci_base_nl.rl_type = type;
835 	cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
836 	return (REP_PROTOCOL_SUCCESS);
837 }
838 
839 static int
840 service_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
841 {
842 	switch (type) {
843 	case REP_PROTOCOL_ENTITY_INSTANCE:
844 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
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_main_id;
855 
856 	return (REP_PROTOCOL_SUCCESS);
857 }
858 
859 static int
860 instance_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
861 {
862 	switch (type) {
863 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
864 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
865 		break;
866 	default:
867 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
868 	}
869 
870 	bzero(cip, sizeof (*cip));
871 	cip->ci_parent = np;
872 	cip->ci_base_nl.rl_type = type;
873 	cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
874 	cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
875 	cip->ci_base_nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_main_id;
876 
877 	return (REP_PROTOCOL_SUCCESS);
878 }
879 
880 static int
881 snaplevel_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
882 {
883 	if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
884 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
885 
886 	bzero(cip, sizeof (*cip));
887 	cip->ci_parent = np;
888 	cip->ci_base_nl.rl_type = type;
889 	cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
890 	cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
891 	cip->ci_base_nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_ids[ID_INSTANCE];
892 	cip->ci_base_nl.rl_ids[ID_NAME] = np->rn_id.rl_ids[ID_NAME];
893 	cip->ci_base_nl.rl_ids[ID_SNAPSHOT] = np->rn_id.rl_ids[ID_SNAPSHOT];
894 	cip->ci_base_nl.rl_ids[ID_LEVEL] = np->rn_id.rl_main_id;
895 
896 	return (REP_PROTOCOL_SUCCESS);
897 }
898 
899 static int
900 propertygrp_setup_child_info(rc_node_t *pg, uint32_t type, child_info_t *cip)
901 {
902 	if (type != REP_PROTOCOL_ENTITY_PROPERTY)
903 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
904 
905 	bzero(cip, sizeof (*cip));
906 	cip->ci_parent = pg;
907 	cip->ci_base_nl.rl_type = type;
908 	cip->ci_base_nl.rl_backend = pg->rn_id.rl_backend;
909 	cip->ci_base_nl.rl_ids[ID_SERVICE] = pg->rn_id.rl_ids[ID_SERVICE];
910 	cip->ci_base_nl.rl_ids[ID_INSTANCE] = pg->rn_id.rl_ids[ID_INSTANCE];
911 	cip->ci_base_nl.rl_ids[ID_PG] = pg->rn_id.rl_main_id;
912 	cip->ci_base_nl.rl_ids[ID_GEN] = pg->rn_gen_id;
913 	cip->ci_base_nl.rl_ids[ID_NAME] = pg->rn_id.rl_ids[ID_NAME];
914 	cip->ci_base_nl.rl_ids[ID_SNAPSHOT] = pg->rn_id.rl_ids[ID_SNAPSHOT];
915 	cip->ci_base_nl.rl_ids[ID_LEVEL] = pg->rn_id.rl_ids[ID_LEVEL];
916 
917 	return (REP_PROTOCOL_SUCCESS);
918 }
919 
920 /*
921  * The *_fill_children() functions populate the children of the given rc_node_t
922  * by querying the database and calling rc_node_setup_*() functions (usually
923  * via a fill_*_callback()).
924  *
925  * They fail with
926  *   _NO_RESOURCES
927  */
928 
929 /*
930  * Returns
931  *   _NO_RESOURCES
932  *   _SUCCESS
933  */
934 static int
935 scope_fill_children(rc_node_t *np)
936 {
937 	backend_query_t *q;
938 	child_info_t ci;
939 	int res;
940 
941 	(void) scope_setup_child_info(np, REP_PROTOCOL_ENTITY_SERVICE, &ci);
942 
943 	q = backend_query_alloc();
944 	backend_query_append(q, "SELECT svc_name, svc_id FROM service_tbl");
945 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_child_callback, &ci);
946 	backend_query_free(q);
947 
948 	if (res == REP_PROTOCOL_DONE)
949 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
950 	return (res);
951 }
952 
953 /*
954  * Returns
955  *   _NO_RESOURCES
956  *   _SUCCESS
957  */
958 static int
959 service_fill_children(rc_node_t *np)
960 {
961 	backend_query_t *q;
962 	child_info_t ci;
963 	int res;
964 
965 	assert(np->rn_id.rl_backend == BACKEND_TYPE_NORMAL);
966 
967 	(void) service_setup_child_info(np, REP_PROTOCOL_ENTITY_INSTANCE, &ci);
968 
969 	q = backend_query_alloc();
970 	backend_query_add(q,
971 	    "SELECT instance_name, instance_id FROM instance_tbl"
972 	    "    WHERE (instance_svc = %d)",
973 	    np->rn_id.rl_main_id);
974 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_child_callback, &ci);
975 	backend_query_free(q);
976 
977 	if (res == REP_PROTOCOL_DONE)
978 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
979 	if (res != REP_PROTOCOL_SUCCESS)
980 		return (res);
981 
982 	(void) service_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
983 	    &ci);
984 
985 	q = backend_query_alloc();
986 	backend_query_add(q,
987 	    "SELECT pg_name, pg_id, pg_gen_id, pg_type, pg_flags FROM pg_tbl"
988 	    "    WHERE (pg_parent_id = %d)",
989 	    np->rn_id.rl_main_id);
990 
991 	ci.ci_base_nl.rl_backend = BACKEND_TYPE_NORMAL;
992 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
993 	if (res == REP_PROTOCOL_SUCCESS) {
994 		ci.ci_base_nl.rl_backend = BACKEND_TYPE_NONPERSIST;
995 		res = backend_run(BACKEND_TYPE_NONPERSIST, q,
996 		    fill_pg_callback, &ci);
997 		/* nonpersistant database may not exist */
998 		if (res == REP_PROTOCOL_FAIL_BACKEND_ACCESS)
999 			res = REP_PROTOCOL_SUCCESS;
1000 	}
1001 	if (res == REP_PROTOCOL_DONE)
1002 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1003 	backend_query_free(q);
1004 
1005 	return (res);
1006 }
1007 
1008 /*
1009  * Returns
1010  *   _NO_RESOURCES
1011  *   _SUCCESS
1012  */
1013 static int
1014 instance_fill_children(rc_node_t *np)
1015 {
1016 	backend_query_t *q;
1017 	child_info_t ci;
1018 	int res;
1019 
1020 	assert(np->rn_id.rl_backend == BACKEND_TYPE_NORMAL);
1021 
1022 	/* Get child property groups */
1023 	(void) instance_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
1024 	    &ci);
1025 
1026 	q = backend_query_alloc();
1027 	backend_query_add(q,
1028 	    "SELECT pg_name, pg_id, pg_gen_id, pg_type, pg_flags FROM pg_tbl"
1029 	    "    WHERE (pg_parent_id = %d)",
1030 	    np->rn_id.rl_main_id);
1031 	ci.ci_base_nl.rl_backend = BACKEND_TYPE_NORMAL;
1032 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
1033 	if (res == REP_PROTOCOL_SUCCESS) {
1034 		ci.ci_base_nl.rl_backend = BACKEND_TYPE_NONPERSIST;
1035 		res = backend_run(BACKEND_TYPE_NONPERSIST, q,
1036 		    fill_pg_callback, &ci);
1037 		/* nonpersistant database may not exist */
1038 		if (res == REP_PROTOCOL_FAIL_BACKEND_ACCESS)
1039 			res = REP_PROTOCOL_SUCCESS;
1040 	}
1041 	if (res == REP_PROTOCOL_DONE)
1042 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1043 	backend_query_free(q);
1044 
1045 	if (res != REP_PROTOCOL_SUCCESS)
1046 		return (res);
1047 
1048 	/* Get child snapshots */
1049 	(void) instance_setup_child_info(np, REP_PROTOCOL_ENTITY_SNAPSHOT,
1050 	    &ci);
1051 
1052 	q = backend_query_alloc();
1053 	backend_query_add(q,
1054 	    "SELECT lnk_snap_name, lnk_id, lnk_snap_id FROM snapshot_lnk_tbl"
1055 	    "    WHERE (lnk_inst_id = %d)",
1056 	    np->rn_id.rl_main_id);
1057 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_snapshot_callback, &ci);
1058 	if (res == REP_PROTOCOL_DONE)
1059 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1060 	backend_query_free(q);
1061 
1062 	return (res);
1063 }
1064 
1065 /*
1066  * Returns
1067  *   _NO_RESOURCES
1068  *   _SUCCESS
1069  */
1070 static int
1071 snapshot_fill_children(rc_node_t *np)
1072 {
1073 	rc_node_t *nnp;
1074 	rc_snapshot_t *sp, *oldsp;
1075 	rc_snaplevel_t *lvl;
1076 	rc_node_lookup_t nl;
1077 	int r;
1078 
1079 	/* Get the rc_snapshot_t (& its rc_snaplevel_t's). */
1080 	(void) pthread_mutex_lock(&np->rn_lock);
1081 	sp = np->rn_snapshot;
1082 	(void) pthread_mutex_unlock(&np->rn_lock);
1083 	if (sp == NULL) {
1084 		r = rc_snapshot_get(np->rn_snapshot_id, &sp);
1085 		if (r != REP_PROTOCOL_SUCCESS) {
1086 			assert(r == REP_PROTOCOL_FAIL_NO_RESOURCES);
1087 			return (r);
1088 		}
1089 		(void) pthread_mutex_lock(&np->rn_lock);
1090 		oldsp = np->rn_snapshot;
1091 		assert(oldsp == NULL || oldsp == sp);
1092 		np->rn_snapshot = sp;
1093 		(void) pthread_mutex_unlock(&np->rn_lock);
1094 		if (oldsp != NULL)
1095 			rc_snapshot_rele(oldsp);
1096 	}
1097 
1098 	bzero(&nl, sizeof (nl));
1099 	nl.rl_type = REP_PROTOCOL_ENTITY_SNAPLEVEL;
1100 	nl.rl_backend = np->rn_id.rl_backend;
1101 	nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
1102 	nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_ids[ID_INSTANCE];
1103 	nl.rl_ids[ID_NAME] = np->rn_id.rl_main_id;
1104 	nl.rl_ids[ID_SNAPSHOT] = np->rn_snapshot_id;
1105 
1106 	/* Create rc_node_t's for the snapshot's rc_snaplevel_t's. */
1107 	for (lvl = sp->rs_levels; lvl != NULL; lvl = lvl->rsl_next) {
1108 		nnp = rc_node_alloc();
1109 		assert(nnp != NULL);
1110 		nl.rl_main_id = lvl->rsl_level_id;
1111 		nnp = rc_node_setup_snaplevel(nnp, &nl, lvl, np);
1112 		rc_node_rele(nnp);
1113 	}
1114 
1115 	return (REP_PROTOCOL_SUCCESS);
1116 }
1117 
1118 /*
1119  * Returns
1120  *   _NO_RESOURCES
1121  *   _SUCCESS
1122  */
1123 static int
1124 snaplevel_fill_children(rc_node_t *np)
1125 {
1126 	rc_snaplevel_t *lvl = np->rn_snaplevel;
1127 	child_info_t ci;
1128 	int res;
1129 	backend_query_t *q;
1130 
1131 	(void) snaplevel_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
1132 	    &ci);
1133 
1134 	q = backend_query_alloc();
1135 	backend_query_add(q,
1136 	    "SELECT snaplvl_pg_name, snaplvl_pg_id, snaplvl_gen_id, "
1137 	    "    snaplvl_pg_type, snaplvl_pg_flags "
1138 	    "    FROM snaplevel_lnk_tbl "
1139 	    "    WHERE (snaplvl_level_id = %d)",
1140 	    lvl->rsl_level_id);
1141 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
1142 	if (res == REP_PROTOCOL_DONE)
1143 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1144 	backend_query_free(q);
1145 
1146 	return (res);
1147 }
1148 
1149 /*
1150  * Returns
1151  *   _NO_RESOURCES
1152  *   _SUCCESS
1153  */
1154 static int
1155 propertygrp_fill_children(rc_node_t *np)
1156 {
1157 	backend_query_t *q;
1158 	child_info_t ci;
1159 	int res;
1160 	backend_tx_t *tx;
1161 
1162 	backend_type_t backend = np->rn_id.rl_backend;
1163 
1164 	(void) propertygrp_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTY,
1165 	    &ci);
1166 
1167 	res = backend_tx_begin_ro(backend, &tx);
1168 	if (res != REP_PROTOCOL_SUCCESS) {
1169 		/*
1170 		 * If the backend didn't exist, we wouldn't have got this
1171 		 * property group.
1172 		 */
1173 		assert(res != REP_PROTOCOL_FAIL_BACKEND_ACCESS);
1174 		return (res);
1175 	}
1176 
1177 	ci.ci_tx = tx;
1178 
1179 	q = backend_query_alloc();
1180 	backend_query_add(q,
1181 	    "SELECT lnk_prop_name, lnk_prop_id, lnk_prop_type, lnk_val_id "
1182 	    "FROM prop_lnk_tbl "
1183 	    "WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
1184 	    np->rn_id.rl_main_id, np->rn_gen_id);
1185 	res = backend_tx_run(tx, q, fill_property_callback, &ci);
1186 	if (res == REP_PROTOCOL_DONE)
1187 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1188 	backend_query_free(q);
1189 	backend_tx_end_ro(tx);
1190 
1191 	return (res);
1192 }
1193 
1194 /*
1195  * Fails with
1196  *   _TYPE_MISMATCH - lp is not for a service
1197  *   _INVALID_TYPE - lp has invalid type
1198  *   _BAD_REQUEST - name is invalid
1199  */
1200 static int
1201 scope_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
1202 {
1203 	uint32_t type = lp->rl_type;
1204 	int rc;
1205 
1206 	if (type != REP_PROTOCOL_ENTITY_SERVICE)
1207 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1208 
1209 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
1210 		return (rc);
1211 
1212 	backend_query_add(q,
1213 	    "SELECT svc_id FROM service_tbl "
1214 	    "WHERE svc_name = '%q'",
1215 	    name);
1216 
1217 	return (REP_PROTOCOL_SUCCESS);
1218 }
1219 
1220 /*
1221  * Fails with
1222  *   _NO_RESOURCES - out of memory
1223  */
1224 static int
1225 scope_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
1226 {
1227 	return (backend_tx_run_update(tx,
1228 	    "INSERT INTO service_tbl (svc_id, svc_name) "
1229 	    "VALUES (%d, '%q')",
1230 	    lp->rl_main_id, name));
1231 }
1232 
1233 /*
1234  * Fails with
1235  *   _TYPE_MISMATCH - lp is not for an instance or property group
1236  *   _INVALID_TYPE - lp has invalid type
1237  *   _BAD_REQUEST - name is invalid
1238  */
1239 static int
1240 service_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
1241 {
1242 	uint32_t type = lp->rl_type;
1243 	int rc;
1244 
1245 	if (type != REP_PROTOCOL_ENTITY_INSTANCE &&
1246 	    type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
1247 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1248 
1249 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
1250 		return (rc);
1251 
1252 	switch (type) {
1253 	case REP_PROTOCOL_ENTITY_INSTANCE:
1254 		backend_query_add(q,
1255 		    "SELECT instance_id FROM instance_tbl "
1256 		    "WHERE instance_name = '%q' AND instance_svc = %d",
1257 		    name, lp->rl_ids[ID_SERVICE]);
1258 		break;
1259 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
1260 		backend_query_add(q,
1261 		    "SELECT pg_id FROM pg_tbl "
1262 		    "    WHERE pg_name = '%q' AND pg_parent_id = %d",
1263 		    name, lp->rl_ids[ID_SERVICE]);
1264 		break;
1265 	default:
1266 		assert(0);
1267 		abort();
1268 	}
1269 
1270 	return (REP_PROTOCOL_SUCCESS);
1271 }
1272 
1273 /*
1274  * Fails with
1275  *   _NO_RESOURCES - out of memory
1276  */
1277 static int
1278 service_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
1279 {
1280 	return (backend_tx_run_update(tx,
1281 	    "INSERT INTO instance_tbl "
1282 	    "    (instance_id, instance_name, instance_svc) "
1283 	    "VALUES (%d, '%q', %d)",
1284 	    lp->rl_main_id, name, lp->rl_ids[ID_SERVICE]));
1285 }
1286 
1287 /*
1288  * Fails with
1289  *   _NO_RESOURCES - out of memory
1290  */
1291 static int
1292 instance_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
1293 {
1294 	return (backend_tx_run_update(tx,
1295 	    "INSERT INTO snapshot_lnk_tbl "
1296 	    "    (lnk_id, lnk_inst_id, lnk_snap_name, lnk_snap_id) "
1297 	    "VALUES (%d, %d, '%q', 0)",
1298 	    lp->rl_main_id, lp->rl_ids[ID_INSTANCE], name));
1299 }
1300 
1301 /*
1302  * Fails with
1303  *   _TYPE_MISMATCH - lp is not for a property group or snapshot
1304  *   _INVALID_TYPE - lp has invalid type
1305  *   _BAD_REQUEST - name is invalid
1306  */
1307 static int
1308 instance_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
1309 {
1310 	uint32_t type = lp->rl_type;
1311 	int rc;
1312 
1313 	if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP &&
1314 	    type != REP_PROTOCOL_ENTITY_SNAPSHOT)
1315 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1316 
1317 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
1318 		return (rc);
1319 
1320 	switch (type) {
1321 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
1322 		backend_query_add(q,
1323 		    "SELECT pg_id FROM pg_tbl "
1324 		    "    WHERE pg_name = '%q' AND pg_parent_id = %d",
1325 		    name, lp->rl_ids[ID_INSTANCE]);
1326 		break;
1327 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
1328 		backend_query_add(q,
1329 		    "SELECT lnk_id FROM snapshot_lnk_tbl "
1330 		    "    WHERE lnk_snap_name = '%q' AND lnk_inst_id = %d",
1331 		    name, lp->rl_ids[ID_INSTANCE]);
1332 		break;
1333 	default:
1334 		assert(0);
1335 		abort();
1336 	}
1337 
1338 	return (REP_PROTOCOL_SUCCESS);
1339 }
1340 
1341 static int
1342 generic_insert_pg_child(backend_tx_t *tx, rc_node_lookup_t *lp,
1343     const char *name, const char *pgtype, uint32_t flags, uint32_t gen)
1344 {
1345 	int parent_id = (lp->rl_ids[ID_INSTANCE] != 0)?
1346 	    lp->rl_ids[ID_INSTANCE] : lp->rl_ids[ID_SERVICE];
1347 	return (backend_tx_run_update(tx,
1348 	    "INSERT INTO pg_tbl "
1349 	    "    (pg_id, pg_name, pg_parent_id, pg_type, pg_flags, pg_gen_id) "
1350 	    "VALUES (%d, '%q', %d, '%q', %d, %d)",
1351 	    lp->rl_main_id, name, parent_id, pgtype, flags, gen));
1352 }
1353 
1354 static int
1355 service_delete_start(rc_node_t *np, delete_info_t *dip)
1356 {
1357 	int r;
1358 	backend_query_t *q = backend_query_alloc();
1359 
1360 	/*
1361 	 * Check for child instances, and refuse to delete if they exist.
1362 	 */
1363 	backend_query_add(q,
1364 	    "SELECT 1 FROM instance_tbl WHERE instance_svc = %d",
1365 	    np->rn_id.rl_main_id);
1366 
1367 	r = backend_tx_run(dip->di_tx, q, backend_fail_if_seen, NULL);
1368 	backend_query_free(q);
1369 
1370 	if (r == REP_PROTOCOL_DONE)
1371 		return (REP_PROTOCOL_FAIL_EXISTS);	/* instances exist */
1372 
1373 	return (delete_stack_push(dip, BACKEND_TYPE_NORMAL, &service_delete,
1374 	    np->rn_id.rl_main_id, 0));
1375 }
1376 
1377 static int
1378 instance_delete_start(rc_node_t *np, delete_info_t *dip)
1379 {
1380 	return (delete_stack_push(dip, BACKEND_TYPE_NORMAL, &instance_delete,
1381 	    np->rn_id.rl_main_id, 0));
1382 }
1383 
1384 static int
1385 snapshot_delete_start(rc_node_t *np, delete_info_t *dip)
1386 {
1387 	return (delete_stack_push(dip, BACKEND_TYPE_NORMAL,
1388 	    &snapshot_lnk_delete, np->rn_id.rl_main_id, 0));
1389 }
1390 
1391 static int
1392 propertygrp_delete_start(rc_node_t *np, delete_info_t *dip)
1393 {
1394 	return (delete_stack_push(dip, np->rn_id.rl_backend,
1395 	    &propertygrp_delete, np->rn_id.rl_main_id, 0));
1396 }
1397 
1398 static object_info_t info[] = {
1399 	{REP_PROTOCOL_ENTITY_NONE},
1400 	{REP_PROTOCOL_ENTITY_SCOPE,
1401 		BACKEND_ID_INVALID,
1402 		scope_fill_children,
1403 		scope_setup_child_info,
1404 		scope_query_child,
1405 		scope_insert_child,
1406 		NULL,
1407 		NULL,
1408 	},
1409 	{REP_PROTOCOL_ENTITY_SERVICE,
1410 		BACKEND_ID_SERVICE_INSTANCE,
1411 		service_fill_children,
1412 		service_setup_child_info,
1413 		service_query_child,
1414 		service_insert_child,
1415 		generic_insert_pg_child,
1416 		service_delete_start,
1417 	},
1418 	{REP_PROTOCOL_ENTITY_INSTANCE,
1419 		BACKEND_ID_SERVICE_INSTANCE,
1420 		instance_fill_children,
1421 		instance_setup_child_info,
1422 		instance_query_child,
1423 		instance_insert_child,
1424 		generic_insert_pg_child,
1425 		instance_delete_start,
1426 	},
1427 	{REP_PROTOCOL_ENTITY_SNAPSHOT,
1428 		BACKEND_ID_SNAPNAME,
1429 		snapshot_fill_children,
1430 		NULL,
1431 		NULL,
1432 		NULL,
1433 		NULL,
1434 		snapshot_delete_start,
1435 	},
1436 	{REP_PROTOCOL_ENTITY_SNAPLEVEL,
1437 		BACKEND_ID_SNAPLEVEL,
1438 		snaplevel_fill_children,
1439 		snaplevel_setup_child_info,
1440 	},
1441 	{REP_PROTOCOL_ENTITY_PROPERTYGRP,
1442 		BACKEND_ID_PROPERTYGRP,
1443 		propertygrp_fill_children,
1444 		NULL,
1445 		NULL,
1446 		NULL,
1447 		NULL,
1448 		propertygrp_delete_start,
1449 	},
1450 	{REP_PROTOCOL_ENTITY_PROPERTY},
1451 	{-1UL}
1452 };
1453 #define	NUM_INFO (sizeof (info) / sizeof (*info))
1454 
1455 /*
1456  * object_fill_children() populates the child list of an rc_node_t by calling
1457  * the appropriate <type>_fill_children() which runs backend queries that
1458  * call an appropriate fill_*_callback() which takes a row of results,
1459  * decodes them, and calls an rc_node_setup*() function in rc_node.c to create
1460  * a child.
1461  *
1462  * Fails with
1463  *   _NO_RESOURCES
1464  */
1465 int
1466 object_fill_children(rc_node_t *pp)
1467 {
1468 	uint32_t type = pp->rn_id.rl_type;
1469 	assert(type > 0 && type < NUM_INFO);
1470 
1471 	return ((*info[type].obj_fill_children)(pp));
1472 }
1473 
1474 int
1475 object_delete(rc_node_t *pp)
1476 {
1477 	int rc;
1478 
1479 	delete_info_t dip;
1480 	delete_ent_t de;
1481 
1482 	uint32_t type = pp->rn_id.rl_type;
1483 	assert(type > 0 && type < NUM_INFO);
1484 
1485 	if (info[type].obj_delete_start == NULL)
1486 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1487 
1488 	(void) memset(&dip, '\0', sizeof (dip));
1489 	rc = backend_tx_begin(BACKEND_TYPE_NORMAL, &dip.di_tx);
1490 	if (rc != REP_PROTOCOL_SUCCESS)
1491 		return (rc);
1492 
1493 	rc = backend_tx_begin(BACKEND_TYPE_NONPERSIST, &dip.di_np_tx);
1494 	if (rc == REP_PROTOCOL_FAIL_BACKEND_ACCESS ||
1495 	    rc == REP_PROTOCOL_FAIL_BACKEND_READONLY)
1496 		dip.di_np_tx = NULL;
1497 	else if (rc != REP_PROTOCOL_SUCCESS) {
1498 		backend_tx_rollback(dip.di_tx);
1499 		return (rc);
1500 	}
1501 
1502 	if ((rc = (*info[type].obj_delete_start)(pp, &dip)) !=
1503 	    REP_PROTOCOL_SUCCESS) {
1504 		goto fail;
1505 	}
1506 
1507 	while (delete_stack_pop(&dip, &de)) {
1508 		rc = (*de.de_cb)(&dip, &de);
1509 		if (rc != REP_PROTOCOL_SUCCESS)
1510 			goto fail;
1511 	}
1512 
1513 	rc = backend_tx_commit(dip.di_tx);
1514 	if (rc != REP_PROTOCOL_SUCCESS)
1515 		backend_tx_rollback(dip.di_np_tx);
1516 	else if (dip.di_np_tx)
1517 		(void) backend_tx_commit(dip.di_np_tx);
1518 
1519 	delete_stack_cleanup(&dip);
1520 
1521 	return (rc);
1522 
1523 fail:
1524 	backend_tx_rollback(dip.di_tx);
1525 	backend_tx_rollback(dip.di_np_tx);
1526 	delete_stack_cleanup(&dip);
1527 	return (rc);
1528 }
1529 
1530 int
1531 object_do_create(backend_tx_t *tx, child_info_t *cip, rc_node_t *pp,
1532     uint32_t type, const char *name, rc_node_t **cpp)
1533 {
1534 	uint32_t ptype = pp->rn_id.rl_type;
1535 
1536 	backend_query_t *q;
1537 	uint32_t id;
1538 	rc_node_t *np = NULL;
1539 	int rc;
1540 	object_info_t *ip;
1541 
1542 	rc_node_lookup_t *lp = &cip->ci_base_nl;
1543 
1544 	assert(ptype > 0 && ptype < NUM_INFO);
1545 
1546 	ip = &info[ptype];
1547 
1548 	if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP)
1549 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
1550 
1551 	if (ip->obj_setup_child_info == NULL ||
1552 	    ip->obj_query_child == NULL ||
1553 	    ip->obj_insert_child == NULL)
1554 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1555 
1556 	if ((rc = (*ip->obj_setup_child_info)(pp, type, cip)) !=
1557 	    REP_PROTOCOL_SUCCESS)
1558 		return (rc);
1559 
1560 	q = backend_query_alloc();
1561 	if ((rc = (*ip->obj_query_child)(q, lp, name)) !=
1562 	    REP_PROTOCOL_SUCCESS) {
1563 		assert(rc == REP_PROTOCOL_FAIL_BAD_REQUEST);
1564 		backend_query_free(q);
1565 		return (rc);
1566 	}
1567 
1568 	rc = backend_tx_run_single_int(tx, q, &id);
1569 	backend_query_free(q);
1570 
1571 	if (rc == REP_PROTOCOL_SUCCESS)
1572 		return (REP_PROTOCOL_FAIL_EXISTS);
1573 	else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND)
1574 		return (rc);
1575 
1576 	if ((lp->rl_main_id = backend_new_id(tx,
1577 	    info[type].obj_id_space)) == 0) {
1578 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1579 	}
1580 
1581 	if ((np = rc_node_alloc()) == NULL)
1582 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1583 
1584 	if ((rc = (*ip->obj_insert_child)(tx, lp, name)) !=
1585 	    REP_PROTOCOL_SUCCESS) {
1586 		rc_node_destroy(np);
1587 		return (rc);
1588 	}
1589 
1590 	*cpp = np;
1591 	return (REP_PROTOCOL_SUCCESS);
1592 }
1593 
1594 /*
1595  * Fails with
1596  *   _NOT_APPLICABLE - type is _PROPERTYGRP
1597  *   _BAD_REQUEST - cannot create children for this type of node
1598  *		    name is invalid
1599  *   _TYPE_MISMATCH - object cannot have children of type type
1600  *   _NO_RESOURCES - out of memory, or could not allocate new id
1601  *   _BACKEND_READONLY
1602  *   _BACKEND_ACCESS
1603  *   _EXISTS - child already exists
1604  */
1605 int
1606 object_create(rc_node_t *pp, uint32_t type, const char *name, rc_node_t **cpp)
1607 {
1608 	backend_tx_t *tx;
1609 	rc_node_t *np = NULL;
1610 	child_info_t ci;
1611 	int rc;
1612 
1613 	if ((rc = backend_tx_begin(pp->rn_id.rl_backend, &tx)) !=
1614 	    REP_PROTOCOL_SUCCESS) {
1615 		return (rc);
1616 	}
1617 
1618 	if ((rc = object_do_create(tx, &ci, pp, type, name, &np)) !=
1619 	    REP_PROTOCOL_SUCCESS) {
1620 		backend_tx_rollback(tx);
1621 		return (rc);
1622 	}
1623 
1624 	rc = backend_tx_commit(tx);
1625 	if (rc != REP_PROTOCOL_SUCCESS) {
1626 		rc_node_destroy(np);
1627 		return (rc);
1628 	}
1629 
1630 	*cpp = rc_node_setup(np, &ci.ci_base_nl, name, ci.ci_parent);
1631 
1632 	return (REP_PROTOCOL_SUCCESS);
1633 }
1634 
1635 /*ARGSUSED*/
1636 int
1637 object_create_pg(rc_node_t *pp, uint32_t type, const char *name,
1638     const char *pgtype, uint32_t flags, rc_node_t **cpp)
1639 {
1640 	uint32_t ptype = pp->rn_id.rl_type;
1641 	backend_tx_t *tx_ro, *tx_wr;
1642 	backend_query_t *q;
1643 	uint32_t id;
1644 	uint32_t gen = 0;
1645 	rc_node_t *np = NULL;
1646 	int rc;
1647 	int rc_wr;
1648 	int rc_ro;
1649 	object_info_t *ip;
1650 
1651 	int nonpersist = (flags & SCF_PG_FLAG_NONPERSISTENT);
1652 
1653 	child_info_t ci;
1654 	rc_node_lookup_t *lp = &ci.ci_base_nl;
1655 
1656 	assert(ptype > 0 && ptype < NUM_INFO);
1657 
1658 	if (ptype != REP_PROTOCOL_ENTITY_SERVICE &&
1659 	    ptype != REP_PROTOCOL_ENTITY_INSTANCE)
1660 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1661 
1662 	ip = &info[ptype];
1663 
1664 	assert(ip->obj_setup_child_info != NULL &&
1665 	    ip->obj_query_child != NULL &&
1666 	    ip->obj_insert_pg_child != NULL);
1667 
1668 	if ((rc = (*ip->obj_setup_child_info)(pp, type, &ci)) !=
1669 	    REP_PROTOCOL_SUCCESS)
1670 		return (rc);
1671 
1672 	q = backend_query_alloc();
1673 	if ((rc = (*ip->obj_query_child)(q, lp, name)) !=
1674 	    REP_PROTOCOL_SUCCESS) {
1675 		backend_query_free(q);
1676 		return (rc);
1677 	}
1678 
1679 	if (!nonpersist) {
1680 		lp->rl_backend = BACKEND_TYPE_NORMAL;
1681 		rc_wr = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx_wr);
1682 		rc_ro = backend_tx_begin_ro(BACKEND_TYPE_NONPERSIST, &tx_ro);
1683 	} else {
1684 		lp->rl_backend = BACKEND_TYPE_NONPERSIST;
1685 		rc_ro = backend_tx_begin_ro(BACKEND_TYPE_NORMAL, &tx_ro);
1686 		rc_wr = backend_tx_begin(BACKEND_TYPE_NONPERSIST, &tx_wr);
1687 	}
1688 
1689 	if (rc_wr != REP_PROTOCOL_SUCCESS) {
1690 		rc = rc_wr;
1691 		goto fail;
1692 	}
1693 	if (rc_ro != REP_PROTOCOL_SUCCESS &&
1694 	    rc_ro != REP_PROTOCOL_FAIL_BACKEND_ACCESS) {
1695 		rc = rc_ro;
1696 		goto fail;
1697 	}
1698 
1699 	if (tx_ro != NULL) {
1700 		rc = backend_tx_run_single_int(tx_ro, q, &id);
1701 
1702 		if (rc == REP_PROTOCOL_SUCCESS) {
1703 			backend_query_free(q);
1704 			rc = REP_PROTOCOL_FAIL_EXISTS;
1705 			goto fail;
1706 		} else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND) {
1707 			backend_query_free(q);
1708 			goto fail;
1709 		}
1710 	}
1711 
1712 	rc = backend_tx_run_single_int(tx_wr, q, &id);
1713 	backend_query_free(q);
1714 
1715 	if (rc == REP_PROTOCOL_SUCCESS) {
1716 		rc = REP_PROTOCOL_FAIL_EXISTS;
1717 		goto fail;
1718 	} else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND) {
1719 		goto fail;
1720 	}
1721 
1722 	if (tx_ro != NULL)
1723 		backend_tx_end_ro(tx_ro);
1724 	tx_ro = NULL;
1725 
1726 	if ((lp->rl_main_id = backend_new_id(tx_wr,
1727 	    info[type].obj_id_space)) == 0) {
1728 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1729 		goto fail;
1730 	}
1731 
1732 	if ((np = rc_node_alloc()) == NULL) {
1733 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1734 		goto fail;
1735 	}
1736 
1737 	if ((rc = (*ip->obj_insert_pg_child)(tx_wr, lp, name, pgtype, flags,
1738 	    gen)) != REP_PROTOCOL_SUCCESS) {
1739 		rc_node_destroy(np);
1740 		goto fail;
1741 	}
1742 
1743 	rc = backend_tx_commit(tx_wr);
1744 	if (rc != REP_PROTOCOL_SUCCESS) {
1745 		rc_node_destroy(np);
1746 		return (rc);
1747 	}
1748 
1749 	*cpp = rc_node_setup_pg(np, lp, name, pgtype, flags, gen, ci.ci_parent);
1750 
1751 	return (REP_PROTOCOL_SUCCESS);
1752 
1753 fail:
1754 	if (tx_ro != NULL)
1755 		backend_tx_end_ro(tx_ro);
1756 	if (tx_wr != NULL)
1757 		backend_tx_rollback(tx_wr);
1758 	return (rc);
1759 }
1760 
1761 /*
1762  * Given a row of snaplevel number, snaplevel id, service id, service name,
1763  * instance id, & instance name, create a rc_snaplevel_t & prepend it onto the
1764  * rs_levels list of the rc_snapshot_t passed in as data.
1765  * Returns _CONTINUE on success or _ABORT if any allocations fail.
1766  */
1767 /*ARGSUSED*/
1768 static int
1769 fill_snapshot_cb(void *data, int columns, char **vals, char **names)
1770 {
1771 	rc_snapshot_t *sp = data;
1772 	rc_snaplevel_t *lvl;
1773 	char *num = vals[0];
1774 	char *id = vals[1];
1775 	char *service_id = vals[2];
1776 	char *service = vals[3];
1777 	char *instance_id = vals[4];
1778 	char *instance = vals[5];
1779 	assert(columns == 6);
1780 
1781 	lvl = uu_zalloc(sizeof (*lvl));
1782 	if (lvl == NULL)
1783 		return (BACKEND_CALLBACK_ABORT);
1784 	lvl->rsl_parent = sp;
1785 	lvl->rsl_next = sp->rs_levels;
1786 	sp->rs_levels = lvl;
1787 
1788 	string_to_id(num, &lvl->rsl_level_num, "snap_level_num");
1789 	string_to_id(id, &lvl->rsl_level_id, "snap_level_id");
1790 	string_to_id(service_id, &lvl->rsl_service_id, "snap_level_service_id");
1791 	if (instance_id != NULL)
1792 		string_to_id(instance_id, &lvl->rsl_instance_id,
1793 		    "snap_level_instance_id");
1794 
1795 	lvl->rsl_scope = (const char *)"localhost";
1796 	lvl->rsl_service = strdup(service);
1797 	if (lvl->rsl_service == NULL) {
1798 		uu_free(lvl);
1799 		return (BACKEND_CALLBACK_ABORT);
1800 	}
1801 	if (instance) {
1802 		assert(lvl->rsl_instance_id != 0);
1803 		lvl->rsl_instance = strdup(instance);
1804 		if (lvl->rsl_instance == NULL) {
1805 			free((void *)lvl->rsl_instance);
1806 			uu_free(lvl);
1807 			return (BACKEND_CALLBACK_ABORT);
1808 		}
1809 	} else {
1810 		assert(lvl->rsl_instance_id == 0);
1811 	}
1812 
1813 	return (BACKEND_CALLBACK_CONTINUE);
1814 }
1815 
1816 /*
1817  * Populate sp's rs_levels list from the snaplevel_tbl table.
1818  * Fails with
1819  *   _NO_RESOURCES
1820  */
1821 int
1822 object_fill_snapshot(rc_snapshot_t *sp)
1823 {
1824 	backend_query_t *q;
1825 	rc_snaplevel_t *sl;
1826 	int result;
1827 	int i;
1828 
1829 	q = backend_query_alloc();
1830 	backend_query_add(q,
1831 	    "SELECT snap_level_num, snap_level_id, "
1832 	    "    snap_level_service_id, snap_level_service, "
1833 	    "    snap_level_instance_id, snap_level_instance "
1834 	    "FROM snaplevel_tbl "
1835 	    "WHERE snap_id = %d "
1836 	    "ORDER BY snap_level_id DESC",
1837 	    sp->rs_snap_id);
1838 
1839 	result = backend_run(BACKEND_TYPE_NORMAL, q, fill_snapshot_cb, sp);
1840 	if (result == REP_PROTOCOL_DONE)
1841 		result = REP_PROTOCOL_FAIL_NO_RESOURCES;
1842 	backend_query_free(q);
1843 
1844 	if (result == REP_PROTOCOL_SUCCESS) {
1845 		i = 0;
1846 		for (sl = sp->rs_levels; sl != NULL; sl = sl->rsl_next) {
1847 			if (sl->rsl_level_num != ++i) {
1848 				backend_panic("snaplevels corrupt; expected "
1849 				    "level %d, got %d", i, sl->rsl_level_num);
1850 			}
1851 		}
1852 	}
1853 	return (result);
1854 }
1855 
1856 /*
1857  * This represents a property group in a snapshot.
1858  */
1859 typedef struct check_snapshot_elem {
1860 	uint32_t cse_parent;
1861 	uint32_t cse_pg_id;
1862 	uint32_t cse_pg_gen;
1863 	char	cse_seen;
1864 } check_snapshot_elem_t;
1865 
1866 #define	CSI_MAX_PARENTS		COMPOSITION_DEPTH
1867 typedef struct check_snapshot_info {
1868 	size_t			csi_count;
1869 	size_t			csi_array_size;
1870 	check_snapshot_elem_t	*csi_array;
1871 	size_t			csi_nparents;
1872 	uint32_t		csi_parent_ids[CSI_MAX_PARENTS];
1873 } check_snapshot_info_t;
1874 
1875 /*ARGSUSED*/
1876 static int
1877 check_snapshot_fill_cb(void *data, int columns, char **vals, char **names)
1878 {
1879 	check_snapshot_info_t *csip = data;
1880 	check_snapshot_elem_t *cur;
1881 	const char *parent;
1882 	const char *pg_id;
1883 	const char *pg_gen_id;
1884 
1885 	if (columns == 1) {
1886 		uint32_t *target;
1887 
1888 		if (csip->csi_nparents >= CSI_MAX_PARENTS)
1889 			backend_panic("snaplevel table has too many elements");
1890 
1891 		target = &csip->csi_parent_ids[csip->csi_nparents++];
1892 		string_to_id(vals[0], target, "snap_level_*_id");
1893 
1894 		return (BACKEND_CALLBACK_CONTINUE);
1895 	}
1896 
1897 	assert(columns == 3);
1898 
1899 	parent = vals[0];
1900 	pg_id = vals[1];
1901 	pg_gen_id = vals[2];
1902 
1903 	if (csip->csi_count == csip->csi_array_size) {
1904 		size_t newsz = (csip->csi_array_size > 0) ?
1905 		    csip->csi_array_size * 2 : 8;
1906 		check_snapshot_elem_t *new = uu_zalloc(newsz * sizeof (*new));
1907 
1908 		if (new == NULL)
1909 			return (BACKEND_CALLBACK_ABORT);
1910 
1911 		(void) memcpy(new, csip->csi_array,
1912 		    sizeof (*new) * csip->csi_array_size);
1913 		uu_free(csip->csi_array);
1914 		csip->csi_array = new;
1915 		csip->csi_array_size = newsz;
1916 	}
1917 
1918 	cur = &csip->csi_array[csip->csi_count++];
1919 
1920 	string_to_id(parent, &cur->cse_parent, "snap_level_*_id");
1921 	string_to_id(pg_id, &cur->cse_pg_id, "snaplvl_pg_id");
1922 	string_to_id(pg_gen_id, &cur->cse_pg_gen, "snaplvl_gen_id");
1923 	cur->cse_seen = 0;
1924 
1925 	return (BACKEND_CALLBACK_CONTINUE);
1926 }
1927 
1928 static int
1929 check_snapshot_elem_cmp(const void *lhs_arg, const void *rhs_arg)
1930 {
1931 	const check_snapshot_elem_t *lhs = lhs_arg;
1932 	const check_snapshot_elem_t *rhs = rhs_arg;
1933 
1934 	if (lhs->cse_parent < rhs->cse_parent)
1935 		return (-1);
1936 	if (lhs->cse_parent > rhs->cse_parent)
1937 		return (1);
1938 
1939 	if (lhs->cse_pg_id < rhs->cse_pg_id)
1940 		return (-1);
1941 	if (lhs->cse_pg_id > rhs->cse_pg_id)
1942 		return (1);
1943 
1944 	if (lhs->cse_pg_gen < rhs->cse_pg_gen)
1945 		return (-1);
1946 	if (lhs->cse_pg_gen > rhs->cse_pg_gen)
1947 		return (1);
1948 
1949 	return (0);
1950 }
1951 
1952 /*ARGSUSED*/
1953 static int
1954 check_snapshot_check_cb(void *data, int columns, char **vals, char **names)
1955 {
1956 	check_snapshot_info_t *csip = data;
1957 	check_snapshot_elem_t elem;
1958 	check_snapshot_elem_t *cur;
1959 
1960 	const char *parent = vals[0];
1961 	const char *pg_id = vals[1];
1962 	const char *pg_gen_id = vals[2];
1963 
1964 	assert(columns == 3);
1965 
1966 	string_to_id(parent, &elem.cse_parent, "snap_level_*_id");
1967 	string_to_id(pg_id, &elem.cse_pg_id, "snaplvl_pg_id");
1968 	string_to_id(pg_gen_id, &elem.cse_pg_gen, "snaplvl_gen_id");
1969 
1970 	if ((cur = bsearch(&elem, csip->csi_array, csip->csi_count,
1971 	    sizeof (*csip->csi_array), check_snapshot_elem_cmp)) == NULL)
1972 		return (BACKEND_CALLBACK_ABORT);
1973 
1974 	if (cur->cse_seen)
1975 		backend_panic("duplicate property group reported");
1976 	cur->cse_seen = 1;
1977 	return (BACKEND_CALLBACK_CONTINUE);
1978 }
1979 
1980 /*
1981  * Check that a snapshot matches up with the latest in the repository.
1982  * Returns:
1983  *	REP_PROTOCOL_SUCCESS		if it is up-to-date,
1984  *	REP_PROTOCOL_DONE		if it is out-of-date, or
1985  *	REP_PROTOCOL_FAIL_NO_RESOURCES	if we ran out of memory.
1986  */
1987 static int
1988 object_check_snapshot(uint32_t snap_id)
1989 {
1990 	check_snapshot_info_t csi;
1991 	backend_query_t *q;
1992 	int result;
1993 	size_t idx;
1994 
1995 	/* if the snapshot has never been taken, it must be out of date. */
1996 	if (snap_id == 0)
1997 		return (REP_PROTOCOL_DONE);
1998 
1999 	(void) memset(&csi, '\0', sizeof (csi));
2000 
2001 	q = backend_query_alloc();
2002 	backend_query_add(q,
2003 	    "SELECT\n"
2004 	    "    CASE snap_level_instance_id\n"
2005 	    "        WHEN 0 THEN snap_level_service_id\n"
2006 	    "        ELSE snap_level_instance_id\n"
2007 	    "    END\n"
2008 	    "FROM snaplevel_tbl\n"
2009 	    "WHERE snap_id = %d;\n"
2010 	    "\n"
2011 	    "SELECT\n"
2012 	    "    CASE snap_level_instance_id\n"
2013 	    "        WHEN 0 THEN snap_level_service_id\n"
2014 	    "        ELSE snap_level_instance_id\n"
2015 	    "    END,\n"
2016 	    "    snaplvl_pg_id,\n"
2017 	    "    snaplvl_gen_id\n"
2018 	    "FROM snaplevel_tbl, snaplevel_lnk_tbl\n"
2019 	    "WHERE\n"
2020 	    "    (snaplvl_level_id = snap_level_id AND\n"
2021 	    "    snap_id = %d);",
2022 	    snap_id, snap_id);
2023 
2024 	result = backend_run(BACKEND_TYPE_NORMAL, q, check_snapshot_fill_cb,
2025 	    &csi);
2026 	if (result == REP_PROTOCOL_DONE)
2027 		result = REP_PROTOCOL_FAIL_NO_RESOURCES;
2028 	backend_query_free(q);
2029 
2030 	if (result != REP_PROTOCOL_SUCCESS)
2031 		goto fail;
2032 
2033 	if (csi.csi_count > 0) {
2034 		qsort(csi.csi_array, csi.csi_count, sizeof (*csi.csi_array),
2035 		    check_snapshot_elem_cmp);
2036 	}
2037 
2038 #if COMPOSITION_DEPTH == 2
2039 	if (csi.csi_nparents != COMPOSITION_DEPTH) {
2040 		result = REP_PROTOCOL_DONE;
2041 		goto fail;
2042 	}
2043 
2044 	q = backend_query_alloc();
2045 	backend_query_add(q,
2046 	    "SELECT "
2047 	    "    pg_parent_id, pg_id, pg_gen_id "
2048 	    "FROM "
2049 	    "    pg_tbl "
2050 	    "WHERE (pg_parent_id = %d OR pg_parent_id = %d)",
2051 	    csi.csi_parent_ids[0], csi.csi_parent_ids[1]);
2052 
2053 	result = backend_run(BACKEND_TYPE_NORMAL, q, check_snapshot_check_cb,
2054 	    &csi);
2055 #else
2056 #error This code must be updated
2057 #endif
2058 	/*
2059 	 * To succeed, the callback must not have aborted, and we must have
2060 	 * found all of the items.
2061 	 */
2062 	if (result == REP_PROTOCOL_SUCCESS) {
2063 		for (idx = 0; idx < csi.csi_count; idx++) {
2064 			if (csi.csi_array[idx].cse_seen == 0) {
2065 				result = REP_PROTOCOL_DONE;
2066 				goto fail;
2067 			}
2068 		}
2069 	}
2070 
2071 fail:
2072 	uu_free(csi.csi_array);
2073 	return (result);
2074 }
2075 
2076 /*ARGSUSED*/
2077 static int
2078 object_copy_string(void *data_arg, int columns, char **vals, char **names)
2079 {
2080 	char **data = data_arg;
2081 
2082 	assert(columns == 1);
2083 
2084 	if (*data != NULL)
2085 		free(*data);
2086 	*data = NULL;
2087 
2088 	if (vals[0] != NULL) {
2089 		if ((*data = strdup(vals[0])) == NULL)
2090 			return (BACKEND_CALLBACK_ABORT);
2091 	}
2092 
2093 	return (BACKEND_CALLBACK_CONTINUE);
2094 }
2095 
2096 struct snaplevel_add_info {
2097 	backend_query_t *sai_q;
2098 	uint32_t	sai_level_id;
2099 	int		sai_used;		/* sai_q has been used */
2100 };
2101 
2102 /*ARGSUSED*/
2103 static int
2104 object_snaplevel_process_pg(void *data_arg, int columns, char **vals,
2105     char **names)
2106 {
2107 	struct snaplevel_add_info *data = data_arg;
2108 
2109 	assert(columns == 5);
2110 
2111 	backend_query_add(data->sai_q,
2112 	    "INSERT INTO snaplevel_lnk_tbl "
2113 	    "    (snaplvl_level_id, snaplvl_pg_id, snaplvl_pg_name, "
2114 	    "    snaplvl_pg_type, snaplvl_pg_flags, snaplvl_gen_id)"
2115 	    "VALUES (%d, %s, '%q', '%q', %s, %s);",
2116 	    data->sai_level_id, vals[0], vals[1], vals[2], vals[3], vals[4]);
2117 
2118 	data->sai_used = 1;
2119 
2120 	return (BACKEND_CALLBACK_CONTINUE);
2121 }
2122 
2123 /*ARGSUSED*/
2124 static int
2125 object_snapshot_add_level(backend_tx_t *tx, uint32_t snap_id,
2126     uint32_t snap_level_num, uint32_t svc_id, const char *svc_name,
2127     uint32_t inst_id, const char *inst_name)
2128 {
2129 	struct snaplevel_add_info data;
2130 	backend_query_t *q;
2131 	int result;
2132 
2133 	assert((snap_level_num == 1 && inst_name != NULL) ||
2134 	    snap_level_num == 2 && inst_name == NULL);
2135 
2136 	data.sai_level_id = backend_new_id(tx, BACKEND_ID_SNAPLEVEL);
2137 	if (data.sai_level_id == 0) {
2138 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
2139 	}
2140 
2141 	result = backend_tx_run_update(tx,
2142 	    "INSERT INTO snaplevel_tbl "
2143 	    "    (snap_id, snap_level_num, snap_level_id, "
2144 	    "    snap_level_service_id, snap_level_service, "
2145 	    "    snap_level_instance_id, snap_level_instance) "
2146 	    "VALUES (%d, %d, %d, %d, %Q, %d, %Q);",
2147 	    snap_id, snap_level_num, data.sai_level_id, svc_id, svc_name,
2148 	    inst_id, inst_name);
2149 
2150 	q = backend_query_alloc();
2151 	backend_query_add(q,
2152 	    "SELECT pg_id, pg_name, pg_type, pg_flags, pg_gen_id FROM pg_tbl "
2153 	    "WHERE (pg_parent_id = %d);",
2154 	    (inst_name != NULL)? inst_id : svc_id);
2155 
2156 	data.sai_q = backend_query_alloc();
2157 	data.sai_used = 0;
2158 	result = backend_tx_run(tx, q, object_snaplevel_process_pg,
2159 	    &data);
2160 	backend_query_free(q);
2161 
2162 	if (result == REP_PROTOCOL_SUCCESS && data.sai_used != 0)
2163 		result = backend_tx_run(tx, data.sai_q, NULL, NULL);
2164 	backend_query_free(data.sai_q);
2165 
2166 	return (result);
2167 }
2168 
2169 /*
2170  * Fails with:
2171  *	_NO_RESOURCES - no new id or out of disk space
2172  *	_BACKEND_READONLY - persistent backend is read-only
2173  */
2174 static int
2175 object_snapshot_do_take(uint32_t instid, const char *inst_name,
2176     uint32_t svcid, const char *svc_name,
2177     backend_tx_t **tx_out, uint32_t *snapid_out)
2178 {
2179 	backend_tx_t *tx;
2180 	backend_query_t *q;
2181 	int result;
2182 
2183 	char *svc_name_alloc = NULL;
2184 	char *inst_name_alloc = NULL;
2185 	uint32_t snapid;
2186 
2187 	result = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx);
2188 	if (result != REP_PROTOCOL_SUCCESS)
2189 		return (result);
2190 
2191 	snapid = backend_new_id(tx, BACKEND_ID_SNAPSHOT);
2192 	if (snapid == 0) {
2193 		result = REP_PROTOCOL_FAIL_NO_RESOURCES;
2194 		goto fail;
2195 	}
2196 
2197 	if (svc_name == NULL) {
2198 		q = backend_query_alloc();
2199 		backend_query_add(q,
2200 		    "SELECT svc_name FROM service_tbl "
2201 		    "WHERE (svc_id = %d)", svcid);
2202 		result = backend_tx_run(tx, q, object_copy_string,
2203 		    &svc_name_alloc);
2204 		backend_query_free(q);
2205 
2206 		svc_name = svc_name_alloc;
2207 
2208 		if (result == REP_PROTOCOL_DONE) {
2209 			result = REP_PROTOCOL_FAIL_NO_RESOURCES;
2210 			goto fail;
2211 		}
2212 		if (result == REP_PROTOCOL_SUCCESS && svc_name == NULL)
2213 			backend_panic("unable to find name for svc id %d\n",
2214 			    svcid);
2215 
2216 		if (result != REP_PROTOCOL_SUCCESS)
2217 			goto fail;
2218 	}
2219 
2220 	if (inst_name == NULL) {
2221 		q = backend_query_alloc();
2222 		backend_query_add(q,
2223 		    "SELECT instance_name FROM instance_tbl "
2224 		    "WHERE (instance_id = %d)", instid);
2225 		result = backend_tx_run(tx, q, object_copy_string,
2226 		    &inst_name_alloc);
2227 		backend_query_free(q);
2228 
2229 		inst_name = inst_name_alloc;
2230 
2231 		if (result == REP_PROTOCOL_DONE) {
2232 			result = REP_PROTOCOL_FAIL_NO_RESOURCES;
2233 			goto fail;
2234 		}
2235 
2236 		if (result == REP_PROTOCOL_SUCCESS && inst_name == NULL)
2237 			backend_panic(
2238 			    "unable to find name for instance id %d\n", instid);
2239 
2240 		if (result != REP_PROTOCOL_SUCCESS)
2241 			goto fail;
2242 	}
2243 
2244 	result = object_snapshot_add_level(tx, snapid, 1,
2245 	    svcid, svc_name, instid, inst_name);
2246 
2247 	if (result != REP_PROTOCOL_SUCCESS)
2248 		goto fail;
2249 
2250 	result = object_snapshot_add_level(tx, snapid, 2,
2251 	    svcid, svc_name, 0, NULL);
2252 
2253 	if (result != REP_PROTOCOL_SUCCESS)
2254 		goto fail;
2255 
2256 	*snapid_out = snapid;
2257 	*tx_out = tx;
2258 
2259 	free(svc_name_alloc);
2260 	free(inst_name_alloc);
2261 
2262 	return (REP_PROTOCOL_SUCCESS);
2263 
2264 fail:
2265 	backend_tx_rollback(tx);
2266 	free(svc_name_alloc);
2267 	free(inst_name_alloc);
2268 	return (result);
2269 }
2270 
2271 /*
2272  * Fails with:
2273  *	_TYPE_MISMATCH - pp is not an instance
2274  *	_NO_RESOURCES - no new id or out of disk space
2275  *	_BACKEND_READONLY - persistent backend is read-only
2276  */
2277 int
2278 object_snapshot_take_new(rc_node_t *pp,
2279     const char *svc_name, const char *inst_name,
2280     const char *name, rc_node_t **outp)
2281 {
2282 	rc_node_lookup_t *insti = &pp->rn_id;
2283 
2284 	uint32_t instid = insti->rl_main_id;
2285 	uint32_t svcid = insti->rl_ids[ID_SERVICE];
2286 	uint32_t snapid = 0;
2287 	backend_tx_t *tx = NULL;
2288 	child_info_t ci;
2289 	rc_node_t *np;
2290 	int result;
2291 
2292 	if (insti->rl_type != REP_PROTOCOL_ENTITY_INSTANCE)
2293 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2294 
2295 	result = object_snapshot_do_take(instid, inst_name, svcid, svc_name,
2296 	    &tx, &snapid);
2297 	if (result != REP_PROTOCOL_SUCCESS)
2298 		return (result);
2299 
2300 	if ((result = object_do_create(tx, &ci, pp,
2301 	    REP_PROTOCOL_ENTITY_SNAPSHOT, name, &np)) != REP_PROTOCOL_SUCCESS) {
2302 		backend_tx_rollback(tx);
2303 		return (result);
2304 	}
2305 
2306 	/*
2307 	 * link the new object to the new snapshot.
2308 	 */
2309 	np->rn_snapshot_id = snapid;
2310 
2311 	result = backend_tx_run_update(tx,
2312 	    "UPDATE snapshot_lnk_tbl SET lnk_snap_id = %d WHERE lnk_id = %d;",
2313 	    snapid, ci.ci_base_nl.rl_main_id);
2314 	if (result != REP_PROTOCOL_SUCCESS) {
2315 		backend_tx_rollback(tx);
2316 		rc_node_destroy(np);
2317 		return (result);
2318 	}
2319 	result = backend_tx_commit(tx);
2320 	if (result != REP_PROTOCOL_SUCCESS) {
2321 		rc_node_destroy(np);
2322 		return (result);
2323 	}
2324 
2325 	*outp = rc_node_setup(np, &ci.ci_base_nl, name, ci.ci_parent);
2326 	return (REP_PROTOCOL_SUCCESS);
2327 }
2328 
2329 /*
2330  * Fails with:
2331  *	_TYPE_MISMATCH - pp is not an instance
2332  *	_NO_RESOURCES - no new id or out of disk space
2333  *	_BACKEND_READONLY - persistent backend is read-only
2334  */
2335 int
2336 object_snapshot_attach(rc_node_lookup_t *snapi, uint32_t *snapid_ptr,
2337     int takesnap)
2338 {
2339 	uint32_t svcid = snapi->rl_ids[ID_SERVICE];
2340 	uint32_t instid = snapi->rl_ids[ID_INSTANCE];
2341 	uint32_t snapid = *snapid_ptr;
2342 	uint32_t oldsnapid = 0;
2343 	backend_tx_t *tx = NULL;
2344 	backend_query_t *q;
2345 	int result;
2346 
2347 	delete_info_t dip;
2348 	delete_ent_t de;
2349 
2350 	if (snapi->rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT)
2351 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2352 
2353 	if (takesnap) {
2354 		/* first, check that we're actually out of date */
2355 		if (object_check_snapshot(snapid) == REP_PROTOCOL_SUCCESS)
2356 			return (REP_PROTOCOL_SUCCESS);
2357 
2358 		result = object_snapshot_do_take(instid, NULL,
2359 		    svcid, NULL, &tx, &snapid);
2360 		if (result != REP_PROTOCOL_SUCCESS)
2361 			return (result);
2362 	} else {
2363 		result = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx);
2364 		if (result != REP_PROTOCOL_SUCCESS)
2365 			return (result);
2366 	}
2367 
2368 	q = backend_query_alloc();
2369 	backend_query_add(q,
2370 	    "SELECT lnk_snap_id FROM snapshot_lnk_tbl WHERE lnk_id = %d; "
2371 	    "UPDATE snapshot_lnk_tbl SET lnk_snap_id = %d WHERE lnk_id = %d;",
2372 	    snapi->rl_main_id, snapid, snapi->rl_main_id);
2373 	result = backend_tx_run_single_int(tx, q, &oldsnapid);
2374 	backend_query_free(q);
2375 
2376 	if (result == REP_PROTOCOL_FAIL_NOT_FOUND) {
2377 		backend_tx_rollback(tx);
2378 		backend_panic("unable to find snapshot id %d",
2379 		    snapi->rl_main_id);
2380 	}
2381 	if (result != REP_PROTOCOL_SUCCESS)
2382 		goto fail;
2383 
2384 	/*
2385 	 * Now we use the delete stack to handle the possible unreferencing
2386 	 * of oldsnapid.
2387 	 */
2388 	(void) memset(&dip, 0, sizeof (dip));
2389 	dip.di_tx = tx;
2390 	dip.di_np_tx = NULL;	/* no need for non-persistant backend */
2391 
2392 	if ((result = delete_stack_push(&dip, BACKEND_TYPE_NORMAL,
2393 	    &snaplevel_tbl_delete, oldsnapid, 0)) != REP_PROTOCOL_SUCCESS)
2394 		goto fail;
2395 
2396 	while (delete_stack_pop(&dip, &de)) {
2397 		result = (*de.de_cb)(&dip, &de);
2398 		if (result != REP_PROTOCOL_SUCCESS)
2399 			goto fail;
2400 	}
2401 
2402 	result = backend_tx_commit(tx);
2403 	if (result != REP_PROTOCOL_SUCCESS)
2404 		goto fail;
2405 
2406 	delete_stack_cleanup(&dip);
2407 	*snapid_ptr = snapid;
2408 	return (REP_PROTOCOL_SUCCESS);
2409 
2410 fail:
2411 	backend_tx_rollback(tx);
2412 	delete_stack_cleanup(&dip);
2413 	return (result);
2414 }
2415