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