1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/types.h> 29 #include <sys/unistd.h> 30 #include <sys/sysmacros.h> 31 #include <sys/sunddi.h> 32 #include <sys/zfs_vfsops.h> 33 #include <sys/zfs_znode.h> 34 #include <sys/zfs_fuid.h> 35 #include <sys/dmu.h> 36 #include <sys/refcount.h> 37 #include <sys/avl.h> 38 #include <sys/zap.h> 39 #include <sys/nvpair.h> 40 #include <sys/kidmap.h> 41 #include <sys/sid.h> 42 43 /* 44 * FUID Domain table(s). 45 * 46 * The FUID table is stored as a packed nvlist of an array 47 * of nvlists which contain an index, domain string and offset 48 * 49 * During file system initialization the nvlist(s) are read and 50 * two AVL trees are created. One tree is keyed by the index number 51 * and the other by the domain string. Nodes are never removed from 52 * trees, but new entries may be added. If a new entry is added then the 53 * on-disk packed nvlist will also be updated. 54 */ 55 56 #define FUID_IDX "fuid_idx" 57 #define FUID_DOMAIN "fuid_domain" 58 #define FUID_OFFSET "fuid_offset" 59 #define FUID_NVP_ARRAY "fuid_nvlist" 60 61 typedef struct fuid_domain { 62 avl_node_t f_node; 63 ksiddomain_t *f_ksid; 64 int f_idx; 65 uint32_t f_offset; 66 } fuid_domain_t; 67 68 typedef struct fuid_idx { 69 avl_node_t f_node; 70 int f_idx; 71 fuid_domain_t *f_domain; 72 } fuid_idx_t; 73 74 /* 75 * Compare two indexes. 76 */ 77 static int 78 idx_compare(const void *arg1, const void *arg2) 79 { 80 const fuid_idx_t *node1 = arg1; 81 const fuid_idx_t *node2 = arg2; 82 83 if (node1->f_idx < node2->f_idx) 84 return (-1); 85 else if (node1->f_idx > node2->f_idx) 86 return (1); 87 return (0); 88 } 89 90 /* 91 * Compare two domain strings. 92 */ 93 static int 94 domain_compare(const void *arg1, const void *arg2) 95 { 96 const fuid_domain_t *node1 = arg1; 97 const fuid_domain_t *node2 = arg2; 98 int val; 99 100 val = strcmp(node1->f_ksid->kd_name, node2->f_ksid->kd_name); 101 if (val == 0) 102 return (0); 103 return (val > 0 ? 1 : -1); 104 } 105 106 /* 107 * Load the fuid table(s) into memory. 108 */ 109 static void 110 zfs_fuid_init(zfsvfs_t *zfsvfs, dmu_tx_t *tx) 111 { 112 dmu_buf_t *db; 113 char *packed; 114 int error = 0; 115 int i; 116 117 rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); 118 119 if (zfsvfs->z_fuid_loaded) { 120 rw_exit(&zfsvfs->z_fuid_lock); 121 return; 122 } 123 124 if (zfsvfs->z_fuid_obj == 0) { 125 126 /* first make sure we need to allocate object */ 127 128 error = zap_lookup(zfsvfs->z_os, MASTER_NODE_OBJ, 129 ZFS_FUID_TABLES, 8, 1, &zfsvfs->z_fuid_obj); 130 if (error == ENOENT && tx != NULL) { 131 zfsvfs->z_fuid_obj = dmu_object_alloc(zfsvfs->z_os, 132 DMU_OT_FUID, 1 << 14, DMU_OT_FUID_SIZE, 133 sizeof (uint64_t), tx); 134 VERIFY(zap_add(zfsvfs->z_os, MASTER_NODE_OBJ, 135 ZFS_FUID_TABLES, sizeof (uint64_t), 1, 136 &zfsvfs->z_fuid_obj, tx) == 0); 137 } 138 } 139 140 avl_create(&zfsvfs->z_fuid_idx, idx_compare, 141 sizeof (fuid_idx_t), offsetof(fuid_idx_t, f_node)); 142 avl_create(&zfsvfs->z_fuid_domain, domain_compare, 143 sizeof (fuid_domain_t), offsetof(fuid_domain_t, f_node)); 144 145 if (zfsvfs->z_fuid_obj) { 146 VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, zfsvfs->z_fuid_obj, 147 FTAG, &db)); 148 zfsvfs->z_fuid_size = *(uint64_t *)db->db_data; 149 dmu_buf_rele(db, FTAG); 150 } 151 152 if (zfsvfs->z_fuid_size == 0) 153 goto initialized; 154 155 packed = kmem_alloc(zfsvfs->z_fuid_size, KM_SLEEP); 156 error = dmu_read(zfsvfs->z_os, zfsvfs->z_fuid_obj, 0, 157 zfsvfs->z_fuid_size, packed); 158 if (error == 0) { 159 nvlist_t **fuidnvp; 160 nvlist_t *nvp = NULL; 161 uint_t count; 162 163 VERIFY(nvlist_unpack(packed, zfsvfs->z_fuid_size, 164 &nvp, 0) == 0); 165 VERIFY((error = nvlist_lookup_nvlist_array(nvp, FUID_NVP_ARRAY, 166 &fuidnvp, &count)) == 0); 167 168 for (i = 0; i != count; i++) { 169 fuid_idx_t *idxnode; 170 fuid_domain_t *domnode; 171 char *domain; 172 avl_index_t loc; 173 uint64_t idx, offset; 174 175 VERIFY(nvlist_lookup_string(fuidnvp[i], FUID_DOMAIN, 176 &domain) == 0); 177 VERIFY(nvlist_lookup_uint64(fuidnvp[i], FUID_IDX, 178 &idx) == 0); 179 VERIFY(nvlist_lookup_uint64(fuidnvp[i], FUID_OFFSET, 180 &offset) == 0); 181 182 idxnode = kmem_alloc(sizeof (fuid_idx_t), KM_SLEEP); 183 domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP); 184 185 domnode->f_idx = idxnode->f_idx = idx; 186 domnode->f_ksid = ksid_lookupdomain(domain); 187 idxnode->f_domain = domnode; 188 domnode->f_offset = offset; 189 if (avl_find(&zfsvfs->z_fuid_idx, 190 idxnode, &loc) == NULL) { 191 avl_insert(&zfsvfs->z_fuid_idx, idxnode, loc); 192 } 193 if (avl_find(&zfsvfs->z_fuid_domain, 194 domnode, &loc) == NULL) { 195 avl_insert(&zfsvfs->z_fuid_domain, 196 domnode, loc); 197 } 198 } 199 nvlist_free(nvp); 200 } 201 kmem_free(packed, zfsvfs->z_fuid_size); 202 203 initialized: 204 zfsvfs->z_fuid_loaded = B_TRUE; 205 rw_exit(&zfsvfs->z_fuid_lock); 206 } 207 208 /* 209 * Query domain table for a given domain. 210 * 211 * If domain isn't found it is added to AVL trees and 212 * the results are pushed out to disk. 213 */ 214 int 215 zfs_fuid_find_by_domain(zfsvfs_t *zfsvfs, const char *domain, char **retdomain, 216 dmu_tx_t *tx) 217 { 218 fuid_domain_t searchnode, *findnode; 219 avl_index_t loc; 220 221 /* 222 * If the dummy "nobody" domain then return an index of 0 223 * to cause the created FUID to be a standard POSIX id 224 * for the user nobody. 225 */ 226 if (domain[0] == '\0') { 227 *retdomain = ""; 228 return (0); 229 } 230 231 searchnode.f_ksid = ksid_lookupdomain(domain); 232 if (retdomain) { 233 *retdomain = searchnode.f_ksid->kd_name; 234 } 235 if (zfsvfs->z_fuid_loaded == B_FALSE) 236 zfs_fuid_init(zfsvfs, tx); 237 238 rw_enter(&zfsvfs->z_fuid_lock, RW_READER); 239 findnode = avl_find(&zfsvfs->z_fuid_domain, &searchnode, &loc); 240 rw_exit(&zfsvfs->z_fuid_lock); 241 242 if (findnode) { 243 ksiddomain_rele(searchnode.f_ksid); 244 return (findnode->f_idx); 245 } else { 246 fuid_domain_t *domnode; 247 fuid_idx_t *newidxnode; 248 nvlist_t *nvp; 249 nvlist_t **fuids; 250 uint64_t retidx; 251 size_t nvsize = 0; 252 char *packed; 253 dmu_buf_t *db; 254 int i = 0; 255 256 domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP); 257 domnode->f_ksid = searchnode.f_ksid; 258 domnode->f_offset = 0; 259 260 newidxnode = kmem_alloc(sizeof (fuid_idx_t), KM_SLEEP); 261 newidxnode->f_domain = domnode; 262 263 rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); 264 retidx = domnode->f_idx = newidxnode->f_idx = 265 avl_numnodes(&zfsvfs->z_fuid_idx) + 1; 266 267 avl_add(&zfsvfs->z_fuid_domain, domnode); 268 avl_add(&zfsvfs->z_fuid_idx, newidxnode); 269 /* 270 * Now resync the on-disk nvlist. 271 */ 272 VERIFY(nvlist_alloc(&nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 273 274 domnode = avl_first(&zfsvfs->z_fuid_domain); 275 fuids = kmem_alloc(retidx * sizeof (void *), KM_SLEEP); 276 while (domnode) { 277 VERIFY(nvlist_alloc(&fuids[i], 278 NV_UNIQUE_NAME, KM_SLEEP) == 0); 279 VERIFY(nvlist_add_uint64(fuids[i], FUID_IDX, 280 domnode->f_idx) == 0); 281 VERIFY(nvlist_add_uint64(fuids[i], FUID_OFFSET, 282 domnode->f_offset) == 0); 283 VERIFY(nvlist_add_string(fuids[i++], FUID_DOMAIN, 284 domnode->f_ksid->kd_name) == 0); 285 domnode = AVL_NEXT(&zfsvfs->z_fuid_domain, domnode); 286 } 287 VERIFY(nvlist_add_nvlist_array(nvp, FUID_NVP_ARRAY, 288 fuids, retidx) == 0); 289 for (i = 0; i != retidx; i++) 290 nvlist_free(fuids[i]); 291 kmem_free(fuids, retidx * sizeof (void *)); 292 VERIFY(nvlist_size(nvp, &nvsize, NV_ENCODE_XDR) == 0); 293 packed = kmem_alloc(nvsize, KM_SLEEP); 294 VERIFY(nvlist_pack(nvp, &packed, &nvsize, 295 NV_ENCODE_XDR, KM_SLEEP) == 0); 296 nvlist_free(nvp); 297 zfsvfs->z_fuid_size = nvsize; 298 dmu_write(zfsvfs->z_os, zfsvfs->z_fuid_obj, 0, 299 zfsvfs->z_fuid_size, packed, tx); 300 kmem_free(packed, zfsvfs->z_fuid_size); 301 VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, zfsvfs->z_fuid_obj, 302 FTAG, &db)); 303 dmu_buf_will_dirty(db, tx); 304 *(uint64_t *)db->db_data = zfsvfs->z_fuid_size; 305 dmu_buf_rele(db, FTAG); 306 307 rw_exit(&zfsvfs->z_fuid_lock); 308 return (retidx); 309 } 310 } 311 312 /* 313 * Query domain table by index, returning domain string 314 * 315 * Returns a pointer from an avl node of the domain string. 316 * 317 */ 318 char * 319 zfs_fuid_find_by_idx(zfsvfs_t *zfsvfs, uint64_t idx) 320 { 321 fuid_idx_t searchnode, *findnode; 322 avl_index_t loc; 323 324 if (idx == 0 || zfsvfs->z_use_fuids == B_FALSE) 325 return (NULL); 326 327 if (zfsvfs->z_fuid_loaded == B_FALSE) 328 zfs_fuid_init(zfsvfs, NULL); 329 330 searchnode.f_idx = idx; 331 332 rw_enter(&zfsvfs->z_fuid_lock, RW_READER); 333 findnode = avl_find(&zfsvfs->z_fuid_idx, &searchnode, &loc); 334 rw_exit(&zfsvfs->z_fuid_lock); 335 336 ASSERT(findnode); 337 return (findnode->f_domain->f_ksid->kd_name); 338 } 339 340 void 341 zfs_fuid_get_mappings(zfs_fuid_hdl_t *hdl) 342 { 343 VERIFY(hdl != NULL); 344 if (hdl->z_map_needed == B_FALSE) 345 return; 346 347 (void) kidmap_get_mappings(hdl->z_hdl); 348 349 kidmap_get_destroy(hdl->z_hdl); 350 hdl->z_hdl = NULL; 351 hdl->z_map_needed = B_FALSE; 352 } 353 354 void 355 zfs_fuid_queue_map_id(zfsvfs_t *zfsvfs, zfs_fuid_hdl_t *hdl, 356 uint64_t fuid, cred_t *cr, zfs_fuid_type_t type, uid_t *id) 357 { 358 uint32_t index = FUID_INDEX(fuid); 359 char *domain; 360 int status; 361 362 VERIFY(hdl); 363 364 if (index == 0 || zfsvfs->z_use_fuids == B_FALSE) { 365 *id = (uid_t)fuid; 366 return; 367 } 368 369 if (hdl->z_hdl == NULL) { 370 hdl->z_hdl = kidmap_get_create(crgetzone(cr)); 371 hdl->z_map_needed = B_TRUE; 372 } 373 374 domain = zfs_fuid_find_by_idx(zfsvfs, index); 375 ASSERT(domain != NULL); 376 377 if (type == ZFS_OWNER || type == ZFS_ACE_USER) 378 status = kidmap_batch_getuidbysid(hdl->z_hdl, domain, 379 FUID_RID(fuid), id, &hdl->z_status); 380 else 381 status = kidmap_batch_getgidbysid(hdl->z_hdl, domain, 382 FUID_RID(fuid), id, &hdl->z_status); 383 ASSERT(status == 0); 384 } 385 386 void 387 zfs_fuid_map_ids(znode_t *zp, cred_t *cr, uid_t *uid, uid_t *gid) 388 { 389 uint32_t uid_index = FUID_INDEX(zp->z_phys->zp_uid); 390 uint32_t gid_index = FUID_INDEX(zp->z_phys->zp_gid); 391 392 /* Favor the common case, neither will be ephemeral */ 393 if (uid_index == 0 && gid_index == 0) { 394 *uid = zp->z_phys->zp_uid; 395 *gid = zp->z_phys->zp_gid; 396 return; 397 } else { 398 zfs_fuid_hdl_t hdl = { 0 }; 399 400 zfs_fuid_queue_map_id(zp->z_zfsvfs, &hdl, 401 zp->z_phys->zp_uid, cr, ZFS_OWNER, uid); 402 403 zfs_fuid_queue_map_id(zp->z_zfsvfs, &hdl, 404 zp->z_phys->zp_gid, cr, ZFS_GROUP, gid); 405 406 zfs_fuid_get_mappings(&hdl); 407 } 408 } 409 410 void 411 zfs_fuid_map_id(zfsvfs_t *zfsvfs, uint64_t fuid, 412 cred_t *cr, zfs_fuid_type_t type, uid_t *id) 413 { 414 uint32_t index = FUID_INDEX(fuid); 415 char *domain; 416 417 if (index == 0) { 418 *id = (uid_t)fuid; 419 return; 420 } 421 422 domain = zfs_fuid_find_by_idx(zfsvfs, index); 423 ASSERT(domain != NULL); 424 425 if (type == ZFS_OWNER || type == ZFS_ACE_USER) 426 (void) kidmap_getuidbysid(crgetzone(cr), domain, 427 FUID_RID(fuid), id); 428 else 429 (void) kidmap_getgidbysid(crgetzone(cr), domain, 430 FUID_RID(fuid), id); 431 } 432 433 /* 434 * Add a FUID node to the list of fuid's being created for this 435 * ACL 436 * 437 * If ACL has multiple domains, then keep only one copy of each unique 438 * domain. 439 */ 440 static void 441 zfs_fuid_node_add(zfs_fuid_info_t **fuidpp, const char *domain, uint32_t rid, 442 uint64_t idx, uint64_t id, zfs_fuid_type_t type) 443 { 444 zfs_fuid_t *fuid; 445 zfs_fuid_domain_t *fuid_domain; 446 zfs_fuid_info_t *fuidp; 447 uint64_t fuididx; 448 boolean_t found = B_FALSE; 449 450 if (*fuidpp == NULL) 451 *fuidpp = zfs_fuid_info_alloc(); 452 453 fuidp = *fuidpp; 454 /* 455 * First find fuid domain index in linked list 456 * 457 * If one isn't found then create an entry. 458 */ 459 460 for (fuididx = 1, fuid_domain = list_head(&fuidp->z_domains); 461 fuid_domain; fuid_domain = list_next(&fuidp->z_domains, 462 fuid_domain), fuididx++) { 463 if (idx == fuid_domain->z_domidx) { 464 found = B_TRUE; 465 break; 466 } 467 } 468 469 if (found == B_FALSE) { 470 fuid_domain = kmem_alloc(sizeof (zfs_fuid_domain_t), KM_SLEEP); 471 fuid_domain->z_domain = domain; 472 fuid_domain->z_domidx = idx; 473 list_insert_tail(&fuidp->z_domains, fuid_domain); 474 fuidp->z_domain_str_sz += strlen(domain) + 1; 475 fuidp->z_domain_cnt++; 476 } 477 478 if (type == ZFS_ACE_USER || type == ZFS_ACE_GROUP) { 479 /* 480 * Now allocate fuid entry and add it on the end of the list 481 */ 482 483 fuid = kmem_alloc(sizeof (zfs_fuid_t), KM_SLEEP); 484 fuid->z_id = id; 485 fuid->z_domidx = idx; 486 fuid->z_logfuid = FUID_ENCODE(fuididx, rid); 487 488 list_insert_tail(&fuidp->z_fuids, fuid); 489 fuidp->z_fuid_cnt++; 490 } else { 491 if (type == ZFS_OWNER) 492 fuidp->z_fuid_owner = FUID_ENCODE(fuididx, rid); 493 else 494 fuidp->z_fuid_group = FUID_ENCODE(fuididx, rid); 495 } 496 } 497 498 /* 499 * Create a file system FUID, based on information in the users cred 500 */ 501 uint64_t 502 zfs_fuid_create_cred(zfsvfs_t *zfsvfs, uint64_t id, 503 zfs_fuid_type_t type, dmu_tx_t *tx, cred_t *cr, zfs_fuid_info_t **fuidp) 504 { 505 uint64_t idx; 506 ksid_t *ksid; 507 uint32_t rid; 508 char *kdomain; 509 const char *domain; 510 511 VERIFY(type == ZFS_OWNER || type == ZFS_GROUP); 512 513 if (zfsvfs->z_use_fuids == B_FALSE || !IS_EPHEMERAL(id)) 514 return ((uint64_t)id); 515 516 ksid = crgetsid(cr, (type == ZFS_OWNER) ? KSID_OWNER : KSID_GROUP); 517 518 VERIFY(ksid != NULL); 519 rid = ksid_getrid(ksid); 520 domain = ksid_getdomain(ksid); 521 522 idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, tx); 523 524 zfs_fuid_node_add(fuidp, kdomain, rid, idx, id, type); 525 526 return (FUID_ENCODE(idx, rid)); 527 } 528 529 /* 530 * Create a file system FUID for an ACL ace 531 * or a chown/chgrp of the file. 532 * This is similar to zfs_fuid_create_cred, except that 533 * we can't find the domain + rid information in the 534 * cred. Instead we have to query Winchester for the 535 * domain and rid. 536 * 537 * During replay operations the domain+rid information is 538 * found in the zfs_fuid_info_t that the replay code has 539 * attached to the zfsvfs of the file system. 540 */ 541 uint64_t 542 zfs_fuid_create(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr, 543 zfs_fuid_type_t type, dmu_tx_t *tx, zfs_fuid_info_t **fuidpp) 544 { 545 const char *domain; 546 char *kdomain; 547 uint32_t fuid_idx = FUID_INDEX(id); 548 uint32_t rid; 549 idmap_stat status; 550 uint64_t idx; 551 boolean_t is_replay = (zfsvfs->z_assign >= TXG_INITIAL); 552 zfs_fuid_t *zfuid = NULL; 553 zfs_fuid_info_t *fuidp; 554 555 /* 556 * If POSIX ID, or entry is already a FUID then 557 * just return the id 558 */ 559 if (!IS_EPHEMERAL(id) || fuid_idx != 0) 560 return (id); 561 562 if (is_replay) { 563 fuidp = zfsvfs->z_fuid_replay; 564 565 /* 566 * If we are passed an ephemeral id, but no 567 * fuid_info was logged then return NOBODY. 568 * This is most likely a result of idmap service 569 * not being available. 570 */ 571 if (fuidp == NULL) 572 return (UID_NOBODY); 573 574 switch (type) { 575 case ZFS_ACE_USER: 576 case ZFS_ACE_GROUP: 577 zfuid = list_head(&fuidp->z_fuids); 578 rid = FUID_RID(zfuid->z_logfuid); 579 idx = FUID_INDEX(zfuid->z_logfuid); 580 break; 581 case ZFS_OWNER: 582 rid = FUID_RID(fuidp->z_fuid_owner); 583 idx = FUID_INDEX(fuidp->z_fuid_owner); 584 break; 585 case ZFS_GROUP: 586 rid = FUID_RID(fuidp->z_fuid_group); 587 idx = FUID_INDEX(fuidp->z_fuid_group); 588 break; 589 }; 590 domain = fuidp->z_domain_table[idx -1]; 591 } else { 592 if (type == ZFS_OWNER || type == ZFS_ACE_USER) 593 status = kidmap_getsidbyuid(crgetzone(cr), id, 594 &domain, &rid); 595 else 596 status = kidmap_getsidbygid(crgetzone(cr), id, 597 &domain, &rid); 598 599 if (status != 0) { 600 /* 601 * When returning nobody we will need to 602 * make a dummy fuid table entry for logging 603 * purposes. 604 */ 605 rid = UID_NOBODY; 606 domain = ""; 607 } 608 609 } 610 611 idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, tx); 612 613 if (is_replay == B_FALSE) 614 zfs_fuid_node_add(fuidpp, kdomain, rid, idx, id, type); 615 else if (zfuid != NULL) { 616 list_remove(&fuidp->z_fuids, zfuid); 617 kmem_free(zfuid, sizeof (zfs_fuid_t)); 618 } 619 return (FUID_ENCODE(idx, rid)); 620 } 621 622 void 623 zfs_fuid_destroy(zfsvfs_t *zfsvfs) 624 { 625 fuid_domain_t *domnode; 626 fuid_idx_t *idxnode; 627 void *cookie; 628 629 rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); 630 if (zfsvfs->z_fuid_loaded == B_FALSE) { 631 rw_exit(&zfsvfs->z_fuid_lock); 632 return; 633 } 634 cookie = NULL; 635 while (domnode = avl_destroy_nodes(&zfsvfs->z_fuid_domain, &cookie)) { 636 ksiddomain_rele(domnode->f_ksid); 637 kmem_free(domnode, sizeof (fuid_domain_t)); 638 } 639 avl_destroy(&zfsvfs->z_fuid_domain); 640 cookie = NULL; 641 while (idxnode = avl_destroy_nodes(&zfsvfs->z_fuid_idx, &cookie)) 642 kmem_free(idxnode, sizeof (fuid_idx_t)); 643 avl_destroy(&zfsvfs->z_fuid_idx); 644 rw_exit(&zfsvfs->z_fuid_lock); 645 } 646 647 /* 648 * Allocate zfs_fuid_info for tracking FUIDs created during 649 * zfs_mknode, VOP_SETATTR() or VOP_SETSECATTR() 650 */ 651 zfs_fuid_info_t * 652 zfs_fuid_info_alloc(void) 653 { 654 zfs_fuid_info_t *fuidp; 655 656 fuidp = kmem_zalloc(sizeof (zfs_fuid_info_t), KM_SLEEP); 657 list_create(&fuidp->z_domains, sizeof (zfs_fuid_domain_t), 658 offsetof(zfs_fuid_domain_t, z_next)); 659 list_create(&fuidp->z_fuids, sizeof (zfs_fuid_t), 660 offsetof(zfs_fuid_t, z_next)); 661 return (fuidp); 662 } 663 664 /* 665 * Release all memory associated with zfs_fuid_info_t 666 */ 667 void 668 zfs_fuid_info_free(zfs_fuid_info_t *fuidp) 669 { 670 zfs_fuid_t *zfuid; 671 zfs_fuid_domain_t *zdomain; 672 673 while ((zfuid = list_head(&fuidp->z_fuids)) != NULL) { 674 list_remove(&fuidp->z_fuids, zfuid); 675 kmem_free(zfuid, sizeof (zfs_fuid_t)); 676 } 677 678 if (fuidp->z_domain_table != NULL) 679 kmem_free(fuidp->z_domain_table, 680 (sizeof (char **)) * fuidp->z_domain_cnt); 681 682 while ((zdomain = list_head(&fuidp->z_domains)) != NULL) { 683 list_remove(&fuidp->z_domains, zdomain); 684 kmem_free(zdomain, sizeof (zfs_fuid_domain_t)); 685 } 686 687 kmem_free(fuidp, sizeof (zfs_fuid_info_t)); 688 } 689 690 /* 691 * Check to see if id is a groupmember. If cred 692 * has ksid info then sidlist is checked first 693 * and if still not found then POSIX groups are checked 694 * 695 * Will use a straight FUID compare when possible. 696 */ 697 boolean_t 698 zfs_groupmember(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr) 699 { 700 ksid_t *ksid = crgetsid(cr, KSID_GROUP); 701 uid_t gid; 702 703 if (ksid) { 704 int i; 705 ksid_t *ksid_groups; 706 ksidlist_t *ksidlist = crgetsidlist(cr); 707 uint32_t idx = FUID_INDEX(id); 708 uint32_t rid = FUID_RID(id); 709 710 ASSERT(ksidlist); 711 ksid_groups = ksidlist->ksl_sids; 712 713 for (i = 0; i != ksidlist->ksl_nsid; i++) { 714 if (idx == 0) { 715 if (id != IDMAP_WK_CREATOR_GROUP_GID && 716 id == ksid_groups[i].ks_id) { 717 return (B_TRUE); 718 } 719 } else { 720 char *domain; 721 722 domain = zfs_fuid_find_by_idx(zfsvfs, idx); 723 ASSERT(domain != NULL); 724 725 if (strcmp(domain, 726 IDMAP_WK_CREATOR_SID_AUTHORITY) == 0) { 727 return (B_FALSE); 728 } 729 730 if ((strcmp(domain, 731 ksid_groups[i].ks_domain->kd_name) == 0) && 732 rid == ksid_groups[i].ks_rid) { 733 return (B_TRUE); 734 } 735 } 736 } 737 } 738 739 /* 740 * Not found in ksidlist, check posix groups 741 */ 742 zfs_fuid_map_id(zfsvfs, id, cr, ZFS_GROUP, &gid); 743 return (groupmember(gid, cr)); 744 } 745