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