1185029Spjd/* 2185029Spjd * CDDL HEADER START 3185029Spjd * 4185029Spjd * The contents of this file are subject to the terms of the 5185029Spjd * Common Development and Distribution License (the "License"). 6185029Spjd * You may not use this file except in compliance with the License. 7185029Spjd * 8185029Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9185029Spjd * or http://www.opensolaris.org/os/licensing. 10185029Spjd * See the License for the specific language governing permissions 11185029Spjd * and limitations under the License. 12185029Spjd * 13185029Spjd * When distributing Covered Code, include this CDDL HEADER in each 14185029Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15185029Spjd * If applicable, add the following below this CDDL HEADER, with the 16185029Spjd * fields enclosed by brackets "[]" replaced with your own identifying 17185029Spjd * information: Portions Copyright [yyyy] [name of copyright owner] 18185029Spjd * 19185029Spjd * CDDL HEADER END 20185029Spjd */ 21185029Spjd/* 22219089Spjd * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23185029Spjd */ 24185029Spjd 25185029Spjd#include <sys/zfs_context.h> 26185029Spjd#include <sys/dmu.h> 27185029Spjd#include <sys/avl.h> 28185029Spjd#include <sys/zap.h> 29185029Spjd#include <sys/refcount.h> 30185029Spjd#include <sys/nvpair.h> 31185029Spjd#ifdef _KERNEL 32185029Spjd#include <sys/kidmap.h> 33185029Spjd#include <sys/sid.h> 34185029Spjd#include <sys/zfs_vfsops.h> 35185029Spjd#include <sys/zfs_znode.h> 36185029Spjd#endif 37185029Spjd#include <sys/zfs_fuid.h> 38185029Spjd 39185029Spjd/* 40185029Spjd * FUID Domain table(s). 41185029Spjd * 42185029Spjd * The FUID table is stored as a packed nvlist of an array 43185029Spjd * of nvlists which contain an index, domain string and offset 44185029Spjd * 45185029Spjd * During file system initialization the nvlist(s) are read and 46185029Spjd * two AVL trees are created. One tree is keyed by the index number 47185029Spjd * and the other by the domain string. Nodes are never removed from 48209962Smm * trees, but new entries may be added. If a new entry is added then 49209962Smm * the zfsvfs->z_fuid_dirty flag is set to true and the caller will then 50209962Smm * be responsible for calling zfs_fuid_sync() to sync the changes to disk. 51209962Smm * 52185029Spjd */ 53185029Spjd 54185029Spjd#define FUID_IDX "fuid_idx" 55185029Spjd#define FUID_DOMAIN "fuid_domain" 56185029Spjd#define FUID_OFFSET "fuid_offset" 57185029Spjd#define FUID_NVP_ARRAY "fuid_nvlist" 58185029Spjd 59185029Spjdtypedef struct fuid_domain { 60185029Spjd avl_node_t f_domnode; 61185029Spjd avl_node_t f_idxnode; 62185029Spjd ksiddomain_t *f_ksid; 63185029Spjd uint64_t f_idx; 64185029Spjd} fuid_domain_t; 65185029Spjd 66185029Spjdstatic char *nulldomain = ""; 67185029Spjd 68185029Spjd/* 69185029Spjd * Compare two indexes. 70185029Spjd */ 71185029Spjdstatic int 72185029Spjdidx_compare(const void *arg1, const void *arg2) 73185029Spjd{ 74185029Spjd const fuid_domain_t *node1 = arg1; 75185029Spjd const fuid_domain_t *node2 = arg2; 76185029Spjd 77185029Spjd if (node1->f_idx < node2->f_idx) 78185029Spjd return (-1); 79185029Spjd else if (node1->f_idx > node2->f_idx) 80185029Spjd return (1); 81185029Spjd return (0); 82185029Spjd} 83185029Spjd 84185029Spjd/* 85185029Spjd * Compare two domain strings. 86185029Spjd */ 87185029Spjdstatic int 88185029Spjddomain_compare(const void *arg1, const void *arg2) 89185029Spjd{ 90185029Spjd const fuid_domain_t *node1 = arg1; 91185029Spjd const fuid_domain_t *node2 = arg2; 92185029Spjd int val; 93185029Spjd 94185029Spjd val = strcmp(node1->f_ksid->kd_name, node2->f_ksid->kd_name); 95185029Spjd if (val == 0) 96185029Spjd return (0); 97185029Spjd return (val > 0 ? 1 : -1); 98185029Spjd} 99185029Spjd 100209962Smmvoid 101209962Smmzfs_fuid_avl_tree_create(avl_tree_t *idx_tree, avl_tree_t *domain_tree) 102209962Smm{ 103209962Smm avl_create(idx_tree, idx_compare, 104209962Smm sizeof (fuid_domain_t), offsetof(fuid_domain_t, f_idxnode)); 105209962Smm avl_create(domain_tree, domain_compare, 106209962Smm sizeof (fuid_domain_t), offsetof(fuid_domain_t, f_domnode)); 107209962Smm} 108209962Smm 109185029Spjd/* 110185029Spjd * load initial fuid domain and idx trees. This function is used by 111185029Spjd * both the kernel and zdb. 112185029Spjd */ 113185029Spjduint64_t 114185029Spjdzfs_fuid_table_load(objset_t *os, uint64_t fuid_obj, avl_tree_t *idx_tree, 115185029Spjd avl_tree_t *domain_tree) 116185029Spjd{ 117185029Spjd dmu_buf_t *db; 118185029Spjd uint64_t fuid_size; 119185029Spjd 120209962Smm ASSERT(fuid_obj != 0); 121209962Smm VERIFY(0 == dmu_bonus_hold(os, fuid_obj, 122209962Smm FTAG, &db)); 123185029Spjd fuid_size = *(uint64_t *)db->db_data; 124185029Spjd dmu_buf_rele(db, FTAG); 125185029Spjd 126185029Spjd if (fuid_size) { 127185029Spjd nvlist_t **fuidnvp; 128185029Spjd nvlist_t *nvp = NULL; 129185029Spjd uint_t count; 130185029Spjd char *packed; 131185029Spjd int i; 132185029Spjd 133185029Spjd packed = kmem_alloc(fuid_size, KM_SLEEP); 134209962Smm VERIFY(dmu_read(os, fuid_obj, 0, 135209962Smm fuid_size, packed, DMU_READ_PREFETCH) == 0); 136185029Spjd VERIFY(nvlist_unpack(packed, fuid_size, 137185029Spjd &nvp, 0) == 0); 138185029Spjd VERIFY(nvlist_lookup_nvlist_array(nvp, FUID_NVP_ARRAY, 139185029Spjd &fuidnvp, &count) == 0); 140185029Spjd 141185029Spjd for (i = 0; i != count; i++) { 142185029Spjd fuid_domain_t *domnode; 143185029Spjd char *domain; 144185029Spjd uint64_t idx; 145185029Spjd 146185029Spjd VERIFY(nvlist_lookup_string(fuidnvp[i], FUID_DOMAIN, 147185029Spjd &domain) == 0); 148185029Spjd VERIFY(nvlist_lookup_uint64(fuidnvp[i], FUID_IDX, 149185029Spjd &idx) == 0); 150185029Spjd 151185029Spjd domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP); 152185029Spjd 153185029Spjd domnode->f_idx = idx; 154185029Spjd domnode->f_ksid = ksid_lookupdomain(domain); 155185029Spjd avl_add(idx_tree, domnode); 156185029Spjd avl_add(domain_tree, domnode); 157185029Spjd } 158185029Spjd nvlist_free(nvp); 159185029Spjd kmem_free(packed, fuid_size); 160185029Spjd } 161185029Spjd return (fuid_size); 162185029Spjd} 163185029Spjd 164185029Spjdvoid 165185029Spjdzfs_fuid_table_destroy(avl_tree_t *idx_tree, avl_tree_t *domain_tree) 166185029Spjd{ 167185029Spjd fuid_domain_t *domnode; 168185029Spjd void *cookie; 169185029Spjd 170185029Spjd cookie = NULL; 171185029Spjd while (domnode = avl_destroy_nodes(domain_tree, &cookie)) 172185029Spjd ksiddomain_rele(domnode->f_ksid); 173185029Spjd 174185029Spjd avl_destroy(domain_tree); 175185029Spjd cookie = NULL; 176185029Spjd while (domnode = avl_destroy_nodes(idx_tree, &cookie)) 177185029Spjd kmem_free(domnode, sizeof (fuid_domain_t)); 178185029Spjd avl_destroy(idx_tree); 179185029Spjd} 180185029Spjd 181185029Spjdchar * 182185029Spjdzfs_fuid_idx_domain(avl_tree_t *idx_tree, uint32_t idx) 183185029Spjd{ 184185029Spjd fuid_domain_t searchnode, *findnode; 185185029Spjd avl_index_t loc; 186185029Spjd 187185029Spjd searchnode.f_idx = idx; 188185029Spjd 189185029Spjd findnode = avl_find(idx_tree, &searchnode, &loc); 190185029Spjd 191185029Spjd return (findnode ? findnode->f_ksid->kd_name : nulldomain); 192185029Spjd} 193185029Spjd 194185029Spjd#ifdef _KERNEL 195185029Spjd/* 196185029Spjd * Load the fuid table(s) into memory. 197185029Spjd */ 198185029Spjdstatic void 199209962Smmzfs_fuid_init(zfsvfs_t *zfsvfs) 200185029Spjd{ 201185029Spjd rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); 202185029Spjd 203185029Spjd if (zfsvfs->z_fuid_loaded) { 204185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 205185029Spjd return; 206185029Spjd } 207185029Spjd 208209962Smm zfs_fuid_avl_tree_create(&zfsvfs->z_fuid_idx, &zfsvfs->z_fuid_domain); 209185029Spjd 210209962Smm (void) zap_lookup(zfsvfs->z_os, MASTER_NODE_OBJ, 211209962Smm ZFS_FUID_TABLES, 8, 1, &zfsvfs->z_fuid_obj); 212185029Spjd if (zfsvfs->z_fuid_obj != 0) { 213185029Spjd zfsvfs->z_fuid_size = zfs_fuid_table_load(zfsvfs->z_os, 214185029Spjd zfsvfs->z_fuid_obj, &zfsvfs->z_fuid_idx, 215185029Spjd &zfsvfs->z_fuid_domain); 216185029Spjd } 217185029Spjd 218209962Smm zfsvfs->z_fuid_loaded = B_TRUE; 219185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 220185029Spjd} 221185029Spjd 222185029Spjd/* 223209962Smm * sync out AVL trees to persistent storage. 224209962Smm */ 225209962Smmvoid 226209962Smmzfs_fuid_sync(zfsvfs_t *zfsvfs, dmu_tx_t *tx) 227209962Smm{ 228209962Smm nvlist_t *nvp; 229209962Smm nvlist_t **fuids; 230209962Smm size_t nvsize = 0; 231209962Smm char *packed; 232209962Smm dmu_buf_t *db; 233209962Smm fuid_domain_t *domnode; 234209962Smm int numnodes; 235209962Smm int i; 236209962Smm 237209962Smm if (!zfsvfs->z_fuid_dirty) { 238209962Smm return; 239209962Smm } 240209962Smm 241209962Smm rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); 242209962Smm 243209962Smm /* 244209962Smm * First see if table needs to be created? 245209962Smm */ 246209962Smm if (zfsvfs->z_fuid_obj == 0) { 247209962Smm zfsvfs->z_fuid_obj = dmu_object_alloc(zfsvfs->z_os, 248209962Smm DMU_OT_FUID, 1 << 14, DMU_OT_FUID_SIZE, 249209962Smm sizeof (uint64_t), tx); 250209962Smm VERIFY(zap_add(zfsvfs->z_os, MASTER_NODE_OBJ, 251209962Smm ZFS_FUID_TABLES, sizeof (uint64_t), 1, 252209962Smm &zfsvfs->z_fuid_obj, tx) == 0); 253209962Smm } 254209962Smm 255209962Smm VERIFY(nvlist_alloc(&nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 256209962Smm 257209962Smm numnodes = avl_numnodes(&zfsvfs->z_fuid_idx); 258209962Smm fuids = kmem_alloc(numnodes * sizeof (void *), KM_SLEEP); 259209962Smm for (i = 0, domnode = avl_first(&zfsvfs->z_fuid_domain); domnode; i++, 260209962Smm domnode = AVL_NEXT(&zfsvfs->z_fuid_domain, domnode)) { 261209962Smm VERIFY(nvlist_alloc(&fuids[i], NV_UNIQUE_NAME, KM_SLEEP) == 0); 262209962Smm VERIFY(nvlist_add_uint64(fuids[i], FUID_IDX, 263209962Smm domnode->f_idx) == 0); 264209962Smm VERIFY(nvlist_add_uint64(fuids[i], FUID_OFFSET, 0) == 0); 265209962Smm VERIFY(nvlist_add_string(fuids[i], FUID_DOMAIN, 266209962Smm domnode->f_ksid->kd_name) == 0); 267209962Smm } 268209962Smm VERIFY(nvlist_add_nvlist_array(nvp, FUID_NVP_ARRAY, 269209962Smm fuids, numnodes) == 0); 270209962Smm for (i = 0; i != numnodes; i++) 271209962Smm nvlist_free(fuids[i]); 272209962Smm kmem_free(fuids, numnodes * sizeof (void *)); 273209962Smm VERIFY(nvlist_size(nvp, &nvsize, NV_ENCODE_XDR) == 0); 274209962Smm packed = kmem_alloc(nvsize, KM_SLEEP); 275209962Smm VERIFY(nvlist_pack(nvp, &packed, &nvsize, 276209962Smm NV_ENCODE_XDR, KM_SLEEP) == 0); 277209962Smm nvlist_free(nvp); 278209962Smm zfsvfs->z_fuid_size = nvsize; 279209962Smm dmu_write(zfsvfs->z_os, zfsvfs->z_fuid_obj, 0, 280209962Smm zfsvfs->z_fuid_size, packed, tx); 281209962Smm kmem_free(packed, zfsvfs->z_fuid_size); 282209962Smm VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, zfsvfs->z_fuid_obj, 283209962Smm FTAG, &db)); 284209962Smm dmu_buf_will_dirty(db, tx); 285209962Smm *(uint64_t *)db->db_data = zfsvfs->z_fuid_size; 286209962Smm dmu_buf_rele(db, FTAG); 287209962Smm 288209962Smm zfsvfs->z_fuid_dirty = B_FALSE; 289209962Smm rw_exit(&zfsvfs->z_fuid_lock); 290209962Smm} 291209962Smm 292209962Smm/* 293185029Spjd * Query domain table for a given domain. 294185029Spjd * 295209962Smm * If domain isn't found and addok is set, it is added to AVL trees and 296209962Smm * the zfsvfs->z_fuid_dirty flag will be set to TRUE. It will then be 297209962Smm * necessary for the caller or another thread to detect the dirty table 298209962Smm * and sync out the changes. 299185029Spjd */ 300185029Spjdint 301209962Smmzfs_fuid_find_by_domain(zfsvfs_t *zfsvfs, const char *domain, 302209962Smm char **retdomain, boolean_t addok) 303185029Spjd{ 304185029Spjd fuid_domain_t searchnode, *findnode; 305185029Spjd avl_index_t loc; 306185029Spjd krw_t rw = RW_READER; 307185029Spjd 308185029Spjd /* 309185029Spjd * If the dummy "nobody" domain then return an index of 0 310185029Spjd * to cause the created FUID to be a standard POSIX id 311185029Spjd * for the user nobody. 312185029Spjd */ 313185029Spjd if (domain[0] == '\0') { 314209962Smm if (retdomain) 315209962Smm *retdomain = nulldomain; 316185029Spjd return (0); 317185029Spjd } 318185029Spjd 319185029Spjd searchnode.f_ksid = ksid_lookupdomain(domain); 320209962Smm if (retdomain) 321185029Spjd *retdomain = searchnode.f_ksid->kd_name; 322185029Spjd if (!zfsvfs->z_fuid_loaded) 323209962Smm zfs_fuid_init(zfsvfs); 324185029Spjd 325185029Spjdretry: 326185029Spjd rw_enter(&zfsvfs->z_fuid_lock, rw); 327185029Spjd findnode = avl_find(&zfsvfs->z_fuid_domain, &searchnode, &loc); 328185029Spjd 329185029Spjd if (findnode) { 330185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 331185029Spjd ksiddomain_rele(searchnode.f_ksid); 332185029Spjd return (findnode->f_idx); 333209962Smm } else if (addok) { 334185029Spjd fuid_domain_t *domnode; 335185029Spjd uint64_t retidx; 336185029Spjd 337185029Spjd if (rw == RW_READER && !rw_tryupgrade(&zfsvfs->z_fuid_lock)) { 338185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 339185029Spjd rw = RW_WRITER; 340185029Spjd goto retry; 341185029Spjd } 342185029Spjd 343185029Spjd domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP); 344185029Spjd domnode->f_ksid = searchnode.f_ksid; 345185029Spjd 346185029Spjd retidx = domnode->f_idx = avl_numnodes(&zfsvfs->z_fuid_idx) + 1; 347185029Spjd 348185029Spjd avl_add(&zfsvfs->z_fuid_domain, domnode); 349185029Spjd avl_add(&zfsvfs->z_fuid_idx, domnode); 350209962Smm zfsvfs->z_fuid_dirty = B_TRUE; 351185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 352185029Spjd return (retidx); 353209962Smm } else { 354209962Smm rw_exit(&zfsvfs->z_fuid_lock); 355209962Smm return (-1); 356185029Spjd } 357185029Spjd} 358185029Spjd 359185029Spjd/* 360185029Spjd * Query domain table by index, returning domain string 361185029Spjd * 362185029Spjd * Returns a pointer from an avl node of the domain string. 363185029Spjd * 364185029Spjd */ 365209962Smmconst char * 366185029Spjdzfs_fuid_find_by_idx(zfsvfs_t *zfsvfs, uint32_t idx) 367185029Spjd{ 368185029Spjd char *domain; 369185029Spjd 370185029Spjd if (idx == 0 || !zfsvfs->z_use_fuids) 371185029Spjd return (NULL); 372185029Spjd 373185029Spjd if (!zfsvfs->z_fuid_loaded) 374209962Smm zfs_fuid_init(zfsvfs); 375185029Spjd 376185029Spjd rw_enter(&zfsvfs->z_fuid_lock, RW_READER); 377185029Spjd 378219089Spjd if (zfsvfs->z_fuid_obj || zfsvfs->z_fuid_dirty) 379185029Spjd domain = zfs_fuid_idx_domain(&zfsvfs->z_fuid_idx, idx); 380185029Spjd else 381185029Spjd domain = nulldomain; 382185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 383185029Spjd 384185029Spjd ASSERT(domain); 385185029Spjd return (domain); 386185029Spjd} 387185029Spjd 388185029Spjdvoid 389185029Spjdzfs_fuid_map_ids(znode_t *zp, cred_t *cr, uid_t *uidp, uid_t *gidp) 390185029Spjd{ 391219089Spjd *uidp = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_uid, cr, ZFS_OWNER); 392219089Spjd *gidp = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_gid, cr, ZFS_GROUP); 393185029Spjd} 394185029Spjd 395185029Spjduid_t 396185029Spjdzfs_fuid_map_id(zfsvfs_t *zfsvfs, uint64_t fuid, 397185029Spjd cred_t *cr, zfs_fuid_type_t type) 398185029Spjd{ 399185029Spjd uint32_t index = FUID_INDEX(fuid); 400209962Smm const char *domain; 401185029Spjd uid_t id; 402185029Spjd 403185029Spjd if (index == 0) 404185029Spjd return (fuid); 405185029Spjd 406185029Spjd domain = zfs_fuid_find_by_idx(zfsvfs, index); 407185029Spjd ASSERT(domain != NULL); 408185029Spjd 409210398Smm#ifdef sun 410185029Spjd if (type == ZFS_OWNER || type == ZFS_ACE_USER) { 411185029Spjd (void) kidmap_getuidbysid(crgetzone(cr), domain, 412185029Spjd FUID_RID(fuid), &id); 413185029Spjd } else { 414185029Spjd (void) kidmap_getgidbysid(crgetzone(cr), domain, 415185029Spjd FUID_RID(fuid), &id); 416185029Spjd } 417219089Spjd#else /* !sun */ 418210398Smm id = UID_NOBODY; 419219089Spjd#endif /* !sun */ 420185029Spjd return (id); 421185029Spjd} 422185029Spjd 423185029Spjd/* 424185029Spjd * Add a FUID node to the list of fuid's being created for this 425185029Spjd * ACL 426185029Spjd * 427185029Spjd * If ACL has multiple domains, then keep only one copy of each unique 428185029Spjd * domain. 429185029Spjd */ 430219089Spjdvoid 431185029Spjdzfs_fuid_node_add(zfs_fuid_info_t **fuidpp, const char *domain, uint32_t rid, 432185029Spjd uint64_t idx, uint64_t id, zfs_fuid_type_t type) 433185029Spjd{ 434185029Spjd zfs_fuid_t *fuid; 435185029Spjd zfs_fuid_domain_t *fuid_domain; 436185029Spjd zfs_fuid_info_t *fuidp; 437185029Spjd uint64_t fuididx; 438185029Spjd boolean_t found = B_FALSE; 439185029Spjd 440185029Spjd if (*fuidpp == NULL) 441185029Spjd *fuidpp = zfs_fuid_info_alloc(); 442185029Spjd 443185029Spjd fuidp = *fuidpp; 444185029Spjd /* 445185029Spjd * First find fuid domain index in linked list 446185029Spjd * 447185029Spjd * If one isn't found then create an entry. 448185029Spjd */ 449185029Spjd 450185029Spjd for (fuididx = 1, fuid_domain = list_head(&fuidp->z_domains); 451185029Spjd fuid_domain; fuid_domain = list_next(&fuidp->z_domains, 452185029Spjd fuid_domain), fuididx++) { 453185029Spjd if (idx == fuid_domain->z_domidx) { 454185029Spjd found = B_TRUE; 455185029Spjd break; 456185029Spjd } 457185029Spjd } 458185029Spjd 459185029Spjd if (!found) { 460185029Spjd fuid_domain = kmem_alloc(sizeof (zfs_fuid_domain_t), KM_SLEEP); 461185029Spjd fuid_domain->z_domain = domain; 462185029Spjd fuid_domain->z_domidx = idx; 463185029Spjd list_insert_tail(&fuidp->z_domains, fuid_domain); 464185029Spjd fuidp->z_domain_str_sz += strlen(domain) + 1; 465185029Spjd fuidp->z_domain_cnt++; 466185029Spjd } 467185029Spjd 468185029Spjd if (type == ZFS_ACE_USER || type == ZFS_ACE_GROUP) { 469209962Smm 470185029Spjd /* 471185029Spjd * Now allocate fuid entry and add it on the end of the list 472185029Spjd */ 473185029Spjd 474185029Spjd fuid = kmem_alloc(sizeof (zfs_fuid_t), KM_SLEEP); 475185029Spjd fuid->z_id = id; 476185029Spjd fuid->z_domidx = idx; 477185029Spjd fuid->z_logfuid = FUID_ENCODE(fuididx, rid); 478185029Spjd 479185029Spjd list_insert_tail(&fuidp->z_fuids, fuid); 480185029Spjd fuidp->z_fuid_cnt++; 481185029Spjd } else { 482185029Spjd if (type == ZFS_OWNER) 483185029Spjd fuidp->z_fuid_owner = FUID_ENCODE(fuididx, rid); 484185029Spjd else 485185029Spjd fuidp->z_fuid_group = FUID_ENCODE(fuididx, rid); 486185029Spjd } 487185029Spjd} 488185029Spjd 489185029Spjd/* 490185029Spjd * Create a file system FUID, based on information in the users cred 491219089Spjd * 492219089Spjd * If cred contains KSID_OWNER then it should be used to determine 493219089Spjd * the uid otherwise cred's uid will be used. By default cred's gid 494219089Spjd * is used unless it's an ephemeral ID in which case KSID_GROUP will 495219089Spjd * be used if it exists. 496185029Spjd */ 497185029Spjduint64_t 498185029Spjdzfs_fuid_create_cred(zfsvfs_t *zfsvfs, zfs_fuid_type_t type, 499209962Smm cred_t *cr, zfs_fuid_info_t **fuidp) 500185029Spjd{ 501185029Spjd uint64_t idx; 502185029Spjd ksid_t *ksid; 503185029Spjd uint32_t rid; 504185029Spjd char *kdomain; 505185029Spjd const char *domain; 506185029Spjd uid_t id; 507185029Spjd 508185029Spjd VERIFY(type == ZFS_OWNER || type == ZFS_GROUP); 509185029Spjd 510219089Spjd ksid = crgetsid(cr, (type == ZFS_OWNER) ? KSID_OWNER : KSID_GROUP); 511185029Spjd 512219089Spjd if (!zfsvfs->z_use_fuids || (ksid == NULL)) { 513219089Spjd id = (type == ZFS_OWNER) ? crgetuid(cr) : crgetgid(cr); 514219089Spjd 515219089Spjd if (IS_EPHEMERAL(id)) 516219089Spjd return ((type == ZFS_OWNER) ? UID_NOBODY : GID_NOBODY); 517219089Spjd 518185029Spjd return ((uint64_t)id); 519219089Spjd } 520185029Spjd 521219089Spjd /* 522219089Spjd * ksid is present and FUID is supported 523219089Spjd */ 524219089Spjd id = (type == ZFS_OWNER) ? ksid_getid(ksid) : crgetgid(cr); 525185029Spjd 526219089Spjd if (!IS_EPHEMERAL(id)) 527219089Spjd return ((uint64_t)id); 528219089Spjd 529219089Spjd if (type == ZFS_GROUP) 530219089Spjd id = ksid_getid(ksid); 531219089Spjd 532185029Spjd rid = ksid_getrid(ksid); 533185029Spjd domain = ksid_getdomain(ksid); 534219089Spjd 535209962Smm idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, B_TRUE); 536185029Spjd 537185029Spjd zfs_fuid_node_add(fuidp, kdomain, rid, idx, id, type); 538185029Spjd 539185029Spjd return (FUID_ENCODE(idx, rid)); 540185029Spjd} 541185029Spjd 542185029Spjd/* 543185029Spjd * Create a file system FUID for an ACL ace 544185029Spjd * or a chown/chgrp of the file. 545185029Spjd * This is similar to zfs_fuid_create_cred, except that 546185029Spjd * we can't find the domain + rid information in the 547185029Spjd * cred. Instead we have to query Winchester for the 548185029Spjd * domain and rid. 549185029Spjd * 550185029Spjd * During replay operations the domain+rid information is 551185029Spjd * found in the zfs_fuid_info_t that the replay code has 552185029Spjd * attached to the zfsvfs of the file system. 553185029Spjd */ 554185029Spjduint64_t 555185029Spjdzfs_fuid_create(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr, 556209962Smm zfs_fuid_type_t type, zfs_fuid_info_t **fuidpp) 557185029Spjd{ 558185029Spjd const char *domain; 559185029Spjd char *kdomain; 560185029Spjd uint32_t fuid_idx = FUID_INDEX(id); 561185029Spjd uint32_t rid; 562185029Spjd idmap_stat status; 563248369Smm uint64_t idx = 0; 564185029Spjd zfs_fuid_t *zfuid = NULL; 565248369Smm zfs_fuid_info_t *fuidp = NULL; 566185029Spjd 567185029Spjd /* 568185029Spjd * If POSIX ID, or entry is already a FUID then 569185029Spjd * just return the id 570185029Spjd * 571185029Spjd * We may also be handed an already FUID'ized id via 572185029Spjd * chmod. 573185029Spjd */ 574185029Spjd 575185029Spjd if (!zfsvfs->z_use_fuids || !IS_EPHEMERAL(id) || fuid_idx != 0) 576185029Spjd return (id); 577185029Spjd 578209962Smm if (zfsvfs->z_replay) { 579185029Spjd fuidp = zfsvfs->z_fuid_replay; 580185029Spjd 581185029Spjd /* 582185029Spjd * If we are passed an ephemeral id, but no 583185029Spjd * fuid_info was logged then return NOBODY. 584185029Spjd * This is most likely a result of idmap service 585185029Spjd * not being available. 586185029Spjd */ 587185029Spjd if (fuidp == NULL) 588185029Spjd return (UID_NOBODY); 589185029Spjd 590248369Smm VERIFY3U(type, >=, ZFS_OWNER); 591248369Smm VERIFY3U(type, <=, ZFS_ACE_GROUP); 592248369Smm 593185029Spjd switch (type) { 594185029Spjd case ZFS_ACE_USER: 595185029Spjd case ZFS_ACE_GROUP: 596185029Spjd zfuid = list_head(&fuidp->z_fuids); 597185029Spjd rid = FUID_RID(zfuid->z_logfuid); 598185029Spjd idx = FUID_INDEX(zfuid->z_logfuid); 599185029Spjd break; 600185029Spjd case ZFS_OWNER: 601185029Spjd rid = FUID_RID(fuidp->z_fuid_owner); 602185029Spjd idx = FUID_INDEX(fuidp->z_fuid_owner); 603185029Spjd break; 604185029Spjd case ZFS_GROUP: 605185029Spjd rid = FUID_RID(fuidp->z_fuid_group); 606185029Spjd idx = FUID_INDEX(fuidp->z_fuid_group); 607185029Spjd break; 608185029Spjd }; 609248369Smm domain = fuidp->z_domain_table[idx - 1]; 610185029Spjd } else { 611185029Spjd if (type == ZFS_OWNER || type == ZFS_ACE_USER) 612185029Spjd status = kidmap_getsidbyuid(crgetzone(cr), id, 613185029Spjd &domain, &rid); 614185029Spjd else 615185029Spjd status = kidmap_getsidbygid(crgetzone(cr), id, 616185029Spjd &domain, &rid); 617185029Spjd 618185029Spjd if (status != 0) { 619185029Spjd /* 620185029Spjd * When returning nobody we will need to 621185029Spjd * make a dummy fuid table entry for logging 622185029Spjd * purposes. 623185029Spjd */ 624185029Spjd rid = UID_NOBODY; 625185029Spjd domain = nulldomain; 626185029Spjd } 627185029Spjd } 628185029Spjd 629209962Smm idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, B_TRUE); 630185029Spjd 631209962Smm if (!zfsvfs->z_replay) 632209962Smm zfs_fuid_node_add(fuidpp, kdomain, 633209962Smm rid, idx, id, type); 634185029Spjd else if (zfuid != NULL) { 635185029Spjd list_remove(&fuidp->z_fuids, zfuid); 636185029Spjd kmem_free(zfuid, sizeof (zfs_fuid_t)); 637185029Spjd } 638185029Spjd return (FUID_ENCODE(idx, rid)); 639185029Spjd} 640185029Spjd 641185029Spjdvoid 642185029Spjdzfs_fuid_destroy(zfsvfs_t *zfsvfs) 643185029Spjd{ 644185029Spjd rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); 645185029Spjd if (!zfsvfs->z_fuid_loaded) { 646185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 647185029Spjd return; 648185029Spjd } 649185029Spjd zfs_fuid_table_destroy(&zfsvfs->z_fuid_idx, &zfsvfs->z_fuid_domain); 650185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 651185029Spjd} 652185029Spjd 653185029Spjd/* 654185029Spjd * Allocate zfs_fuid_info for tracking FUIDs created during 655185029Spjd * zfs_mknode, VOP_SETATTR() or VOP_SETSECATTR() 656185029Spjd */ 657185029Spjdzfs_fuid_info_t * 658185029Spjdzfs_fuid_info_alloc(void) 659185029Spjd{ 660185029Spjd zfs_fuid_info_t *fuidp; 661185029Spjd 662185029Spjd fuidp = kmem_zalloc(sizeof (zfs_fuid_info_t), KM_SLEEP); 663185029Spjd list_create(&fuidp->z_domains, sizeof (zfs_fuid_domain_t), 664185029Spjd offsetof(zfs_fuid_domain_t, z_next)); 665185029Spjd list_create(&fuidp->z_fuids, sizeof (zfs_fuid_t), 666185029Spjd offsetof(zfs_fuid_t, z_next)); 667185029Spjd return (fuidp); 668185029Spjd} 669185029Spjd 670185029Spjd/* 671185029Spjd * Release all memory associated with zfs_fuid_info_t 672185029Spjd */ 673185029Spjdvoid 674185029Spjdzfs_fuid_info_free(zfs_fuid_info_t *fuidp) 675185029Spjd{ 676185029Spjd zfs_fuid_t *zfuid; 677185029Spjd zfs_fuid_domain_t *zdomain; 678185029Spjd 679185029Spjd while ((zfuid = list_head(&fuidp->z_fuids)) != NULL) { 680185029Spjd list_remove(&fuidp->z_fuids, zfuid); 681185029Spjd kmem_free(zfuid, sizeof (zfs_fuid_t)); 682185029Spjd } 683185029Spjd 684185029Spjd if (fuidp->z_domain_table != NULL) 685185029Spjd kmem_free(fuidp->z_domain_table, 686185029Spjd (sizeof (char **)) * fuidp->z_domain_cnt); 687185029Spjd 688185029Spjd while ((zdomain = list_head(&fuidp->z_domains)) != NULL) { 689185029Spjd list_remove(&fuidp->z_domains, zdomain); 690185029Spjd kmem_free(zdomain, sizeof (zfs_fuid_domain_t)); 691185029Spjd } 692185029Spjd 693185029Spjd kmem_free(fuidp, sizeof (zfs_fuid_info_t)); 694185029Spjd} 695185029Spjd 696185029Spjd/* 697185029Spjd * Check to see if id is a groupmember. If cred 698185029Spjd * has ksid info then sidlist is checked first 699185029Spjd * and if still not found then POSIX groups are checked 700185029Spjd * 701185029Spjd * Will use a straight FUID compare when possible. 702185029Spjd */ 703185029Spjdboolean_t 704185029Spjdzfs_groupmember(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr) 705185029Spjd{ 706209962Smm#ifdef sun 707185029Spjd ksid_t *ksid = crgetsid(cr, KSID_GROUP); 708209962Smm ksidlist_t *ksidlist = crgetsidlist(cr); 709219089Spjd#endif /* !sun */ 710185029Spjd uid_t gid; 711185029Spjd 712209962Smm#ifdef sun 713209962Smm if (ksid && ksidlist) { 714185029Spjd int i; 715185029Spjd ksid_t *ksid_groups; 716185029Spjd uint32_t idx = FUID_INDEX(id); 717185029Spjd uint32_t rid = FUID_RID(id); 718185029Spjd 719185029Spjd ksid_groups = ksidlist->ksl_sids; 720185029Spjd 721185029Spjd for (i = 0; i != ksidlist->ksl_nsid; i++) { 722185029Spjd if (idx == 0) { 723185029Spjd if (id != IDMAP_WK_CREATOR_GROUP_GID && 724185029Spjd id == ksid_groups[i].ks_id) { 725185029Spjd return (B_TRUE); 726185029Spjd } 727185029Spjd } else { 728209962Smm const char *domain; 729185029Spjd 730185029Spjd domain = zfs_fuid_find_by_idx(zfsvfs, idx); 731185029Spjd ASSERT(domain != NULL); 732185029Spjd 733185029Spjd if (strcmp(domain, 734185029Spjd IDMAP_WK_CREATOR_SID_AUTHORITY) == 0) 735185029Spjd return (B_FALSE); 736185029Spjd 737185029Spjd if ((strcmp(domain, 738185029Spjd ksid_groups[i].ks_domain->kd_name) == 0) && 739185029Spjd rid == ksid_groups[i].ks_rid) 740185029Spjd return (B_TRUE); 741185029Spjd } 742185029Spjd } 743185029Spjd } 744219089Spjd#endif /* !sun */ 745185029Spjd 746185029Spjd /* 747185029Spjd * Not found in ksidlist, check posix groups 748185029Spjd */ 749185029Spjd gid = zfs_fuid_map_id(zfsvfs, id, cr, ZFS_GROUP); 750185029Spjd return (groupmember(gid, cr)); 751185029Spjd} 752209962Smm 753209962Smmvoid 754209962Smmzfs_fuid_txhold(zfsvfs_t *zfsvfs, dmu_tx_t *tx) 755209962Smm{ 756209962Smm if (zfsvfs->z_fuid_obj == 0) { 757209962Smm dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); 758209962Smm dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, 759209962Smm FUID_SIZE_ESTIMATE(zfsvfs)); 760209962Smm dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); 761209962Smm } else { 762209962Smm dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); 763209962Smm dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, 764209962Smm FUID_SIZE_ESTIMATE(zfsvfs)); 765209962Smm } 766209962Smm} 767185029Spjd#endif 768