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