xref: /titanic_52/usr/src/cmd/svc/configd/rc_node.c (revision 1a7c1b724419d3cb5fa6eea75123c6b2060ba31b)
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  * rc_node.c - object management primitives
31  *
32  * This layer manages entities, their data structure, its locking, iterators,
33  * transactions, and change notification requests.  Entities (scopes,
34  * services, instances, snapshots, snaplevels, property groups, "composed"
35  * property groups (see composition below), and properties) are represented by
36  * rc_node_t's and are kept in the cache_hash hash table.  (Property values
37  * are kept in the rn_values member of the respective property -- not as
38  * separate objects.)  Iterators are represented by rc_node_iter_t's.
39  * Transactions are represented by rc_node_tx_t's and are only allocated as
40  * part of repcache_tx_t's in the client layer (client.c).  Change
41  * notification requests are represented by rc_notify_t structures and are
42  * described below.
43  *
44  * The entity tree is rooted at rc_scope, which rc_node_init() initializes to
45  * the "localhost" scope.  The tree is filled in from the database on-demand
46  * by rc_node_fill_children(), usually from rc_iter_create() since iterators
47  * are the only way to find the children of an entity.
48  *
49  * Each rc_node_t is protected by its rn_lock member.  Operations which can
50  * take too long, however, should serialize on an RC_NODE_WAITING_FLAGS bit in
51  * rn_flags with the rc_node_{hold,rele}_flag() functions.  And since pointers
52  * to rc_node_t's are allowed, rn_refs is a reference count maintained by
53  * rc_node_{hold,rele}().  See configd.h for locking order information.
54  *
55  * When a node (property group or snapshot) is updated, a new node takes the
56  * place of the old node in the global hash, and the old node is hung off of
57  * the rn_former list of the new node.  At the same time, all of its children
58  * have their rn_parent_ref pointer set, and any holds they have are reflected
59  * in the old node's rn_other_refs count.  This is automatically kept up
60  * to date, until the final reference to the subgraph is dropped, at which
61  * point the node is unrefed and destroyed, along with all of its children.
62  *
63  * Locking rules: To dereference an rc_node_t * (usually to lock it), you must
64  * have a hold (rc_node_hold()) on it or otherwise be sure that it hasn't been
65  * rc_node_destroy()ed (hold a lock on its parent or child, hold a flag,
66  * etc.).  Once you have locked an rc_node_t you must check its rn_flags for
67  * RC_NODE_DEAD before you can use it.  This is usually done with the
68  * rc_node_{wait,hold}_flag() functions (often via the rc_node_check_*()
69  * functions & RC_NODE_*() macros), which fail if the object has died.
70  *
71  * An ITER_START for a non-ENTITY_VALUE induces an rc_node_fill_children()
72  * call via rc_node_setup_iter() to populate the rn_children uu_list of the
73  * rc_node_t * in question and a call to uu_list_walk_start() on that list.  For
74  * ITER_READ, rc_iter_next() uses uu_list_walk_next() to find the next
75  * apropriate child.
76  *
77  * An ITER_START for an ENTITY_VALUE makes sure the node has its values
78  * filled, and sets up the iterator.  An ITER_READ_VALUE just copies out
79  * the proper values and updates the offset information.
80  *
81  * When a property group gets changed by a transaction, it sticks around as
82  * a child of its replacement property group, but is removed from the parent.
83  *
84  * To allow aliases, snapshots are implemented with a level of indirection.
85  * A snapshot rc_node_t has a snapid which refers to an rc_snapshot_t in
86  * snapshot.c which contains the authoritative snaplevel information.  The
87  * snapid is "assigned" by rc_attach_snapshot().
88  *
89  * We provide the client layer with rc_node_ptr_t's to reference objects.
90  * Objects referred to by them are automatically held & released by
91  * rc_node_assign() & rc_node_clear().  The RC_NODE_PTR_*() macros are used at
92  * client.c entry points to read the pointers.  They fetch the pointer to the
93  * object, return (from the function) if it is dead, and lock, hold, or hold
94  * a flag of the object.
95  */
96 
97 /*
98  * Permission checking is authorization-based: some operations may only
99  * proceed if the user has been assigned at least one of a set of
100  * authorization strings.  The set of enabling authorizations depends on the
101  * operation and the target object.  The set of authorizations assigned to
102  * a user is determined by reading /etc/security/policy.conf, querying the
103  * user_attr database, and possibly querying the prof_attr database, as per
104  * chkauthattr() in libsecdb.
105  *
106  * The fastest way to decide whether the two sets intersect is by entering the
107  * strings into a hash table and detecting collisions, which takes linear time
108  * in the total size of the sets.  Except for the authorization patterns which
109  * may be assigned to users, which without advanced pattern-matching
110  * algorithms will take O(n) in the number of enabling authorizations, per
111  * pattern.
112  *
113  * We can achieve some practical speed-ups by noting that if we enter all of
114  * the authorizations from one of the sets into the hash table we can merely
115  * check the elements of the second set for existence without adding them.
116  * This reduces memory requirements and hash table clutter.  The enabling set
117  * is well suited for this because it is internal to configd (for now, at
118  * least).  Combine this with short-circuiting and we can even minimize the
119  * number of queries to the security databases (user_attr & prof_attr).
120  *
121  * To force this usage onto clients we provide functions for adding
122  * authorizations to the enabling set of a permission context structure
123  * (perm_add_*()) and one to decide whether the the user associated with the
124  * current door call client possesses any of them (perm_granted()).
125  *
126  * At some point, a generic version of this should move to libsecdb.
127  */
128 
129 /*
130  * Composition is the combination of sets of properties.  The sets are ordered
131  * and properties in higher sets obscure properties of the same name in lower
132  * sets.  Here we present a composed view of an instance's properties as the
133  * union of its properties and its service's properties.  Similarly the
134  * properties of snaplevels are combined to form a composed view of the
135  * properties of a snapshot (which should match the composed view of the
136  * properties of the instance when the snapshot was taken).
137  *
138  * In terms of the client interface, the client may request that a property
139  * group iterator for an instance or snapshot be composed.  Property groups
140  * traversed by such an iterator may not have the target entity as a parent.
141  * Similarly, the properties traversed by a property iterator for those
142  * property groups may not have the property groups iterated as parents.
143  *
144  * Implementation requires that iterators for instances and snapshots be
145  * composition-savvy, and that we have a "composed property group" entity
146  * which represents the composition of a number of property groups.  Iteration
147  * over "composed property groups" yields properties which may have different
148  * parents, but for all other operations a composed property group behaves
149  * like the top-most property group it represents.
150  *
151  * The implementation is based on the rn_cchain[] array of rc_node_t pointers
152  * in rc_node_t.  For instances, the pointers point to the instance and its
153  * parent service.  For snapshots they point to the child snaplevels, and for
154  * composed property groups they point to property groups.  A composed
155  * iterator carries an index into rn_cchain[].  Thus most of the magic ends up
156  * int the rc_iter_*() code.
157  */
158 
159 #include <assert.h>
160 #include <atomic.h>
161 #include <errno.h>
162 #include <libuutil.h>
163 #include <libscf.h>
164 #include <libscf_priv.h>
165 #include <prof_attr.h>
166 #include <pthread.h>
167 #include <stdio.h>
168 #include <stdlib.h>
169 #include <strings.h>
170 #include <sys/types.h>
171 #include <unistd.h>
172 #include <user_attr.h>
173 
174 #include "configd.h"
175 
176 #define	AUTH_PREFIX		"solaris.smf."
177 #define	AUTH_MANAGE		AUTH_PREFIX "manage"
178 #define	AUTH_MODIFY		AUTH_PREFIX "modify"
179 #define	AUTH_MODIFY_PREFIX	AUTH_MODIFY "."
180 #define	AUTH_PG_ACTIONS		SCF_PG_RESTARTER_ACTIONS
181 #define	AUTH_PG_ACTIONS_TYPE	SCF_PG_RESTARTER_ACTIONS_TYPE
182 #define	AUTH_PG_GENERAL		SCF_PG_GENERAL
183 #define	AUTH_PG_GENERAL_TYPE	SCF_PG_GENERAL_TYPE
184 #define	AUTH_PG_GENERAL_OVR	SCF_PG_GENERAL_OVR
185 #define	AUTH_PG_GENERAL_OVR_TYPE  SCF_PG_GENERAL_OVR_TYPE
186 #define	AUTH_PROP_ACTION	"action_authorization"
187 #define	AUTH_PROP_ENABLED	"enabled"
188 #define	AUTH_PROP_MODIFY	"modify_authorization"
189 #define	AUTH_PROP_VALUE		"value_authorization"
190 /* libsecdb should take care of this. */
191 #define	RBAC_AUTH_SEP		","
192 
193 #define	MAX_VALID_CHILDREN 3
194 
195 typedef struct rc_type_info {
196 	uint32_t	rt_type;		/* matches array index */
197 	uint32_t	rt_num_ids;
198 	uint32_t	rt_name_flags;
199 	uint32_t	rt_valid_children[MAX_VALID_CHILDREN];
200 } rc_type_info_t;
201 
202 #define	RT_NO_NAME	-1U
203 
204 static rc_type_info_t rc_types[] = {
205 	{REP_PROTOCOL_ENTITY_NONE, 0, RT_NO_NAME},
206 	{REP_PROTOCOL_ENTITY_SCOPE, 0, 0,
207 	    {REP_PROTOCOL_ENTITY_SERVICE, REP_PROTOCOL_ENTITY_SCOPE}},
208 	{REP_PROTOCOL_ENTITY_SERVICE, 0, UU_NAME_DOMAIN | UU_NAME_PATH,
209 	    {REP_PROTOCOL_ENTITY_INSTANCE, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
210 	{REP_PROTOCOL_ENTITY_INSTANCE, 1, UU_NAME_DOMAIN,
211 	    {REP_PROTOCOL_ENTITY_SNAPSHOT, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
212 	{REP_PROTOCOL_ENTITY_SNAPSHOT, 2, UU_NAME_DOMAIN,
213 	    {REP_PROTOCOL_ENTITY_SNAPLEVEL, REP_PROTOCOL_ENTITY_PROPERTYGRP}},
214 	{REP_PROTOCOL_ENTITY_SNAPLEVEL, 4, RT_NO_NAME,
215 	    {REP_PROTOCOL_ENTITY_PROPERTYGRP}},
216 	{REP_PROTOCOL_ENTITY_PROPERTYGRP, 5, UU_NAME_DOMAIN,
217 	    {REP_PROTOCOL_ENTITY_PROPERTY}},
218 	{REP_PROTOCOL_ENTITY_CPROPERTYGRP, 0, UU_NAME_DOMAIN,
219 	    {REP_PROTOCOL_ENTITY_PROPERTY}},
220 	{REP_PROTOCOL_ENTITY_PROPERTY, 7, UU_NAME_DOMAIN},
221 	{-1UL}
222 };
223 #define	NUM_TYPES	((sizeof (rc_types) / sizeof (*rc_types)))
224 
225 /* Element of a permcheck_t hash table. */
226 struct pc_elt {
227 	struct pc_elt	*pce_next;
228 	char		pce_auth[1];
229 };
230 
231 /* An authorization set hash table. */
232 typedef struct {
233 	struct pc_elt	**pc_buckets;
234 	uint_t		pc_bnum;		/* number of buckets */
235 	uint_t		pc_enum;		/* number of elements */
236 } permcheck_t;
237 
238 static uu_list_pool_t *rc_children_pool;
239 static uu_list_pool_t *rc_pg_notify_pool;
240 static uu_list_pool_t *rc_notify_pool;
241 static uu_list_pool_t *rc_notify_info_pool;
242 
243 static rc_node_t *rc_scope;
244 
245 static pthread_mutex_t	rc_pg_notify_lock = PTHREAD_MUTEX_INITIALIZER;
246 static pthread_cond_t	rc_pg_notify_cv = PTHREAD_COND_INITIALIZER;
247 static uint_t		rc_notify_in_use;	/* blocks removals */
248 
249 static pthread_mutex_t	perm_lock = PTHREAD_MUTEX_INITIALIZER;
250 
251 static void rc_node_unrefed(rc_node_t *np);
252 
253 /*
254  * We support an arbitrary number of clients interested in events for certain
255  * types of changes.  Each client is represented by an rc_notify_info_t, and
256  * all clients are chained onto the rc_notify_info_list.
257  *
258  * The rc_notify_list is the global notification list.  Each entry is of
259  * type rc_notify_t, which is embedded in one of three other structures:
260  *
261  *	rc_node_t		property group update notification
262  *	rc_notify_delete_t	object deletion notification
263  *	rc_notify_info_t	notification clients
264  *
265  * Which type of object is determined by which pointer in the rc_notify_t is
266  * non-NULL.
267  *
268  * New notifications and clients are added to the end of the list.
269  * Notifications no-one is interested in are never added to the list.
270  *
271  * Clients use their position in the list to track which notifications they
272  * have not yet reported.  As they process notifications, they move forward
273  * in the list past them.  There is always a client at the beginning of the
274  * list -- as he moves past notifications, he removes them from the list and
275  * cleans them up.
276  *
277  * The rc_pg_notify_lock protects all notification state.  The rc_pg_notify_cv
278  * is used for global signalling, and each client has a cv which he waits for
279  * events of interest on.
280  */
281 static uu_list_t	*rc_notify_info_list;
282 static uu_list_t	*rc_notify_list;
283 
284 #define	HASH_SIZE	512
285 #define	HASH_MASK	(HASH_SIZE - 1)
286 
287 #pragma align 64(cache_hash)
288 static cache_bucket_t cache_hash[HASH_SIZE];
289 
290 #define	CACHE_BUCKET(h)		(&cache_hash[(h) & HASH_MASK])
291 
292 static uint32_t
293 rc_node_hash(rc_node_lookup_t *lp)
294 {
295 	uint32_t type = lp->rl_type;
296 	uint32_t backend = lp->rl_backend;
297 	uint32_t main = lp->rl_main_id;
298 	uint32_t *ids = lp->rl_ids;
299 
300 	rc_type_info_t *tp = &rc_types[type];
301 	uint32_t num_ids;
302 	uint32_t left;
303 	uint32_t hash;
304 
305 	assert(backend == BACKEND_TYPE_NORMAL ||
306 	    backend == BACKEND_TYPE_NONPERSIST);
307 
308 	assert(type > 0 && type < NUM_TYPES);
309 	num_ids = tp->rt_num_ids;
310 
311 	left = MAX_IDS - num_ids;
312 	assert(num_ids <= MAX_IDS);
313 
314 	hash = type * 7 + main * 5 + backend;
315 
316 	while (num_ids-- > 0)
317 		hash = hash * 11 + *ids++ * 7;
318 
319 	/*
320 	 * the rest should be zeroed
321 	 */
322 	while (left-- > 0)
323 		assert(*ids++ == 0);
324 
325 	return (hash);
326 }
327 
328 static int
329 rc_node_match(rc_node_t *np, rc_node_lookup_t *l)
330 {
331 	rc_node_lookup_t *r = &np->rn_id;
332 	rc_type_info_t *tp;
333 	uint32_t type;
334 	uint32_t num_ids;
335 
336 	if (r->rl_main_id != l->rl_main_id)
337 		return (0);
338 
339 	type = r->rl_type;
340 	if (type != l->rl_type)
341 		return (0);
342 
343 	assert(type > 0 && type < NUM_TYPES);
344 
345 	tp = &rc_types[r->rl_type];
346 	num_ids = tp->rt_num_ids;
347 
348 	assert(num_ids <= MAX_IDS);
349 	while (num_ids-- > 0)
350 		if (r->rl_ids[num_ids] != l->rl_ids[num_ids])
351 			return (0);
352 
353 	return (1);
354 }
355 
356 /*
357  * the "other" references on a node are maintained in an atomically
358  * updated refcount, rn_other_refs.  This can be bumped from arbitrary
359  * context, and tracks references to a possibly out-of-date node's children.
360  *
361  * To prevent the node from disappearing between the final drop of
362  * rn_other_refs and the unref handling, rn_other_refs_held is bumped on
363  * 0->1 transitions and decremented (with the node lock held) on 1->0
364  * transitions.
365  */
366 static void
367 rc_node_hold_other(rc_node_t *np)
368 {
369 	if (atomic_add_32_nv(&np->rn_other_refs, 1) == 1) {
370 		atomic_add_32(&np->rn_other_refs_held, 1);
371 		assert(np->rn_other_refs_held > 0);
372 	}
373 	assert(np->rn_other_refs > 0);
374 }
375 
376 /*
377  * No node locks may be held
378  */
379 static void
380 rc_node_rele_other(rc_node_t *np)
381 {
382 	assert(np->rn_other_refs > 0);
383 	if (atomic_add_32_nv(&np->rn_other_refs, -1) == 0) {
384 		(void) pthread_mutex_lock(&np->rn_lock);
385 		assert(np->rn_other_refs_held > 0);
386 		if (atomic_add_32_nv(&np->rn_other_refs_held, -1) == 0 &&
387 		    np->rn_refs == 0 && (np->rn_flags & RC_NODE_OLD))
388 			rc_node_unrefed(np);
389 		else
390 			(void) pthread_mutex_unlock(&np->rn_lock);
391 	}
392 }
393 
394 static void
395 rc_node_hold_locked(rc_node_t *np)
396 {
397 	assert(MUTEX_HELD(&np->rn_lock));
398 
399 	if (np->rn_refs == 0 && (np->rn_flags & RC_NODE_PARENT_REF))
400 		rc_node_hold_other(np->rn_parent_ref);
401 	np->rn_refs++;
402 	assert(np->rn_refs > 0);
403 }
404 
405 static void
406 rc_node_hold(rc_node_t *np)
407 {
408 	(void) pthread_mutex_lock(&np->rn_lock);
409 	rc_node_hold_locked(np);
410 	(void) pthread_mutex_unlock(&np->rn_lock);
411 }
412 
413 static void
414 rc_node_rele_locked(rc_node_t *np)
415 {
416 	int unref = 0;
417 	rc_node_t *par_ref = NULL;
418 
419 	assert(MUTEX_HELD(&np->rn_lock));
420 	assert(np->rn_refs > 0);
421 
422 	if (--np->rn_refs == 0) {
423 		if (np->rn_flags & RC_NODE_PARENT_REF)
424 			par_ref = np->rn_parent_ref;
425 
426 		if ((np->rn_flags & (RC_NODE_DEAD|RC_NODE_OLD)) &&
427 		    np->rn_other_refs == 0 && np->rn_other_refs_held == 0)
428 			unref = 1;
429 	}
430 
431 	if (unref)
432 		rc_node_unrefed(np);
433 	else
434 		(void) pthread_mutex_unlock(&np->rn_lock);
435 
436 	if (par_ref != NULL)
437 		rc_node_rele_other(par_ref);
438 }
439 
440 void
441 rc_node_rele(rc_node_t *np)
442 {
443 	(void) pthread_mutex_lock(&np->rn_lock);
444 	rc_node_rele_locked(np);
445 }
446 
447 static cache_bucket_t *
448 cache_hold(uint32_t h)
449 {
450 	cache_bucket_t *bp = CACHE_BUCKET(h);
451 	(void) pthread_mutex_lock(&bp->cb_lock);
452 	return (bp);
453 }
454 
455 static void
456 cache_release(cache_bucket_t *bp)
457 {
458 	(void) pthread_mutex_unlock(&bp->cb_lock);
459 }
460 
461 static rc_node_t *
462 cache_lookup_unlocked(cache_bucket_t *bp, rc_node_lookup_t *lp)
463 {
464 	uint32_t h = rc_node_hash(lp);
465 	rc_node_t *np;
466 
467 	assert(MUTEX_HELD(&bp->cb_lock));
468 	assert(bp == CACHE_BUCKET(h));
469 
470 	for (np = bp->cb_head; np != NULL; np = np->rn_hash_next) {
471 		if (np->rn_hash == h && rc_node_match(np, lp)) {
472 			rc_node_hold(np);
473 			return (np);
474 		}
475 	}
476 
477 	return (NULL);
478 }
479 
480 static rc_node_t *
481 cache_lookup(rc_node_lookup_t *lp)
482 {
483 	uint32_t h;
484 	cache_bucket_t *bp;
485 	rc_node_t *np;
486 
487 	h = rc_node_hash(lp);
488 	bp = cache_hold(h);
489 
490 	np = cache_lookup_unlocked(bp, lp);
491 
492 	cache_release(bp);
493 
494 	return (np);
495 }
496 
497 static void
498 cache_insert_unlocked(cache_bucket_t *bp, rc_node_t *np)
499 {
500 	assert(MUTEX_HELD(&bp->cb_lock));
501 	assert(np->rn_hash == rc_node_hash(&np->rn_id));
502 	assert(bp == CACHE_BUCKET(np->rn_hash));
503 
504 	assert(np->rn_hash_next == NULL);
505 
506 	np->rn_hash_next = bp->cb_head;
507 	bp->cb_head = np;
508 }
509 
510 static void
511 cache_remove_unlocked(cache_bucket_t *bp, rc_node_t *np)
512 {
513 	rc_node_t **npp;
514 
515 	assert(MUTEX_HELD(&bp->cb_lock));
516 	assert(np->rn_hash == rc_node_hash(&np->rn_id));
517 	assert(bp == CACHE_BUCKET(np->rn_hash));
518 
519 	for (npp = &bp->cb_head; *npp != NULL; npp = &(*npp)->rn_hash_next)
520 		if (*npp == np)
521 			break;
522 
523 	assert(*npp == np);
524 	*npp = np->rn_hash_next;
525 	np->rn_hash_next = NULL;
526 }
527 
528 /*
529  * verify that the 'parent' type can have a child typed 'child'
530  * Fails with
531  *   _INVALID_TYPE - argument is invalid
532  *   _TYPE_MISMATCH - parent type cannot have children of type child
533  */
534 static int
535 rc_check_parent_child(uint32_t parent, uint32_t child)
536 {
537 	int idx;
538 	uint32_t type;
539 
540 	if (parent == 0 || parent >= NUM_TYPES ||
541 	    child == 0 || child >= NUM_TYPES)
542 		return (REP_PROTOCOL_FAIL_INVALID_TYPE); /* invalid types */
543 
544 	for (idx = 0; idx < MAX_VALID_CHILDREN; idx++) {
545 		type = rc_types[parent].rt_valid_children[idx];
546 		if (type == child)
547 			return (REP_PROTOCOL_SUCCESS);
548 	}
549 
550 	return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
551 }
552 
553 /*
554  * Fails with
555  *   _INVALID_TYPE - type is invalid
556  *   _BAD_REQUEST - name is an invalid name for a node of type type
557  */
558 int
559 rc_check_type_name(uint32_t type, const char *name)
560 {
561 	if (type == 0 || type >= NUM_TYPES)
562 		return (REP_PROTOCOL_FAIL_INVALID_TYPE); /* invalid types */
563 
564 	if (uu_check_name(name, rc_types[type].rt_name_flags) == -1)
565 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
566 
567 	return (REP_PROTOCOL_SUCCESS);
568 }
569 
570 static int
571 rc_check_pgtype_name(const char *name)
572 {
573 	if (uu_check_name(name, UU_NAME_DOMAIN) == -1)
574 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
575 
576 	return (REP_PROTOCOL_SUCCESS);
577 }
578 
579 static int
580 rc_notify_info_interested(rc_notify_info_t *rnip, rc_notify_t *np)
581 {
582 	rc_node_t *nnp = np->rcn_node;
583 	int i;
584 
585 	assert(MUTEX_HELD(&rc_pg_notify_lock));
586 
587 	if (np->rcn_delete != NULL) {
588 		assert(np->rcn_info == NULL && np->rcn_node == NULL);
589 		return (1);		/* everyone likes deletes */
590 	}
591 	if (np->rcn_node == NULL) {
592 		assert(np->rcn_info != NULL || np->rcn_delete != NULL);
593 		return (0);
594 	}
595 	assert(np->rcn_info == NULL);
596 
597 	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
598 		if (rnip->rni_namelist[i] != NULL) {
599 			if (strcmp(nnp->rn_name, rnip->rni_namelist[i]) == 0)
600 				return (1);
601 		}
602 		if (rnip->rni_typelist[i] != NULL) {
603 			if (strcmp(nnp->rn_type, rnip->rni_typelist[i]) == 0)
604 				return (1);
605 		}
606 	}
607 	return (0);
608 }
609 
610 static void
611 rc_notify_insert_node(rc_node_t *nnp)
612 {
613 	rc_notify_t *np = &nnp->rn_notify;
614 	rc_notify_info_t *nip;
615 	int found = 0;
616 
617 	assert(np->rcn_info == NULL);
618 
619 	if (nnp->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
620 		return;
621 
622 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
623 	np->rcn_node = nnp;
624 	for (nip = uu_list_first(rc_notify_info_list); nip != NULL;
625 	    nip = uu_list_next(rc_notify_info_list, nip)) {
626 		if (rc_notify_info_interested(nip, np)) {
627 			(void) pthread_cond_broadcast(&nip->rni_cv);
628 			found++;
629 		}
630 	}
631 	if (found)
632 		(void) uu_list_insert_before(rc_notify_list, NULL, np);
633 	else
634 		np->rcn_node = NULL;
635 
636 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
637 }
638 
639 static void
640 rc_notify_deletion(rc_notify_delete_t *ndp, const char *service,
641     const char *instance, const char *pg)
642 {
643 	rc_notify_info_t *nip;
644 
645 	uu_list_node_init(&ndp->rnd_notify, &ndp->rnd_notify.rcn_list_node,
646 	    rc_notify_pool);
647 	ndp->rnd_notify.rcn_delete = ndp;
648 
649 	(void) snprintf(ndp->rnd_fmri, sizeof (ndp->rnd_fmri),
650 	    "svc:/%s%s%s%s%s", service,
651 	    (instance != NULL)? ":" : "", (instance != NULL)? instance : "",
652 	    (pg != NULL)? "/:properties/" : "", (pg != NULL)? pg : "");
653 
654 	/*
655 	 * add to notification list, notify watchers
656 	 */
657 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
658 	for (nip = uu_list_first(rc_notify_info_list); nip != NULL;
659 	    nip = uu_list_next(rc_notify_info_list, nip))
660 		(void) pthread_cond_broadcast(&nip->rni_cv);
661 	(void) uu_list_insert_before(rc_notify_list, NULL, ndp);
662 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
663 }
664 
665 static void
666 rc_notify_remove_node(rc_node_t *nnp)
667 {
668 	rc_notify_t *np = &nnp->rn_notify;
669 
670 	assert(np->rcn_info == NULL);
671 	assert(!MUTEX_HELD(&nnp->rn_lock));
672 
673 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
674 	while (np->rcn_node != NULL) {
675 		if (rc_notify_in_use) {
676 			(void) pthread_cond_wait(&rc_pg_notify_cv,
677 			    &rc_pg_notify_lock);
678 			continue;
679 		}
680 		(void) uu_list_remove(rc_notify_list, np);
681 		np->rcn_node = NULL;
682 		break;
683 	}
684 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
685 }
686 
687 static void
688 rc_notify_remove_locked(rc_notify_t *np)
689 {
690 	assert(MUTEX_HELD(&rc_pg_notify_lock));
691 	assert(rc_notify_in_use == 0);
692 
693 	(void) uu_list_remove(rc_notify_list, np);
694 	if (np->rcn_node) {
695 		np->rcn_node = NULL;
696 	} else if (np->rcn_delete) {
697 		uu_free(np->rcn_delete);
698 	} else {
699 		assert(0);	/* CAN'T HAPPEN */
700 	}
701 }
702 
703 /*
704  * Permission checking functions.  See comment atop this file.
705  */
706 #ifndef NATIVE_BUILD
707 static permcheck_t *
708 pc_create()
709 {
710 	permcheck_t *p;
711 
712 	p = uu_zalloc(sizeof (*p));
713 	if (p == NULL)
714 		return (NULL);
715 	p->pc_bnum = 8;			/* Normal case will only have 2 elts. */
716 	p->pc_buckets = uu_zalloc(sizeof (*p->pc_buckets) * p->pc_bnum);
717 	if (p->pc_buckets == NULL) {
718 		uu_free(p);
719 		return (NULL);
720 	}
721 
722 	p->pc_enum = 0;
723 	return (p);
724 }
725 
726 static void
727 pc_free(permcheck_t *pcp)
728 {
729 	uint_t i;
730 	struct pc_elt *ep, *next;
731 
732 	for (i = 0; i < pcp->pc_bnum; ++i) {
733 		for (ep = pcp->pc_buckets[i]; ep != NULL; ep = next) {
734 			next = ep->pce_next;
735 			free(ep);
736 		}
737 	}
738 
739 	free(pcp->pc_buckets);
740 	free(pcp);
741 }
742 
743 static uint32_t
744 pc_hash(const char *auth)
745 {
746 	uint32_t h = 0, g;
747 	const char *p;
748 
749 	/*
750 	 * Generic hash function from uts/common/os/modhash.c.
751 	 */
752 	for (p = auth; *p != '\0'; ++p) {
753 		h = (h << 4) + *p;
754 		g = (h & 0xf0000000);
755 		if (g != 0) {
756 			h ^= (g >> 24);
757 			h ^= g;
758 		}
759 	}
760 
761 	return (h);
762 }
763 
764 static int
765 pc_exists(const permcheck_t *pcp, const char *auth)
766 {
767 	uint32_t h;
768 	struct pc_elt *ep;
769 
770 	h = pc_hash(auth);
771 	for (ep = pcp->pc_buckets[h & (pcp->pc_bnum - 1)];
772 	    ep != NULL;
773 	    ep = ep->pce_next) {
774 		if (strcmp(auth, ep->pce_auth) == 0)
775 			return (1);
776 	}
777 
778 	return (0);
779 }
780 
781 static int
782 pc_match(const permcheck_t *pcp, const char *pattern)
783 {
784 	uint_t i;
785 	struct pc_elt *ep;
786 
787 	for (i = 0; i < pcp->pc_bnum; ++i) {
788 		for (ep = pcp->pc_buckets[i]; ep != NULL; ep = ep->pce_next) {
789 			if (_auth_match(pattern, ep->pce_auth))
790 				return (1);
791 		}
792 	}
793 
794 	return (0);
795 }
796 
797 static int
798 pc_grow(permcheck_t *pcp)
799 {
800 	uint_t new_bnum, i, j;
801 	struct pc_elt **new_buckets;
802 	struct pc_elt *ep, *next;
803 
804 	new_bnum = pcp->pc_bnum * 2;
805 	if (new_bnum < pcp->pc_bnum)
806 		/* Homey don't play that. */
807 		return (-1);
808 
809 	new_buckets = uu_zalloc(sizeof (*new_buckets) * new_bnum);
810 	if (new_buckets == NULL)
811 		return (-1);
812 
813 	for (i = 0; i < pcp->pc_bnum; ++i) {
814 		for (ep = pcp->pc_buckets[i]; ep != NULL; ep = next) {
815 			next = ep->pce_next;
816 			j = pc_hash(ep->pce_auth) & (new_bnum - 1);
817 			ep->pce_next = new_buckets[j];
818 			new_buckets[j] = ep;
819 		}
820 	}
821 
822 	uu_free(pcp->pc_buckets);
823 	pcp->pc_buckets = new_buckets;
824 	pcp->pc_bnum = new_bnum;
825 
826 	return (0);
827 }
828 
829 static int
830 pc_add(permcheck_t *pcp, const char *auth)
831 {
832 	struct pc_elt *ep;
833 	uint_t i;
834 
835 	ep = uu_zalloc(offsetof(struct pc_elt, pce_auth) + strlen(auth) + 1);
836 	if (ep == NULL)
837 		return (-1);
838 
839 	/* Grow if pc_enum / pc_bnum > 3/4. */
840 	if (pcp->pc_enum * 4 > 3 * pcp->pc_bnum)
841 		/* Failure is not a stopper; we'll try again next time. */
842 		(void) pc_grow(pcp);
843 
844 	(void) strcpy(ep->pce_auth, auth);
845 
846 	i = pc_hash(auth) & (pcp->pc_bnum - 1);
847 	ep->pce_next = pcp->pc_buckets[i];
848 	pcp->pc_buckets[i] = ep;
849 
850 	++pcp->pc_enum;
851 
852 	return (0);
853 }
854 
855 /*
856  * For the type of a property group, return the authorization which may be
857  * used to modify it.
858  */
859 static const char *
860 perm_auth_for_pgtype(const char *pgtype)
861 {
862 	if (strcmp(pgtype, SCF_GROUP_METHOD) == 0)
863 		return (AUTH_MODIFY_PREFIX "method");
864 	else if (strcmp(pgtype, SCF_GROUP_DEPENDENCY) == 0)
865 		return (AUTH_MODIFY_PREFIX "dependency");
866 	else if (strcmp(pgtype, SCF_GROUP_APPLICATION) == 0)
867 		return (AUTH_MODIFY_PREFIX "application");
868 	else if (strcmp(pgtype, SCF_GROUP_FRAMEWORK) == 0)
869 		return (AUTH_MODIFY_PREFIX "framework");
870 	else
871 		return (NULL);
872 }
873 
874 /*
875  * Fails with
876  *   _NO_RESOURCES - out of memory
877  */
878 static int
879 perm_add_enabling(permcheck_t *pcp, const char *auth)
880 {
881 	return (pc_add(pcp, auth) == 0 ? REP_PROTOCOL_SUCCESS :
882 	    REP_PROTOCOL_FAIL_NO_RESOURCES);
883 }
884 
885 /* Note that perm_add_enabling_values() is defined below. */
886 
887 /*
888  * perm_granted() returns 1 if the current door caller has one of the enabling
889  * authorizations in pcp, 0 if it doesn't, and -1 if an error (usually lack of
890  * memory) occurs.  check_auth_list() checks an RBAC_AUTH_SEP-separated list
891  * of authorizations for existance in pcp, and check_prof_list() checks the
892  * authorizations granted to an RBAC_AUTH_SEP-separated list of profiles.
893  */
894 static int
895 check_auth_list(const permcheck_t *pcp, char *authlist)
896 {
897 	char *auth, *lasts;
898 	int ret;
899 
900 	for (auth = (char *)strtok_r(authlist, RBAC_AUTH_SEP, &lasts);
901 	    auth != NULL;
902 	    auth = (char *)strtok_r(NULL, RBAC_AUTH_SEP, &lasts)) {
903 		if (strchr(auth, KV_WILDCHAR) == NULL)
904 			ret = pc_exists(pcp, auth);
905 		else
906 			ret = pc_match(pcp, auth);
907 
908 		if (ret)
909 			return (ret);
910 	}
911 
912 	return (0);
913 }
914 
915 static int
916 check_prof_list(const permcheck_t *pcp, char *proflist)
917 {
918 	char *prof, *lasts, *authlist, *subproflist;
919 	profattr_t *pap;
920 	int ret = 0;
921 
922 	for (prof = strtok_r(proflist, RBAC_AUTH_SEP, &lasts);
923 	    prof != NULL;
924 	    prof = strtok_r(NULL, RBAC_AUTH_SEP, &lasts)) {
925 		pap = getprofnam(prof);
926 		if (pap == NULL)
927 			continue;
928 
929 		authlist = kva_match(pap->attr, PROFATTR_AUTHS_KW);
930 		if (authlist != NULL)
931 			ret = check_auth_list(pcp, authlist);
932 
933 		if (!ret) {
934 			subproflist = kva_match(pap->attr, PROFATTR_PROFS_KW);
935 			if (subproflist != NULL)
936 				/* depth check to avoid invinite recursion? */
937 				ret = check_prof_list(pcp, subproflist);
938 		}
939 
940 		free_profattr(pap);
941 		if (ret)
942 			return (ret);
943 	}
944 
945 	return (ret);
946 }
947 
948 static int
949 perm_granted(const permcheck_t *pcp)
950 {
951 	ucred_t *uc;
952 
953 	int ret = 0;
954 	uid_t uid;
955 	userattr_t *uap;
956 	char *authlist, *proflist, *def_prof = NULL;
957 
958 	/*
959 	 * Get generic authorizations from policy.conf
960 	 *
961 	 * Note that _get_auth_policy is not threadsafe, so we single-thread
962 	 * access to it.
963 	 */
964 	(void) pthread_mutex_lock(&perm_lock);
965 	ret = _get_auth_policy(&authlist, &def_prof);
966 	(void) pthread_mutex_unlock(&perm_lock);
967 
968 	if (ret != 0)
969 		return (-1);
970 
971 	if (authlist != NULL) {
972 		ret = check_auth_list(pcp, authlist);
973 		free(authlist);
974 
975 		if (ret) {
976 			free(def_prof);
977 			return (ret);
978 		}
979 	}
980 
981 	/*
982 	 * Put off checking def_prof for later in an attempt to consolidate
983 	 * prof_attr accesses.
984 	 */
985 
986 	/* Get the uid */
987 	if ((uc = get_ucred()) == NULL) {
988 		free(def_prof);
989 
990 		if (errno == EINVAL) {
991 			/*
992 			 * Client is no longer waiting for our response (e.g.,
993 			 * it received a signal & resumed with EINTR).
994 			 * Punting with door_return() would be nice but we
995 			 * need to release all of the locks & references we
996 			 * hold.  And we must report failure to the client
997 			 * layer to keep it from ignoring retries as
998 			 * already-done (idempotency & all that).  None of the
999 			 * error codes fit very well, so we might as well
1000 			 * force the return of _PERMISSION_DENIED since we
1001 			 * couldn't determine the user.
1002 			 */
1003 			return (0);
1004 		}
1005 		assert(0);
1006 		abort();
1007 	}
1008 
1009 	uid = ucred_geteuid(uc);
1010 	assert(uid != -1);
1011 
1012 	uap = getuseruid(uid);
1013 	if (uap != NULL) {
1014 		/* Get the authorizations from user_attr. */
1015 		authlist = kva_match(uap->attr, USERATTR_AUTHS_KW);
1016 		if (authlist != NULL)
1017 			ret = check_auth_list(pcp, authlist);
1018 	}
1019 
1020 	if (!ret && def_prof != NULL) {
1021 		/* Check generic profiles. */
1022 		ret = check_prof_list(pcp, def_prof);
1023 	}
1024 
1025 	if (!ret && uap != NULL) {
1026 		proflist = kva_match(uap->attr, USERATTR_PROFILES_KW);
1027 		if (proflist != NULL)
1028 			ret = check_prof_list(pcp, proflist);
1029 	}
1030 
1031 	if (def_prof != NULL)
1032 		free(def_prof);
1033 	if (uap != NULL)
1034 		free_userattr(uap);
1035 
1036 	return (ret);
1037 }
1038 #endif /* NATIVE_BUILD */
1039 
1040 /*
1041  * flags in RC_NODE_WAITING_FLAGS are broadcast when unset, and are used to
1042  * serialize certain actions, and to wait for certain operations to complete
1043  *
1044  * The waiting flags are:
1045  *	RC_NODE_CHILDREN_CHANGING
1046  *		The child list is being built or changed (due to creation
1047  *		or deletion).  All iterators pause.
1048  *
1049  *	RC_NODE_USING_PARENT
1050  *		Someone is actively using the parent pointer, so we can't
1051  *		be removed from the parent list.
1052  *
1053  *	RC_NODE_CREATING_CHILD
1054  *		A child is being created -- locks out other creations, to
1055  *		prevent insert-insert races.
1056  *
1057  *	RC_NODE_IN_TX
1058  *		This object is running a transaction.
1059  *
1060  *	RC_NODE_DYING
1061  *		This node might be dying.  Always set as a set, using
1062  *		RC_NODE_DYING_FLAGS (which is everything but
1063  *		RC_NODE_USING_PARENT)
1064  */
1065 static int
1066 rc_node_hold_flag(rc_node_t *np, uint32_t flag)
1067 {
1068 	assert(MUTEX_HELD(&np->rn_lock));
1069 	assert((flag & ~RC_NODE_WAITING_FLAGS) == 0);
1070 
1071 	while (!(np->rn_flags & RC_NODE_DEAD) && (np->rn_flags & flag)) {
1072 		(void) pthread_cond_wait(&np->rn_cv, &np->rn_lock);
1073 	}
1074 	if (np->rn_flags & RC_NODE_DEAD)
1075 		return (0);
1076 
1077 	np->rn_flags |= flag;
1078 	return (1);
1079 }
1080 
1081 static void
1082 rc_node_rele_flag(rc_node_t *np, uint32_t flag)
1083 {
1084 	assert((flag & ~RC_NODE_WAITING_FLAGS) == 0);
1085 	assert(MUTEX_HELD(&np->rn_lock));
1086 	assert((np->rn_flags & flag) == flag);
1087 	np->rn_flags &= ~flag;
1088 	(void) pthread_cond_broadcast(&np->rn_cv);
1089 }
1090 
1091 /*
1092  * wait until a particular flag has cleared.  Fails if the object dies.
1093  */
1094 static int
1095 rc_node_wait_flag(rc_node_t *np, uint32_t flag)
1096 {
1097 	assert(MUTEX_HELD(&np->rn_lock));
1098 	while (!(np->rn_flags & RC_NODE_DEAD) && (np->rn_flags & flag))
1099 		(void) pthread_cond_wait(&np->rn_cv, &np->rn_lock);
1100 
1101 	return (!(np->rn_flags & RC_NODE_DEAD));
1102 }
1103 
1104 /*
1105  * On entry, np's lock must be held, and this thread must be holding
1106  * RC_NODE_USING_PARENT.  On return, both of them are released.
1107  *
1108  * If the return value is NULL, np either does not have a parent, or
1109  * the parent has been marked DEAD.
1110  *
1111  * If the return value is non-NULL, it is the parent of np, and both
1112  * its lock and the requested flags are held.
1113  */
1114 static rc_node_t *
1115 rc_node_hold_parent_flag(rc_node_t *np, uint32_t flag)
1116 {
1117 	rc_node_t *pp;
1118 
1119 	assert(MUTEX_HELD(&np->rn_lock));
1120 	assert(np->rn_flags & RC_NODE_USING_PARENT);
1121 
1122 	if ((pp = np->rn_parent) == NULL) {
1123 		rc_node_rele_flag(np, RC_NODE_USING_PARENT);
1124 		(void) pthread_mutex_unlock(&np->rn_lock);
1125 		return (NULL);
1126 	}
1127 	(void) pthread_mutex_unlock(&np->rn_lock);
1128 
1129 	(void) pthread_mutex_lock(&pp->rn_lock);
1130 	(void) pthread_mutex_lock(&np->rn_lock);
1131 	rc_node_rele_flag(np, RC_NODE_USING_PARENT);
1132 	(void) pthread_mutex_unlock(&np->rn_lock);
1133 
1134 	if (!rc_node_hold_flag(pp, flag)) {
1135 		(void) pthread_mutex_unlock(&pp->rn_lock);
1136 		return (NULL);
1137 	}
1138 	return (pp);
1139 }
1140 
1141 rc_node_t *
1142 rc_node_alloc(void)
1143 {
1144 	rc_node_t *np = uu_zalloc(sizeof (*np));
1145 
1146 	if (np == NULL)
1147 		return (NULL);
1148 
1149 	(void) pthread_mutex_init(&np->rn_lock, NULL);
1150 	(void) pthread_cond_init(&np->rn_cv, NULL);
1151 
1152 	np->rn_children = uu_list_create(rc_children_pool, np, 0);
1153 	np->rn_pg_notify_list = uu_list_create(rc_pg_notify_pool, np, 0);
1154 
1155 	uu_list_node_init(np, &np->rn_sibling_node, rc_children_pool);
1156 
1157 	uu_list_node_init(&np->rn_notify, &np->rn_notify.rcn_list_node,
1158 	    rc_notify_pool);
1159 
1160 	return (np);
1161 }
1162 
1163 void
1164 rc_node_destroy(rc_node_t *np)
1165 {
1166 	int i;
1167 
1168 	if (np->rn_flags & RC_NODE_UNREFED)
1169 		return;				/* being handled elsewhere */
1170 
1171 	assert(np->rn_refs == 0 && np->rn_other_refs == 0);
1172 	assert(np->rn_former == NULL);
1173 
1174 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
1175 		/* Release the holds from rc_iter_next(). */
1176 		for (i = 0; i < COMPOSITION_DEPTH; ++i) {
1177 			/* rn_cchain[i] may be NULL for empty snapshots. */
1178 			if (np->rn_cchain[i] != NULL)
1179 				rc_node_rele(np->rn_cchain[i]);
1180 		}
1181 	}
1182 
1183 	if (np->rn_name != NULL)
1184 		free((void *)np->rn_name);
1185 	np->rn_name = NULL;
1186 	if (np->rn_type != NULL)
1187 		free((void *)np->rn_type);
1188 	np->rn_type = NULL;
1189 	if (np->rn_values != NULL)
1190 		object_free_values(np->rn_values, np->rn_valtype,
1191 		    np->rn_values_count, np->rn_values_size);
1192 	np->rn_values = NULL;
1193 
1194 	if (np->rn_snaplevel != NULL)
1195 		rc_snaplevel_rele(np->rn_snaplevel);
1196 	np->rn_snaplevel = NULL;
1197 
1198 	uu_list_node_fini(np, &np->rn_sibling_node, rc_children_pool);
1199 
1200 	uu_list_node_fini(&np->rn_notify, &np->rn_notify.rcn_list_node,
1201 	    rc_notify_pool);
1202 
1203 	assert(uu_list_first(np->rn_children) == NULL);
1204 	uu_list_destroy(np->rn_children);
1205 	uu_list_destroy(np->rn_pg_notify_list);
1206 
1207 	(void) pthread_mutex_destroy(&np->rn_lock);
1208 	(void) pthread_cond_destroy(&np->rn_cv);
1209 
1210 	uu_free(np);
1211 }
1212 
1213 /*
1214  * Link in a child node.
1215  *
1216  * Because of the lock ordering, cp has to already be in the hash table with
1217  * its lock dropped before we get it.  To prevent anyone from noticing that
1218  * it is parentless, the creation code sets the RC_NODE_USING_PARENT.  Once
1219  * we've linked it in, we release the flag.
1220  */
1221 static void
1222 rc_node_link_child(rc_node_t *np, rc_node_t *cp)
1223 {
1224 	assert(!MUTEX_HELD(&np->rn_lock));
1225 	assert(!MUTEX_HELD(&cp->rn_lock));
1226 
1227 	(void) pthread_mutex_lock(&np->rn_lock);
1228 	(void) pthread_mutex_lock(&cp->rn_lock);
1229 	assert(!(cp->rn_flags & RC_NODE_IN_PARENT) &&
1230 	    (cp->rn_flags & RC_NODE_USING_PARENT));
1231 
1232 	assert(rc_check_parent_child(np->rn_id.rl_type, cp->rn_id.rl_type) ==
1233 	    REP_PROTOCOL_SUCCESS);
1234 
1235 	cp->rn_parent = np;
1236 	cp->rn_flags |= RC_NODE_IN_PARENT;
1237 	(void) uu_list_insert_before(np->rn_children, NULL, cp);
1238 
1239 	(void) pthread_mutex_unlock(&np->rn_lock);
1240 
1241 	rc_node_rele_flag(cp, RC_NODE_USING_PARENT);
1242 	(void) pthread_mutex_unlock(&cp->rn_lock);
1243 }
1244 
1245 /*
1246  * Sets the rn_parent_ref field of all the children of np to pp -- always
1247  * initially invoked as rc_node_setup_parent_ref(np, np), we then recurse.
1248  *
1249  * This is used when we mark a node RC_NODE_OLD, so that when the object and
1250  * its children are no longer referenced, they will all be deleted as a unit.
1251  */
1252 static void
1253 rc_node_setup_parent_ref(rc_node_t *np, rc_node_t *pp)
1254 {
1255 	rc_node_t *cp;
1256 
1257 	assert(MUTEX_HELD(&np->rn_lock));
1258 
1259 	for (cp = uu_list_first(np->rn_children); cp != NULL;
1260 	    cp = uu_list_next(np->rn_children, cp)) {
1261 		(void) pthread_mutex_lock(&cp->rn_lock);
1262 		if (cp->rn_flags & RC_NODE_PARENT_REF) {
1263 			assert(cp->rn_parent_ref == pp);
1264 		} else {
1265 			assert(cp->rn_parent_ref == NULL);
1266 
1267 			cp->rn_flags |= RC_NODE_PARENT_REF;
1268 			cp->rn_parent_ref = pp;
1269 			if (cp->rn_refs != 0)
1270 				rc_node_hold_other(pp);
1271 		}
1272 		rc_node_setup_parent_ref(cp, pp);		/* recurse */
1273 		(void) pthread_mutex_unlock(&cp->rn_lock);
1274 	}
1275 }
1276 
1277 /*
1278  * Atomically replace 'np' with 'newp', with a parent of 'pp'.
1279  *
1280  * Requirements:
1281  *	*no* node locks may be held.
1282  *	pp must be held with RC_NODE_CHILDREN_CHANGING
1283  *	newp and np must be held with RC_NODE_IN_TX
1284  *	np must be marked RC_NODE_IN_PARENT, newp must not be
1285  *	np must be marked RC_NODE_OLD
1286  *
1287  * Afterwards:
1288  *	pp's RC_NODE_CHILDREN_CHANGING is dropped
1289  *	newp and np's RC_NODE_IN_TX is dropped
1290  *	newp->rn_former = np;
1291  *	newp is RC_NODE_IN_PARENT, np is not.
1292  *	interested notify subscribers have been notified of newp's new status.
1293  */
1294 static void
1295 rc_node_relink_child(rc_node_t *pp, rc_node_t *np, rc_node_t *newp)
1296 {
1297 	cache_bucket_t *bp;
1298 	/*
1299 	 * First, swap np and nnp in the cache.  newp's RC_NODE_IN_TX flag
1300 	 * keeps rc_node_update() from seeing it until we are done.
1301 	 */
1302 	bp = cache_hold(newp->rn_hash);
1303 	cache_remove_unlocked(bp, np);
1304 	cache_insert_unlocked(bp, newp);
1305 	cache_release(bp);
1306 
1307 	/*
1308 	 * replace np with newp in pp's list, and attach it to newp's rn_former
1309 	 * link.
1310 	 */
1311 	(void) pthread_mutex_lock(&pp->rn_lock);
1312 	assert(pp->rn_flags & RC_NODE_CHILDREN_CHANGING);
1313 
1314 	(void) pthread_mutex_lock(&newp->rn_lock);
1315 	assert(!(newp->rn_flags & RC_NODE_IN_PARENT));
1316 	assert(newp->rn_flags & RC_NODE_IN_TX);
1317 
1318 	(void) pthread_mutex_lock(&np->rn_lock);
1319 	assert(np->rn_flags & RC_NODE_IN_PARENT);
1320 	assert(np->rn_flags & RC_NODE_OLD);
1321 	assert(np->rn_flags & RC_NODE_IN_TX);
1322 
1323 	newp->rn_parent = pp;
1324 	newp->rn_flags |= RC_NODE_IN_PARENT;
1325 
1326 	/*
1327 	 * Note that we carefully add newp before removing np -- this
1328 	 * keeps iterators on the list from missing us.
1329 	 */
1330 	(void) uu_list_insert_after(pp->rn_children, np, newp);
1331 	(void) uu_list_remove(pp->rn_children, np);
1332 
1333 	/*
1334 	 * re-set np
1335 	 */
1336 	newp->rn_former = np;
1337 	np->rn_parent = NULL;
1338 	np->rn_flags &= ~RC_NODE_IN_PARENT;
1339 	np->rn_flags |= RC_NODE_ON_FORMER;
1340 
1341 	rc_notify_insert_node(newp);
1342 
1343 	rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
1344 	(void) pthread_mutex_unlock(&pp->rn_lock);
1345 	rc_node_rele_flag(newp, RC_NODE_USING_PARENT | RC_NODE_IN_TX);
1346 	(void) pthread_mutex_unlock(&newp->rn_lock);
1347 	rc_node_setup_parent_ref(np, np);
1348 	rc_node_rele_flag(np, RC_NODE_IN_TX);
1349 	(void) pthread_mutex_unlock(&np->rn_lock);
1350 }
1351 
1352 /*
1353  * makes sure a node with lookup 'nip', name 'name', and parent 'pp' exists.
1354  * 'cp' is used (and returned) if the node does not yet exist.  If it does
1355  * exist, 'cp' is freed, and the existent node is returned instead.
1356  */
1357 rc_node_t *
1358 rc_node_setup(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
1359     rc_node_t *pp)
1360 {
1361 	rc_node_t *np;
1362 	cache_bucket_t *bp;
1363 	uint32_t h = rc_node_hash(nip);
1364 
1365 	assert(cp->rn_refs == 0);
1366 
1367 	bp = cache_hold(h);
1368 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
1369 		cache_release(bp);
1370 
1371 		/*
1372 		 * make sure it matches our expectations
1373 		 */
1374 		assert(np->rn_parent == pp);
1375 		assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
1376 		assert(strcmp(np->rn_name, name) == 0);
1377 		assert(np->rn_type == NULL);
1378 		assert(np->rn_flags & RC_NODE_IN_PARENT);
1379 
1380 		rc_node_destroy(cp);
1381 		return (np);
1382 	}
1383 
1384 	/*
1385 	 * No one is there -- create a new node.
1386 	 */
1387 	np = cp;
1388 	rc_node_hold(np);
1389 	np->rn_id = *nip;
1390 	np->rn_hash = h;
1391 	np->rn_name = strdup(name);
1392 
1393 	np->rn_flags |= RC_NODE_USING_PARENT;
1394 
1395 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE) {
1396 #if COMPOSITION_DEPTH == 2
1397 		np->rn_cchain[0] = np;
1398 		np->rn_cchain[1] = pp;
1399 #else
1400 #error This code must be updated.
1401 #endif
1402 	}
1403 
1404 	cache_insert_unlocked(bp, np);
1405 	cache_release(bp);		/* we are now visible */
1406 
1407 	rc_node_link_child(pp, np);
1408 
1409 	return (np);
1410 }
1411 
1412 /*
1413  * makes sure a snapshot with lookup 'nip', name 'name', and parent 'pp' exists.
1414  * 'cp' is used (and returned) if the node does not yet exist.  If it does
1415  * exist, 'cp' is freed, and the existent node is returned instead.
1416  */
1417 rc_node_t *
1418 rc_node_setup_snapshot(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
1419     uint32_t snap_id, rc_node_t *pp)
1420 {
1421 	rc_node_t *np;
1422 	cache_bucket_t *bp;
1423 	uint32_t h = rc_node_hash(nip);
1424 
1425 	assert(cp->rn_refs == 0);
1426 
1427 	bp = cache_hold(h);
1428 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
1429 		cache_release(bp);
1430 
1431 		/*
1432 		 * make sure it matches our expectations
1433 		 */
1434 		assert(np->rn_parent == pp);
1435 		assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
1436 		assert(strcmp(np->rn_name, name) == 0);
1437 		assert(np->rn_type == NULL);
1438 		assert(np->rn_flags & RC_NODE_IN_PARENT);
1439 
1440 		rc_node_destroy(cp);
1441 		return (np);
1442 	}
1443 
1444 	/*
1445 	 * No one is there -- create a new node.
1446 	 */
1447 	np = cp;
1448 	rc_node_hold(np);
1449 	np->rn_id = *nip;
1450 	np->rn_hash = h;
1451 	np->rn_name = strdup(name);
1452 	np->rn_snapshot_id = snap_id;
1453 
1454 	np->rn_flags |= RC_NODE_USING_PARENT;
1455 
1456 	cache_insert_unlocked(bp, np);
1457 	cache_release(bp);		/* we are now visible */
1458 
1459 	rc_node_link_child(pp, np);
1460 
1461 	return (np);
1462 }
1463 
1464 /*
1465  * makes sure a snaplevel with lookup 'nip' and parent 'pp' exists.  'cp' is
1466  * used (and returned) if the node does not yet exist.  If it does exist, 'cp'
1467  * is freed, and the existent node is returned instead.
1468  */
1469 rc_node_t *
1470 rc_node_setup_snaplevel(rc_node_t *cp, rc_node_lookup_t *nip,
1471     rc_snaplevel_t *lvl, rc_node_t *pp)
1472 {
1473 	rc_node_t *np;
1474 	cache_bucket_t *bp;
1475 	uint32_t h = rc_node_hash(nip);
1476 
1477 	assert(cp->rn_refs == 0);
1478 
1479 	bp = cache_hold(h);
1480 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
1481 		cache_release(bp);
1482 
1483 		/*
1484 		 * make sure it matches our expectations
1485 		 */
1486 		assert(np->rn_parent == pp);
1487 		assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
1488 		assert(np->rn_name == NULL);
1489 		assert(np->rn_type == NULL);
1490 		assert(np->rn_flags & RC_NODE_IN_PARENT);
1491 
1492 		rc_node_destroy(cp);
1493 		return (np);
1494 	}
1495 
1496 	/*
1497 	 * No one is there -- create a new node.
1498 	 */
1499 	np = cp;
1500 	rc_node_hold(np);	/* released in snapshot_fill_children() */
1501 	np->rn_id = *nip;
1502 	np->rn_hash = h;
1503 
1504 	rc_snaplevel_hold(lvl);
1505 	np->rn_snaplevel = lvl;
1506 
1507 	np->rn_flags |= RC_NODE_USING_PARENT;
1508 
1509 	cache_insert_unlocked(bp, np);
1510 	cache_release(bp);		/* we are now visible */
1511 
1512 	/* Add this snaplevel to the snapshot's composition chain. */
1513 	assert(pp->rn_cchain[lvl->rsl_level_num - 1] == NULL);
1514 	pp->rn_cchain[lvl->rsl_level_num - 1] = np;
1515 
1516 	rc_node_link_child(pp, np);
1517 
1518 	return (np);
1519 }
1520 
1521 /*
1522  * Returns NULL if strdup() fails.
1523  */
1524 rc_node_t *
1525 rc_node_setup_pg(rc_node_t *cp, rc_node_lookup_t *nip, const char *name,
1526     const char *type, uint32_t flags, uint32_t gen_id, rc_node_t *pp)
1527 {
1528 	rc_node_t *np;
1529 	cache_bucket_t *bp;
1530 
1531 	uint32_t h = rc_node_hash(nip);
1532 	bp = cache_hold(h);
1533 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
1534 		cache_release(bp);
1535 
1536 		/*
1537 		 * make sure it matches our expectations (don't check
1538 		 * the generation number or parent, since someone could
1539 		 * have gotten a transaction through while we weren't
1540 		 * looking)
1541 		 */
1542 		assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
1543 		assert(strcmp(np->rn_name, name) == 0);
1544 		assert(strcmp(np->rn_type, type) == 0);
1545 		assert(np->rn_pgflags == flags);
1546 		assert(np->rn_flags & RC_NODE_IN_PARENT);
1547 
1548 		rc_node_destroy(cp);
1549 		return (np);
1550 	}
1551 
1552 	np = cp;
1553 	rc_node_hold(np);		/* released in fill_pg_callback() */
1554 	np->rn_id = *nip;
1555 	np->rn_hash = h;
1556 	np->rn_name = strdup(name);
1557 	if (np->rn_name == NULL) {
1558 		rc_node_rele(np);
1559 		return (NULL);
1560 	}
1561 	np->rn_type = strdup(type);
1562 	if (np->rn_type == NULL) {
1563 		free((void *)np->rn_name);
1564 		rc_node_rele(np);
1565 		return (NULL);
1566 	}
1567 	np->rn_pgflags = flags;
1568 	np->rn_gen_id = gen_id;
1569 
1570 	np->rn_flags |= RC_NODE_USING_PARENT;
1571 
1572 	cache_insert_unlocked(bp, np);
1573 	cache_release(bp);		/* we are now visible */
1574 
1575 	rc_node_link_child(pp, np);
1576 
1577 	return (np);
1578 }
1579 
1580 #if COMPOSITION_DEPTH == 2
1581 /*
1582  * Initialize a "composed property group" which represents the composition of
1583  * property groups pg1 & pg2.  It is ephemeral: once created & returned for an
1584  * ITER_READ request, keeping it out of cache_hash and any child lists
1585  * prevents it from being looked up.  Operations besides iteration are passed
1586  * through to pg1.
1587  *
1588  * pg1 & pg2 should be held before entering this function.  They will be
1589  * released in rc_node_destroy().
1590  */
1591 static int
1592 rc_node_setup_cpg(rc_node_t *cpg, rc_node_t *pg1, rc_node_t *pg2)
1593 {
1594 	if (strcmp(pg1->rn_type, pg2->rn_type) != 0)
1595 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1596 
1597 	cpg->rn_id.rl_type = REP_PROTOCOL_ENTITY_CPROPERTYGRP;
1598 	cpg->rn_name = strdup(pg1->rn_name);
1599 	if (cpg->rn_name == NULL)
1600 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1601 
1602 	cpg->rn_cchain[0] = pg1;
1603 	cpg->rn_cchain[1] = pg2;
1604 
1605 	return (REP_PROTOCOL_SUCCESS);
1606 }
1607 #else
1608 #error This code must be updated.
1609 #endif
1610 
1611 /*
1612  * Fails with _NO_RESOURCES.
1613  */
1614 int
1615 rc_node_create_property(rc_node_t *pp, rc_node_lookup_t *nip,
1616     const char *name, rep_protocol_value_type_t type,
1617     const char *vals, size_t count, size_t size)
1618 {
1619 	rc_node_t *np;
1620 	cache_bucket_t *bp;
1621 
1622 	uint32_t h = rc_node_hash(nip);
1623 	bp = cache_hold(h);
1624 	if ((np = cache_lookup_unlocked(bp, nip)) != NULL) {
1625 		cache_release(bp);
1626 		/*
1627 		 * make sure it matches our expectations
1628 		 */
1629 		(void) pthread_mutex_lock(&np->rn_lock);
1630 		if (rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
1631 			assert(np->rn_parent == pp);
1632 			assert(memcmp(&np->rn_id, nip, sizeof (*nip)) == 0);
1633 			assert(strcmp(np->rn_name, name) == 0);
1634 			assert(np->rn_valtype == type);
1635 			assert(np->rn_values_count == count);
1636 			assert(np->rn_values_size == size);
1637 			assert(vals == NULL ||
1638 			    memcmp(np->rn_values, vals, size) == 0);
1639 			assert(np->rn_flags & RC_NODE_IN_PARENT);
1640 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
1641 		}
1642 		rc_node_rele_locked(np);
1643 		object_free_values(vals, type, count, size);
1644 		return (REP_PROTOCOL_SUCCESS);
1645 	}
1646 
1647 	/*
1648 	 * No one is there -- create a new node.
1649 	 */
1650 	np = rc_node_alloc();
1651 	if (np == NULL) {
1652 		cache_release(bp);
1653 		object_free_values(vals, type, count, size);
1654 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1655 	}
1656 	np->rn_id = *nip;
1657 	np->rn_hash = h;
1658 	np->rn_name = strdup(name);
1659 	if (np->rn_name == NULL) {
1660 		cache_release(bp);
1661 		object_free_values(vals, type, count, size);
1662 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1663 	}
1664 
1665 	np->rn_valtype = type;
1666 	np->rn_values = vals;
1667 	np->rn_values_count = count;
1668 	np->rn_values_size = size;
1669 
1670 	np->rn_flags |= RC_NODE_USING_PARENT;
1671 
1672 	cache_insert_unlocked(bp, np);
1673 	cache_release(bp);		/* we are now visible */
1674 
1675 	rc_node_link_child(pp, np);
1676 
1677 	return (REP_PROTOCOL_SUCCESS);
1678 }
1679 
1680 int
1681 rc_node_init(void)
1682 {
1683 	rc_node_t *np;
1684 	cache_bucket_t *bp;
1685 
1686 	rc_children_pool = uu_list_pool_create("rc_children_pool",
1687 	    sizeof (rc_node_t), offsetof(rc_node_t, rn_sibling_node),
1688 	    NULL, UU_LIST_POOL_DEBUG);
1689 
1690 	rc_pg_notify_pool = uu_list_pool_create("rc_pg_notify_pool",
1691 	    sizeof (rc_node_pg_notify_t),
1692 	    offsetof(rc_node_pg_notify_t, rnpn_node),
1693 	    NULL, UU_LIST_POOL_DEBUG);
1694 
1695 	rc_notify_pool = uu_list_pool_create("rc_notify_pool",
1696 	    sizeof (rc_notify_t), offsetof(rc_notify_t, rcn_list_node),
1697 	    NULL, UU_LIST_POOL_DEBUG);
1698 
1699 	rc_notify_info_pool = uu_list_pool_create("rc_notify_info_pool",
1700 	    sizeof (rc_notify_info_t),
1701 	    offsetof(rc_notify_info_t, rni_list_node),
1702 	    NULL, UU_LIST_POOL_DEBUG);
1703 
1704 	if (rc_children_pool == NULL || rc_pg_notify_pool == NULL ||
1705 	    rc_notify_pool == NULL || rc_notify_info_pool == NULL)
1706 		uu_die("out of memory");
1707 
1708 	rc_notify_list = uu_list_create(rc_notify_pool,
1709 	    &rc_notify_list, 0);
1710 
1711 	rc_notify_info_list = uu_list_create(rc_notify_info_pool,
1712 	    &rc_notify_info_list, 0);
1713 
1714 	if (rc_notify_list == NULL || rc_notify_info_list == NULL)
1715 		uu_die("out of memory");
1716 
1717 	if ((np = rc_node_alloc()) == NULL)
1718 		uu_die("out of memory");
1719 
1720 	rc_node_hold(np);
1721 	np->rn_id.rl_type = REP_PROTOCOL_ENTITY_SCOPE;
1722 	np->rn_id.rl_backend = BACKEND_TYPE_NORMAL;
1723 	np->rn_hash = rc_node_hash(&np->rn_id);
1724 	np->rn_name = "localhost";
1725 
1726 	bp = cache_hold(np->rn_hash);
1727 	cache_insert_unlocked(bp, np);
1728 	cache_release(bp);
1729 
1730 	rc_scope = np;
1731 	return (1);
1732 }
1733 
1734 /*
1735  * Fails with
1736  *   _INVALID_TYPE - type is invalid
1737  *   _TYPE_MISMATCH - np doesn't carry children of type type
1738  *   _DELETED - np has been deleted
1739  *   _NO_RESOURCES
1740  */
1741 static int
1742 rc_node_fill_children(rc_node_t *np, uint32_t type)
1743 {
1744 	int rc;
1745 
1746 	assert(MUTEX_HELD(&np->rn_lock));
1747 
1748 	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
1749 	    REP_PROTOCOL_SUCCESS)
1750 		return (rc);
1751 
1752 	if (!rc_node_hold_flag(np, RC_NODE_CHILDREN_CHANGING))
1753 		return (REP_PROTOCOL_FAIL_DELETED);
1754 
1755 	if (np->rn_flags & RC_NODE_HAS_CHILDREN) {
1756 		rc_node_rele_flag(np, RC_NODE_CHILDREN_CHANGING);
1757 		return (REP_PROTOCOL_SUCCESS);
1758 	}
1759 
1760 	(void) pthread_mutex_unlock(&np->rn_lock);
1761 	rc = object_fill_children(np);
1762 	(void) pthread_mutex_lock(&np->rn_lock);
1763 
1764 	if (rc == REP_PROTOCOL_SUCCESS) {
1765 		np->rn_flags |= RC_NODE_HAS_CHILDREN;
1766 	}
1767 	rc_node_rele_flag(np, RC_NODE_CHILDREN_CHANGING);
1768 
1769 	return (rc);
1770 }
1771 
1772 /*
1773  * Returns
1774  *   _INVALID_TYPE - type is invalid
1775  *   _TYPE_MISMATCH - np doesn't carry children of type type
1776  *   _DELETED - np has been deleted
1777  *   _NO_RESOURCES
1778  *   _SUCCESS - if *cpp is not NULL, it is held
1779  */
1780 static int
1781 rc_node_find_named_child(rc_node_t *np, const char *name, uint32_t type,
1782     rc_node_t **cpp)
1783 {
1784 	int ret;
1785 	rc_node_t *cp;
1786 
1787 	assert(MUTEX_HELD(&np->rn_lock));
1788 	assert(np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP);
1789 
1790 	ret = rc_node_fill_children(np, type);
1791 	if (ret != REP_PROTOCOL_SUCCESS)
1792 		return (ret);
1793 
1794 	for (cp = uu_list_first(np->rn_children);
1795 	    cp != NULL;
1796 	    cp = uu_list_next(np->rn_children, cp)) {
1797 		if (cp->rn_id.rl_type == type && strcmp(cp->rn_name, name) == 0)
1798 			break;
1799 	}
1800 
1801 	if (cp != NULL)
1802 		rc_node_hold(cp);
1803 	*cpp = cp;
1804 
1805 	return (REP_PROTOCOL_SUCCESS);
1806 }
1807 
1808 #ifndef NATIVE_BUILD
1809 static int rc_node_parent(rc_node_t *, rc_node_t **);
1810 
1811 /*
1812  * If the propname property exists in pg, and it is of type string, add its
1813  * values as authorizations to pcp.  pg must not be locked on entry, and it is
1814  * returned unlocked.  Returns
1815  *   _DELETED - pg was deleted
1816  *   _NO_RESOURCES
1817  *   _NOT_FOUND - pg has no property named propname
1818  *   _SUCCESS
1819  */
1820 static int
1821 perm_add_pg_prop_values(permcheck_t *pcp, rc_node_t *pg, const char *propname)
1822 {
1823 	rc_node_t *prop;
1824 	int result;
1825 
1826 	uint_t count;
1827 	const char *cp;
1828 
1829 	assert(!MUTEX_HELD(&pg->rn_lock));
1830 	assert(pg->rn_id.rl_type == REP_PROTOCOL_ENTITY_PROPERTYGRP);
1831 	assert(pg->rn_id.rl_ids[ID_SNAPSHOT] == 0);
1832 
1833 	(void) pthread_mutex_lock(&pg->rn_lock);
1834 	result = rc_node_find_named_child(pg, propname,
1835 	    REP_PROTOCOL_ENTITY_PROPERTY, &prop);
1836 	(void) pthread_mutex_unlock(&pg->rn_lock);
1837 	if (result != REP_PROTOCOL_SUCCESS) {
1838 		switch (result) {
1839 		case REP_PROTOCOL_FAIL_DELETED:
1840 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
1841 			return (result);
1842 
1843 		case REP_PROTOCOL_FAIL_INVALID_TYPE:
1844 		case REP_PROTOCOL_FAIL_TYPE_MISMATCH:
1845 		default:
1846 			bad_error("rc_node_find_named_child", result);
1847 		}
1848 	}
1849 
1850 	if (prop == NULL)
1851 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
1852 
1853 	/* rn_valtype is immutable, so no locking. */
1854 	if (prop->rn_valtype != REP_PROTOCOL_TYPE_STRING) {
1855 		rc_node_rele(prop);
1856 		return (REP_PROTOCOL_SUCCESS);
1857 	}
1858 
1859 	(void) pthread_mutex_lock(&prop->rn_lock);
1860 	for (count = prop->rn_values_count, cp = prop->rn_values;
1861 	    count > 0;
1862 	    --count) {
1863 		result = perm_add_enabling(pcp, cp);
1864 		if (result != REP_PROTOCOL_SUCCESS)
1865 			break;
1866 
1867 		cp = strchr(cp, '\0') + 1;
1868 	}
1869 
1870 	rc_node_rele_locked(prop);
1871 
1872 	return (result);
1873 }
1874 
1875 /*
1876  * Assuming that ent is a service or instance node, if the pgname property
1877  * group has type pgtype, and it has a propname property with string type, add
1878  * its values as authorizations to pcp.  If pgtype is NULL, it is not checked.
1879  * Returns
1880  *   _SUCCESS
1881  *   _DELETED - ent was deleted
1882  *   _NO_RESOURCES - no resources
1883  *   _NOT_FOUND - ent does not have pgname pg or propname property
1884  */
1885 static int
1886 perm_add_ent_prop_values(permcheck_t *pcp, rc_node_t *ent, const char *pgname,
1887     const char *pgtype, const char *propname)
1888 {
1889 	int r;
1890 	rc_node_t *pg;
1891 
1892 	assert(!MUTEX_HELD(&ent->rn_lock));
1893 
1894 	(void) pthread_mutex_lock(&ent->rn_lock);
1895 	r = rc_node_find_named_child(ent, pgname,
1896 	    REP_PROTOCOL_ENTITY_PROPERTYGRP, &pg);
1897 	(void) pthread_mutex_unlock(&ent->rn_lock);
1898 
1899 	switch (r) {
1900 	case REP_PROTOCOL_SUCCESS:
1901 		break;
1902 
1903 	case REP_PROTOCOL_FAIL_DELETED:
1904 	case REP_PROTOCOL_FAIL_NO_RESOURCES:
1905 		return (r);
1906 
1907 	default:
1908 		bad_error("rc_node_find_named_child", r);
1909 	}
1910 
1911 	if (pg == NULL)
1912 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
1913 
1914 	if (pgtype == NULL || strcmp(pg->rn_type, pgtype) == 0) {
1915 		r = perm_add_pg_prop_values(pcp, pg, propname);
1916 		switch (r) {
1917 		case REP_PROTOCOL_FAIL_DELETED:
1918 			r = REP_PROTOCOL_FAIL_NOT_FOUND;
1919 			break;
1920 
1921 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
1922 		case REP_PROTOCOL_SUCCESS:
1923 		case REP_PROTOCOL_FAIL_NOT_FOUND:
1924 			break;
1925 
1926 		default:
1927 			bad_error("perm_add_pg_prop_values", r);
1928 		}
1929 	}
1930 
1931 	rc_node_rele(pg);
1932 
1933 	return (r);
1934 }
1935 
1936 /*
1937  * If pg has a property named propname, and it string typed, add its values as
1938  * authorizations to pcp.  If pg has no such property, and its parent is an
1939  * instance, walk up to the service and try doing the same with the property
1940  * of the same name from the property group of the same name.  Returns
1941  *   _SUCCESS
1942  *   _NO_RESOURCES
1943  *   _DELETED - pg (or an ancestor) was deleted
1944  */
1945 static int
1946 perm_add_enabling_values(permcheck_t *pcp, rc_node_t *pg, const char *propname)
1947 {
1948 	int r;
1949 
1950 	r = perm_add_pg_prop_values(pcp, pg, propname);
1951 
1952 	if (r == REP_PROTOCOL_FAIL_NOT_FOUND) {
1953 		char pgname[REP_PROTOCOL_NAME_LEN + 1];
1954 		rc_node_t *inst, *svc;
1955 		size_t sz;
1956 
1957 		assert(!MUTEX_HELD(&pg->rn_lock));
1958 
1959 		if (pg->rn_id.rl_ids[ID_INSTANCE] == 0) {
1960 			/* not an instance pg */
1961 			return (REP_PROTOCOL_SUCCESS);
1962 		}
1963 
1964 		sz = strlcpy(pgname, pg->rn_name, sizeof (pgname));
1965 		assert(sz < sizeof (pgname));
1966 
1967 		/* get pg's parent */
1968 		r = rc_node_parent(pg, &inst);
1969 		if (r != REP_PROTOCOL_SUCCESS) {
1970 			assert(r == REP_PROTOCOL_FAIL_DELETED);
1971 			return (r);
1972 		}
1973 
1974 		assert(inst->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE);
1975 
1976 		/* get instance's parent */
1977 		r = rc_node_parent(inst, &svc);
1978 		rc_node_rele(inst);
1979 		if (r != REP_PROTOCOL_SUCCESS) {
1980 			assert(r == REP_PROTOCOL_FAIL_DELETED);
1981 			return (r);
1982 		}
1983 
1984 		assert(svc->rn_id.rl_type == REP_PROTOCOL_ENTITY_SERVICE);
1985 
1986 		r = perm_add_ent_prop_values(pcp, svc, pgname, NULL, propname);
1987 
1988 		rc_node_rele(svc);
1989 
1990 		if (r == REP_PROTOCOL_FAIL_NOT_FOUND)
1991 			r = REP_PROTOCOL_SUCCESS;
1992 	}
1993 
1994 	return (r);
1995 }
1996 
1997 /*
1998  * Call perm_add_enabling_values() for the "action_authorization" property of
1999  * the "general" property group of inst.  Returns
2000  *   _DELETED - inst (or an ancestor) was deleted
2001  *   _NO_RESOURCES
2002  *   _SUCCESS
2003  */
2004 static int
2005 perm_add_inst_action_auth(permcheck_t *pcp, rc_node_t *inst)
2006 {
2007 	int r;
2008 	rc_node_t *svc;
2009 
2010 	assert(inst->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE);
2011 
2012 	r = perm_add_ent_prop_values(pcp, inst, AUTH_PG_GENERAL,
2013 	    AUTH_PG_GENERAL_TYPE, AUTH_PROP_ACTION);
2014 
2015 	if (r != REP_PROTOCOL_FAIL_NOT_FOUND)
2016 		return (r);
2017 
2018 	r = rc_node_parent(inst, &svc);
2019 	if (r != REP_PROTOCOL_SUCCESS) {
2020 		assert(r == REP_PROTOCOL_FAIL_DELETED);
2021 		return (r);
2022 	}
2023 
2024 	r = perm_add_ent_prop_values(pcp, svc, AUTH_PG_GENERAL,
2025 	    AUTH_PG_GENERAL_TYPE, AUTH_PROP_ACTION);
2026 
2027 	return (r == REP_PROTOCOL_FAIL_NOT_FOUND ? REP_PROTOCOL_SUCCESS : r);
2028 }
2029 #endif /* NATIVE_BUILD */
2030 
2031 void
2032 rc_node_ptr_init(rc_node_ptr_t *out)
2033 {
2034 	out->rnp_node = NULL;
2035 	out->rnp_authorized = 0;
2036 	out->rnp_deleted = 0;
2037 }
2038 
2039 static void
2040 rc_node_assign(rc_node_ptr_t *out, rc_node_t *val)
2041 {
2042 	rc_node_t *cur = out->rnp_node;
2043 	if (val != NULL)
2044 		rc_node_hold(val);
2045 	out->rnp_node = val;
2046 	if (cur != NULL)
2047 		rc_node_rele(cur);
2048 	out->rnp_authorized = 0;
2049 	out->rnp_deleted = 0;
2050 }
2051 
2052 void
2053 rc_node_clear(rc_node_ptr_t *out, int deleted)
2054 {
2055 	rc_node_assign(out, NULL);
2056 	out->rnp_deleted = deleted;
2057 }
2058 
2059 void
2060 rc_node_ptr_assign(rc_node_ptr_t *out, const rc_node_ptr_t *val)
2061 {
2062 	rc_node_assign(out, val->rnp_node);
2063 }
2064 
2065 /*
2066  * rc_node_check()/RC_NODE_CHECK()
2067  *	generic "entry" checks, run before the use of an rc_node pointer.
2068  *
2069  * Fails with
2070  *   _NOT_SET
2071  *   _DELETED
2072  */
2073 static int
2074 rc_node_check_and_lock(rc_node_t *np)
2075 {
2076 	int result = REP_PROTOCOL_SUCCESS;
2077 	if (np == NULL)
2078 		return (REP_PROTOCOL_FAIL_NOT_SET);
2079 
2080 	(void) pthread_mutex_lock(&np->rn_lock);
2081 	if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
2082 		result = REP_PROTOCOL_FAIL_DELETED;
2083 		(void) pthread_mutex_unlock(&np->rn_lock);
2084 	}
2085 
2086 	return (result);
2087 }
2088 
2089 /*
2090  * Fails with
2091  *   _NOT_SET - ptr is reset
2092  *   _DELETED - node has been deleted
2093  */
2094 static rc_node_t *
2095 rc_node_ptr_check_and_lock(rc_node_ptr_t *npp, int *res)
2096 {
2097 	rc_node_t *np = npp->rnp_node;
2098 	if (np == NULL) {
2099 		if (npp->rnp_deleted)
2100 			*res = REP_PROTOCOL_FAIL_DELETED;
2101 		else
2102 			*res = REP_PROTOCOL_FAIL_NOT_SET;
2103 		return (NULL);
2104 	}
2105 
2106 	(void) pthread_mutex_lock(&np->rn_lock);
2107 	if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
2108 		(void) pthread_mutex_unlock(&np->rn_lock);
2109 		rc_node_clear(npp, 1);
2110 		*res = REP_PROTOCOL_FAIL_DELETED;
2111 		return (NULL);
2112 	}
2113 	return (np);
2114 }
2115 
2116 #define	RC_NODE_CHECK_AND_LOCK(n) {					\
2117 	int rc__res;							\
2118 	if ((rc__res = rc_node_check_and_lock(n)) != REP_PROTOCOL_SUCCESS) \
2119 		return (rc__res);					\
2120 }
2121 
2122 #define	RC_NODE_CHECK(n) {						\
2123 	RC_NODE_CHECK_AND_LOCK(n);					\
2124 	(void) pthread_mutex_unlock(&(n)->rn_lock);			\
2125 }
2126 
2127 #define	RC_NODE_CHECK_AND_HOLD(n) {					\
2128 	RC_NODE_CHECK_AND_LOCK(n);					\
2129 	rc_node_hold_locked(n);						\
2130 	(void) pthread_mutex_unlock(&(n)->rn_lock);			\
2131 }
2132 
2133 #define	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp) {			\
2134 	int rc__res;							\
2135 	if (((np) = rc_node_ptr_check_and_lock(npp, &rc__res)) == NULL)	\
2136 		return (rc__res);					\
2137 }
2138 
2139 #define	RC_NODE_PTR_GET_CHECK(np, npp) {				\
2140 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);			\
2141 	(void) pthread_mutex_unlock(&(np)->rn_lock);			\
2142 }
2143 
2144 #define	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp) {			\
2145 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);			\
2146 	rc_node_hold_locked(np);					\
2147 	(void) pthread_mutex_unlock(&(np)->rn_lock);			\
2148 }
2149 
2150 #define	HOLD_FLAG_OR_RETURN(np, flag) {					\
2151 	assert(MUTEX_HELD(&(np)->rn_lock));				\
2152 	assert(!((np)->rn_flags & RC_NODE_DEAD));			\
2153 	if (!rc_node_hold_flag((np), flag)) {				\
2154 		(void) pthread_mutex_unlock(&(np)->rn_lock);		\
2155 		return (REP_PROTOCOL_FAIL_DELETED);			\
2156 	}								\
2157 }
2158 
2159 #define	HOLD_PTR_FLAG_OR_RETURN(np, npp, flag) {			\
2160 	assert(MUTEX_HELD(&(np)->rn_lock));				\
2161 	assert(!((np)->rn_flags & RC_NODE_DEAD));			\
2162 	if (!rc_node_hold_flag((np), flag)) {				\
2163 		(void) pthread_mutex_unlock(&(np)->rn_lock);		\
2164 		assert((np) == (npp)->rnp_node);			\
2165 		rc_node_clear(npp, 1);					\
2166 		return (REP_PROTOCOL_FAIL_DELETED);			\
2167 	}								\
2168 }
2169 
2170 int
2171 rc_local_scope(uint32_t type, rc_node_ptr_t *out)
2172 {
2173 	if (type != REP_PROTOCOL_ENTITY_SCOPE) {
2174 		rc_node_clear(out, 0);
2175 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2176 	}
2177 
2178 	/*
2179 	 * the main scope never gets destroyed
2180 	 */
2181 	rc_node_assign(out, rc_scope);
2182 
2183 	return (REP_PROTOCOL_SUCCESS);
2184 }
2185 
2186 /*
2187  * Fails with
2188  *   _NOT_SET - npp is not set
2189  *   _DELETED - the node npp pointed at has been deleted
2190  *   _TYPE_MISMATCH - type is not _SCOPE
2191  *   _NOT_FOUND - scope has no parent
2192  */
2193 static int
2194 rc_scope_parent_scope(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out)
2195 {
2196 	rc_node_t *np;
2197 
2198 	rc_node_clear(out, 0);
2199 
2200 	RC_NODE_PTR_GET_CHECK(np, npp);
2201 
2202 	if (type != REP_PROTOCOL_ENTITY_SCOPE)
2203 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2204 
2205 	return (REP_PROTOCOL_FAIL_NOT_FOUND);
2206 }
2207 
2208 /*
2209  * Fails with
2210  *   _NOT_SET
2211  *   _DELETED
2212  *   _NOT_APPLICABLE
2213  *   _NOT_FOUND
2214  *   _BAD_REQUEST
2215  *   _TRUNCATED
2216  */
2217 int
2218 rc_node_name(rc_node_ptr_t *npp, char *buf, size_t sz, uint32_t answertype,
2219     size_t *sz_out)
2220 {
2221 	size_t actual;
2222 	rc_node_t *np;
2223 
2224 	assert(sz == *sz_out);
2225 
2226 	RC_NODE_PTR_GET_CHECK(np, npp);
2227 
2228 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
2229 		np = np->rn_cchain[0];
2230 		RC_NODE_CHECK(np);
2231 	}
2232 
2233 	switch (answertype) {
2234 	case RP_ENTITY_NAME_NAME:
2235 		if (np->rn_name == NULL)
2236 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2237 		actual = strlcpy(buf, np->rn_name, sz);
2238 		break;
2239 	case RP_ENTITY_NAME_PGTYPE:
2240 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
2241 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2242 		actual = strlcpy(buf, np->rn_type, sz);
2243 		break;
2244 	case RP_ENTITY_NAME_PGFLAGS:
2245 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
2246 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2247 		actual = snprintf(buf, sz, "%d", np->rn_pgflags);
2248 		break;
2249 	case RP_ENTITY_NAME_SNAPLEVEL_SCOPE:
2250 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
2251 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2252 		actual = strlcpy(buf, np->rn_snaplevel->rsl_scope, sz);
2253 		break;
2254 	case RP_ENTITY_NAME_SNAPLEVEL_SERVICE:
2255 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
2256 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2257 		actual = strlcpy(buf, np->rn_snaplevel->rsl_service, sz);
2258 		break;
2259 	case RP_ENTITY_NAME_SNAPLEVEL_INSTANCE:
2260 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
2261 			return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2262 		if (np->rn_snaplevel->rsl_instance == NULL)
2263 			return (REP_PROTOCOL_FAIL_NOT_FOUND);
2264 		actual = strlcpy(buf, np->rn_snaplevel->rsl_instance, sz);
2265 		break;
2266 	default:
2267 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
2268 	}
2269 	if (actual >= sz)
2270 		return (REP_PROTOCOL_FAIL_TRUNCATED);
2271 
2272 	*sz_out = actual;
2273 	return (REP_PROTOCOL_SUCCESS);
2274 }
2275 
2276 int
2277 rc_node_get_property_type(rc_node_ptr_t *npp, rep_protocol_value_type_t *out)
2278 {
2279 	rc_node_t *np;
2280 
2281 	RC_NODE_PTR_GET_CHECK(np, npp);
2282 
2283 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
2284 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2285 
2286 	*out = np->rn_valtype;
2287 
2288 	return (REP_PROTOCOL_SUCCESS);
2289 }
2290 
2291 /*
2292  * Get np's parent.  If np is deleted, returns _DELETED.  Otherwise puts a hold
2293  * on the parent, returns a pointer to it in *out, and returns _SUCCESS.
2294  */
2295 static int
2296 rc_node_parent(rc_node_t *np, rc_node_t **out)
2297 {
2298 	rc_node_t *pnp;
2299 	rc_node_t *np_orig;
2300 
2301 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
2302 		RC_NODE_CHECK_AND_LOCK(np);
2303 	} else {
2304 		np = np->rn_cchain[0];
2305 		RC_NODE_CHECK_AND_LOCK(np);
2306 	}
2307 
2308 	np_orig = np;
2309 	rc_node_hold_locked(np);		/* simplifies the remainder */
2310 
2311 	for (;;) {
2312 		if (!rc_node_wait_flag(np,
2313 		    RC_NODE_IN_TX | RC_NODE_USING_PARENT)) {
2314 			rc_node_rele_locked(np);
2315 			return (REP_PROTOCOL_FAIL_DELETED);
2316 		}
2317 
2318 		if (!(np->rn_flags & RC_NODE_OLD))
2319 			break;
2320 
2321 		rc_node_rele_locked(np);
2322 		np = cache_lookup(&np_orig->rn_id);
2323 		assert(np != np_orig);
2324 
2325 		if (np == NULL)
2326 			goto deleted;
2327 		(void) pthread_mutex_lock(&np->rn_lock);
2328 	}
2329 
2330 	/* guaranteed to succeed without dropping the lock */
2331 	if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
2332 		(void) pthread_mutex_unlock(&np->rn_lock);
2333 		*out = NULL;
2334 		rc_node_rele(np);
2335 		return (REP_PROTOCOL_FAIL_DELETED);
2336 	}
2337 
2338 	assert(np->rn_parent != NULL);
2339 	pnp = np->rn_parent;
2340 	(void) pthread_mutex_unlock(&np->rn_lock);
2341 
2342 	(void) pthread_mutex_lock(&pnp->rn_lock);
2343 	(void) pthread_mutex_lock(&np->rn_lock);
2344 	rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2345 	(void) pthread_mutex_unlock(&np->rn_lock);
2346 
2347 	rc_node_hold_locked(pnp);
2348 
2349 	(void) pthread_mutex_unlock(&pnp->rn_lock);
2350 
2351 	rc_node_rele(np);
2352 	*out = pnp;
2353 	return (REP_PROTOCOL_SUCCESS);
2354 
2355 deleted:
2356 	rc_node_rele(np);
2357 	return (REP_PROTOCOL_FAIL_DELETED);
2358 }
2359 
2360 /*
2361  * Fails with
2362  *   _NOT_SET
2363  *   _DELETED
2364  */
2365 static int
2366 rc_node_ptr_parent(rc_node_ptr_t *npp, rc_node_t **out)
2367 {
2368 	rc_node_t *np;
2369 
2370 	RC_NODE_PTR_GET_CHECK(np, npp);
2371 
2372 	return (rc_node_parent(np, out));
2373 }
2374 
2375 /*
2376  * Fails with
2377  *   _NOT_SET - npp is not set
2378  *   _DELETED - the node npp pointed at has been deleted
2379  *   _TYPE_MISMATCH - npp's node's parent is not of type type
2380  *
2381  * If npp points to a scope, can also fail with
2382  *   _NOT_FOUND - scope has no parent
2383  */
2384 int
2385 rc_node_get_parent(rc_node_ptr_t *npp, uint32_t type, rc_node_ptr_t *out)
2386 {
2387 	rc_node_t *pnp;
2388 	int rc;
2389 
2390 	if (npp->rnp_node != NULL &&
2391 	    npp->rnp_node->rn_id.rl_type == REP_PROTOCOL_ENTITY_SCOPE)
2392 		return (rc_scope_parent_scope(npp, type, out));
2393 
2394 	if ((rc = rc_node_ptr_parent(npp, &pnp)) != REP_PROTOCOL_SUCCESS) {
2395 		rc_node_clear(out, 0);
2396 		return (rc);
2397 	}
2398 
2399 	if (type != pnp->rn_id.rl_type) {
2400 		rc_node_rele(pnp);
2401 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2402 	}
2403 
2404 	rc_node_assign(out, pnp);
2405 	rc_node_rele(pnp);
2406 
2407 	return (REP_PROTOCOL_SUCCESS);
2408 }
2409 
2410 int
2411 rc_node_parent_type(rc_node_ptr_t *npp, uint32_t *type_out)
2412 {
2413 	rc_node_t *pnp;
2414 	int rc;
2415 
2416 	if (npp->rnp_node != NULL &&
2417 	    npp->rnp_node->rn_id.rl_type == REP_PROTOCOL_ENTITY_SCOPE) {
2418 		*type_out = REP_PROTOCOL_ENTITY_SCOPE;
2419 		return (REP_PROTOCOL_SUCCESS);
2420 	}
2421 
2422 	if ((rc = rc_node_ptr_parent(npp, &pnp)) != REP_PROTOCOL_SUCCESS)
2423 		return (rc);
2424 
2425 	*type_out = pnp->rn_id.rl_type;
2426 
2427 	rc_node_rele(pnp);
2428 
2429 	return (REP_PROTOCOL_SUCCESS);
2430 }
2431 
2432 /*
2433  * Fails with
2434  *   _INVALID_TYPE - type is invalid
2435  *   _TYPE_MISMATCH - np doesn't carry children of type type
2436  *   _DELETED - np has been deleted
2437  *   _NOT_FOUND - no child with that name/type combo found
2438  *   _NO_RESOURCES
2439  *   _BACKEND_ACCESS
2440  */
2441 int
2442 rc_node_get_child(rc_node_ptr_t *npp, const char *name, uint32_t type,
2443     rc_node_ptr_t *outp)
2444 {
2445 	rc_node_t *np, *cp;
2446 	rc_node_t *child = NULL;
2447 	int ret, idx;
2448 
2449 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
2450 	if ((ret = rc_check_type_name(type, name)) == REP_PROTOCOL_SUCCESS) {
2451 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
2452 			ret = rc_node_find_named_child(np, name, type, &child);
2453 		} else {
2454 			(void) pthread_mutex_unlock(&np->rn_lock);
2455 			ret = REP_PROTOCOL_SUCCESS;
2456 			for (idx = 0; idx < COMPOSITION_DEPTH; idx++) {
2457 				cp = np->rn_cchain[idx];
2458 				if (cp == NULL)
2459 					break;
2460 				RC_NODE_CHECK_AND_LOCK(cp);
2461 				ret = rc_node_find_named_child(cp, name, type,
2462 				    &child);
2463 				(void) pthread_mutex_unlock(&cp->rn_lock);
2464 				/*
2465 				 * loop only if we succeeded, but no child of
2466 				 * the correct name was found.
2467 				 */
2468 				if (ret != REP_PROTOCOL_SUCCESS ||
2469 				    child != NULL)
2470 					break;
2471 			}
2472 			(void) pthread_mutex_lock(&np->rn_lock);
2473 		}
2474 	}
2475 	(void) pthread_mutex_unlock(&np->rn_lock);
2476 
2477 	if (ret == REP_PROTOCOL_SUCCESS) {
2478 		rc_node_assign(outp, child);
2479 		if (child != NULL)
2480 			rc_node_rele(child);
2481 		else
2482 			ret = REP_PROTOCOL_FAIL_NOT_FOUND;
2483 	} else {
2484 		rc_node_assign(outp, NULL);
2485 	}
2486 	return (ret);
2487 }
2488 
2489 int
2490 rc_node_update(rc_node_ptr_t *npp)
2491 {
2492 	cache_bucket_t *bp;
2493 	rc_node_t *np = npp->rnp_node;
2494 	rc_node_t *nnp;
2495 	rc_node_t *cpg = NULL;
2496 
2497 	if (np != NULL &&
2498 	    np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
2499 		/*
2500 		 * If we're updating a composed property group, actually
2501 		 * update the top-level property group & return the
2502 		 * appropriate value.  But leave *nnp pointing at us.
2503 		 */
2504 		cpg = np;
2505 		np = np->rn_cchain[0];
2506 	}
2507 
2508 	RC_NODE_CHECK(np);
2509 
2510 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP &&
2511 	    np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT)
2512 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
2513 
2514 	for (;;) {
2515 		bp = cache_hold(np->rn_hash);
2516 		nnp = cache_lookup_unlocked(bp, &np->rn_id);
2517 		if (nnp == NULL) {
2518 			cache_release(bp);
2519 			rc_node_clear(npp, 1);
2520 			return (REP_PROTOCOL_FAIL_DELETED);
2521 		}
2522 		/*
2523 		 * grab the lock before dropping the cache bucket, so
2524 		 * that no one else can sneak in
2525 		 */
2526 		(void) pthread_mutex_lock(&nnp->rn_lock);
2527 		cache_release(bp);
2528 
2529 		if (!(nnp->rn_flags & RC_NODE_IN_TX) ||
2530 		    !rc_node_wait_flag(nnp, RC_NODE_IN_TX))
2531 			break;
2532 
2533 		rc_node_rele_locked(nnp);
2534 	}
2535 
2536 	/*
2537 	 * If it is dead, we want to update it so that it will continue to
2538 	 * report being dead.
2539 	 */
2540 	if (nnp->rn_flags & RC_NODE_DEAD) {
2541 		(void) pthread_mutex_unlock(&nnp->rn_lock);
2542 		if (nnp != np && cpg == NULL)
2543 			rc_node_assign(npp, nnp);	/* updated */
2544 		rc_node_rele(nnp);
2545 		return (REP_PROTOCOL_FAIL_DELETED);
2546 	}
2547 
2548 	assert(!(nnp->rn_flags & RC_NODE_OLD));
2549 	(void) pthread_mutex_unlock(&nnp->rn_lock);
2550 
2551 	if (nnp != np && cpg == NULL)
2552 		rc_node_assign(npp, nnp);		/* updated */
2553 
2554 	rc_node_rele(nnp);
2555 
2556 	return ((nnp == np)? REP_PROTOCOL_SUCCESS : REP_PROTOCOL_DONE);
2557 }
2558 
2559 /*
2560  * does a generic modification check, for creation, deletion, and snapshot
2561  * management only.  Property group transactions have different checks.
2562  */
2563 int
2564 rc_node_modify_permission_check(void)
2565 {
2566 	int rc = REP_PROTOCOL_SUCCESS;
2567 	permcheck_t *pcp;
2568 	int granted;
2569 
2570 	if (!client_is_privileged()) {
2571 #ifdef NATIVE_BUILD
2572 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
2573 #else
2574 		pcp = pc_create();
2575 		if (pcp != NULL) {
2576 			rc = perm_add_enabling(pcp, AUTH_MODIFY);
2577 
2578 			if (rc == REP_PROTOCOL_SUCCESS) {
2579 				granted = perm_granted(pcp);
2580 
2581 				if (granted < 0)
2582 					rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
2583 			}
2584 
2585 			pc_free(pcp);
2586 		} else {
2587 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
2588 		}
2589 
2590 		if (rc == REP_PROTOCOL_SUCCESS && !granted)
2591 			rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
2592 #endif /* NATIVE_BUILD */
2593 	}
2594 	return (rc);
2595 }
2596 
2597 /*
2598  * Fails with
2599  *   _DELETED - node has been deleted
2600  *   _NOT_SET - npp is reset
2601  *   _NOT_APPLICABLE - type is _PROPERTYGRP
2602  *   _INVALID_TYPE - node is corrupt or type is invalid
2603  *   _TYPE_MISMATCH - node cannot have children of type type
2604  *   _BAD_REQUEST - name is invalid
2605  *		    cannot create children for this type of node
2606  *   _NO_RESOURCES - out of memory, or could not allocate new id
2607  *   _PERMISSION_DENIED
2608  *   _BACKEND_ACCESS
2609  *   _BACKEND_READONLY
2610  *   _EXISTS - child already exists
2611  */
2612 int
2613 rc_node_create_child(rc_node_ptr_t *npp, uint32_t type, const char *name,
2614     rc_node_ptr_t *cpp)
2615 {
2616 	rc_node_t *np;
2617 	rc_node_t *cp = NULL;
2618 	int rc;
2619 
2620 	rc_node_clear(cpp, 0);
2621 
2622 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
2623 
2624 	/*
2625 	 * there is a separate interface for creating property groups
2626 	 */
2627 	if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
2628 		(void) pthread_mutex_unlock(&np->rn_lock);
2629 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2630 	}
2631 
2632 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
2633 		(void) pthread_mutex_unlock(&np->rn_lock);
2634 		np = np->rn_cchain[0];
2635 		RC_NODE_CHECK_AND_LOCK(np);
2636 	}
2637 
2638 	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
2639 	    REP_PROTOCOL_SUCCESS) {
2640 		(void) pthread_mutex_unlock(&np->rn_lock);
2641 		return (rc);
2642 	}
2643 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS) {
2644 		(void) pthread_mutex_unlock(&np->rn_lock);
2645 		return (rc);
2646 	}
2647 
2648 	if ((rc = rc_node_modify_permission_check()) != REP_PROTOCOL_SUCCESS) {
2649 		(void) pthread_mutex_unlock(&np->rn_lock);
2650 		return (rc);
2651 	}
2652 
2653 	HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_CREATING_CHILD);
2654 	(void) pthread_mutex_unlock(&np->rn_lock);
2655 
2656 	rc = object_create(np, type, name, &cp);
2657 	assert(rc != REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2658 
2659 	if (rc == REP_PROTOCOL_SUCCESS) {
2660 		rc_node_assign(cpp, cp);
2661 		rc_node_rele(cp);
2662 	}
2663 
2664 	(void) pthread_mutex_lock(&np->rn_lock);
2665 	rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
2666 	(void) pthread_mutex_unlock(&np->rn_lock);
2667 
2668 	return (rc);
2669 }
2670 
2671 int
2672 rc_node_create_child_pg(rc_node_ptr_t *npp, uint32_t type, const char *name,
2673     const char *pgtype, uint32_t flags, rc_node_ptr_t *cpp)
2674 {
2675 	rc_node_t *np;
2676 	rc_node_t *cp;
2677 	int rc;
2678 	permcheck_t *pcp;
2679 	int granted;
2680 
2681 	rc_node_clear(cpp, 0);
2682 
2683 	/* verify flags is valid */
2684 	if (flags & ~SCF_PG_FLAG_NONPERSISTENT)
2685 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
2686 
2687 	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
2688 
2689 	if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
2690 		rc_node_rele(np);
2691 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
2692 	}
2693 
2694 	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
2695 	    REP_PROTOCOL_SUCCESS) {
2696 		rc_node_rele(np);
2697 		return (rc);
2698 	}
2699 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS ||
2700 	    (rc = rc_check_pgtype_name(pgtype)) != REP_PROTOCOL_SUCCESS) {
2701 		rc_node_rele(np);
2702 		return (rc);
2703 	}
2704 
2705 	if (!client_is_privileged()) {
2706 #ifdef NATIVE_BUILD
2707 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
2708 #else
2709 		/* Must have .smf.modify or smf.modify.<type> authorization */
2710 		pcp = pc_create();
2711 		if (pcp != NULL) {
2712 			rc = perm_add_enabling(pcp, AUTH_MODIFY);
2713 
2714 			if (rc == REP_PROTOCOL_SUCCESS) {
2715 				const char * const auth =
2716 				    perm_auth_for_pgtype(pgtype);
2717 
2718 				if (auth != NULL)
2719 					rc = perm_add_enabling(pcp, auth);
2720 			}
2721 
2722 			/*
2723 			 * .manage or $action_authorization can be used to
2724 			 * create the actions pg and the general_ovr pg.
2725 			 */
2726 			if (rc == REP_PROTOCOL_SUCCESS &&
2727 			    (flags & SCF_PG_FLAG_NONPERSISTENT) != 0 &&
2728 			    np->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE &&
2729 			    ((strcmp(name, AUTH_PG_ACTIONS) == 0 &&
2730 			    strcmp(pgtype, AUTH_PG_ACTIONS_TYPE) == 0) ||
2731 			    (strcmp(name, AUTH_PG_GENERAL_OVR) == 0 &&
2732 			    strcmp(pgtype, AUTH_PG_GENERAL_OVR_TYPE) == 0))) {
2733 				rc = perm_add_enabling(pcp, AUTH_MANAGE);
2734 
2735 				if (rc == REP_PROTOCOL_SUCCESS)
2736 					rc = perm_add_inst_action_auth(pcp, np);
2737 			}
2738 
2739 			if (rc == REP_PROTOCOL_SUCCESS) {
2740 				granted = perm_granted(pcp);
2741 
2742 				if (granted < 0)
2743 					rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
2744 			}
2745 
2746 			pc_free(pcp);
2747 		} else {
2748 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
2749 		}
2750 
2751 		if (rc == REP_PROTOCOL_SUCCESS && !granted)
2752 			rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
2753 #endif /* NATIVE_BUILD */
2754 
2755 		if (rc != REP_PROTOCOL_SUCCESS) {
2756 			rc_node_rele(np);
2757 			return (rc);
2758 		}
2759 	}
2760 
2761 	(void) pthread_mutex_lock(&np->rn_lock);
2762 	HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_CREATING_CHILD);
2763 	(void) pthread_mutex_unlock(&np->rn_lock);
2764 
2765 	rc = object_create_pg(np, type, name, pgtype, flags, &cp);
2766 
2767 	if (rc == REP_PROTOCOL_SUCCESS) {
2768 		rc_node_assign(cpp, cp);
2769 		rc_node_rele(cp);
2770 	}
2771 
2772 	(void) pthread_mutex_lock(&np->rn_lock);
2773 	rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
2774 	(void) pthread_mutex_unlock(&np->rn_lock);
2775 
2776 	return (rc);
2777 }
2778 
2779 static void
2780 rc_pg_notify_fire(rc_node_pg_notify_t *pnp)
2781 {
2782 	assert(MUTEX_HELD(&rc_pg_notify_lock));
2783 
2784 	if (pnp->rnpn_pg != NULL) {
2785 		uu_list_remove(pnp->rnpn_pg->rn_pg_notify_list, pnp);
2786 		(void) close(pnp->rnpn_fd);
2787 
2788 		pnp->rnpn_pg = NULL;
2789 		pnp->rnpn_fd = -1;
2790 	} else {
2791 		assert(pnp->rnpn_fd == -1);
2792 	}
2793 }
2794 
2795 static void
2796 rc_notify_node_delete(rc_notify_delete_t *ndp, rc_node_t *np_arg)
2797 {
2798 	rc_node_t *svc = NULL;
2799 	rc_node_t *inst = NULL;
2800 	rc_node_t *pg = NULL;
2801 	rc_node_t *np = np_arg;
2802 	rc_node_t *nnp;
2803 
2804 	while (svc == NULL) {
2805 		(void) pthread_mutex_lock(&np->rn_lock);
2806 		if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
2807 			(void) pthread_mutex_unlock(&np->rn_lock);
2808 			goto cleanup;
2809 		}
2810 		nnp = np->rn_parent;
2811 		rc_node_hold_locked(np);	/* hold it in place */
2812 
2813 		switch (np->rn_id.rl_type) {
2814 		case REP_PROTOCOL_ENTITY_PROPERTYGRP:
2815 			assert(pg == NULL);
2816 			pg = np;
2817 			break;
2818 		case REP_PROTOCOL_ENTITY_INSTANCE:
2819 			assert(inst == NULL);
2820 			inst = np;
2821 			break;
2822 		case REP_PROTOCOL_ENTITY_SERVICE:
2823 			assert(svc == NULL);
2824 			svc = np;
2825 			break;
2826 		default:
2827 			rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2828 			rc_node_rele_locked(np);
2829 			goto cleanup;
2830 		}
2831 
2832 		(void) pthread_mutex_unlock(&np->rn_lock);
2833 
2834 		np = nnp;
2835 		if (np == NULL)
2836 			goto cleanup;
2837 	}
2838 
2839 	rc_notify_deletion(ndp,
2840 	    svc->rn_name,
2841 	    inst != NULL ? inst->rn_name : NULL,
2842 	    pg != NULL ? pg->rn_name : NULL);
2843 
2844 	ndp = NULL;
2845 
2846 cleanup:
2847 	if (ndp != NULL)
2848 		uu_free(ndp);
2849 
2850 	for (;;) {
2851 		if (svc != NULL) {
2852 			np = svc;
2853 			svc = NULL;
2854 		} else if (inst != NULL) {
2855 			np = inst;
2856 			inst = NULL;
2857 		} else if (pg != NULL) {
2858 			np = pg;
2859 			pg = NULL;
2860 		} else
2861 			break;
2862 
2863 		(void) pthread_mutex_lock(&np->rn_lock);
2864 		rc_node_rele_flag(np, RC_NODE_USING_PARENT);
2865 		rc_node_rele_locked(np);
2866 	}
2867 }
2868 
2869 /*
2870  * N.B.:  this function drops np->rn_lock on the way out.
2871  */
2872 static void
2873 rc_node_delete_hold(rc_node_t *np, int andformer)
2874 {
2875 	rc_node_t *cp;
2876 
2877 again:
2878 	assert(MUTEX_HELD(&np->rn_lock));
2879 	assert((np->rn_flags & RC_NODE_DYING_FLAGS) == RC_NODE_DYING_FLAGS);
2880 
2881 	for (cp = uu_list_first(np->rn_children); cp != NULL;
2882 	    cp = uu_list_next(np->rn_children, cp)) {
2883 		(void) pthread_mutex_lock(&cp->rn_lock);
2884 		(void) pthread_mutex_unlock(&np->rn_lock);
2885 		if (!rc_node_hold_flag(cp, RC_NODE_DYING_FLAGS)) {
2886 			/*
2887 			 * already marked as dead -- can't happen, since that
2888 			 * would require setting RC_NODE_CHILDREN_CHANGING
2889 			 * in np, and we're holding that...
2890 			 */
2891 			abort();
2892 		}
2893 		rc_node_delete_hold(cp, andformer);	/* recurse, drop lock */
2894 
2895 		(void) pthread_mutex_lock(&np->rn_lock);
2896 	}
2897 	if (andformer && (cp = np->rn_former) != NULL) {
2898 		(void) pthread_mutex_lock(&cp->rn_lock);
2899 		(void) pthread_mutex_unlock(&np->rn_lock);
2900 		if (!rc_node_hold_flag(cp, RC_NODE_DYING_FLAGS))
2901 			abort();		/* can't happen, see above */
2902 		np = cp;
2903 		goto again;		/* tail-recurse down rn_former */
2904 	}
2905 	(void) pthread_mutex_unlock(&np->rn_lock);
2906 }
2907 
2908 /*
2909  * N.B.:  this function drops np->rn_lock on the way out.
2910  */
2911 static void
2912 rc_node_delete_rele(rc_node_t *np, int andformer)
2913 {
2914 	rc_node_t *cp;
2915 
2916 again:
2917 	assert(MUTEX_HELD(&np->rn_lock));
2918 	assert((np->rn_flags & RC_NODE_DYING_FLAGS) == RC_NODE_DYING_FLAGS);
2919 
2920 	for (cp = uu_list_first(np->rn_children); cp != NULL;
2921 	    cp = uu_list_next(np->rn_children, cp)) {
2922 		(void) pthread_mutex_lock(&cp->rn_lock);
2923 		(void) pthread_mutex_unlock(&np->rn_lock);
2924 		rc_node_delete_rele(cp, andformer);	/* recurse, drop lock */
2925 		(void) pthread_mutex_lock(&np->rn_lock);
2926 	}
2927 	if (andformer && (cp = np->rn_former) != NULL) {
2928 		(void) pthread_mutex_lock(&cp->rn_lock);
2929 		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
2930 		(void) pthread_mutex_unlock(&np->rn_lock);
2931 
2932 		np = cp;
2933 		goto again;		/* tail-recurse down rn_former */
2934 	}
2935 	rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
2936 	(void) pthread_mutex_unlock(&np->rn_lock);
2937 }
2938 
2939 static void
2940 rc_node_finish_delete(rc_node_t *cp)
2941 {
2942 	cache_bucket_t *bp;
2943 	rc_node_pg_notify_t *pnp;
2944 
2945 	assert(MUTEX_HELD(&cp->rn_lock));
2946 
2947 	if (!(cp->rn_flags & RC_NODE_OLD)) {
2948 		assert(cp->rn_flags & RC_NODE_IN_PARENT);
2949 		if (!rc_node_wait_flag(cp, RC_NODE_USING_PARENT)) {
2950 			abort();		/* can't happen, see above */
2951 		}
2952 		cp->rn_flags &= ~RC_NODE_IN_PARENT;
2953 		cp->rn_parent = NULL;
2954 	}
2955 
2956 	cp->rn_flags |= RC_NODE_DEAD;
2957 
2958 	/*
2959 	 * If this node is not out-dated, we need to remove it from
2960 	 * the notify list and cache hash table.
2961 	 */
2962 	if (!(cp->rn_flags & RC_NODE_OLD)) {
2963 		assert(cp->rn_refs > 0);	/* can't go away yet */
2964 		(void) pthread_mutex_unlock(&cp->rn_lock);
2965 
2966 		(void) pthread_mutex_lock(&rc_pg_notify_lock);
2967 		while ((pnp = uu_list_first(cp->rn_pg_notify_list)) != NULL)
2968 			rc_pg_notify_fire(pnp);
2969 		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
2970 		rc_notify_remove_node(cp);
2971 
2972 		bp = cache_hold(cp->rn_hash);
2973 		(void) pthread_mutex_lock(&cp->rn_lock);
2974 		cache_remove_unlocked(bp, cp);
2975 		cache_release(bp);
2976 	}
2977 }
2978 
2979 /*
2980  * N.B.:  this function drops np->rn_lock and a reference on the way out.
2981  */
2982 static void
2983 rc_node_delete_children(rc_node_t *np, int andformer)
2984 {
2985 	rc_node_t *cp;
2986 
2987 again:
2988 	assert(np->rn_refs > 0);
2989 	assert(MUTEX_HELD(&np->rn_lock));
2990 	assert(np->rn_flags & RC_NODE_DEAD);
2991 
2992 	while ((cp = uu_list_first(np->rn_children)) != NULL) {
2993 		uu_list_remove(np->rn_children, cp);
2994 		(void) pthread_mutex_lock(&cp->rn_lock);
2995 		(void) pthread_mutex_unlock(&np->rn_lock);
2996 		rc_node_hold_locked(cp);	/* hold while we recurse */
2997 		rc_node_finish_delete(cp);
2998 		rc_node_delete_children(cp, andformer);	/* drops lock + ref */
2999 		(void) pthread_mutex_lock(&np->rn_lock);
3000 	}
3001 
3002 	/*
3003 	 * when we drop cp's lock, all the children will be gone, so we
3004 	 * can release DYING_FLAGS.
3005 	 */
3006 	rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
3007 	if (andformer && (cp = np->rn_former) != NULL) {
3008 		np->rn_former = NULL;		/* unlink */
3009 		(void) pthread_mutex_lock(&cp->rn_lock);
3010 		(void) pthread_mutex_unlock(&np->rn_lock);
3011 		np->rn_flags &= ~RC_NODE_ON_FORMER;
3012 
3013 		rc_node_hold_locked(cp);	/* hold while we loop */
3014 
3015 		rc_node_finish_delete(cp);
3016 
3017 		rc_node_rele(np);		/* drop the old reference */
3018 
3019 		np = cp;
3020 		goto again;		/* tail-recurse down rn_former */
3021 	}
3022 	rc_node_rele_locked(np);
3023 }
3024 
3025 static void
3026 rc_node_unrefed(rc_node_t *np)
3027 {
3028 	int unrefed;
3029 	rc_node_t *pp, *cur;
3030 
3031 	assert(MUTEX_HELD(&np->rn_lock));
3032 	assert(np->rn_refs == 0);
3033 	assert(np->rn_other_refs == 0);
3034 	assert(np->rn_other_refs_held == 0);
3035 
3036 	if (np->rn_flags & RC_NODE_DEAD) {
3037 		(void) pthread_mutex_unlock(&np->rn_lock);
3038 		rc_node_destroy(np);
3039 		return;
3040 	}
3041 
3042 	assert(np->rn_flags & RC_NODE_OLD);
3043 	if (np->rn_flags & RC_NODE_UNREFED) {
3044 		(void) pthread_mutex_unlock(&np->rn_lock);
3045 		return;
3046 	}
3047 	np->rn_flags |= RC_NODE_UNREFED;
3048 
3049 	(void) pthread_mutex_unlock(&np->rn_lock);
3050 
3051 	/*
3052 	 * find the current in-hash object, and grab it's RC_NODE_IN_TX
3053 	 * flag.  That protects the entire rn_former chain.
3054 	 */
3055 	for (;;) {
3056 		pp = cache_lookup(&np->rn_id);
3057 		if (pp == NULL) {
3058 			(void) pthread_mutex_lock(&np->rn_lock);
3059 			if (np->rn_flags & RC_NODE_DEAD)
3060 				goto died;
3061 			/*
3062 			 * We are trying to unreference this node, but the
3063 			 * owner of the former list does not exist.  It must
3064 			 * be the case that another thread is deleting this
3065 			 * entire sub-branch, but has not yet reached us.
3066 			 * We will in short order be deleted.
3067 			 */
3068 			np->rn_flags &= ~RC_NODE_UNREFED;
3069 			(void) pthread_mutex_unlock(&np->rn_lock);
3070 			return;
3071 		}
3072 		if (pp == np) {
3073 			/*
3074 			 * no longer unreferenced
3075 			 */
3076 			(void) pthread_mutex_lock(&np->rn_lock);
3077 			np->rn_flags &= ~RC_NODE_UNREFED;
3078 			rc_node_rele_locked(np);
3079 			return;
3080 		}
3081 		(void) pthread_mutex_lock(&pp->rn_lock);
3082 		if ((pp->rn_flags & RC_NODE_OLD) ||
3083 		    !rc_node_hold_flag(pp, RC_NODE_IN_TX)) {
3084 			rc_node_rele_locked(pp);
3085 			continue;
3086 		}
3087 		if (!(pp->rn_flags & RC_NODE_OLD)) {
3088 			(void) pthread_mutex_unlock(&pp->rn_lock);
3089 			break;
3090 		}
3091 		rc_node_rele_flag(pp, RC_NODE_IN_TX);
3092 		rc_node_rele_locked(pp);
3093 	}
3094 
3095 	(void) pthread_mutex_lock(&np->rn_lock);
3096 	if (!(np->rn_flags & (RC_NODE_OLD | RC_NODE_DEAD)) ||
3097 	    np->rn_refs != 0 || np->rn_other_refs != 0 ||
3098 	    np->rn_other_refs_held != 0) {
3099 		np->rn_flags &= ~RC_NODE_UNREFED;
3100 		(void) pthread_mutex_lock(&pp->rn_lock);
3101 
3102 		rc_node_rele_flag(pp, RC_NODE_IN_TX);
3103 		rc_node_rele_locked(pp);
3104 		return;
3105 	}
3106 
3107 	if (!rc_node_hold_flag(np, RC_NODE_DYING_FLAGS)) {
3108 		(void) pthread_mutex_unlock(&np->rn_lock);
3109 
3110 		rc_node_rele_flag(pp, RC_NODE_IN_TX);
3111 		rc_node_rele_locked(pp);
3112 
3113 		(void) pthread_mutex_lock(&np->rn_lock);
3114 		goto died;
3115 	}
3116 
3117 	rc_node_delete_hold(np, 0);
3118 
3119 	(void) pthread_mutex_lock(&np->rn_lock);
3120 	if (!(np->rn_flags & RC_NODE_OLD) ||
3121 	    np->rn_refs != 0 || np->rn_other_refs != 0 ||
3122 	    np->rn_other_refs_held != 0) {
3123 		np->rn_flags &= ~RC_NODE_UNREFED;
3124 		rc_node_delete_rele(np, 0);
3125 
3126 		(void) pthread_mutex_lock(&pp->rn_lock);
3127 		rc_node_rele_flag(pp, RC_NODE_IN_TX);
3128 		rc_node_rele_locked(pp);
3129 		return;
3130 	}
3131 
3132 	np->rn_flags |= RC_NODE_DEAD;
3133 	rc_node_hold_locked(np);
3134 	rc_node_delete_children(np, 0);
3135 
3136 	/*
3137 	 * It's gone -- remove it from the former chain and destroy it.
3138 	 */
3139 	(void) pthread_mutex_lock(&pp->rn_lock);
3140 	for (cur = pp; cur != NULL && cur->rn_former != np;
3141 	    cur = cur->rn_former)
3142 		;
3143 	assert(cur != NULL && cur != np);
3144 
3145 	cur->rn_former = np->rn_former;
3146 	np->rn_former = NULL;
3147 
3148 	rc_node_rele_flag(pp, RC_NODE_IN_TX);
3149 	rc_node_rele_locked(pp);
3150 
3151 	(void) pthread_mutex_lock(&np->rn_lock);
3152 	assert(np->rn_flags & RC_NODE_ON_FORMER);
3153 	np->rn_flags &= ~(RC_NODE_UNREFED | RC_NODE_ON_FORMER);
3154 	(void) pthread_mutex_unlock(&np->rn_lock);
3155 	rc_node_destroy(np);
3156 	return;
3157 
3158 died:
3159 	np->rn_flags &= ~RC_NODE_UNREFED;
3160 	unrefed = (np->rn_refs == 0 && np->rn_other_refs == 0 &&
3161 	    np->rn_other_refs_held == 0);
3162 	(void) pthread_mutex_unlock(&np->rn_lock);
3163 	if (unrefed)
3164 		rc_node_destroy(np);
3165 }
3166 
3167 /*
3168  * Fails with
3169  *   _NOT_SET
3170  *   _DELETED
3171  *   _BAD_REQUEST
3172  *   _PERMISSION_DENIED
3173  *   _NO_RESOURCES
3174  * and whatever object_delete() fails with.
3175  */
3176 int
3177 rc_node_delete(rc_node_ptr_t *npp)
3178 {
3179 	rc_node_t *np, *np_orig;
3180 	rc_node_t *pp = NULL;
3181 	int rc;
3182 	rc_node_pg_notify_t *pnp;
3183 	cache_bucket_t *bp;
3184 	rc_notify_delete_t *ndp;
3185 	permcheck_t *pcp;
3186 	int granted;
3187 
3188 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
3189 
3190 	switch (np->rn_id.rl_type) {
3191 	case REP_PROTOCOL_ENTITY_SERVICE:
3192 	case REP_PROTOCOL_ENTITY_INSTANCE:
3193 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
3194 		break;			/* deletable */
3195 
3196 	case REP_PROTOCOL_ENTITY_SCOPE:
3197 	case REP_PROTOCOL_ENTITY_SNAPLEVEL:
3198 		/* Scopes and snaplevels are indelible. */
3199 		(void) pthread_mutex_unlock(&np->rn_lock);
3200 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
3201 
3202 	case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
3203 		(void) pthread_mutex_unlock(&np->rn_lock);
3204 		np = np->rn_cchain[0];
3205 		RC_NODE_CHECK_AND_LOCK(np);
3206 		break;
3207 
3208 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
3209 		if (np->rn_id.rl_ids[ID_SNAPSHOT] == 0)
3210 			break;
3211 
3212 		/* Snapshot property groups are indelible. */
3213 		(void) pthread_mutex_unlock(&np->rn_lock);
3214 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
3215 
3216 	case REP_PROTOCOL_ENTITY_PROPERTY:
3217 		(void) pthread_mutex_unlock(&np->rn_lock);
3218 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
3219 
3220 	default:
3221 		assert(0);
3222 		abort();
3223 		break;
3224 	}
3225 
3226 	np_orig = np;
3227 	rc_node_hold_locked(np);	/* simplifies rest of the code */
3228 
3229 again:
3230 	/*
3231 	 * The following loop is to deal with the fact that snapshots and
3232 	 * property groups are moving targets -- changes to them result
3233 	 * in a new "child" node.  Since we can only delete from the top node,
3234 	 * we have to loop until we have a non-RC_NODE_OLD version.
3235 	 */
3236 	for (;;) {
3237 		if (!rc_node_wait_flag(np,
3238 		    RC_NODE_IN_TX | RC_NODE_USING_PARENT)) {
3239 			rc_node_rele_locked(np);
3240 			return (REP_PROTOCOL_FAIL_DELETED);
3241 		}
3242 
3243 		if (np->rn_flags & RC_NODE_OLD) {
3244 			rc_node_rele_locked(np);
3245 			np = cache_lookup(&np_orig->rn_id);
3246 			assert(np != np_orig);
3247 
3248 			if (np == NULL) {
3249 				rc = REP_PROTOCOL_FAIL_DELETED;
3250 				goto fail;
3251 			}
3252 			(void) pthread_mutex_lock(&np->rn_lock);
3253 			continue;
3254 		}
3255 
3256 		if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
3257 			rc_node_rele_locked(np);
3258 			rc_node_clear(npp, 1);
3259 			return (REP_PROTOCOL_FAIL_DELETED);
3260 		}
3261 
3262 		/*
3263 		 * Mark our parent as children changing.  this call drops our
3264 		 * lock and the RC_NODE_USING_PARENT flag, and returns with
3265 		 * pp's lock held
3266 		 */
3267 		pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
3268 		if (pp == NULL) {
3269 			/* our parent is gone, we're going next... */
3270 			rc_node_rele(np);
3271 
3272 			rc_node_clear(npp, 1);
3273 			return (REP_PROTOCOL_FAIL_DELETED);
3274 		}
3275 
3276 		rc_node_hold_locked(pp);		/* hold for later */
3277 		(void) pthread_mutex_unlock(&pp->rn_lock);
3278 
3279 		(void) pthread_mutex_lock(&np->rn_lock);
3280 		if (!(np->rn_flags & RC_NODE_OLD))
3281 			break;			/* not old -- we're done */
3282 
3283 		(void) pthread_mutex_unlock(&np->rn_lock);
3284 		(void) pthread_mutex_lock(&pp->rn_lock);
3285 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
3286 		rc_node_rele_locked(pp);
3287 		(void) pthread_mutex_lock(&np->rn_lock);
3288 		continue;			/* loop around and try again */
3289 	}
3290 	/*
3291 	 * Everyone out of the pool -- we grab everything but
3292 	 * RC_NODE_USING_PARENT (including RC_NODE_DYING) to keep
3293 	 * any changes from occurring while we are attempting to
3294 	 * delete the node.
3295 	 */
3296 	if (!rc_node_hold_flag(np, RC_NODE_DYING_FLAGS)) {
3297 		(void) pthread_mutex_unlock(&np->rn_lock);
3298 		rc = REP_PROTOCOL_FAIL_DELETED;
3299 		goto fail;
3300 	}
3301 
3302 	assert(!(np->rn_flags & RC_NODE_OLD));
3303 
3304 	if (!client_is_privileged()) {
3305 		/* permission check */
3306 		(void) pthread_mutex_unlock(&np->rn_lock);
3307 
3308 #ifdef NATIVE_BUILD
3309 		rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
3310 #else
3311 		pcp = pc_create();
3312 		if (pcp != NULL) {
3313 			rc = perm_add_enabling(pcp, AUTH_MODIFY);
3314 
3315 			/* add .smf.modify.<type> for pgs. */
3316 			if (rc == REP_PROTOCOL_SUCCESS && np->rn_id.rl_type ==
3317 			    REP_PROTOCOL_ENTITY_PROPERTYGRP) {
3318 				const char * const auth =
3319 				    perm_auth_for_pgtype(np->rn_type);
3320 
3321 				if (auth != NULL)
3322 					rc = perm_add_enabling(pcp, auth);
3323 			}
3324 
3325 			if (rc == REP_PROTOCOL_SUCCESS) {
3326 				granted = perm_granted(pcp);
3327 
3328 				if (granted < 0)
3329 					rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
3330 			}
3331 
3332 			pc_free(pcp);
3333 		} else {
3334 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
3335 		}
3336 
3337 		if (rc == REP_PROTOCOL_SUCCESS && !granted)
3338 			rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
3339 #endif /* NATIVE_BUILD */
3340 
3341 		if (rc != REP_PROTOCOL_SUCCESS) {
3342 			(void) pthread_mutex_lock(&np->rn_lock);
3343 			rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
3344 			(void) pthread_mutex_unlock(&np->rn_lock);
3345 			goto fail;
3346 		}
3347 
3348 		(void) pthread_mutex_lock(&np->rn_lock);
3349 	}
3350 
3351 	ndp = uu_zalloc(sizeof (*ndp));
3352 	if (ndp == NULL) {
3353 		rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
3354 		(void) pthread_mutex_unlock(&np->rn_lock);
3355 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
3356 		goto fail;
3357 	}
3358 
3359 	rc_node_delete_hold(np, 1);	/* hold entire subgraph, drop lock */
3360 
3361 	rc = object_delete(np);
3362 
3363 	if (rc != REP_PROTOCOL_SUCCESS) {
3364 		(void) pthread_mutex_lock(&np->rn_lock);
3365 		rc_node_delete_rele(np, 1);		/* drops lock */
3366 		uu_free(ndp);
3367 		goto fail;
3368 	}
3369 
3370 	/*
3371 	 * Now, delicately unlink and delete the object.
3372 	 *
3373 	 * Create the delete notification, atomically remove
3374 	 * from the hash table and set the NODE_DEAD flag, and
3375 	 * remove from the parent's children list.
3376 	 */
3377 	rc_notify_node_delete(ndp, np); /* frees or uses ndp */
3378 
3379 	bp = cache_hold(np->rn_hash);
3380 
3381 	(void) pthread_mutex_lock(&np->rn_lock);
3382 	cache_remove_unlocked(bp, np);
3383 	cache_release(bp);
3384 
3385 	np->rn_flags |= RC_NODE_DEAD;
3386 	if (pp != NULL) {
3387 		(void) pthread_mutex_unlock(&np->rn_lock);
3388 
3389 		(void) pthread_mutex_lock(&pp->rn_lock);
3390 		(void) pthread_mutex_lock(&np->rn_lock);
3391 		uu_list_remove(pp->rn_children, np);
3392 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
3393 		(void) pthread_mutex_unlock(&pp->rn_lock);
3394 		np->rn_flags &= ~RC_NODE_IN_PARENT;
3395 	}
3396 	/*
3397 	 * finally, propagate death to our children, handle notifications,
3398 	 * and release our hold.
3399 	 */
3400 	rc_node_hold_locked(np);	/* hold for delete */
3401 	rc_node_delete_children(np, 1);	/* drops DYING_FLAGS, lock, ref */
3402 
3403 	rc_node_clear(npp, 1);
3404 
3405 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
3406 	while ((pnp = uu_list_first(np->rn_pg_notify_list)) != NULL)
3407 		rc_pg_notify_fire(pnp);
3408 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
3409 	rc_notify_remove_node(np);
3410 
3411 	rc_node_rele(np);
3412 
3413 	return (rc);
3414 
3415 fail:
3416 	rc_node_rele(np);
3417 	if (rc == REP_PROTOCOL_FAIL_DELETED)
3418 		rc_node_clear(npp, 1);
3419 	if (pp != NULL) {
3420 		(void) pthread_mutex_lock(&pp->rn_lock);
3421 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
3422 		rc_node_rele_locked(pp);	/* drop ref and lock */
3423 	}
3424 	return (rc);
3425 }
3426 
3427 int
3428 rc_node_next_snaplevel(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
3429 {
3430 	rc_node_t *np;
3431 	rc_node_t *cp, *pp;
3432 	int res;
3433 
3434 	rc_node_clear(cpp, 0);
3435 
3436 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
3437 
3438 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT &&
3439 	    np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL) {
3440 		(void) pthread_mutex_unlock(&np->rn_lock);
3441 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
3442 	}
3443 
3444 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_SNAPSHOT) {
3445 		if ((res = rc_node_fill_children(np,
3446 		    REP_PROTOCOL_ENTITY_SNAPLEVEL)) != REP_PROTOCOL_SUCCESS) {
3447 			(void) pthread_mutex_unlock(&np->rn_lock);
3448 			return (res);
3449 		}
3450 
3451 		for (cp = uu_list_first(np->rn_children);
3452 		    cp != NULL;
3453 		    cp = uu_list_next(np->rn_children, cp)) {
3454 			if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
3455 				continue;
3456 			rc_node_hold(cp);
3457 			break;
3458 		}
3459 
3460 		(void) pthread_mutex_unlock(&np->rn_lock);
3461 	} else {
3462 		HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_USING_PARENT);
3463 		/*
3464 		 * mark our parent as children changing.  This call drops our
3465 		 * lock and the RC_NODE_USING_PARENT flag, and returns with
3466 		 * pp's lock held
3467 		 */
3468 		pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
3469 		if (pp == NULL) {
3470 			/* our parent is gone, we're going next... */
3471 
3472 			rc_node_clear(npp, 1);
3473 			return (REP_PROTOCOL_FAIL_DELETED);
3474 		}
3475 
3476 		/*
3477 		 * find the next snaplevel
3478 		 */
3479 		cp = np;
3480 		while ((cp = uu_list_next(pp->rn_children, cp)) != NULL &&
3481 		    cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPLEVEL)
3482 			;
3483 
3484 		/* it must match the snaplevel list */
3485 		assert((cp == NULL && np->rn_snaplevel->rsl_next == NULL) ||
3486 		    (cp != NULL && np->rn_snaplevel->rsl_next ==
3487 		    cp->rn_snaplevel));
3488 
3489 		if (cp != NULL)
3490 			rc_node_hold(cp);
3491 
3492 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
3493 
3494 		(void) pthread_mutex_unlock(&pp->rn_lock);
3495 	}
3496 
3497 	rc_node_assign(cpp, cp);
3498 	if (cp != NULL) {
3499 		rc_node_rele(cp);
3500 
3501 		return (REP_PROTOCOL_SUCCESS);
3502 	}
3503 	return (REP_PROTOCOL_FAIL_NOT_FOUND);
3504 }
3505 
3506 /*
3507  * This call takes a snapshot (np) and either:
3508  *	an existing snapid (to be associated with np), or
3509  *	a non-NULL parentp (from which a new snapshot is taken, and associated
3510  *	    with np)
3511  *
3512  * To do the association, np is duplicated, the duplicate is made to
3513  * represent the new snapid, and np is replaced with the new rc_node_t on
3514  * np's parent's child list. np is placed on the new node's rn_former list,
3515  * and replaces np in cache_hash (so rc_node_update() will find the new one).
3516  */
3517 static int
3518 rc_attach_snapshot(rc_node_t *np, uint32_t snapid, rc_node_t *parentp)
3519 {
3520 	rc_node_t *np_orig;
3521 	rc_node_t *nnp, *prev;
3522 	rc_node_t *pp;
3523 	int rc;
3524 
3525 	if (parentp != NULL)
3526 		assert(snapid == 0);
3527 
3528 	assert(MUTEX_HELD(&np->rn_lock));
3529 
3530 	if ((rc = rc_node_modify_permission_check()) != REP_PROTOCOL_SUCCESS) {
3531 		(void) pthread_mutex_unlock(&np->rn_lock);
3532 		return (rc);
3533 	}
3534 
3535 	np_orig = np;
3536 	rc_node_hold_locked(np);		/* simplifies the remainder */
3537 
3538 	/*
3539 	 * get the latest node, holding RC_NODE_IN_TX to keep the rn_former
3540 	 * list from changing.
3541 	 */
3542 	for (;;) {
3543 		if (!(np->rn_flags & RC_NODE_OLD)) {
3544 			if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
3545 				goto again;
3546 			}
3547 			pp = rc_node_hold_parent_flag(np,
3548 			    RC_NODE_CHILDREN_CHANGING);
3549 
3550 			(void) pthread_mutex_lock(&np->rn_lock);
3551 			if (pp == NULL) {
3552 				goto again;
3553 			}
3554 			if (np->rn_flags & RC_NODE_OLD) {
3555 				rc_node_rele_flag(pp,
3556 				    RC_NODE_CHILDREN_CHANGING);
3557 				(void) pthread_mutex_unlock(&pp->rn_lock);
3558 				goto again;
3559 			}
3560 			(void) pthread_mutex_unlock(&pp->rn_lock);
3561 
3562 			if (!rc_node_hold_flag(np, RC_NODE_IN_TX)) {
3563 				/*
3564 				 * Can't happen, since we're holding our
3565 				 * parent's CHILDREN_CHANGING flag...
3566 				 */
3567 				abort();
3568 			}
3569 			break;			/* everything's ready */
3570 		}
3571 again:
3572 		rc_node_rele_locked(np);
3573 		np = cache_lookup(&np_orig->rn_id);
3574 
3575 		if (np == NULL)
3576 			return (REP_PROTOCOL_FAIL_DELETED);
3577 
3578 		(void) pthread_mutex_lock(&np->rn_lock);
3579 	}
3580 
3581 	if (parentp != NULL) {
3582 		if (pp != parentp) {
3583 			rc = REP_PROTOCOL_FAIL_BAD_REQUEST;
3584 			goto fail;
3585 		}
3586 		nnp = NULL;
3587 	} else {
3588 		/*
3589 		 * look for a former node with the snapid we need.
3590 		 */
3591 		if (np->rn_snapshot_id == snapid) {
3592 			rc_node_rele_flag(np, RC_NODE_IN_TX);
3593 			rc_node_rele_locked(np);
3594 
3595 			(void) pthread_mutex_lock(&pp->rn_lock);
3596 			rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
3597 			(void) pthread_mutex_unlock(&pp->rn_lock);
3598 			return (REP_PROTOCOL_SUCCESS);	/* nothing to do */
3599 		}
3600 
3601 		prev = np;
3602 		while ((nnp = prev->rn_former) != NULL) {
3603 			if (nnp->rn_snapshot_id == snapid) {
3604 				rc_node_hold(nnp);
3605 				break;		/* existing node with that id */
3606 			}
3607 			prev = nnp;
3608 		}
3609 	}
3610 
3611 	if (nnp == NULL) {
3612 		prev = NULL;
3613 		nnp = rc_node_alloc();
3614 		if (nnp == NULL) {
3615 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
3616 			goto fail;
3617 		}
3618 
3619 		nnp->rn_id = np->rn_id;		/* structure assignment */
3620 		nnp->rn_hash = np->rn_hash;
3621 		nnp->rn_name = strdup(np->rn_name);
3622 		nnp->rn_snapshot_id = snapid;
3623 		nnp->rn_flags = RC_NODE_IN_TX | RC_NODE_USING_PARENT;
3624 
3625 		if (nnp->rn_name == NULL) {
3626 			rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
3627 			goto fail;
3628 		}
3629 	}
3630 
3631 	(void) pthread_mutex_unlock(&np->rn_lock);
3632 
3633 	rc = object_snapshot_attach(&np->rn_id, &snapid, (parentp != NULL));
3634 
3635 	if (parentp != NULL)
3636 		nnp->rn_snapshot_id = snapid;	/* fill in new snapid */
3637 	else
3638 		assert(nnp->rn_snapshot_id == snapid);
3639 
3640 	(void) pthread_mutex_lock(&np->rn_lock);
3641 	if (rc != REP_PROTOCOL_SUCCESS)
3642 		goto fail;
3643 
3644 	/*
3645 	 * fix up the former chain
3646 	 */
3647 	if (prev != NULL) {
3648 		prev->rn_former = nnp->rn_former;
3649 		(void) pthread_mutex_lock(&nnp->rn_lock);
3650 		nnp->rn_flags &= ~RC_NODE_ON_FORMER;
3651 		nnp->rn_former = NULL;
3652 		(void) pthread_mutex_unlock(&nnp->rn_lock);
3653 	}
3654 	np->rn_flags |= RC_NODE_OLD;
3655 	(void) pthread_mutex_unlock(&np->rn_lock);
3656 
3657 	/*
3658 	 * replace np with nnp
3659 	 */
3660 	rc_node_relink_child(pp, np, nnp);
3661 
3662 	rc_node_rele(np);
3663 
3664 	return (REP_PROTOCOL_SUCCESS);
3665 
3666 fail:
3667 	rc_node_rele_flag(np, RC_NODE_IN_TX);
3668 	rc_node_rele_locked(np);
3669 	(void) pthread_mutex_lock(&pp->rn_lock);
3670 	rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
3671 	(void) pthread_mutex_unlock(&pp->rn_lock);
3672 
3673 	if (nnp != NULL) {
3674 		if (prev == NULL)
3675 			rc_node_destroy(nnp);
3676 		else
3677 			rc_node_rele(nnp);
3678 	}
3679 
3680 	return (rc);
3681 }
3682 
3683 int
3684 rc_snapshot_take_new(rc_node_ptr_t *npp, const char *svcname,
3685     const char *instname, const char *name, rc_node_ptr_t *outpp)
3686 {
3687 	rc_node_t *np;
3688 	rc_node_t *outp = NULL;
3689 	int rc;
3690 
3691 	rc_node_clear(outpp, 0);
3692 
3693 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
3694 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) {
3695 		(void) pthread_mutex_unlock(&np->rn_lock);
3696 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
3697 	}
3698 
3699 	rc = rc_check_type_name(REP_PROTOCOL_ENTITY_SNAPSHOT, name);
3700 	if (rc != REP_PROTOCOL_SUCCESS) {
3701 		(void) pthread_mutex_unlock(&np->rn_lock);
3702 		return (rc);
3703 	}
3704 
3705 	if (svcname != NULL && (rc =
3706 	    rc_check_type_name(REP_PROTOCOL_ENTITY_SERVICE, svcname)) !=
3707 	    REP_PROTOCOL_SUCCESS) {
3708 		(void) pthread_mutex_unlock(&np->rn_lock);
3709 		return (rc);
3710 	}
3711 
3712 	if (instname != NULL && (rc =
3713 	    rc_check_type_name(REP_PROTOCOL_ENTITY_INSTANCE, instname)) !=
3714 	    REP_PROTOCOL_SUCCESS) {
3715 		(void) pthread_mutex_unlock(&np->rn_lock);
3716 		return (rc);
3717 	}
3718 
3719 	if ((rc = rc_node_modify_permission_check()) != REP_PROTOCOL_SUCCESS) {
3720 		(void) pthread_mutex_unlock(&np->rn_lock);
3721 		return (rc);
3722 	}
3723 
3724 	HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_CREATING_CHILD);
3725 	(void) pthread_mutex_unlock(&np->rn_lock);
3726 
3727 	rc = object_snapshot_take_new(np, svcname, instname, name, &outp);
3728 
3729 	if (rc == REP_PROTOCOL_SUCCESS) {
3730 		rc_node_assign(outpp, outp);
3731 		rc_node_rele(outp);
3732 	}
3733 
3734 	(void) pthread_mutex_lock(&np->rn_lock);
3735 	rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
3736 	(void) pthread_mutex_unlock(&np->rn_lock);
3737 
3738 	return (rc);
3739 }
3740 
3741 int
3742 rc_snapshot_take_attach(rc_node_ptr_t *npp, rc_node_ptr_t *outpp)
3743 {
3744 	rc_node_t *np, *outp;
3745 
3746 	RC_NODE_PTR_GET_CHECK(np, npp);
3747 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) {
3748 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
3749 	}
3750 
3751 	RC_NODE_PTR_GET_CHECK_AND_LOCK(outp, outpp);
3752 	if (outp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
3753 		(void) pthread_mutex_unlock(&outp->rn_lock);
3754 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
3755 	}
3756 
3757 	return (rc_attach_snapshot(outp, 0, np));	/* drops outp's lock */
3758 }
3759 
3760 int
3761 rc_snapshot_attach(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
3762 {
3763 	rc_node_t *np;
3764 	rc_node_t *cp;
3765 	uint32_t snapid;
3766 
3767 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
3768 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
3769 		(void) pthread_mutex_unlock(&np->rn_lock);
3770 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
3771 	}
3772 	snapid = np->rn_snapshot_id;
3773 	(void) pthread_mutex_unlock(&np->rn_lock);
3774 
3775 	RC_NODE_PTR_GET_CHECK_AND_LOCK(cp, cpp);
3776 	if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
3777 		(void) pthread_mutex_unlock(&cp->rn_lock);
3778 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
3779 	}
3780 
3781 	return (rc_attach_snapshot(cp, snapid, NULL));	/* drops cp's lock */
3782 }
3783 
3784 /*
3785  * Iteration
3786  */
3787 static int
3788 rc_iter_filter_name(rc_node_t *np, void *s)
3789 {
3790 	const char *name = s;
3791 
3792 	return (strcmp(np->rn_name, name) == 0);
3793 }
3794 
3795 static int
3796 rc_iter_filter_type(rc_node_t *np, void *s)
3797 {
3798 	const char *type = s;
3799 
3800 	return (np->rn_type != NULL && strcmp(np->rn_type, type) == 0);
3801 }
3802 
3803 /*ARGSUSED*/
3804 static int
3805 rc_iter_null_filter(rc_node_t *np, void *s)
3806 {
3807 	return (1);
3808 }
3809 
3810 /*
3811  * Allocate & initialize an rc_node_iter_t structure.  Essentially, ensure
3812  * np->rn_children is populated and call uu_list_walk_start(np->rn_children).
3813  * If successful, leaves a hold on np & increments np->rn_other_refs
3814  *
3815  * If composed is true, then set up for iteration across the top level of np's
3816  * composition chain.  If successful, leaves a hold on np and increments
3817  * rn_other_refs for the top level of np's composition chain.
3818  *
3819  * Fails with
3820  *   _NO_RESOURCES
3821  *   _INVALID_TYPE
3822  *   _TYPE_MISMATCH - np cannot carry type children
3823  *   _DELETED
3824  */
3825 static int
3826 rc_iter_create(rc_node_iter_t **resp, rc_node_t *np, uint32_t type,
3827     rc_iter_filter_func *filter, void *arg, boolean_t composed)
3828 {
3829 	rc_node_iter_t *nip;
3830 	int res;
3831 
3832 	assert(*resp == NULL);
3833 
3834 	nip = uu_zalloc(sizeof (*nip));
3835 	if (nip == NULL)
3836 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
3837 
3838 	/* np is held by the client's rc_node_ptr_t */
3839 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP)
3840 		composed = 1;
3841 
3842 	if (!composed) {
3843 		(void) pthread_mutex_lock(&np->rn_lock);
3844 
3845 		if ((res = rc_node_fill_children(np, type)) !=
3846 		    REP_PROTOCOL_SUCCESS) {
3847 			(void) pthread_mutex_unlock(&np->rn_lock);
3848 			uu_free(nip);
3849 			return (res);
3850 		}
3851 
3852 		nip->rni_clevel = -1;
3853 
3854 		nip->rni_iter = uu_list_walk_start(np->rn_children,
3855 		    UU_WALK_ROBUST);
3856 		if (nip->rni_iter != NULL) {
3857 			nip->rni_iter_node = np;
3858 			rc_node_hold_other(np);
3859 		} else {
3860 			(void) pthread_mutex_unlock(&np->rn_lock);
3861 			uu_free(nip);
3862 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
3863 		}
3864 		(void) pthread_mutex_unlock(&np->rn_lock);
3865 	} else {
3866 		rc_node_t *ent;
3867 
3868 		if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_SNAPSHOT) {
3869 			/* rn_cchain isn't valid until children are loaded. */
3870 			(void) pthread_mutex_lock(&np->rn_lock);
3871 			res = rc_node_fill_children(np,
3872 			    REP_PROTOCOL_ENTITY_SNAPLEVEL);
3873 			(void) pthread_mutex_unlock(&np->rn_lock);
3874 			if (res != REP_PROTOCOL_SUCCESS) {
3875 				uu_free(nip);
3876 				return (res);
3877 			}
3878 
3879 			/* Check for an empty snapshot. */
3880 			if (np->rn_cchain[0] == NULL)
3881 				goto empty;
3882 		}
3883 
3884 		/* Start at the top of the composition chain. */
3885 		for (nip->rni_clevel = 0; ; ++nip->rni_clevel) {
3886 			if (nip->rni_clevel >= COMPOSITION_DEPTH) {
3887 				/* Empty composition chain. */
3888 empty:
3889 				nip->rni_clevel = -1;
3890 				nip->rni_iter = NULL;
3891 				/* It's ok, iter_next() will return _DONE. */
3892 				goto out;
3893 			}
3894 
3895 			ent = np->rn_cchain[nip->rni_clevel];
3896 			assert(ent != NULL);
3897 
3898 			if (rc_node_check_and_lock(ent) == REP_PROTOCOL_SUCCESS)
3899 				break;
3900 
3901 			/* Someone deleted it, so try the next one. */
3902 		}
3903 
3904 		res = rc_node_fill_children(ent, type);
3905 
3906 		if (res == REP_PROTOCOL_SUCCESS) {
3907 			nip->rni_iter = uu_list_walk_start(ent->rn_children,
3908 			    UU_WALK_ROBUST);
3909 
3910 			if (nip->rni_iter == NULL)
3911 				res = REP_PROTOCOL_FAIL_NO_RESOURCES;
3912 			else {
3913 				nip->rni_iter_node = ent;
3914 				rc_node_hold_other(ent);
3915 			}
3916 		}
3917 
3918 		if (res != REP_PROTOCOL_SUCCESS) {
3919 			(void) pthread_mutex_unlock(&ent->rn_lock);
3920 			uu_free(nip);
3921 			return (res);
3922 		}
3923 
3924 		(void) pthread_mutex_unlock(&ent->rn_lock);
3925 	}
3926 
3927 out:
3928 	rc_node_hold(np);		/* released by rc_iter_end() */
3929 	nip->rni_parent = np;
3930 	nip->rni_type = type;
3931 	nip->rni_filter = (filter != NULL)? filter : rc_iter_null_filter;
3932 	nip->rni_filter_arg = arg;
3933 	*resp = nip;
3934 	return (REP_PROTOCOL_SUCCESS);
3935 }
3936 
3937 static void
3938 rc_iter_end(rc_node_iter_t *iter)
3939 {
3940 	rc_node_t *np = iter->rni_parent;
3941 
3942 	if (iter->rni_clevel >= 0)
3943 		np = np->rn_cchain[iter->rni_clevel];
3944 
3945 	assert(MUTEX_HELD(&np->rn_lock));
3946 	if (iter->rni_iter != NULL)
3947 		uu_list_walk_end(iter->rni_iter);
3948 	iter->rni_iter = NULL;
3949 
3950 	(void) pthread_mutex_unlock(&np->rn_lock);
3951 	rc_node_rele(iter->rni_parent);
3952 	if (iter->rni_iter_node != NULL)
3953 		rc_node_rele_other(iter->rni_iter_node);
3954 }
3955 
3956 /*
3957  * Fails with
3958  *   _NOT_SET - npp is reset
3959  *   _DELETED - npp's node has been deleted
3960  *   _NOT_APPLICABLE - npp's node is not a property
3961  *   _NO_RESOURCES - out of memory
3962  */
3963 static int
3964 rc_node_setup_value_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp)
3965 {
3966 	rc_node_t *np;
3967 
3968 	rc_node_iter_t *nip;
3969 
3970 	assert(*iterp == NULL);
3971 
3972 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
3973 
3974 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) {
3975 		(void) pthread_mutex_unlock(&np->rn_lock);
3976 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
3977 	}
3978 
3979 	nip = uu_zalloc(sizeof (*nip));
3980 	if (nip == NULL) {
3981 		(void) pthread_mutex_unlock(&np->rn_lock);
3982 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
3983 	}
3984 
3985 	nip->rni_parent = np;
3986 	nip->rni_iter = NULL;
3987 	nip->rni_clevel = -1;
3988 	nip->rni_type = REP_PROTOCOL_ENTITY_VALUE;
3989 	nip->rni_offset = 0;
3990 	nip->rni_last_offset = 0;
3991 
3992 	rc_node_hold_locked(np);
3993 
3994 	*iterp = nip;
3995 	(void) pthread_mutex_unlock(&np->rn_lock);
3996 
3997 	return (REP_PROTOCOL_SUCCESS);
3998 }
3999 
4000 /*
4001  * Returns:
4002  *   _NOT_SET - npp is reset
4003  *   _DELETED - npp's node has been deleted
4004  *   _TYPE_MISMATCH - npp's node is not a property
4005  *   _NOT_FOUND - property has no values
4006  *   _TRUNCATED - property has >1 values (first is written into out)
4007  *   _SUCCESS - property has 1 value (which is written into out)
4008  *
4009  * We shorten *sz_out to not include anything after the final '\0'.
4010  */
4011 int
4012 rc_node_get_property_value(rc_node_ptr_t *npp,
4013     struct rep_protocol_value_response *out, size_t *sz_out)
4014 {
4015 	rc_node_t *np;
4016 	size_t w;
4017 	int ret;
4018 
4019 	assert(*sz_out == sizeof (*out));
4020 
4021 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
4022 
4023 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY) {
4024 		(void) pthread_mutex_unlock(&np->rn_lock);
4025 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
4026 	}
4027 
4028 	if (np->rn_values_size == 0) {
4029 		(void) pthread_mutex_unlock(&np->rn_lock);
4030 		return (REP_PROTOCOL_FAIL_NOT_FOUND);
4031 	}
4032 	out->rpr_type = np->rn_valtype;
4033 	w = strlcpy(out->rpr_value, &np->rn_values[0],
4034 	    sizeof (out->rpr_value));
4035 
4036 	if (w >= sizeof (out->rpr_value))
4037 		backend_panic("value too large");
4038 
4039 	*sz_out = offsetof(struct rep_protocol_value_response,
4040 	    rpr_value[w + 1]);
4041 
4042 	ret = (np->rn_values_count != 1)? REP_PROTOCOL_FAIL_TRUNCATED :
4043 	    REP_PROTOCOL_SUCCESS;
4044 	(void) pthread_mutex_unlock(&np->rn_lock);
4045 	return (ret);
4046 }
4047 
4048 int
4049 rc_iter_next_value(rc_node_iter_t *iter,
4050     struct rep_protocol_value_response *out, size_t *sz_out, int repeat)
4051 {
4052 	rc_node_t *np = iter->rni_parent;
4053 	const char *vals;
4054 	size_t len;
4055 
4056 	size_t start;
4057 	size_t w;
4058 
4059 	rep_protocol_responseid_t result;
4060 
4061 	assert(*sz_out == sizeof (*out));
4062 
4063 	(void) memset(out, '\0', *sz_out);
4064 
4065 	if (iter->rni_type != REP_PROTOCOL_ENTITY_VALUE)
4066 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
4067 
4068 	RC_NODE_CHECK_AND_LOCK(np);
4069 
4070 	vals = np->rn_values;
4071 	len = np->rn_values_size;
4072 
4073 	out->rpr_type = np->rn_valtype;
4074 
4075 	start = (repeat)? iter->rni_last_offset : iter->rni_offset;
4076 
4077 	if (len == 0 || start >= len) {
4078 		result = REP_PROTOCOL_DONE;
4079 		*sz_out -= sizeof (out->rpr_value);
4080 	} else {
4081 		w = strlcpy(out->rpr_value, &vals[start],
4082 		    sizeof (out->rpr_value));
4083 
4084 		if (w >= sizeof (out->rpr_value))
4085 			backend_panic("value too large");
4086 
4087 		*sz_out = offsetof(struct rep_protocol_value_response,
4088 		    rpr_value[w + 1]);
4089 
4090 		/*
4091 		 * update the offsets if we're not repeating
4092 		 */
4093 		if (!repeat) {
4094 			iter->rni_last_offset = iter->rni_offset;
4095 			iter->rni_offset += (w + 1);
4096 		}
4097 
4098 		result = REP_PROTOCOL_SUCCESS;
4099 	}
4100 
4101 	(void) pthread_mutex_unlock(&np->rn_lock);
4102 	return (result);
4103 }
4104 
4105 /*
4106  * Entry point for ITER_START from client.c.  Validate the arguments & call
4107  * rc_iter_create().
4108  *
4109  * Fails with
4110  *   _NOT_SET
4111  *   _DELETED
4112  *   _TYPE_MISMATCH - np cannot carry type children
4113  *   _BAD_REQUEST - flags is invalid
4114  *		    pattern is invalid
4115  *   _NO_RESOURCES
4116  *   _INVALID_TYPE
4117  *   _TYPE_MISMATCH - *npp cannot have children of type
4118  *   _BACKEND_ACCESS
4119  */
4120 int
4121 rc_node_setup_iter(rc_node_ptr_t *npp, rc_node_iter_t **iterp,
4122     uint32_t type, uint32_t flags, const char *pattern)
4123 {
4124 	rc_node_t *np;
4125 	rc_iter_filter_func *f = NULL;
4126 	int rc;
4127 
4128 	RC_NODE_PTR_GET_CHECK(np, npp);
4129 
4130 	if (pattern != NULL && pattern[0] == '\0')
4131 		pattern = NULL;
4132 
4133 	if (type == REP_PROTOCOL_ENTITY_VALUE) {
4134 		if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
4135 			return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
4136 		if (flags != RP_ITER_START_ALL || pattern != NULL)
4137 			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
4138 
4139 		rc = rc_node_setup_value_iter(npp, iterp);
4140 		assert(rc != REP_PROTOCOL_FAIL_NOT_APPLICABLE);
4141 		return (rc);
4142 	}
4143 
4144 	if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
4145 	    REP_PROTOCOL_SUCCESS)
4146 		return (rc);
4147 
4148 	if (((flags & RP_ITER_START_FILT_MASK) == RP_ITER_START_ALL) ^
4149 	    (pattern == NULL))
4150 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
4151 
4152 	/* Composition only works for instances & snapshots. */
4153 	if ((flags & RP_ITER_START_COMPOSED) &&
4154 	    (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE &&
4155 	    np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT))
4156 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
4157 
4158 	if (pattern != NULL) {
4159 		if ((rc = rc_check_type_name(type, pattern)) !=
4160 		    REP_PROTOCOL_SUCCESS)
4161 			return (rc);
4162 		pattern = strdup(pattern);
4163 		if (pattern == NULL)
4164 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
4165 	}
4166 
4167 	switch (flags & RP_ITER_START_FILT_MASK) {
4168 	case RP_ITER_START_ALL:
4169 		f = NULL;
4170 		break;
4171 	case RP_ITER_START_EXACT:
4172 		f = rc_iter_filter_name;
4173 		break;
4174 	case RP_ITER_START_PGTYPE:
4175 		if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
4176 			free((void *)pattern);
4177 			return (REP_PROTOCOL_FAIL_BAD_REQUEST);
4178 		}
4179 		f = rc_iter_filter_type;
4180 		break;
4181 	default:
4182 		free((void *)pattern);
4183 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
4184 	}
4185 
4186 	rc = rc_iter_create(iterp, np, type, f, (void *)pattern,
4187 	    flags & RP_ITER_START_COMPOSED);
4188 	if (rc != REP_PROTOCOL_SUCCESS && pattern != NULL)
4189 		free((void *)pattern);
4190 
4191 	return (rc);
4192 }
4193 
4194 /*
4195  * Do uu_list_walk_next(iter->rni_iter) until we find a child which matches
4196  * the filter.
4197  * For composed iterators, then check to see if there's an overlapping entity
4198  * (see embedded comments).  If we reach the end of the list, start over at
4199  * the next level.
4200  *
4201  * Returns
4202  *   _BAD_REQUEST - iter walks values
4203  *   _TYPE_MISMATCH - iter does not walk type entities
4204  *   _DELETED - parent was deleted
4205  *   _NO_RESOURCES
4206  *   _INVALID_TYPE - type is invalid
4207  *   _DONE
4208  *   _SUCCESS
4209  *
4210  * For composed property group iterators, can also return
4211  *   _TYPE_MISMATCH - parent cannot have type children
4212  */
4213 int
4214 rc_iter_next(rc_node_iter_t *iter, rc_node_ptr_t *out, uint32_t type)
4215 {
4216 	rc_node_t *np = iter->rni_parent;
4217 	rc_node_t *res;
4218 	int rc;
4219 
4220 	if (iter->rni_type == REP_PROTOCOL_ENTITY_VALUE)
4221 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
4222 
4223 	if (iter->rni_iter == NULL) {
4224 		rc_node_clear(out, 0);
4225 		return (REP_PROTOCOL_DONE);
4226 	}
4227 
4228 	if (iter->rni_type != type) {
4229 		rc_node_clear(out, 0);
4230 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
4231 	}
4232 
4233 	(void) pthread_mutex_lock(&np->rn_lock);  /* held by _iter_create() */
4234 
4235 	if (!rc_node_wait_flag(np, RC_NODE_CHILDREN_CHANGING)) {
4236 		(void) pthread_mutex_unlock(&np->rn_lock);
4237 		rc_node_clear(out, 1);
4238 		return (REP_PROTOCOL_FAIL_DELETED);
4239 	}
4240 
4241 	if (iter->rni_clevel >= 0) {
4242 		/* Composed iterator.  Iterate over appropriate level. */
4243 		(void) pthread_mutex_unlock(&np->rn_lock);
4244 		np = np->rn_cchain[iter->rni_clevel];
4245 		/*
4246 		 * If iter->rni_parent is an instance or a snapshot, np must
4247 		 * be valid since iter holds iter->rni_parent & possible
4248 		 * levels (service, instance, snaplevel) cannot be destroyed
4249 		 * while rni_parent is held.  If iter->rni_parent is
4250 		 * a composed property group then rc_node_setup_cpg() put
4251 		 * a hold on np.
4252 		 */
4253 
4254 		(void) pthread_mutex_lock(&np->rn_lock);
4255 
4256 		if (!rc_node_wait_flag(np, RC_NODE_CHILDREN_CHANGING)) {
4257 			(void) pthread_mutex_unlock(&np->rn_lock);
4258 			rc_node_clear(out, 1);
4259 			return (REP_PROTOCOL_FAIL_DELETED);
4260 		}
4261 	}
4262 
4263 	assert(np->rn_flags & RC_NODE_HAS_CHILDREN);
4264 
4265 	for (;;) {
4266 		res = uu_list_walk_next(iter->rni_iter);
4267 		if (res == NULL) {
4268 			rc_node_t *parent = iter->rni_parent;
4269 
4270 #if COMPOSITION_DEPTH == 2
4271 			if (iter->rni_clevel < 0 || iter->rni_clevel == 1) {
4272 				/* release walker and lock */
4273 				rc_iter_end(iter);
4274 				break;
4275 			}
4276 
4277 			/* Stop walking current level. */
4278 			uu_list_walk_end(iter->rni_iter);
4279 			iter->rni_iter = NULL;
4280 			(void) pthread_mutex_unlock(&np->rn_lock);
4281 			rc_node_rele_other(iter->rni_iter_node);
4282 			iter->rni_iter_node = NULL;
4283 
4284 			/* Start walking next level. */
4285 			++iter->rni_clevel;
4286 			np = parent->rn_cchain[iter->rni_clevel];
4287 			assert(np != NULL);
4288 #else
4289 #error This code must be updated.
4290 #endif
4291 
4292 			(void) pthread_mutex_lock(&np->rn_lock);
4293 
4294 			rc = rc_node_fill_children(np, iter->rni_type);
4295 
4296 			if (rc == REP_PROTOCOL_SUCCESS) {
4297 				iter->rni_iter =
4298 				    uu_list_walk_start(np->rn_children,
4299 					UU_WALK_ROBUST);
4300 
4301 				if (iter->rni_iter == NULL)
4302 					rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
4303 				else {
4304 					iter->rni_iter_node = np;
4305 					rc_node_hold_other(np);
4306 				}
4307 			}
4308 
4309 			if (rc != REP_PROTOCOL_SUCCESS) {
4310 				(void) pthread_mutex_unlock(&np->rn_lock);
4311 				rc_node_clear(out, 0);
4312 				return (rc);
4313 			}
4314 
4315 			continue;
4316 		}
4317 
4318 		if (res->rn_id.rl_type != type ||
4319 		    !iter->rni_filter(res, iter->rni_filter_arg))
4320 			continue;
4321 
4322 		/*
4323 		 * If we're composed and not at the top level, check to see if
4324 		 * there's an entity at a higher level with the same name.  If
4325 		 * so, skip this one.
4326 		 */
4327 		if (iter->rni_clevel > 0) {
4328 			rc_node_t *ent = iter->rni_parent->rn_cchain[0];
4329 			rc_node_t *pg;
4330 
4331 #if COMPOSITION_DEPTH == 2
4332 			assert(iter->rni_clevel == 1);
4333 
4334 			(void) pthread_mutex_unlock(&np->rn_lock);
4335 			(void) pthread_mutex_lock(&ent->rn_lock);
4336 			rc = rc_node_find_named_child(ent, res->rn_name, type,
4337 			    &pg);
4338 			if (rc == REP_PROTOCOL_SUCCESS && pg != NULL)
4339 				rc_node_rele(pg);
4340 			(void) pthread_mutex_unlock(&ent->rn_lock);
4341 			if (rc != REP_PROTOCOL_SUCCESS) {
4342 				rc_node_clear(out, 0);
4343 				return (rc);
4344 			}
4345 			(void) pthread_mutex_lock(&np->rn_lock);
4346 
4347 			/* Make sure np isn't being deleted all of a sudden. */
4348 			if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
4349 				(void) pthread_mutex_unlock(&np->rn_lock);
4350 				rc_node_clear(out, 1);
4351 				return (REP_PROTOCOL_FAIL_DELETED);
4352 			}
4353 
4354 			if (pg != NULL)
4355 				/* Keep going. */
4356 				continue;
4357 #else
4358 #error This code must be updated.
4359 #endif
4360 		}
4361 
4362 		/*
4363 		 * If we're composed, iterating over property groups, and not
4364 		 * at the bottom level, check to see if there's a pg at lower
4365 		 * level with the same name.  If so, return a cpg.
4366 		 */
4367 		if (iter->rni_clevel >= 0 &&
4368 		    type == REP_PROTOCOL_ENTITY_PROPERTYGRP &&
4369 		    iter->rni_clevel < COMPOSITION_DEPTH - 1) {
4370 #if COMPOSITION_DEPTH == 2
4371 			rc_node_t *pg;
4372 			rc_node_t *ent = iter->rni_parent->rn_cchain[1];
4373 
4374 			rc_node_hold(res);	/* While we drop np->rn_lock */
4375 
4376 			(void) pthread_mutex_unlock(&np->rn_lock);
4377 			(void) pthread_mutex_lock(&ent->rn_lock);
4378 			rc = rc_node_find_named_child(ent, res->rn_name, type,
4379 			    &pg);
4380 			/* holds pg if not NULL */
4381 			(void) pthread_mutex_unlock(&ent->rn_lock);
4382 			if (rc != REP_PROTOCOL_SUCCESS) {
4383 				rc_node_rele(res);
4384 				rc_node_clear(out, 0);
4385 				return (rc);
4386 			}
4387 
4388 			(void) pthread_mutex_lock(&np->rn_lock);
4389 			if (!rc_node_wait_flag(np, RC_NODE_DYING)) {
4390 				(void) pthread_mutex_unlock(&np->rn_lock);
4391 				rc_node_rele(res);
4392 				if (pg != NULL)
4393 					rc_node_rele(pg);
4394 				rc_node_clear(out, 1);
4395 				return (REP_PROTOCOL_FAIL_DELETED);
4396 			}
4397 
4398 			if (pg == NULL) {
4399 				rc_node_rele(res);
4400 			} else {
4401 				rc_node_t *cpg;
4402 
4403 				/* Keep res held for rc_node_setup_cpg(). */
4404 
4405 				cpg = rc_node_alloc();
4406 				if (cpg == NULL) {
4407 					(void) pthread_mutex_unlock(
4408 					    &np->rn_lock);
4409 					rc_node_rele(res);
4410 					rc_node_rele(pg);
4411 					rc_node_clear(out, 0);
4412 					return (REP_PROTOCOL_FAIL_NO_RESOURCES);
4413 				}
4414 
4415 				switch (rc_node_setup_cpg(cpg, res, pg)) {
4416 				case REP_PROTOCOL_SUCCESS:
4417 					res = cpg;
4418 					break;
4419 
4420 				case REP_PROTOCOL_FAIL_TYPE_MISMATCH:
4421 					/* Nevermind. */
4422 					rc_node_destroy(cpg);
4423 					rc_node_rele(pg);
4424 					rc_node_rele(res);
4425 					break;
4426 
4427 				case REP_PROTOCOL_FAIL_NO_RESOURCES:
4428 					rc_node_destroy(cpg);
4429 					(void) pthread_mutex_unlock(
4430 					    &np->rn_lock);
4431 					rc_node_rele(res);
4432 					rc_node_rele(pg);
4433 					rc_node_clear(out, 0);
4434 					return (REP_PROTOCOL_FAIL_NO_RESOURCES);
4435 
4436 				default:
4437 					assert(0);
4438 					abort();
4439 				}
4440 			}
4441 #else
4442 #error This code must be updated.
4443 #endif
4444 		}
4445 
4446 		rc_node_hold(res);
4447 		(void) pthread_mutex_unlock(&np->rn_lock);
4448 		break;
4449 	}
4450 	rc_node_assign(out, res);
4451 
4452 	if (res == NULL)
4453 		return (REP_PROTOCOL_DONE);
4454 	rc_node_rele(res);
4455 	return (REP_PROTOCOL_SUCCESS);
4456 }
4457 
4458 void
4459 rc_iter_destroy(rc_node_iter_t **nipp)
4460 {
4461 	rc_node_iter_t *nip = *nipp;
4462 	rc_node_t *np;
4463 
4464 	if (nip == NULL)
4465 		return;				/* already freed */
4466 
4467 	np = nip->rni_parent;
4468 
4469 	if (nip->rni_filter_arg != NULL)
4470 		free(nip->rni_filter_arg);
4471 	nip->rni_filter_arg = NULL;
4472 
4473 	if (nip->rni_type == REP_PROTOCOL_ENTITY_VALUE ||
4474 	    nip->rni_iter != NULL) {
4475 		if (nip->rni_clevel < 0)
4476 			(void) pthread_mutex_lock(&np->rn_lock);
4477 		else
4478 			(void) pthread_mutex_lock(
4479 			    &np->rn_cchain[nip->rni_clevel]->rn_lock);
4480 		rc_iter_end(nip);		/* release walker and lock */
4481 	}
4482 	nip->rni_parent = NULL;
4483 
4484 	uu_free(nip);
4485 	*nipp = NULL;
4486 }
4487 
4488 int
4489 rc_node_setup_tx(rc_node_ptr_t *npp, rc_node_ptr_t *txp)
4490 {
4491 	rc_node_t *np;
4492 	permcheck_t *pcp;
4493 	int ret;
4494 	int authorized = 0;
4495 
4496 	RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
4497 
4498 	if (np->rn_id.rl_type == REP_PROTOCOL_ENTITY_CPROPERTYGRP) {
4499 		rc_node_rele(np);
4500 		np = np->rn_cchain[0];
4501 		RC_NODE_CHECK_AND_HOLD(np);
4502 	}
4503 
4504 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
4505 		rc_node_rele(np);
4506 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
4507 	}
4508 
4509 	if (np->rn_id.rl_ids[ID_SNAPSHOT] != 0) {
4510 		rc_node_rele(np);
4511 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
4512 	}
4513 
4514 	if (client_is_privileged())
4515 		goto skip_checks;
4516 
4517 #ifdef NATIVE_BUILD
4518 	rc_node_rele(np);
4519 	return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
4520 #else
4521 	/* permission check */
4522 	pcp = pc_create();
4523 	if (pcp == NULL) {
4524 		rc_node_rele(np);
4525 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
4526 	}
4527 
4528 	if (np->rn_id.rl_ids[ID_INSTANCE] != 0 &&	/* instance pg */
4529 	    ((strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0 &&
4530 	    strcmp(np->rn_type, AUTH_PG_ACTIONS_TYPE) == 0) ||
4531 	    (strcmp(np->rn_name, AUTH_PG_GENERAL_OVR) == 0 &&
4532 	    strcmp(np->rn_type, AUTH_PG_GENERAL_OVR_TYPE) == 0))) {
4533 		rc_node_t *instn;
4534 
4535 		/* solaris.smf.manage can be used. */
4536 		ret = perm_add_enabling(pcp, AUTH_MANAGE);
4537 
4538 		if (ret != REP_PROTOCOL_SUCCESS) {
4539 			pc_free(pcp);
4540 			rc_node_rele(np);
4541 			return (ret);
4542 		}
4543 
4544 		/* general/action_authorization values can be used. */
4545 		ret = rc_node_parent(np, &instn);
4546 		if (ret != REP_PROTOCOL_SUCCESS) {
4547 			assert(ret == REP_PROTOCOL_FAIL_DELETED);
4548 			rc_node_rele(np);
4549 			pc_free(pcp);
4550 			return (REP_PROTOCOL_FAIL_DELETED);
4551 		}
4552 
4553 		assert(instn->rn_id.rl_type == REP_PROTOCOL_ENTITY_INSTANCE);
4554 
4555 		ret = perm_add_inst_action_auth(pcp, instn);
4556 		rc_node_rele(instn);
4557 		switch (ret) {
4558 		case REP_PROTOCOL_SUCCESS:
4559 			break;
4560 
4561 		case REP_PROTOCOL_FAIL_DELETED:
4562 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
4563 			rc_node_rele(np);
4564 			pc_free(pcp);
4565 			return (ret);
4566 
4567 		default:
4568 			bad_error("perm_add_inst_action_auth", ret);
4569 		}
4570 
4571 		if (strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0)
4572 			authorized = 1;		/* Don't check on commit. */
4573 	} else {
4574 		ret = perm_add_enabling(pcp, AUTH_MODIFY);
4575 
4576 		if (ret == REP_PROTOCOL_SUCCESS) {
4577 			/* propertygroup-type-specific authorization */
4578 			/* no locking because rn_type won't change anyway */
4579 			const char * const auth =
4580 			    perm_auth_for_pgtype(np->rn_type);
4581 
4582 			if (auth != NULL)
4583 				ret = perm_add_enabling(pcp, auth);
4584 		}
4585 
4586 		if (ret == REP_PROTOCOL_SUCCESS)
4587 			/* propertygroup/transaction-type-specific auths */
4588 			ret =
4589 			    perm_add_enabling_values(pcp, np, AUTH_PROP_VALUE);
4590 
4591 		if (ret == REP_PROTOCOL_SUCCESS)
4592 			ret =
4593 			    perm_add_enabling_values(pcp, np, AUTH_PROP_MODIFY);
4594 
4595 		/* AUTH_MANAGE can manipulate general/AUTH_PROP_ACTION */
4596 		if (ret == REP_PROTOCOL_SUCCESS &&
4597 		    strcmp(np->rn_name, AUTH_PG_GENERAL) == 0 &&
4598 		    strcmp(np->rn_type, AUTH_PG_GENERAL_TYPE) == 0)
4599 			ret = perm_add_enabling(pcp, AUTH_MANAGE);
4600 
4601 		if (ret != REP_PROTOCOL_SUCCESS) {
4602 			pc_free(pcp);
4603 			rc_node_rele(np);
4604 			return (ret);
4605 		}
4606 	}
4607 
4608 	ret = perm_granted(pcp);
4609 	if (ret != 1) {
4610 		pc_free(pcp);
4611 		rc_node_rele(np);
4612 		return (ret == 0 ? REP_PROTOCOL_FAIL_PERMISSION_DENIED :
4613 		    REP_PROTOCOL_FAIL_NO_RESOURCES);
4614 	}
4615 
4616 	pc_free(pcp);
4617 #endif /* NATIVE_BUILD */
4618 
4619 skip_checks:
4620 	rc_node_assign(txp, np);
4621 	txp->rnp_authorized = authorized;
4622 
4623 	rc_node_rele(np);
4624 	return (REP_PROTOCOL_SUCCESS);
4625 }
4626 
4627 /*
4628  * Return 1 if the given transaction commands only modify the values of
4629  * properties other than "modify_authorization".  Return -1 if any of the
4630  * commands are invalid, and 0 otherwise.
4631  */
4632 static int
4633 tx_allow_value(const void *cmds_arg, size_t cmds_sz, rc_node_t *pg)
4634 {
4635 	const struct rep_protocol_transaction_cmd *cmds;
4636 	uintptr_t loc;
4637 	uint32_t sz;
4638 	rc_node_t *prop;
4639 	boolean_t ok;
4640 
4641 	assert(!MUTEX_HELD(&pg->rn_lock));
4642 
4643 	loc = (uintptr_t)cmds_arg;
4644 
4645 	while (cmds_sz > 0) {
4646 		cmds = (struct rep_protocol_transaction_cmd *)loc;
4647 
4648 		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
4649 			return (-1);
4650 
4651 		sz = cmds->rptc_size;
4652 		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
4653 			return (-1);
4654 
4655 		sz = TX_SIZE(sz);
4656 		if (sz > cmds_sz)
4657 			return (-1);
4658 
4659 		switch (cmds[0].rptc_action) {
4660 		case REP_PROTOCOL_TX_ENTRY_CLEAR:
4661 			break;
4662 
4663 		case REP_PROTOCOL_TX_ENTRY_REPLACE:
4664 			/* Check type */
4665 			(void) pthread_mutex_lock(&pg->rn_lock);
4666 			if (rc_node_find_named_child(pg,
4667 			    (const char *)cmds[0].rptc_data,
4668 			    REP_PROTOCOL_ENTITY_PROPERTY, &prop) ==
4669 			    REP_PROTOCOL_SUCCESS) {
4670 				ok = (prop != NULL &&
4671 				    prop->rn_valtype == cmds[0].rptc_type);
4672 			} else {
4673 				/* Return more particular error? */
4674 				ok = B_FALSE;
4675 			}
4676 			(void) pthread_mutex_unlock(&pg->rn_lock);
4677 			if (ok)
4678 				break;
4679 			return (0);
4680 
4681 		default:
4682 			return (0);
4683 		}
4684 
4685 		if (strcmp((const char *)cmds[0].rptc_data, AUTH_PROP_MODIFY)
4686 		    == 0)
4687 			return (0);
4688 
4689 		loc += sz;
4690 		cmds_sz -= sz;
4691 	}
4692 
4693 	return (1);
4694 }
4695 
4696 /*
4697  * Return 1 if any of the given transaction commands affect
4698  * "action_authorization".  Return -1 if any of the commands are invalid and
4699  * 0 in all other cases.
4700  */
4701 static int
4702 tx_modifies_action(const void *cmds_arg, size_t cmds_sz)
4703 {
4704 	const struct rep_protocol_transaction_cmd *cmds;
4705 	uintptr_t loc;
4706 	uint32_t sz;
4707 
4708 	loc = (uintptr_t)cmds_arg;
4709 
4710 	while (cmds_sz > 0) {
4711 		cmds = (struct rep_protocol_transaction_cmd *)loc;
4712 
4713 		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
4714 			return (-1);
4715 
4716 		sz = cmds->rptc_size;
4717 		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
4718 			return (-1);
4719 
4720 		sz = TX_SIZE(sz);
4721 		if (sz > cmds_sz)
4722 			return (-1);
4723 
4724 		if (strcmp((const char *)cmds[0].rptc_data, AUTH_PROP_ACTION)
4725 		    == 0)
4726 			return (1);
4727 
4728 		loc += sz;
4729 		cmds_sz -= sz;
4730 	}
4731 
4732 	return (0);
4733 }
4734 
4735 /*
4736  * Returns 1 if the transaction commands only modify properties named
4737  * 'enabled'.
4738  */
4739 static int
4740 tx_only_enabled(const void *cmds_arg, size_t cmds_sz)
4741 {
4742 	const struct rep_protocol_transaction_cmd *cmd;
4743 	uintptr_t loc;
4744 	uint32_t sz;
4745 
4746 	loc = (uintptr_t)cmds_arg;
4747 
4748 	while (cmds_sz > 0) {
4749 		cmd = (struct rep_protocol_transaction_cmd *)loc;
4750 
4751 		if (cmds_sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
4752 			return (-1);
4753 
4754 		sz = cmd->rptc_size;
4755 		if (sz <= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE)
4756 			return (-1);
4757 
4758 		sz = TX_SIZE(sz);
4759 		if (sz > cmds_sz)
4760 			return (-1);
4761 
4762 		if (strcmp((const char *)cmd->rptc_data, AUTH_PROP_ENABLED)
4763 		    != 0)
4764 			return (0);
4765 
4766 		loc += sz;
4767 		cmds_sz -= sz;
4768 	}
4769 
4770 	return (1);
4771 }
4772 
4773 int
4774 rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
4775 {
4776 	rc_node_t *np = txp->rnp_node;
4777 	rc_node_t *pp;
4778 	rc_node_t *nnp;
4779 	rc_node_pg_notify_t *pnp;
4780 	int rc;
4781 	permcheck_t *pcp;
4782 	int granted, normal;
4783 
4784 	RC_NODE_CHECK(np);
4785 
4786 	if (!client_is_privileged() && !txp->rnp_authorized) {
4787 #ifdef NATIVE_BUILD
4788 		return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
4789 #else
4790 		/* permission check: depends on contents of transaction */
4791 		pcp = pc_create();
4792 		if (pcp == NULL)
4793 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
4794 
4795 		/* If normal is cleared, we won't do the normal checks. */
4796 		normal = 1;
4797 		rc = REP_PROTOCOL_SUCCESS;
4798 
4799 		if (strcmp(np->rn_name, AUTH_PG_GENERAL) == 0 &&
4800 		    strcmp(np->rn_type, AUTH_PG_GENERAL_TYPE) == 0) {
4801 			/* Touching general[framework]/action_authorization? */
4802 			rc = tx_modifies_action(cmds, cmds_sz);
4803 			if (rc == -1) {
4804 				pc_free(pcp);
4805 				return (REP_PROTOCOL_FAIL_BAD_REQUEST);
4806 			}
4807 
4808 			if (rc) {
4809 				/* Yes: only AUTH_MANAGE can be used. */
4810 				rc = perm_add_enabling(pcp, AUTH_MANAGE);
4811 				normal = 0;
4812 			} else {
4813 				rc = REP_PROTOCOL_SUCCESS;
4814 			}
4815 		} else if (np->rn_id.rl_ids[ID_INSTANCE] != 0 &&
4816 		    strcmp(np->rn_name, AUTH_PG_GENERAL_OVR) == 0 &&
4817 		    strcmp(np->rn_type, AUTH_PG_GENERAL_OVR_TYPE) == 0) {
4818 			rc_node_t *instn;
4819 
4820 			rc = tx_only_enabled(cmds, cmds_sz);
4821 			if (rc == -1) {
4822 				pc_free(pcp);
4823 				return (REP_PROTOCOL_FAIL_BAD_REQUEST);
4824 			}
4825 
4826 			if (rc) {
4827 				rc = rc_node_parent(np, &instn);
4828 				if (rc != REP_PROTOCOL_SUCCESS) {
4829 					assert(rc == REP_PROTOCOL_FAIL_DELETED);
4830 					pc_free(pcp);
4831 					return (rc);
4832 				}
4833 
4834 				assert(instn->rn_id.rl_type ==
4835 				    REP_PROTOCOL_ENTITY_INSTANCE);
4836 
4837 				rc = perm_add_inst_action_auth(pcp, instn);
4838 				rc_node_rele(instn);
4839 				switch (rc) {
4840 				case REP_PROTOCOL_SUCCESS:
4841 					break;
4842 
4843 				case REP_PROTOCOL_FAIL_DELETED:
4844 				case REP_PROTOCOL_FAIL_NO_RESOURCES:
4845 					pc_free(pcp);
4846 					return (rc);
4847 
4848 				default:
4849 					bad_error("perm_add_inst_action_auth",
4850 					    rc);
4851 				}
4852 			} else {
4853 				rc = REP_PROTOCOL_SUCCESS;
4854 			}
4855 		}
4856 
4857 		if (rc == REP_PROTOCOL_SUCCESS && normal) {
4858 			rc = perm_add_enabling(pcp, AUTH_MODIFY);
4859 
4860 			if (rc == REP_PROTOCOL_SUCCESS) {
4861 				/* Add pgtype-specific authorization. */
4862 				const char * const auth =
4863 				    perm_auth_for_pgtype(np->rn_type);
4864 
4865 				if (auth != NULL)
4866 					rc = perm_add_enabling(pcp, auth);
4867 			}
4868 
4869 			/* Add pg-specific modify_authorization auths. */
4870 			if (rc == REP_PROTOCOL_SUCCESS)
4871 				rc = perm_add_enabling_values(pcp, np,
4872 				    AUTH_PROP_MODIFY);
4873 
4874 			/* If value_authorization values are ok, add them. */
4875 			if (rc == REP_PROTOCOL_SUCCESS) {
4876 				rc = tx_allow_value(cmds, cmds_sz, np);
4877 				if (rc == -1)
4878 					rc = REP_PROTOCOL_FAIL_BAD_REQUEST;
4879 				else if (rc)
4880 					rc = perm_add_enabling_values(pcp, np,
4881 					    AUTH_PROP_VALUE);
4882 			}
4883 		}
4884 
4885 		if (rc == REP_PROTOCOL_SUCCESS) {
4886 			granted = perm_granted(pcp);
4887 			if (granted < 0)
4888 				rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
4889 		}
4890 
4891 		pc_free(pcp);
4892 
4893 		if (rc != REP_PROTOCOL_SUCCESS)
4894 			return (rc);
4895 
4896 		if (!granted)
4897 			return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
4898 #endif /* NATIVE_BUILD */
4899 	}
4900 
4901 	nnp = rc_node_alloc();
4902 	if (nnp == NULL)
4903 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
4904 
4905 	nnp->rn_id = np->rn_id;			/* structure assignment */
4906 	nnp->rn_hash = np->rn_hash;
4907 	nnp->rn_name = strdup(np->rn_name);
4908 	nnp->rn_type = strdup(np->rn_type);
4909 	nnp->rn_pgflags = np->rn_pgflags;
4910 
4911 	nnp->rn_flags = RC_NODE_IN_TX | RC_NODE_USING_PARENT;
4912 
4913 	if (nnp->rn_name == NULL || nnp->rn_type == NULL) {
4914 		rc_node_destroy(nnp);
4915 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
4916 	}
4917 
4918 	(void) pthread_mutex_lock(&np->rn_lock);
4919 	/*
4920 	 * We must have all of the old properties in the cache, or the
4921 	 * database deletions could cause inconsistencies.
4922 	 */
4923 	if ((rc = rc_node_fill_children(np, REP_PROTOCOL_ENTITY_PROPERTY)) !=
4924 	    REP_PROTOCOL_SUCCESS) {
4925 		(void) pthread_mutex_unlock(&np->rn_lock);
4926 		rc_node_destroy(nnp);
4927 		return (rc);
4928 	}
4929 
4930 	if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
4931 		(void) pthread_mutex_unlock(&np->rn_lock);
4932 		rc_node_destroy(nnp);
4933 		return (REP_PROTOCOL_FAIL_DELETED);
4934 	}
4935 
4936 	if (np->rn_flags & RC_NODE_OLD) {
4937 		rc_node_rele_flag(np, RC_NODE_USING_PARENT);
4938 		(void) pthread_mutex_unlock(&np->rn_lock);
4939 		rc_node_destroy(nnp);
4940 		return (REP_PROTOCOL_FAIL_NOT_LATEST);
4941 	}
4942 
4943 	pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
4944 	if (pp == NULL) {
4945 		/* our parent is gone, we're going next... */
4946 		rc_node_destroy(nnp);
4947 		(void) pthread_mutex_lock(&np->rn_lock);
4948 		if (np->rn_flags & RC_NODE_OLD) {
4949 			(void) pthread_mutex_unlock(&np->rn_lock);
4950 			return (REP_PROTOCOL_FAIL_NOT_LATEST);
4951 		}
4952 		(void) pthread_mutex_unlock(&np->rn_lock);
4953 		return (REP_PROTOCOL_FAIL_DELETED);
4954 	}
4955 	(void) pthread_mutex_unlock(&pp->rn_lock);
4956 
4957 	/*
4958 	 * prepare for the transaction
4959 	 */
4960 	(void) pthread_mutex_lock(&np->rn_lock);
4961 	if (!rc_node_hold_flag(np, RC_NODE_IN_TX)) {
4962 		(void) pthread_mutex_unlock(&np->rn_lock);
4963 		(void) pthread_mutex_lock(&pp->rn_lock);
4964 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
4965 		(void) pthread_mutex_unlock(&pp->rn_lock);
4966 		rc_node_destroy(nnp);
4967 		return (REP_PROTOCOL_FAIL_DELETED);
4968 	}
4969 	nnp->rn_gen_id = np->rn_gen_id;
4970 	(void) pthread_mutex_unlock(&np->rn_lock);
4971 
4972 	/* Sets nnp->rn_gen_id on success. */
4973 	rc = object_tx_commit(&np->rn_id, cmds, cmds_sz, &nnp->rn_gen_id);
4974 
4975 	(void) pthread_mutex_lock(&np->rn_lock);
4976 	if (rc != REP_PROTOCOL_SUCCESS) {
4977 		rc_node_rele_flag(np, RC_NODE_IN_TX);
4978 		(void) pthread_mutex_unlock(&np->rn_lock);
4979 		(void) pthread_mutex_lock(&pp->rn_lock);
4980 		rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
4981 		(void) pthread_mutex_unlock(&pp->rn_lock);
4982 		rc_node_destroy(nnp);
4983 		rc_node_clear(txp, 0);
4984 		if (rc == REP_PROTOCOL_DONE)
4985 			rc = REP_PROTOCOL_SUCCESS; /* successful empty tx */
4986 		return (rc);
4987 	}
4988 
4989 	/*
4990 	 * Notify waiters
4991 	 */
4992 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
4993 	while ((pnp = uu_list_first(np->rn_pg_notify_list)) != NULL)
4994 		rc_pg_notify_fire(pnp);
4995 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
4996 
4997 	np->rn_flags |= RC_NODE_OLD;
4998 	(void) pthread_mutex_unlock(&np->rn_lock);
4999 
5000 	rc_notify_remove_node(np);
5001 
5002 	/*
5003 	 * replace np with nnp
5004 	 */
5005 	rc_node_relink_child(pp, np, nnp);
5006 
5007 	/*
5008 	 * all done -- clear the transaction.
5009 	 */
5010 	rc_node_clear(txp, 0);
5011 
5012 	return (REP_PROTOCOL_SUCCESS);
5013 }
5014 
5015 void
5016 rc_pg_notify_init(rc_node_pg_notify_t *pnp)
5017 {
5018 	uu_list_node_init(pnp, &pnp->rnpn_node, rc_pg_notify_pool);
5019 	pnp->rnpn_pg = NULL;
5020 	pnp->rnpn_fd = -1;
5021 }
5022 
5023 int
5024 rc_pg_notify_setup(rc_node_pg_notify_t *pnp, rc_node_ptr_t *npp, int fd)
5025 {
5026 	rc_node_t *np;
5027 
5028 	RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
5029 
5030 	if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
5031 		(void) pthread_mutex_unlock(&np->rn_lock);
5032 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
5033 	}
5034 
5035 	/*
5036 	 * wait for any transaction in progress to complete
5037 	 */
5038 	if (!rc_node_wait_flag(np, RC_NODE_IN_TX)) {
5039 		(void) pthread_mutex_unlock(&np->rn_lock);
5040 		return (REP_PROTOCOL_FAIL_DELETED);
5041 	}
5042 
5043 	if (np->rn_flags & RC_NODE_OLD) {
5044 		(void) pthread_mutex_unlock(&np->rn_lock);
5045 		return (REP_PROTOCOL_FAIL_NOT_LATEST);
5046 	}
5047 
5048 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
5049 	rc_pg_notify_fire(pnp);
5050 	pnp->rnpn_pg = np;
5051 	pnp->rnpn_fd = fd;
5052 	(void) uu_list_insert_after(np->rn_pg_notify_list, NULL, pnp);
5053 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
5054 
5055 	(void) pthread_mutex_unlock(&np->rn_lock);
5056 	return (REP_PROTOCOL_SUCCESS);
5057 }
5058 
5059 void
5060 rc_pg_notify_fini(rc_node_pg_notify_t *pnp)
5061 {
5062 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
5063 	rc_pg_notify_fire(pnp);
5064 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
5065 
5066 	uu_list_node_fini(pnp, &pnp->rnpn_node, rc_pg_notify_pool);
5067 }
5068 
5069 void
5070 rc_notify_info_init(rc_notify_info_t *rnip)
5071 {
5072 	int i;
5073 
5074 	uu_list_node_init(rnip, &rnip->rni_list_node, rc_notify_info_pool);
5075 	uu_list_node_init(&rnip->rni_notify, &rnip->rni_notify.rcn_list_node,
5076 	    rc_notify_pool);
5077 
5078 	rnip->rni_notify.rcn_node = NULL;
5079 	rnip->rni_notify.rcn_info = rnip;
5080 
5081 	bzero(rnip->rni_namelist, sizeof (rnip->rni_namelist));
5082 	bzero(rnip->rni_typelist, sizeof (rnip->rni_typelist));
5083 
5084 	(void) pthread_cond_init(&rnip->rni_cv, NULL);
5085 
5086 	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
5087 		rnip->rni_namelist[i] = NULL;
5088 		rnip->rni_typelist[i] = NULL;
5089 	}
5090 }
5091 
5092 static void
5093 rc_notify_info_insert_locked(rc_notify_info_t *rnip)
5094 {
5095 	assert(MUTEX_HELD(&rc_pg_notify_lock));
5096 
5097 	assert(!(rnip->rni_flags & RC_NOTIFY_ACTIVE));
5098 
5099 	rnip->rni_flags |= RC_NOTIFY_ACTIVE;
5100 	(void) uu_list_insert_after(rc_notify_info_list, NULL, rnip);
5101 	(void) uu_list_insert_before(rc_notify_list, NULL, &rnip->rni_notify);
5102 }
5103 
5104 static void
5105 rc_notify_info_remove_locked(rc_notify_info_t *rnip)
5106 {
5107 	rc_notify_t *me = &rnip->rni_notify;
5108 	rc_notify_t *np;
5109 
5110 	assert(MUTEX_HELD(&rc_pg_notify_lock));
5111 
5112 	assert(rnip->rni_flags & RC_NOTIFY_ACTIVE);
5113 
5114 	assert(!(rnip->rni_flags & RC_NOTIFY_DRAIN));
5115 	rnip->rni_flags |= RC_NOTIFY_DRAIN;
5116 	(void) pthread_cond_broadcast(&rnip->rni_cv);
5117 
5118 	(void) uu_list_remove(rc_notify_info_list, rnip);
5119 
5120 	/*
5121 	 * clean up any notifications at the beginning of the list
5122 	 */
5123 	if (uu_list_first(rc_notify_list) == me) {
5124 		while ((np = uu_list_next(rc_notify_list, me)) != NULL &&
5125 		    np->rcn_info == NULL)
5126 			rc_notify_remove_locked(np);
5127 	}
5128 	(void) uu_list_remove(rc_notify_list, me);
5129 
5130 	while (rnip->rni_waiters) {
5131 		(void) pthread_cond_broadcast(&rc_pg_notify_cv);
5132 		(void) pthread_cond_broadcast(&rnip->rni_cv);
5133 		(void) pthread_cond_wait(&rnip->rni_cv, &rc_pg_notify_lock);
5134 	}
5135 
5136 	rnip->rni_flags &= ~(RC_NOTIFY_DRAIN | RC_NOTIFY_ACTIVE);
5137 }
5138 
5139 static int
5140 rc_notify_info_add_watch(rc_notify_info_t *rnip, const char **arr,
5141     const char *name)
5142 {
5143 	int i;
5144 	int rc;
5145 	char *f;
5146 
5147 	rc = rc_check_type_name(REP_PROTOCOL_ENTITY_PROPERTYGRP, name);
5148 	if (rc != REP_PROTOCOL_SUCCESS)
5149 		return (rc);
5150 
5151 	f = strdup(name);
5152 	if (f == NULL)
5153 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
5154 
5155 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
5156 
5157 	while (rnip->rni_flags & RC_NOTIFY_EMPTYING)
5158 		(void) pthread_cond_wait(&rnip->rni_cv, &rc_pg_notify_lock);
5159 
5160 	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++)
5161 		if (arr[i] == NULL)
5162 			break;
5163 
5164 	if (i == RC_NOTIFY_MAX_NAMES) {
5165 		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
5166 		free(f);
5167 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
5168 	}
5169 
5170 	arr[i] = f;
5171 	if (!(rnip->rni_flags & RC_NOTIFY_ACTIVE))
5172 		rc_notify_info_insert_locked(rnip);
5173 
5174 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
5175 	return (REP_PROTOCOL_SUCCESS);
5176 }
5177 
5178 int
5179 rc_notify_info_add_name(rc_notify_info_t *rnip, const char *name)
5180 {
5181 	return (rc_notify_info_add_watch(rnip, rnip->rni_namelist, name));
5182 }
5183 
5184 int
5185 rc_notify_info_add_type(rc_notify_info_t *rnip, const char *type)
5186 {
5187 	return (rc_notify_info_add_watch(rnip, rnip->rni_typelist, type));
5188 }
5189 
5190 /*
5191  * Wait for and report an event of interest to rnip, a notification client
5192  */
5193 int
5194 rc_notify_info_wait(rc_notify_info_t *rnip, rc_node_ptr_t *out,
5195     char *outp, size_t sz)
5196 {
5197 	rc_notify_t *np;
5198 	rc_notify_t *me = &rnip->rni_notify;
5199 	rc_node_t *nnp;
5200 	rc_notify_delete_t *ndp;
5201 
5202 	int am_first_info;
5203 
5204 	if (sz > 0)
5205 		outp[0] = 0;
5206 
5207 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
5208 
5209 	while ((rnip->rni_flags & (RC_NOTIFY_ACTIVE | RC_NOTIFY_DRAIN)) ==
5210 	    RC_NOTIFY_ACTIVE) {
5211 		/*
5212 		 * If I'm first on the notify list, it is my job to
5213 		 * clean up any notifications I pass by.  I can't do that
5214 		 * if someone is blocking the list from removals, so I
5215 		 * have to wait until they have all drained.
5216 		 */
5217 		am_first_info = (uu_list_first(rc_notify_list) == me);
5218 		if (am_first_info && rc_notify_in_use) {
5219 			rnip->rni_waiters++;
5220 			(void) pthread_cond_wait(&rc_pg_notify_cv,
5221 			    &rc_pg_notify_lock);
5222 			rnip->rni_waiters--;
5223 			continue;
5224 		}
5225 
5226 		/*
5227 		 * Search the list for a node of interest.
5228 		 */
5229 		np = uu_list_next(rc_notify_list, me);
5230 		while (np != NULL && !rc_notify_info_interested(rnip, np)) {
5231 			rc_notify_t *next = uu_list_next(rc_notify_list, np);
5232 
5233 			if (am_first_info) {
5234 				if (np->rcn_info) {
5235 					/*
5236 					 * Passing another client -- stop
5237 					 * cleaning up notifications
5238 					 */
5239 					am_first_info = 0;
5240 				} else {
5241 					rc_notify_remove_locked(np);
5242 				}
5243 			}
5244 			np = next;
5245 		}
5246 
5247 		/*
5248 		 * Nothing of interest -- wait for notification
5249 		 */
5250 		if (np == NULL) {
5251 			rnip->rni_waiters++;
5252 			(void) pthread_cond_wait(&rnip->rni_cv,
5253 			    &rc_pg_notify_lock);
5254 			rnip->rni_waiters--;
5255 			continue;
5256 		}
5257 
5258 		/*
5259 		 * found something to report -- move myself after the
5260 		 * notification and process it.
5261 		 */
5262 		(void) uu_list_remove(rc_notify_list, me);
5263 		(void) uu_list_insert_after(rc_notify_list, np, me);
5264 
5265 		if ((ndp = np->rcn_delete) != NULL) {
5266 			(void) strlcpy(outp, ndp->rnd_fmri, sz);
5267 			if (am_first_info)
5268 				rc_notify_remove_locked(np);
5269 			(void) pthread_mutex_unlock(&rc_pg_notify_lock);
5270 			rc_node_clear(out, 0);
5271 			return (REP_PROTOCOL_SUCCESS);
5272 		}
5273 
5274 		nnp = np->rcn_node;
5275 		assert(nnp != NULL);
5276 
5277 		/*
5278 		 * We can't bump nnp's reference count without grabbing its
5279 		 * lock, and rc_pg_notify_lock is a leaf lock.  So we
5280 		 * temporarily block all removals to keep nnp from
5281 		 * disappearing.
5282 		 */
5283 		rc_notify_in_use++;
5284 		assert(rc_notify_in_use > 0);
5285 		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
5286 
5287 		rc_node_assign(out, nnp);
5288 
5289 		(void) pthread_mutex_lock(&rc_pg_notify_lock);
5290 		assert(rc_notify_in_use > 0);
5291 		rc_notify_in_use--;
5292 		if (am_first_info)
5293 			rc_notify_remove_locked(np);
5294 		if (rc_notify_in_use == 0)
5295 			(void) pthread_cond_broadcast(&rc_pg_notify_cv);
5296 		(void) pthread_mutex_unlock(&rc_pg_notify_lock);
5297 
5298 		return (REP_PROTOCOL_SUCCESS);
5299 	}
5300 	/*
5301 	 * If we're the last one out, let people know it's clear.
5302 	 */
5303 	if (rnip->rni_waiters == 0)
5304 		(void) pthread_cond_broadcast(&rnip->rni_cv);
5305 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
5306 	return (REP_PROTOCOL_DONE);
5307 }
5308 
5309 static void
5310 rc_notify_info_reset(rc_notify_info_t *rnip)
5311 {
5312 	int i;
5313 
5314 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
5315 	if (rnip->rni_flags & RC_NOTIFY_ACTIVE)
5316 		rc_notify_info_remove_locked(rnip);
5317 	assert(!(rnip->rni_flags & (RC_NOTIFY_DRAIN | RC_NOTIFY_EMPTYING)));
5318 	rnip->rni_flags |= RC_NOTIFY_EMPTYING;
5319 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
5320 
5321 	for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
5322 		if (rnip->rni_namelist[i] != NULL) {
5323 			free((void *)rnip->rni_namelist[i]);
5324 			rnip->rni_namelist[i] = NULL;
5325 		}
5326 		if (rnip->rni_typelist[i] != NULL) {
5327 			free((void *)rnip->rni_typelist[i]);
5328 			rnip->rni_typelist[i] = NULL;
5329 		}
5330 	}
5331 
5332 	(void) pthread_mutex_lock(&rc_pg_notify_lock);
5333 	rnip->rni_flags &= ~RC_NOTIFY_EMPTYING;
5334 	(void) pthread_mutex_unlock(&rc_pg_notify_lock);
5335 }
5336 
5337 void
5338 rc_notify_info_fini(rc_notify_info_t *rnip)
5339 {
5340 	rc_notify_info_reset(rnip);
5341 
5342 	uu_list_node_fini(rnip, &rnip->rni_list_node, rc_notify_info_pool);
5343 	uu_list_node_fini(&rnip->rni_notify, &rnip->rni_notify.rcn_list_node,
5344 	    rc_notify_pool);
5345 }
5346