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{ 74339158Smav const fuid_domain_t *node1 = (const fuid_domain_t *)arg1; 75339158Smav const fuid_domain_t *node2 = (const fuid_domain_t *)arg2; 76185029Spjd 77339158Smav return (AVL_CMP(node1->f_idx, node2->f_idx)); 78185029Spjd} 79185029Spjd 80185029Spjd/* 81185029Spjd * Compare two domain strings. 82185029Spjd */ 83185029Spjdstatic int 84185029Spjddomain_compare(const void *arg1, const void *arg2) 85185029Spjd{ 86339158Smav const fuid_domain_t *node1 = (const fuid_domain_t *)arg1; 87339158Smav const fuid_domain_t *node2 = (const fuid_domain_t *)arg2; 88185029Spjd int val; 89185029Spjd 90185029Spjd val = strcmp(node1->f_ksid->kd_name, node2->f_ksid->kd_name); 91339158Smav 92339158Smav return (AVL_ISIGN(val)); 93185029Spjd} 94185029Spjd 95209962Smmvoid 96209962Smmzfs_fuid_avl_tree_create(avl_tree_t *idx_tree, avl_tree_t *domain_tree) 97209962Smm{ 98209962Smm avl_create(idx_tree, idx_compare, 99209962Smm sizeof (fuid_domain_t), offsetof(fuid_domain_t, f_idxnode)); 100209962Smm avl_create(domain_tree, domain_compare, 101209962Smm sizeof (fuid_domain_t), offsetof(fuid_domain_t, f_domnode)); 102209962Smm} 103209962Smm 104185029Spjd/* 105185029Spjd * load initial fuid domain and idx trees. This function is used by 106185029Spjd * both the kernel and zdb. 107185029Spjd */ 108185029Spjduint64_t 109185029Spjdzfs_fuid_table_load(objset_t *os, uint64_t fuid_obj, avl_tree_t *idx_tree, 110185029Spjd avl_tree_t *domain_tree) 111185029Spjd{ 112185029Spjd dmu_buf_t *db; 113185029Spjd uint64_t fuid_size; 114185029Spjd 115209962Smm ASSERT(fuid_obj != 0); 116209962Smm VERIFY(0 == dmu_bonus_hold(os, fuid_obj, 117209962Smm FTAG, &db)); 118185029Spjd fuid_size = *(uint64_t *)db->db_data; 119185029Spjd dmu_buf_rele(db, FTAG); 120185029Spjd 121185029Spjd if (fuid_size) { 122185029Spjd nvlist_t **fuidnvp; 123185029Spjd nvlist_t *nvp = NULL; 124185029Spjd uint_t count; 125185029Spjd char *packed; 126185029Spjd int i; 127185029Spjd 128185029Spjd packed = kmem_alloc(fuid_size, KM_SLEEP); 129209962Smm VERIFY(dmu_read(os, fuid_obj, 0, 130209962Smm fuid_size, packed, DMU_READ_PREFETCH) == 0); 131185029Spjd VERIFY(nvlist_unpack(packed, fuid_size, 132185029Spjd &nvp, 0) == 0); 133185029Spjd VERIFY(nvlist_lookup_nvlist_array(nvp, FUID_NVP_ARRAY, 134185029Spjd &fuidnvp, &count) == 0); 135185029Spjd 136185029Spjd for (i = 0; i != count; i++) { 137185029Spjd fuid_domain_t *domnode; 138185029Spjd char *domain; 139185029Spjd uint64_t idx; 140185029Spjd 141185029Spjd VERIFY(nvlist_lookup_string(fuidnvp[i], FUID_DOMAIN, 142185029Spjd &domain) == 0); 143185029Spjd VERIFY(nvlist_lookup_uint64(fuidnvp[i], FUID_IDX, 144185029Spjd &idx) == 0); 145185029Spjd 146185029Spjd domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP); 147185029Spjd 148185029Spjd domnode->f_idx = idx; 149185029Spjd domnode->f_ksid = ksid_lookupdomain(domain); 150185029Spjd avl_add(idx_tree, domnode); 151185029Spjd avl_add(domain_tree, domnode); 152185029Spjd } 153185029Spjd nvlist_free(nvp); 154185029Spjd kmem_free(packed, fuid_size); 155185029Spjd } 156185029Spjd return (fuid_size); 157185029Spjd} 158185029Spjd 159185029Spjdvoid 160185029Spjdzfs_fuid_table_destroy(avl_tree_t *idx_tree, avl_tree_t *domain_tree) 161185029Spjd{ 162185029Spjd fuid_domain_t *domnode; 163185029Spjd void *cookie; 164185029Spjd 165185029Spjd cookie = NULL; 166185029Spjd while (domnode = avl_destroy_nodes(domain_tree, &cookie)) 167185029Spjd ksiddomain_rele(domnode->f_ksid); 168185029Spjd 169185029Spjd avl_destroy(domain_tree); 170185029Spjd cookie = NULL; 171185029Spjd while (domnode = avl_destroy_nodes(idx_tree, &cookie)) 172185029Spjd kmem_free(domnode, sizeof (fuid_domain_t)); 173185029Spjd avl_destroy(idx_tree); 174185029Spjd} 175185029Spjd 176185029Spjdchar * 177185029Spjdzfs_fuid_idx_domain(avl_tree_t *idx_tree, uint32_t idx) 178185029Spjd{ 179185029Spjd fuid_domain_t searchnode, *findnode; 180185029Spjd avl_index_t loc; 181185029Spjd 182185029Spjd searchnode.f_idx = idx; 183185029Spjd 184185029Spjd findnode = avl_find(idx_tree, &searchnode, &loc); 185185029Spjd 186185029Spjd return (findnode ? findnode->f_ksid->kd_name : nulldomain); 187185029Spjd} 188185029Spjd 189185029Spjd#ifdef _KERNEL 190185029Spjd/* 191185029Spjd * Load the fuid table(s) into memory. 192185029Spjd */ 193185029Spjdstatic void 194209962Smmzfs_fuid_init(zfsvfs_t *zfsvfs) 195185029Spjd{ 196185029Spjd rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); 197185029Spjd 198185029Spjd if (zfsvfs->z_fuid_loaded) { 199185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 200185029Spjd return; 201185029Spjd } 202185029Spjd 203209962Smm zfs_fuid_avl_tree_create(&zfsvfs->z_fuid_idx, &zfsvfs->z_fuid_domain); 204185029Spjd 205209962Smm (void) zap_lookup(zfsvfs->z_os, MASTER_NODE_OBJ, 206209962Smm ZFS_FUID_TABLES, 8, 1, &zfsvfs->z_fuid_obj); 207185029Spjd if (zfsvfs->z_fuid_obj != 0) { 208185029Spjd zfsvfs->z_fuid_size = zfs_fuid_table_load(zfsvfs->z_os, 209185029Spjd zfsvfs->z_fuid_obj, &zfsvfs->z_fuid_idx, 210185029Spjd &zfsvfs->z_fuid_domain); 211185029Spjd } 212185029Spjd 213209962Smm zfsvfs->z_fuid_loaded = B_TRUE; 214185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 215185029Spjd} 216185029Spjd 217185029Spjd/* 218209962Smm * sync out AVL trees to persistent storage. 219209962Smm */ 220209962Smmvoid 221209962Smmzfs_fuid_sync(zfsvfs_t *zfsvfs, dmu_tx_t *tx) 222209962Smm{ 223209962Smm nvlist_t *nvp; 224209962Smm nvlist_t **fuids; 225209962Smm size_t nvsize = 0; 226209962Smm char *packed; 227209962Smm dmu_buf_t *db; 228209962Smm fuid_domain_t *domnode; 229209962Smm int numnodes; 230209962Smm int i; 231209962Smm 232209962Smm if (!zfsvfs->z_fuid_dirty) { 233209962Smm return; 234209962Smm } 235209962Smm 236209962Smm rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); 237209962Smm 238209962Smm /* 239209962Smm * First see if table needs to be created? 240209962Smm */ 241209962Smm if (zfsvfs->z_fuid_obj == 0) { 242209962Smm zfsvfs->z_fuid_obj = dmu_object_alloc(zfsvfs->z_os, 243209962Smm DMU_OT_FUID, 1 << 14, DMU_OT_FUID_SIZE, 244209962Smm sizeof (uint64_t), tx); 245209962Smm VERIFY(zap_add(zfsvfs->z_os, MASTER_NODE_OBJ, 246209962Smm ZFS_FUID_TABLES, sizeof (uint64_t), 1, 247209962Smm &zfsvfs->z_fuid_obj, tx) == 0); 248209962Smm } 249209962Smm 250209962Smm VERIFY(nvlist_alloc(&nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 251209962Smm 252209962Smm numnodes = avl_numnodes(&zfsvfs->z_fuid_idx); 253209962Smm fuids = kmem_alloc(numnodes * sizeof (void *), KM_SLEEP); 254209962Smm for (i = 0, domnode = avl_first(&zfsvfs->z_fuid_domain); domnode; i++, 255209962Smm domnode = AVL_NEXT(&zfsvfs->z_fuid_domain, domnode)) { 256209962Smm VERIFY(nvlist_alloc(&fuids[i], NV_UNIQUE_NAME, KM_SLEEP) == 0); 257209962Smm VERIFY(nvlist_add_uint64(fuids[i], FUID_IDX, 258209962Smm domnode->f_idx) == 0); 259209962Smm VERIFY(nvlist_add_uint64(fuids[i], FUID_OFFSET, 0) == 0); 260209962Smm VERIFY(nvlist_add_string(fuids[i], FUID_DOMAIN, 261209962Smm domnode->f_ksid->kd_name) == 0); 262209962Smm } 263209962Smm VERIFY(nvlist_add_nvlist_array(nvp, FUID_NVP_ARRAY, 264209962Smm fuids, numnodes) == 0); 265209962Smm for (i = 0; i != numnodes; i++) 266209962Smm nvlist_free(fuids[i]); 267209962Smm kmem_free(fuids, numnodes * sizeof (void *)); 268209962Smm VERIFY(nvlist_size(nvp, &nvsize, NV_ENCODE_XDR) == 0); 269209962Smm packed = kmem_alloc(nvsize, KM_SLEEP); 270209962Smm VERIFY(nvlist_pack(nvp, &packed, &nvsize, 271209962Smm NV_ENCODE_XDR, KM_SLEEP) == 0); 272209962Smm nvlist_free(nvp); 273209962Smm zfsvfs->z_fuid_size = nvsize; 274209962Smm dmu_write(zfsvfs->z_os, zfsvfs->z_fuid_obj, 0, 275209962Smm zfsvfs->z_fuid_size, packed, tx); 276209962Smm kmem_free(packed, zfsvfs->z_fuid_size); 277209962Smm VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, zfsvfs->z_fuid_obj, 278209962Smm FTAG, &db)); 279209962Smm dmu_buf_will_dirty(db, tx); 280209962Smm *(uint64_t *)db->db_data = zfsvfs->z_fuid_size; 281209962Smm dmu_buf_rele(db, FTAG); 282209962Smm 283209962Smm zfsvfs->z_fuid_dirty = B_FALSE; 284209962Smm rw_exit(&zfsvfs->z_fuid_lock); 285209962Smm} 286209962Smm 287209962Smm/* 288185029Spjd * Query domain table for a given domain. 289185029Spjd * 290209962Smm * If domain isn't found and addok is set, it is added to AVL trees and 291209962Smm * the zfsvfs->z_fuid_dirty flag will be set to TRUE. It will then be 292209962Smm * necessary for the caller or another thread to detect the dirty table 293209962Smm * and sync out the changes. 294185029Spjd */ 295185029Spjdint 296209962Smmzfs_fuid_find_by_domain(zfsvfs_t *zfsvfs, const char *domain, 297209962Smm char **retdomain, boolean_t addok) 298185029Spjd{ 299185029Spjd fuid_domain_t searchnode, *findnode; 300185029Spjd avl_index_t loc; 301185029Spjd krw_t rw = RW_READER; 302185029Spjd 303185029Spjd /* 304185029Spjd * If the dummy "nobody" domain then return an index of 0 305185029Spjd * to cause the created FUID to be a standard POSIX id 306185029Spjd * for the user nobody. 307185029Spjd */ 308185029Spjd if (domain[0] == '\0') { 309209962Smm if (retdomain) 310209962Smm *retdomain = nulldomain; 311185029Spjd return (0); 312185029Spjd } 313185029Spjd 314185029Spjd searchnode.f_ksid = ksid_lookupdomain(domain); 315209962Smm if (retdomain) 316185029Spjd *retdomain = searchnode.f_ksid->kd_name; 317185029Spjd if (!zfsvfs->z_fuid_loaded) 318209962Smm zfs_fuid_init(zfsvfs); 319185029Spjd 320185029Spjdretry: 321185029Spjd rw_enter(&zfsvfs->z_fuid_lock, rw); 322185029Spjd findnode = avl_find(&zfsvfs->z_fuid_domain, &searchnode, &loc); 323185029Spjd 324185029Spjd if (findnode) { 325185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 326185029Spjd ksiddomain_rele(searchnode.f_ksid); 327185029Spjd return (findnode->f_idx); 328209962Smm } else if (addok) { 329185029Spjd fuid_domain_t *domnode; 330185029Spjd uint64_t retidx; 331185029Spjd 332185029Spjd if (rw == RW_READER && !rw_tryupgrade(&zfsvfs->z_fuid_lock)) { 333185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 334185029Spjd rw = RW_WRITER; 335185029Spjd goto retry; 336185029Spjd } 337185029Spjd 338185029Spjd domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP); 339185029Spjd domnode->f_ksid = searchnode.f_ksid; 340185029Spjd 341185029Spjd retidx = domnode->f_idx = avl_numnodes(&zfsvfs->z_fuid_idx) + 1; 342185029Spjd 343185029Spjd avl_add(&zfsvfs->z_fuid_domain, domnode); 344185029Spjd avl_add(&zfsvfs->z_fuid_idx, domnode); 345209962Smm zfsvfs->z_fuid_dirty = B_TRUE; 346185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 347185029Spjd return (retidx); 348209962Smm } else { 349209962Smm rw_exit(&zfsvfs->z_fuid_lock); 350209962Smm return (-1); 351185029Spjd } 352185029Spjd} 353185029Spjd 354185029Spjd/* 355185029Spjd * Query domain table by index, returning domain string 356185029Spjd * 357185029Spjd * Returns a pointer from an avl node of the domain string. 358185029Spjd * 359185029Spjd */ 360209962Smmconst char * 361185029Spjdzfs_fuid_find_by_idx(zfsvfs_t *zfsvfs, uint32_t idx) 362185029Spjd{ 363185029Spjd char *domain; 364185029Spjd 365185029Spjd if (idx == 0 || !zfsvfs->z_use_fuids) 366185029Spjd return (NULL); 367185029Spjd 368185029Spjd if (!zfsvfs->z_fuid_loaded) 369209962Smm zfs_fuid_init(zfsvfs); 370185029Spjd 371185029Spjd rw_enter(&zfsvfs->z_fuid_lock, RW_READER); 372185029Spjd 373219089Spjd if (zfsvfs->z_fuid_obj || zfsvfs->z_fuid_dirty) 374185029Spjd domain = zfs_fuid_idx_domain(&zfsvfs->z_fuid_idx, idx); 375185029Spjd else 376185029Spjd domain = nulldomain; 377185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 378185029Spjd 379185029Spjd ASSERT(domain); 380185029Spjd return (domain); 381185029Spjd} 382185029Spjd 383185029Spjdvoid 384185029Spjdzfs_fuid_map_ids(znode_t *zp, cred_t *cr, uid_t *uidp, uid_t *gidp) 385185029Spjd{ 386219089Spjd *uidp = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_uid, cr, ZFS_OWNER); 387219089Spjd *gidp = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_gid, cr, ZFS_GROUP); 388185029Spjd} 389185029Spjd 390185029Spjduid_t 391185029Spjdzfs_fuid_map_id(zfsvfs_t *zfsvfs, uint64_t fuid, 392185029Spjd cred_t *cr, zfs_fuid_type_t type) 393185029Spjd{ 394185029Spjd uint32_t index = FUID_INDEX(fuid); 395209962Smm const char *domain; 396185029Spjd uid_t id; 397185029Spjd 398185029Spjd if (index == 0) 399185029Spjd return (fuid); 400185029Spjd 401185029Spjd domain = zfs_fuid_find_by_idx(zfsvfs, index); 402185029Spjd ASSERT(domain != NULL); 403185029Spjd 404277300Ssmh#ifdef illumos 405185029Spjd if (type == ZFS_OWNER || type == ZFS_ACE_USER) { 406185029Spjd (void) kidmap_getuidbysid(crgetzone(cr), domain, 407185029Spjd FUID_RID(fuid), &id); 408185029Spjd } else { 409185029Spjd (void) kidmap_getgidbysid(crgetzone(cr), domain, 410185029Spjd FUID_RID(fuid), &id); 411185029Spjd } 412277300Ssmh#else 413210398Smm id = UID_NOBODY; 414277300Ssmh#endif 415185029Spjd return (id); 416185029Spjd} 417185029Spjd 418185029Spjd/* 419185029Spjd * Add a FUID node to the list of fuid's being created for this 420185029Spjd * ACL 421185029Spjd * 422185029Spjd * If ACL has multiple domains, then keep only one copy of each unique 423185029Spjd * domain. 424185029Spjd */ 425219089Spjdvoid 426185029Spjdzfs_fuid_node_add(zfs_fuid_info_t **fuidpp, const char *domain, uint32_t rid, 427185029Spjd uint64_t idx, uint64_t id, zfs_fuid_type_t type) 428185029Spjd{ 429185029Spjd zfs_fuid_t *fuid; 430185029Spjd zfs_fuid_domain_t *fuid_domain; 431185029Spjd zfs_fuid_info_t *fuidp; 432185029Spjd uint64_t fuididx; 433185029Spjd boolean_t found = B_FALSE; 434185029Spjd 435185029Spjd if (*fuidpp == NULL) 436185029Spjd *fuidpp = zfs_fuid_info_alloc(); 437185029Spjd 438185029Spjd fuidp = *fuidpp; 439185029Spjd /* 440185029Spjd * First find fuid domain index in linked list 441185029Spjd * 442185029Spjd * If one isn't found then create an entry. 443185029Spjd */ 444185029Spjd 445185029Spjd for (fuididx = 1, fuid_domain = list_head(&fuidp->z_domains); 446185029Spjd fuid_domain; fuid_domain = list_next(&fuidp->z_domains, 447185029Spjd fuid_domain), fuididx++) { 448185029Spjd if (idx == fuid_domain->z_domidx) { 449185029Spjd found = B_TRUE; 450185029Spjd break; 451185029Spjd } 452185029Spjd } 453185029Spjd 454185029Spjd if (!found) { 455185029Spjd fuid_domain = kmem_alloc(sizeof (zfs_fuid_domain_t), KM_SLEEP); 456185029Spjd fuid_domain->z_domain = domain; 457185029Spjd fuid_domain->z_domidx = idx; 458185029Spjd list_insert_tail(&fuidp->z_domains, fuid_domain); 459185029Spjd fuidp->z_domain_str_sz += strlen(domain) + 1; 460185029Spjd fuidp->z_domain_cnt++; 461185029Spjd } 462185029Spjd 463185029Spjd if (type == ZFS_ACE_USER || type == ZFS_ACE_GROUP) { 464209962Smm 465185029Spjd /* 466185029Spjd * Now allocate fuid entry and add it on the end of the list 467185029Spjd */ 468185029Spjd 469185029Spjd fuid = kmem_alloc(sizeof (zfs_fuid_t), KM_SLEEP); 470185029Spjd fuid->z_id = id; 471185029Spjd fuid->z_domidx = idx; 472185029Spjd fuid->z_logfuid = FUID_ENCODE(fuididx, rid); 473185029Spjd 474185029Spjd list_insert_tail(&fuidp->z_fuids, fuid); 475185029Spjd fuidp->z_fuid_cnt++; 476185029Spjd } else { 477185029Spjd if (type == ZFS_OWNER) 478185029Spjd fuidp->z_fuid_owner = FUID_ENCODE(fuididx, rid); 479185029Spjd else 480185029Spjd fuidp->z_fuid_group = FUID_ENCODE(fuididx, rid); 481185029Spjd } 482185029Spjd} 483185029Spjd 484185029Spjd/* 485185029Spjd * Create a file system FUID, based on information in the users cred 486219089Spjd * 487219089Spjd * If cred contains KSID_OWNER then it should be used to determine 488219089Spjd * the uid otherwise cred's uid will be used. By default cred's gid 489219089Spjd * is used unless it's an ephemeral ID in which case KSID_GROUP will 490219089Spjd * be used if it exists. 491185029Spjd */ 492185029Spjduint64_t 493185029Spjdzfs_fuid_create_cred(zfsvfs_t *zfsvfs, zfs_fuid_type_t type, 494209962Smm cred_t *cr, zfs_fuid_info_t **fuidp) 495185029Spjd{ 496185029Spjd uint64_t idx; 497185029Spjd ksid_t *ksid; 498185029Spjd uint32_t rid; 499185029Spjd char *kdomain; 500185029Spjd const char *domain; 501185029Spjd uid_t id; 502185029Spjd 503185029Spjd VERIFY(type == ZFS_OWNER || type == ZFS_GROUP); 504185029Spjd 505219089Spjd ksid = crgetsid(cr, (type == ZFS_OWNER) ? KSID_OWNER : KSID_GROUP); 506185029Spjd 507219089Spjd if (!zfsvfs->z_use_fuids || (ksid == NULL)) { 508219089Spjd id = (type == ZFS_OWNER) ? crgetuid(cr) : crgetgid(cr); 509219089Spjd 510219089Spjd if (IS_EPHEMERAL(id)) 511219089Spjd return ((type == ZFS_OWNER) ? UID_NOBODY : GID_NOBODY); 512219089Spjd 513185029Spjd return ((uint64_t)id); 514219089Spjd } 515185029Spjd 516219089Spjd /* 517219089Spjd * ksid is present and FUID is supported 518219089Spjd */ 519219089Spjd id = (type == ZFS_OWNER) ? ksid_getid(ksid) : crgetgid(cr); 520185029Spjd 521219089Spjd if (!IS_EPHEMERAL(id)) 522219089Spjd return ((uint64_t)id); 523219089Spjd 524219089Spjd if (type == ZFS_GROUP) 525219089Spjd id = ksid_getid(ksid); 526219089Spjd 527185029Spjd rid = ksid_getrid(ksid); 528185029Spjd domain = ksid_getdomain(ksid); 529219089Spjd 530209962Smm idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, B_TRUE); 531185029Spjd 532185029Spjd zfs_fuid_node_add(fuidp, kdomain, rid, idx, id, type); 533185029Spjd 534185029Spjd return (FUID_ENCODE(idx, rid)); 535185029Spjd} 536185029Spjd 537185029Spjd/* 538185029Spjd * Create a file system FUID for an ACL ace 539185029Spjd * or a chown/chgrp of the file. 540185029Spjd * This is similar to zfs_fuid_create_cred, except that 541185029Spjd * we can't find the domain + rid information in the 542185029Spjd * cred. Instead we have to query Winchester for the 543185029Spjd * domain and rid. 544185029Spjd * 545185029Spjd * During replay operations the domain+rid information is 546185029Spjd * found in the zfs_fuid_info_t that the replay code has 547185029Spjd * attached to the zfsvfs of the file system. 548185029Spjd */ 549185029Spjduint64_t 550185029Spjdzfs_fuid_create(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr, 551209962Smm zfs_fuid_type_t type, zfs_fuid_info_t **fuidpp) 552185029Spjd{ 553185029Spjd const char *domain; 554185029Spjd char *kdomain; 555185029Spjd uint32_t fuid_idx = FUID_INDEX(id); 556185029Spjd uint32_t rid; 557185029Spjd idmap_stat status; 558247187Smm uint64_t idx = 0; 559185029Spjd zfs_fuid_t *zfuid = NULL; 560247187Smm zfs_fuid_info_t *fuidp = NULL; 561185029Spjd 562185029Spjd /* 563185029Spjd * If POSIX ID, or entry is already a FUID then 564185029Spjd * just return the id 565185029Spjd * 566185029Spjd * We may also be handed an already FUID'ized id via 567185029Spjd * chmod. 568185029Spjd */ 569185029Spjd 570185029Spjd if (!zfsvfs->z_use_fuids || !IS_EPHEMERAL(id) || fuid_idx != 0) 571185029Spjd return (id); 572185029Spjd 573209962Smm if (zfsvfs->z_replay) { 574185029Spjd fuidp = zfsvfs->z_fuid_replay; 575185029Spjd 576185029Spjd /* 577185029Spjd * If we are passed an ephemeral id, but no 578185029Spjd * fuid_info was logged then return NOBODY. 579185029Spjd * This is most likely a result of idmap service 580185029Spjd * not being available. 581185029Spjd */ 582185029Spjd if (fuidp == NULL) 583185029Spjd return (UID_NOBODY); 584185029Spjd 585247187Smm VERIFY3U(type, >=, ZFS_OWNER); 586247187Smm VERIFY3U(type, <=, ZFS_ACE_GROUP); 587247187Smm 588185029Spjd switch (type) { 589185029Spjd case ZFS_ACE_USER: 590185029Spjd case ZFS_ACE_GROUP: 591185029Spjd zfuid = list_head(&fuidp->z_fuids); 592185029Spjd rid = FUID_RID(zfuid->z_logfuid); 593185029Spjd idx = FUID_INDEX(zfuid->z_logfuid); 594185029Spjd break; 595185029Spjd case ZFS_OWNER: 596185029Spjd rid = FUID_RID(fuidp->z_fuid_owner); 597185029Spjd idx = FUID_INDEX(fuidp->z_fuid_owner); 598185029Spjd break; 599185029Spjd case ZFS_GROUP: 600185029Spjd rid = FUID_RID(fuidp->z_fuid_group); 601185029Spjd idx = FUID_INDEX(fuidp->z_fuid_group); 602185029Spjd break; 603185029Spjd }; 604247187Smm domain = fuidp->z_domain_table[idx - 1]; 605185029Spjd } else { 606185029Spjd if (type == ZFS_OWNER || type == ZFS_ACE_USER) 607185029Spjd status = kidmap_getsidbyuid(crgetzone(cr), id, 608185029Spjd &domain, &rid); 609185029Spjd else 610185029Spjd status = kidmap_getsidbygid(crgetzone(cr), id, 611185029Spjd &domain, &rid); 612185029Spjd 613185029Spjd if (status != 0) { 614185029Spjd /* 615185029Spjd * When returning nobody we will need to 616185029Spjd * make a dummy fuid table entry for logging 617185029Spjd * purposes. 618185029Spjd */ 619185029Spjd rid = UID_NOBODY; 620185029Spjd domain = nulldomain; 621185029Spjd } 622185029Spjd } 623185029Spjd 624209962Smm idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, B_TRUE); 625185029Spjd 626209962Smm if (!zfsvfs->z_replay) 627209962Smm zfs_fuid_node_add(fuidpp, kdomain, 628209962Smm rid, idx, id, type); 629185029Spjd else if (zfuid != NULL) { 630185029Spjd list_remove(&fuidp->z_fuids, zfuid); 631185029Spjd kmem_free(zfuid, sizeof (zfs_fuid_t)); 632185029Spjd } 633185029Spjd return (FUID_ENCODE(idx, rid)); 634185029Spjd} 635185029Spjd 636185029Spjdvoid 637185029Spjdzfs_fuid_destroy(zfsvfs_t *zfsvfs) 638185029Spjd{ 639185029Spjd rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); 640185029Spjd if (!zfsvfs->z_fuid_loaded) { 641185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 642185029Spjd return; 643185029Spjd } 644185029Spjd zfs_fuid_table_destroy(&zfsvfs->z_fuid_idx, &zfsvfs->z_fuid_domain); 645185029Spjd rw_exit(&zfsvfs->z_fuid_lock); 646185029Spjd} 647185029Spjd 648185029Spjd/* 649185029Spjd * Allocate zfs_fuid_info for tracking FUIDs created during 650185029Spjd * zfs_mknode, VOP_SETATTR() or VOP_SETSECATTR() 651185029Spjd */ 652185029Spjdzfs_fuid_info_t * 653185029Spjdzfs_fuid_info_alloc(void) 654185029Spjd{ 655185029Spjd zfs_fuid_info_t *fuidp; 656185029Spjd 657185029Spjd fuidp = kmem_zalloc(sizeof (zfs_fuid_info_t), KM_SLEEP); 658185029Spjd list_create(&fuidp->z_domains, sizeof (zfs_fuid_domain_t), 659185029Spjd offsetof(zfs_fuid_domain_t, z_next)); 660185029Spjd list_create(&fuidp->z_fuids, sizeof (zfs_fuid_t), 661185029Spjd offsetof(zfs_fuid_t, z_next)); 662185029Spjd return (fuidp); 663185029Spjd} 664185029Spjd 665185029Spjd/* 666185029Spjd * Release all memory associated with zfs_fuid_info_t 667185029Spjd */ 668185029Spjdvoid 669185029Spjdzfs_fuid_info_free(zfs_fuid_info_t *fuidp) 670185029Spjd{ 671185029Spjd zfs_fuid_t *zfuid; 672185029Spjd zfs_fuid_domain_t *zdomain; 673185029Spjd 674185029Spjd while ((zfuid = list_head(&fuidp->z_fuids)) != NULL) { 675185029Spjd list_remove(&fuidp->z_fuids, zfuid); 676185029Spjd kmem_free(zfuid, sizeof (zfs_fuid_t)); 677185029Spjd } 678185029Spjd 679185029Spjd if (fuidp->z_domain_table != NULL) 680185029Spjd kmem_free(fuidp->z_domain_table, 681185029Spjd (sizeof (char **)) * fuidp->z_domain_cnt); 682185029Spjd 683185029Spjd while ((zdomain = list_head(&fuidp->z_domains)) != NULL) { 684185029Spjd list_remove(&fuidp->z_domains, zdomain); 685185029Spjd kmem_free(zdomain, sizeof (zfs_fuid_domain_t)); 686185029Spjd } 687185029Spjd 688185029Spjd kmem_free(fuidp, sizeof (zfs_fuid_info_t)); 689185029Spjd} 690185029Spjd 691185029Spjd/* 692185029Spjd * Check to see if id is a groupmember. If cred 693185029Spjd * has ksid info then sidlist is checked first 694185029Spjd * and if still not found then POSIX groups are checked 695185029Spjd * 696185029Spjd * Will use a straight FUID compare when possible. 697185029Spjd */ 698185029Spjdboolean_t 699185029Spjdzfs_groupmember(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr) 700185029Spjd{ 701277300Ssmh#ifdef illumos 702185029Spjd ksid_t *ksid = crgetsid(cr, KSID_GROUP); 703209962Smm ksidlist_t *ksidlist = crgetsidlist(cr); 704277300Ssmh#endif 705185029Spjd uid_t gid; 706185029Spjd 707277300Ssmh#ifdef illumos 708209962Smm if (ksid && ksidlist) { 709185029Spjd int i; 710185029Spjd ksid_t *ksid_groups; 711185029Spjd uint32_t idx = FUID_INDEX(id); 712185029Spjd uint32_t rid = FUID_RID(id); 713185029Spjd 714185029Spjd ksid_groups = ksidlist->ksl_sids; 715185029Spjd 716185029Spjd for (i = 0; i != ksidlist->ksl_nsid; i++) { 717185029Spjd if (idx == 0) { 718185029Spjd if (id != IDMAP_WK_CREATOR_GROUP_GID && 719185029Spjd id == ksid_groups[i].ks_id) { 720185029Spjd return (B_TRUE); 721185029Spjd } 722185029Spjd } else { 723209962Smm const char *domain; 724185029Spjd 725185029Spjd domain = zfs_fuid_find_by_idx(zfsvfs, idx); 726185029Spjd ASSERT(domain != NULL); 727185029Spjd 728185029Spjd if (strcmp(domain, 729185029Spjd IDMAP_WK_CREATOR_SID_AUTHORITY) == 0) 730185029Spjd return (B_FALSE); 731185029Spjd 732185029Spjd if ((strcmp(domain, 733185029Spjd ksid_groups[i].ks_domain->kd_name) == 0) && 734185029Spjd rid == ksid_groups[i].ks_rid) 735185029Spjd return (B_TRUE); 736185029Spjd } 737185029Spjd } 738185029Spjd } 739277300Ssmh#endif /* illumos */ 740185029Spjd 741185029Spjd /* 742185029Spjd * Not found in ksidlist, check posix groups 743185029Spjd */ 744185029Spjd gid = zfs_fuid_map_id(zfsvfs, id, cr, ZFS_GROUP); 745185029Spjd return (groupmember(gid, cr)); 746185029Spjd} 747209962Smm 748209962Smmvoid 749209962Smmzfs_fuid_txhold(zfsvfs_t *zfsvfs, dmu_tx_t *tx) 750209962Smm{ 751209962Smm if (zfsvfs->z_fuid_obj == 0) { 752209962Smm dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); 753209962Smm dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, 754209962Smm FUID_SIZE_ESTIMATE(zfsvfs)); 755209962Smm dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); 756209962Smm } else { 757209962Smm dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); 758209962Smm dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, 759209962Smm FUID_SIZE_ESTIMATE(zfsvfs)); 760209962Smm } 761209962Smm} 762185029Spjd#endif 763