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