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