1da6c28aaSamw /* 2da6c28aaSamw * CDDL HEADER START 3da6c28aaSamw * 4da6c28aaSamw * The contents of this file are subject to the terms of the 5da6c28aaSamw * Common Development and Distribution License (the "License"). 6da6c28aaSamw * You may not use this file except in compliance with the License. 7da6c28aaSamw * 8da6c28aaSamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9da6c28aaSamw * or http://www.opensolaris.org/os/licensing. 10da6c28aaSamw * See the License for the specific language governing permissions 11da6c28aaSamw * and limitations under the License. 12da6c28aaSamw * 13da6c28aaSamw * When distributing Covered Code, include this CDDL HEADER in each 14da6c28aaSamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15da6c28aaSamw * If applicable, add the following below this CDDL HEADER, with the 16da6c28aaSamw * fields enclosed by brackets "[]" replaced with your own identifying 17da6c28aaSamw * information: Portions Copyright [yyyy] [name of copyright owner] 18da6c28aaSamw * 19da6c28aaSamw * CDDL HEADER END 20da6c28aaSamw */ 21da6c28aaSamw /* 228f2529deSMark Shellenbaum * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23da6c28aaSamw */ 24da6c28aaSamw 25e0d35c44Smarks #include <sys/zfs_context.h> 26da6c28aaSamw #include <sys/dmu.h> 27da6c28aaSamw #include <sys/avl.h> 28da6c28aaSamw #include <sys/zap.h> 29e0d35c44Smarks #include <sys/refcount.h> 30da6c28aaSamw #include <sys/nvpair.h> 31e0d35c44Smarks #ifdef _KERNEL 32da6c28aaSamw #include <sys/kidmap.h> 33da6c28aaSamw #include <sys/sid.h> 34e0d35c44Smarks #include <sys/zfs_vfsops.h> 35e0d35c44Smarks #include <sys/zfs_znode.h> 36e0d35c44Smarks #endif 37e0d35c44Smarks #include <sys/zfs_fuid.h> 38da6c28aaSamw 39da6c28aaSamw /* 40da6c28aaSamw * FUID Domain table(s). 41da6c28aaSamw * 42da6c28aaSamw * The FUID table is stored as a packed nvlist of an array 43da6c28aaSamw * of nvlists which contain an index, domain string and offset 44da6c28aaSamw * 45da6c28aaSamw * During file system initialization the nvlist(s) are read and 46da6c28aaSamw * two AVL trees are created. One tree is keyed by the index number 47da6c28aaSamw * and the other by the domain string. Nodes are never removed from 4889459e17SMark Shellenbaum * trees, but new entries may be added. If a new entry is added then 4989459e17SMark Shellenbaum * the zfsvfs->z_fuid_dirty flag is set to true and the caller will then 5089459e17SMark Shellenbaum * be responsible for calling zfs_fuid_sync() to sync the changes to disk. 5189459e17SMark Shellenbaum * 52da6c28aaSamw */ 53da6c28aaSamw 54da6c28aaSamw #define FUID_IDX "fuid_idx" 55da6c28aaSamw #define FUID_DOMAIN "fuid_domain" 56da6c28aaSamw #define FUID_OFFSET "fuid_offset" 57da6c28aaSamw #define FUID_NVP_ARRAY "fuid_nvlist" 58da6c28aaSamw 59da6c28aaSamw typedef struct fuid_domain { 60e0d35c44Smarks avl_node_t f_domnode; 61e0d35c44Smarks avl_node_t f_idxnode; 62da6c28aaSamw ksiddomain_t *f_ksid; 63e0d35c44Smarks uint64_t f_idx; 64da6c28aaSamw } fuid_domain_t; 65da6c28aaSamw 66003c2582SMark Shellenbaum static char *nulldomain = ""; 67003c2582SMark Shellenbaum 68da6c28aaSamw /* 69da6c28aaSamw * Compare two indexes. 70da6c28aaSamw */ 71da6c28aaSamw static int 72da6c28aaSamw idx_compare(const void *arg1, const void *arg2) 73da6c28aaSamw { 74e0d35c44Smarks const fuid_domain_t *node1 = arg1; 75e0d35c44Smarks const fuid_domain_t *node2 = arg2; 76da6c28aaSamw 77da6c28aaSamw if (node1->f_idx < node2->f_idx) 78da6c28aaSamw return (-1); 79da6c28aaSamw else if (node1->f_idx > node2->f_idx) 80da6c28aaSamw return (1); 81da6c28aaSamw return (0); 82da6c28aaSamw } 83da6c28aaSamw 84da6c28aaSamw /* 85da6c28aaSamw * Compare two domain strings. 86da6c28aaSamw */ 87da6c28aaSamw static int 88da6c28aaSamw domain_compare(const void *arg1, const void *arg2) 89da6c28aaSamw { 90da6c28aaSamw const fuid_domain_t *node1 = arg1; 91da6c28aaSamw const fuid_domain_t *node2 = arg2; 92da6c28aaSamw int val; 93da6c28aaSamw 94da6c28aaSamw val = strcmp(node1->f_ksid->kd_name, node2->f_ksid->kd_name); 95da6c28aaSamw if (val == 0) 96da6c28aaSamw return (0); 97da6c28aaSamw return (val > 0 ? 1 : -1); 98da6c28aaSamw } 99da6c28aaSamw 10089459e17SMark Shellenbaum void 10189459e17SMark Shellenbaum zfs_fuid_avl_tree_create(avl_tree_t *idx_tree, avl_tree_t *domain_tree) 10289459e17SMark Shellenbaum { 10389459e17SMark Shellenbaum avl_create(idx_tree, idx_compare, 10489459e17SMark Shellenbaum sizeof (fuid_domain_t), offsetof(fuid_domain_t, f_idxnode)); 10589459e17SMark Shellenbaum avl_create(domain_tree, domain_compare, 10689459e17SMark Shellenbaum sizeof (fuid_domain_t), offsetof(fuid_domain_t, f_domnode)); 10789459e17SMark Shellenbaum } 10889459e17SMark Shellenbaum 109da6c28aaSamw /* 110e0d35c44Smarks * load initial fuid domain and idx trees. This function is used by 111e0d35c44Smarks * both the kernel and zdb. 112e0d35c44Smarks */ 113e0d35c44Smarks uint64_t 114e0d35c44Smarks zfs_fuid_table_load(objset_t *os, uint64_t fuid_obj, avl_tree_t *idx_tree, 115e0d35c44Smarks avl_tree_t *domain_tree) 116e0d35c44Smarks { 117e0d35c44Smarks dmu_buf_t *db; 118e0d35c44Smarks uint64_t fuid_size; 119e0d35c44Smarks 12089459e17SMark Shellenbaum ASSERT(fuid_obj != 0); 12189459e17SMark Shellenbaum VERIFY(0 == dmu_bonus_hold(os, fuid_obj, 12289459e17SMark Shellenbaum FTAG, &db)); 123e0d35c44Smarks fuid_size = *(uint64_t *)db->db_data; 124e0d35c44Smarks dmu_buf_rele(db, FTAG); 125e0d35c44Smarks 126e0d35c44Smarks if (fuid_size) { 127e0d35c44Smarks nvlist_t **fuidnvp; 128e0d35c44Smarks nvlist_t *nvp = NULL; 129e0d35c44Smarks uint_t count; 130e0d35c44Smarks char *packed; 131e0d35c44Smarks int i; 132e0d35c44Smarks 133e0d35c44Smarks packed = kmem_alloc(fuid_size, KM_SLEEP); 13489459e17SMark Shellenbaum VERIFY(dmu_read(os, fuid_obj, 0, 1357bfdf011SNeil Perrin fuid_size, packed, DMU_READ_PREFETCH) == 0); 136e0d35c44Smarks VERIFY(nvlist_unpack(packed, fuid_size, 137e0d35c44Smarks &nvp, 0) == 0); 138e0d35c44Smarks VERIFY(nvlist_lookup_nvlist_array(nvp, FUID_NVP_ARRAY, 139e0d35c44Smarks &fuidnvp, &count) == 0); 140e0d35c44Smarks 141e0d35c44Smarks for (i = 0; i != count; i++) { 142e0d35c44Smarks fuid_domain_t *domnode; 143e0d35c44Smarks char *domain; 144e0d35c44Smarks uint64_t idx; 145e0d35c44Smarks 146e0d35c44Smarks VERIFY(nvlist_lookup_string(fuidnvp[i], FUID_DOMAIN, 147e0d35c44Smarks &domain) == 0); 148e0d35c44Smarks VERIFY(nvlist_lookup_uint64(fuidnvp[i], FUID_IDX, 149e0d35c44Smarks &idx) == 0); 150e0d35c44Smarks 151e0d35c44Smarks domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP); 152e0d35c44Smarks 153e0d35c44Smarks domnode->f_idx = idx; 154e0d35c44Smarks domnode->f_ksid = ksid_lookupdomain(domain); 155e0d35c44Smarks avl_add(idx_tree, domnode); 156e0d35c44Smarks avl_add(domain_tree, domnode); 157e0d35c44Smarks } 158e0d35c44Smarks nvlist_free(nvp); 159e0d35c44Smarks kmem_free(packed, fuid_size); 160e0d35c44Smarks } 161e0d35c44Smarks return (fuid_size); 162e0d35c44Smarks } 163e0d35c44Smarks 164e0d35c44Smarks void 165e0d35c44Smarks zfs_fuid_table_destroy(avl_tree_t *idx_tree, avl_tree_t *domain_tree) 166e0d35c44Smarks { 167e0d35c44Smarks fuid_domain_t *domnode; 168e0d35c44Smarks void *cookie; 169e0d35c44Smarks 170e0d35c44Smarks cookie = NULL; 171e0d35c44Smarks while (domnode = avl_destroy_nodes(domain_tree, &cookie)) 172e0d35c44Smarks ksiddomain_rele(domnode->f_ksid); 173e0d35c44Smarks 174e0d35c44Smarks avl_destroy(domain_tree); 175e0d35c44Smarks cookie = NULL; 176e0d35c44Smarks while (domnode = avl_destroy_nodes(idx_tree, &cookie)) 177e0d35c44Smarks kmem_free(domnode, sizeof (fuid_domain_t)); 178e0d35c44Smarks avl_destroy(idx_tree); 179e0d35c44Smarks } 180e0d35c44Smarks 181e0d35c44Smarks char * 182e0d35c44Smarks zfs_fuid_idx_domain(avl_tree_t *idx_tree, uint32_t idx) 183e0d35c44Smarks { 184e0d35c44Smarks fuid_domain_t searchnode, *findnode; 185e0d35c44Smarks avl_index_t loc; 186e0d35c44Smarks 187e0d35c44Smarks searchnode.f_idx = idx; 188e0d35c44Smarks 189e0d35c44Smarks findnode = avl_find(idx_tree, &searchnode, &loc); 190e0d35c44Smarks 191003c2582SMark Shellenbaum return (findnode ? findnode->f_ksid->kd_name : nulldomain); 192e0d35c44Smarks } 193e0d35c44Smarks 194e0d35c44Smarks #ifdef _KERNEL 195e0d35c44Smarks /* 196da6c28aaSamw * Load the fuid table(s) into memory. 197da6c28aaSamw */ 198da6c28aaSamw static void 19989459e17SMark Shellenbaum zfs_fuid_init(zfsvfs_t *zfsvfs) 200da6c28aaSamw { 201da6c28aaSamw rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); 202da6c28aaSamw 203da6c28aaSamw if (zfsvfs->z_fuid_loaded) { 204da6c28aaSamw rw_exit(&zfsvfs->z_fuid_lock); 205da6c28aaSamw return; 206da6c28aaSamw } 207da6c28aaSamw 20889459e17SMark Shellenbaum zfs_fuid_avl_tree_create(&zfsvfs->z_fuid_idx, &zfsvfs->z_fuid_domain); 209da6c28aaSamw 21089459e17SMark Shellenbaum (void) zap_lookup(zfsvfs->z_os, MASTER_NODE_OBJ, 211da6c28aaSamw ZFS_FUID_TABLES, 8, 1, &zfsvfs->z_fuid_obj); 21289459e17SMark Shellenbaum if (zfsvfs->z_fuid_obj != 0) { 21389459e17SMark Shellenbaum zfsvfs->z_fuid_size = zfs_fuid_table_load(zfsvfs->z_os, 21489459e17SMark Shellenbaum zfsvfs->z_fuid_obj, &zfsvfs->z_fuid_idx, 21589459e17SMark Shellenbaum &zfsvfs->z_fuid_domain); 21689459e17SMark Shellenbaum } 21789459e17SMark Shellenbaum 21889459e17SMark Shellenbaum zfsvfs->z_fuid_loaded = B_TRUE; 21989459e17SMark Shellenbaum rw_exit(&zfsvfs->z_fuid_lock); 22089459e17SMark Shellenbaum } 22189459e17SMark Shellenbaum 22289459e17SMark Shellenbaum /* 22389459e17SMark Shellenbaum * sync out AVL trees to persistent storage. 22489459e17SMark Shellenbaum */ 22589459e17SMark Shellenbaum void 22689459e17SMark Shellenbaum zfs_fuid_sync(zfsvfs_t *zfsvfs, dmu_tx_t *tx) 22789459e17SMark Shellenbaum { 22889459e17SMark Shellenbaum nvlist_t *nvp; 22989459e17SMark Shellenbaum nvlist_t **fuids; 23089459e17SMark Shellenbaum size_t nvsize = 0; 23189459e17SMark Shellenbaum char *packed; 23289459e17SMark Shellenbaum dmu_buf_t *db; 23389459e17SMark Shellenbaum fuid_domain_t *domnode; 23489459e17SMark Shellenbaum int numnodes; 23589459e17SMark Shellenbaum int i; 23689459e17SMark Shellenbaum 23789459e17SMark Shellenbaum if (!zfsvfs->z_fuid_dirty) { 23889459e17SMark Shellenbaum return; 23989459e17SMark Shellenbaum } 24089459e17SMark Shellenbaum 24189459e17SMark Shellenbaum rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); 24289459e17SMark Shellenbaum 24389459e17SMark Shellenbaum /* 24489459e17SMark Shellenbaum * First see if table needs to be created? 24589459e17SMark Shellenbaum */ 24689459e17SMark Shellenbaum if (zfsvfs->z_fuid_obj == 0) { 247da6c28aaSamw zfsvfs->z_fuid_obj = dmu_object_alloc(zfsvfs->z_os, 248da6c28aaSamw DMU_OT_FUID, 1 << 14, DMU_OT_FUID_SIZE, 249da6c28aaSamw sizeof (uint64_t), tx); 250da6c28aaSamw VERIFY(zap_add(zfsvfs->z_os, MASTER_NODE_OBJ, 251da6c28aaSamw ZFS_FUID_TABLES, sizeof (uint64_t), 1, 252da6c28aaSamw &zfsvfs->z_fuid_obj, tx) == 0); 253da6c28aaSamw } 254da6c28aaSamw 25589459e17SMark Shellenbaum VERIFY(nvlist_alloc(&nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 256003c2582SMark Shellenbaum 25789459e17SMark Shellenbaum numnodes = avl_numnodes(&zfsvfs->z_fuid_idx); 25889459e17SMark Shellenbaum fuids = kmem_alloc(numnodes * sizeof (void *), KM_SLEEP); 25989459e17SMark Shellenbaum for (i = 0, domnode = avl_first(&zfsvfs->z_fuid_domain); domnode; i++, 26089459e17SMark Shellenbaum domnode = AVL_NEXT(&zfsvfs->z_fuid_domain, domnode)) { 26189459e17SMark Shellenbaum VERIFY(nvlist_alloc(&fuids[i], NV_UNIQUE_NAME, KM_SLEEP) == 0); 26289459e17SMark Shellenbaum VERIFY(nvlist_add_uint64(fuids[i], FUID_IDX, 26389459e17SMark Shellenbaum domnode->f_idx) == 0); 26489459e17SMark Shellenbaum VERIFY(nvlist_add_uint64(fuids[i], FUID_OFFSET, 0) == 0); 26589459e17SMark Shellenbaum VERIFY(nvlist_add_string(fuids[i], FUID_DOMAIN, 26689459e17SMark Shellenbaum domnode->f_ksid->kd_name) == 0); 26789459e17SMark Shellenbaum } 26889459e17SMark Shellenbaum VERIFY(nvlist_add_nvlist_array(nvp, FUID_NVP_ARRAY, 26989459e17SMark Shellenbaum fuids, numnodes) == 0); 27089459e17SMark Shellenbaum for (i = 0; i != numnodes; i++) 27189459e17SMark Shellenbaum nvlist_free(fuids[i]); 27289459e17SMark Shellenbaum kmem_free(fuids, numnodes * sizeof (void *)); 27389459e17SMark Shellenbaum VERIFY(nvlist_size(nvp, &nvsize, NV_ENCODE_XDR) == 0); 27489459e17SMark Shellenbaum packed = kmem_alloc(nvsize, KM_SLEEP); 27589459e17SMark Shellenbaum VERIFY(nvlist_pack(nvp, &packed, &nvsize, 27689459e17SMark Shellenbaum NV_ENCODE_XDR, KM_SLEEP) == 0); 27789459e17SMark Shellenbaum nvlist_free(nvp); 27889459e17SMark Shellenbaum zfsvfs->z_fuid_size = nvsize; 27989459e17SMark Shellenbaum dmu_write(zfsvfs->z_os, zfsvfs->z_fuid_obj, 0, 28089459e17SMark Shellenbaum zfsvfs->z_fuid_size, packed, tx); 28189459e17SMark Shellenbaum kmem_free(packed, zfsvfs->z_fuid_size); 28289459e17SMark Shellenbaum VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, zfsvfs->z_fuid_obj, 28389459e17SMark Shellenbaum FTAG, &db)); 28489459e17SMark Shellenbaum dmu_buf_will_dirty(db, tx); 28589459e17SMark Shellenbaum *(uint64_t *)db->db_data = zfsvfs->z_fuid_size; 28689459e17SMark Shellenbaum dmu_buf_rele(db, FTAG); 28789459e17SMark Shellenbaum 28889459e17SMark Shellenbaum zfsvfs->z_fuid_dirty = B_FALSE; 289da6c28aaSamw rw_exit(&zfsvfs->z_fuid_lock); 290da6c28aaSamw } 291da6c28aaSamw 292da6c28aaSamw /* 293da6c28aaSamw * Query domain table for a given domain. 294da6c28aaSamw * 29514843421SMatthew Ahrens * If domain isn't found and addok is set, it is added to AVL trees and 29614843421SMatthew Ahrens * the zfsvfs->z_fuid_dirty flag will be set to TRUE. It will then be 29714843421SMatthew Ahrens * necessary for the caller or another thread to detect the dirty table 29814843421SMatthew Ahrens * and sync out the changes. 299da6c28aaSamw */ 30014843421SMatthew Ahrens int 30114843421SMatthew Ahrens zfs_fuid_find_by_domain(zfsvfs_t *zfsvfs, const char *domain, 30214843421SMatthew Ahrens char **retdomain, boolean_t addok) 303da6c28aaSamw { 304da6c28aaSamw fuid_domain_t searchnode, *findnode; 305da6c28aaSamw avl_index_t loc; 306a078ff4aSMark Shellenbaum krw_t rw = RW_READER; 307da6c28aaSamw 308569e6c63Smarks /* 309569e6c63Smarks * If the dummy "nobody" domain then return an index of 0 310569e6c63Smarks * to cause the created FUID to be a standard POSIX id 311569e6c63Smarks * for the user nobody. 312569e6c63Smarks */ 313569e6c63Smarks if (domain[0] == '\0') { 31414843421SMatthew Ahrens if (retdomain) 315003c2582SMark Shellenbaum *retdomain = nulldomain; 316569e6c63Smarks return (0); 317569e6c63Smarks } 318569e6c63Smarks 319da6c28aaSamw searchnode.f_ksid = ksid_lookupdomain(domain); 32014843421SMatthew Ahrens if (retdomain) 321da6c28aaSamw *retdomain = searchnode.f_ksid->kd_name; 322e0d35c44Smarks if (!zfsvfs->z_fuid_loaded) 32389459e17SMark Shellenbaum zfs_fuid_init(zfsvfs); 324da6c28aaSamw 325a078ff4aSMark Shellenbaum retry: 326a078ff4aSMark Shellenbaum rw_enter(&zfsvfs->z_fuid_lock, rw); 327da6c28aaSamw findnode = avl_find(&zfsvfs->z_fuid_domain, &searchnode, &loc); 328da6c28aaSamw 329da6c28aaSamw if (findnode) { 330a078ff4aSMark Shellenbaum rw_exit(&zfsvfs->z_fuid_lock); 331da6c28aaSamw ksiddomain_rele(searchnode.f_ksid); 332da6c28aaSamw return (findnode->f_idx); 33314843421SMatthew Ahrens } else if (addok) { 334da6c28aaSamw fuid_domain_t *domnode; 335da6c28aaSamw uint64_t retidx; 336da6c28aaSamw 337a078ff4aSMark Shellenbaum if (rw == RW_READER && !rw_tryupgrade(&zfsvfs->z_fuid_lock)) { 338a078ff4aSMark Shellenbaum rw_exit(&zfsvfs->z_fuid_lock); 339a078ff4aSMark Shellenbaum rw = RW_WRITER; 340a078ff4aSMark Shellenbaum goto retry; 341a078ff4aSMark Shellenbaum } 342a078ff4aSMark Shellenbaum 343da6c28aaSamw domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP); 344da6c28aaSamw domnode->f_ksid = searchnode.f_ksid; 345da6c28aaSamw 346e0d35c44Smarks retidx = domnode->f_idx = avl_numnodes(&zfsvfs->z_fuid_idx) + 1; 347da6c28aaSamw 348da6c28aaSamw avl_add(&zfsvfs->z_fuid_domain, domnode); 349e0d35c44Smarks avl_add(&zfsvfs->z_fuid_idx, domnode); 35089459e17SMark Shellenbaum zfsvfs->z_fuid_dirty = B_TRUE; 351da6c28aaSamw rw_exit(&zfsvfs->z_fuid_lock); 352da6c28aaSamw return (retidx); 35314843421SMatthew Ahrens } else { 3543b12c289SMatthew Ahrens rw_exit(&zfsvfs->z_fuid_lock); 35514843421SMatthew Ahrens return (-1); 356da6c28aaSamw } 357da6c28aaSamw } 358da6c28aaSamw 359da6c28aaSamw /* 360da6c28aaSamw * Query domain table by index, returning domain string 361da6c28aaSamw * 362da6c28aaSamw * Returns a pointer from an avl node of the domain string. 363da6c28aaSamw * 364da6c28aaSamw */ 36514843421SMatthew Ahrens const char * 366e0d35c44Smarks zfs_fuid_find_by_idx(zfsvfs_t *zfsvfs, uint32_t idx) 367da6c28aaSamw { 368e0d35c44Smarks char *domain; 369da6c28aaSamw 370e0d35c44Smarks if (idx == 0 || !zfsvfs->z_use_fuids) 371da6c28aaSamw return (NULL); 372da6c28aaSamw 373e0d35c44Smarks if (!zfsvfs->z_fuid_loaded) 37489459e17SMark Shellenbaum zfs_fuid_init(zfsvfs); 375da6c28aaSamw 376da6c28aaSamw rw_enter(&zfsvfs->z_fuid_lock, RW_READER); 377003c2582SMark Shellenbaum 3788f2529deSMark Shellenbaum if (zfsvfs->z_fuid_obj || zfsvfs->z_fuid_dirty) 379e0d35c44Smarks domain = zfs_fuid_idx_domain(&zfsvfs->z_fuid_idx, idx); 380003c2582SMark Shellenbaum else 381003c2582SMark Shellenbaum domain = nulldomain; 382da6c28aaSamw rw_exit(&zfsvfs->z_fuid_lock); 383da6c28aaSamw 384e0d35c44Smarks ASSERT(domain); 385e0d35c44Smarks return (domain); 386da6c28aaSamw } 387da6c28aaSamw 388da6c28aaSamw void 389e0d35c44Smarks zfs_fuid_map_ids(znode_t *zp, cred_t *cr, uid_t *uidp, uid_t *gidp) 390da6c28aaSamw { 3910a586ceaSMark Shellenbaum *uidp = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_uid, cr, ZFS_OWNER); 392f1696b23SMark Shellenbaum *gidp = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_gid, cr, ZFS_GROUP); 393da6c28aaSamw } 394da6c28aaSamw 395e0d35c44Smarks uid_t 396da6c28aaSamw zfs_fuid_map_id(zfsvfs_t *zfsvfs, uint64_t fuid, 397e0d35c44Smarks cred_t *cr, zfs_fuid_type_t type) 398da6c28aaSamw { 399da6c28aaSamw uint32_t index = FUID_INDEX(fuid); 40014843421SMatthew Ahrens const char *domain; 401e0d35c44Smarks uid_t id; 402da6c28aaSamw 403e0d35c44Smarks if (index == 0) 404e0d35c44Smarks return (fuid); 405da6c28aaSamw 406da6c28aaSamw domain = zfs_fuid_find_by_idx(zfsvfs, index); 407da6c28aaSamw ASSERT(domain != NULL); 408da6c28aaSamw 409e0d35c44Smarks if (type == ZFS_OWNER || type == ZFS_ACE_USER) { 410bda89588Sjp151216 (void) kidmap_getuidbysid(crgetzone(cr), domain, 411e0d35c44Smarks FUID_RID(fuid), &id); 412e0d35c44Smarks } else { 413bda89588Sjp151216 (void) kidmap_getgidbysid(crgetzone(cr), domain, 414e0d35c44Smarks FUID_RID(fuid), &id); 415e0d35c44Smarks } 416e0d35c44Smarks return (id); 417da6c28aaSamw } 418da6c28aaSamw 419da6c28aaSamw /* 420da6c28aaSamw * Add a FUID node to the list of fuid's being created for this 421da6c28aaSamw * ACL 422da6c28aaSamw * 423da6c28aaSamw * If ACL has multiple domains, then keep only one copy of each unique 424da6c28aaSamw * domain. 425da6c28aaSamw */ 426b3874165SJohn Harres void 427da6c28aaSamw zfs_fuid_node_add(zfs_fuid_info_t **fuidpp, const char *domain, uint32_t rid, 428da6c28aaSamw uint64_t idx, uint64_t id, zfs_fuid_type_t type) 429da6c28aaSamw { 430da6c28aaSamw zfs_fuid_t *fuid; 431da6c28aaSamw zfs_fuid_domain_t *fuid_domain; 432da6c28aaSamw zfs_fuid_info_t *fuidp; 433da6c28aaSamw uint64_t fuididx; 434da6c28aaSamw boolean_t found = B_FALSE; 435da6c28aaSamw 436da6c28aaSamw if (*fuidpp == NULL) 437da6c28aaSamw *fuidpp = zfs_fuid_info_alloc(); 438da6c28aaSamw 439da6c28aaSamw fuidp = *fuidpp; 440da6c28aaSamw /* 441da6c28aaSamw * First find fuid domain index in linked list 442da6c28aaSamw * 443da6c28aaSamw * If one isn't found then create an entry. 444da6c28aaSamw */ 445da6c28aaSamw 446da6c28aaSamw for (fuididx = 1, fuid_domain = list_head(&fuidp->z_domains); 447da6c28aaSamw fuid_domain; fuid_domain = list_next(&fuidp->z_domains, 448da6c28aaSamw fuid_domain), fuididx++) { 449da6c28aaSamw if (idx == fuid_domain->z_domidx) { 450da6c28aaSamw found = B_TRUE; 451da6c28aaSamw break; 452da6c28aaSamw } 453da6c28aaSamw } 454da6c28aaSamw 455e0d35c44Smarks if (!found) { 456da6c28aaSamw fuid_domain = kmem_alloc(sizeof (zfs_fuid_domain_t), KM_SLEEP); 457da6c28aaSamw fuid_domain->z_domain = domain; 458da6c28aaSamw fuid_domain->z_domidx = idx; 459da6c28aaSamw list_insert_tail(&fuidp->z_domains, fuid_domain); 460da6c28aaSamw fuidp->z_domain_str_sz += strlen(domain) + 1; 461da6c28aaSamw fuidp->z_domain_cnt++; 462da6c28aaSamw } 463da6c28aaSamw 464da6c28aaSamw if (type == ZFS_ACE_USER || type == ZFS_ACE_GROUP) { 46589459e17SMark Shellenbaum 466da6c28aaSamw /* 467da6c28aaSamw * Now allocate fuid entry and add it on the end of the list 468da6c28aaSamw */ 469da6c28aaSamw 470da6c28aaSamw fuid = kmem_alloc(sizeof (zfs_fuid_t), KM_SLEEP); 471da6c28aaSamw fuid->z_id = id; 472da6c28aaSamw fuid->z_domidx = idx; 473da6c28aaSamw fuid->z_logfuid = FUID_ENCODE(fuididx, rid); 474da6c28aaSamw 475da6c28aaSamw list_insert_tail(&fuidp->z_fuids, fuid); 476da6c28aaSamw fuidp->z_fuid_cnt++; 477da6c28aaSamw } else { 478da6c28aaSamw if (type == ZFS_OWNER) 479da6c28aaSamw fuidp->z_fuid_owner = FUID_ENCODE(fuididx, rid); 480da6c28aaSamw else 481da6c28aaSamw fuidp->z_fuid_group = FUID_ENCODE(fuididx, rid); 482da6c28aaSamw } 483da6c28aaSamw } 484da6c28aaSamw 485da6c28aaSamw /* 486569e6c63Smarks * Create a file system FUID, based on information in the users cred 4879fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * 4889fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * If cred contains KSID_OWNER then it should be used to determine 4899fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * the uid otherwise cred's uid will be used. By default cred's gid 4909fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * is used unless it's an ephemeral ID in which case KSID_GROUP will 4919fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * be used if it exists. 492da6c28aaSamw */ 493da6c28aaSamw uint64_t 494e0d35c44Smarks zfs_fuid_create_cred(zfsvfs_t *zfsvfs, zfs_fuid_type_t type, 49589459e17SMark Shellenbaum cred_t *cr, zfs_fuid_info_t **fuidp) 496da6c28aaSamw { 497da6c28aaSamw uint64_t idx; 498da6c28aaSamw ksid_t *ksid; 499da6c28aaSamw uint32_t rid; 500da6c28aaSamw char *kdomain; 501da6c28aaSamw const char *domain; 502e0d35c44Smarks uid_t id; 503da6c28aaSamw 504da6c28aaSamw VERIFY(type == ZFS_OWNER || type == ZFS_GROUP); 505da6c28aaSamw 506c1ce5987SMark Shellenbaum ksid = crgetsid(cr, (type == ZFS_OWNER) ? KSID_OWNER : KSID_GROUP); 507b3874165SJohn Harres 5089fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States if (!zfsvfs->z_use_fuids || (ksid == NULL)) { 5099fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States id = (type == ZFS_OWNER) ? crgetuid(cr) : crgetgid(cr); 510e0d35c44Smarks 5119fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States if (IS_EPHEMERAL(id)) 5129fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States return ((type == ZFS_OWNER) ? UID_NOBODY : GID_NOBODY); 5139fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 514da6c28aaSamw return ((uint64_t)id); 5159fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States } 5169fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 5179fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States /* 5189fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States * ksid is present and FUID is supported 5199fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States */ 5209fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States id = (type == ZFS_OWNER) ? ksid_getid(ksid) : crgetgid(cr); 5219fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 5229fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States if (!IS_EPHEMERAL(id)) 5239fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States return ((uint64_t)id); 5249fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States 5259fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States if (type == ZFS_GROUP) 5269fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States id = ksid_getid(ksid); 527da6c28aaSamw 528da6c28aaSamw rid = ksid_getrid(ksid); 529da6c28aaSamw domain = ksid_getdomain(ksid); 530da6c28aaSamw 53114843421SMatthew Ahrens idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, B_TRUE); 532da6c28aaSamw 533da6c28aaSamw zfs_fuid_node_add(fuidp, kdomain, rid, idx, id, type); 534da6c28aaSamw 535da6c28aaSamw return (FUID_ENCODE(idx, rid)); 536da6c28aaSamw } 537da6c28aaSamw 538da6c28aaSamw /* 539da6c28aaSamw * Create a file system FUID for an ACL ace 540da6c28aaSamw * or a chown/chgrp of the file. 541da6c28aaSamw * This is similar to zfs_fuid_create_cred, except that 542da6c28aaSamw * we can't find the domain + rid information in the 543da6c28aaSamw * cred. Instead we have to query Winchester for the 544da6c28aaSamw * domain and rid. 545569e6c63Smarks * 546569e6c63Smarks * During replay operations the domain+rid information is 547569e6c63Smarks * found in the zfs_fuid_info_t that the replay code has 548569e6c63Smarks * attached to the zfsvfs of the file system. 549da6c28aaSamw */ 550da6c28aaSamw uint64_t 551bda89588Sjp151216 zfs_fuid_create(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr, 55289459e17SMark Shellenbaum zfs_fuid_type_t type, zfs_fuid_info_t **fuidpp) 553da6c28aaSamw { 554da6c28aaSamw const char *domain; 555da6c28aaSamw char *kdomain; 556da6c28aaSamw uint32_t fuid_idx = FUID_INDEX(id); 557da6c28aaSamw uint32_t rid; 558da6c28aaSamw idmap_stat status; 559*d5285caeSGeorge Wilson uint64_t idx = 0; 560da6c28aaSamw zfs_fuid_t *zfuid = NULL; 561*d5285caeSGeorge Wilson zfs_fuid_info_t *fuidp = NULL; 562da6c28aaSamw 563da6c28aaSamw /* 564da6c28aaSamw * If POSIX ID, or entry is already a FUID then 565da6c28aaSamw * just return the id 566e0d35c44Smarks * 567e0d35c44Smarks * We may also be handed an already FUID'ized id via 568e0d35c44Smarks * chmod. 569da6c28aaSamw */ 570e0d35c44Smarks 571e0d35c44Smarks if (!zfsvfs->z_use_fuids || !IS_EPHEMERAL(id) || fuid_idx != 0) 572da6c28aaSamw return (id); 573da6c28aaSamw 5741209a471SNeil Perrin if (zfsvfs->z_replay) { 575da6c28aaSamw fuidp = zfsvfs->z_fuid_replay; 576da6c28aaSamw 577da6c28aaSamw /* 578da6c28aaSamw * If we are passed an ephemeral id, but no 579da6c28aaSamw * fuid_info was logged then return NOBODY. 580da6c28aaSamw * This is most likely a result of idmap service 581da6c28aaSamw * not being available. 582da6c28aaSamw */ 583da6c28aaSamw if (fuidp == NULL) 584da6c28aaSamw return (UID_NOBODY); 585da6c28aaSamw 586*d5285caeSGeorge Wilson VERIFY3U(type, >=, ZFS_OWNER); 587*d5285caeSGeorge Wilson VERIFY3U(type, <=, ZFS_ACE_GROUP); 588*d5285caeSGeorge Wilson 589da6c28aaSamw switch (type) { 590da6c28aaSamw case ZFS_ACE_USER: 591da6c28aaSamw case ZFS_ACE_GROUP: 592da6c28aaSamw zfuid = list_head(&fuidp->z_fuids); 593da6c28aaSamw rid = FUID_RID(zfuid->z_logfuid); 594da6c28aaSamw idx = FUID_INDEX(zfuid->z_logfuid); 595da6c28aaSamw break; 596da6c28aaSamw case ZFS_OWNER: 597da6c28aaSamw rid = FUID_RID(fuidp->z_fuid_owner); 598da6c28aaSamw idx = FUID_INDEX(fuidp->z_fuid_owner); 599da6c28aaSamw break; 600da6c28aaSamw case ZFS_GROUP: 601da6c28aaSamw rid = FUID_RID(fuidp->z_fuid_group); 602da6c28aaSamw idx = FUID_INDEX(fuidp->z_fuid_group); 603da6c28aaSamw break; 604da6c28aaSamw }; 605da6c28aaSamw domain = fuidp->z_domain_table[idx - 1]; 606da6c28aaSamw } else { 607da6c28aaSamw if (type == ZFS_OWNER || type == ZFS_ACE_USER) 608bda89588Sjp151216 status = kidmap_getsidbyuid(crgetzone(cr), id, 609bda89588Sjp151216 &domain, &rid); 610da6c28aaSamw else 611bda89588Sjp151216 status = kidmap_getsidbygid(crgetzone(cr), id, 612bda89588Sjp151216 &domain, &rid); 613da6c28aaSamw 614569e6c63Smarks if (status != 0) { 615569e6c63Smarks /* 616569e6c63Smarks * When returning nobody we will need to 617569e6c63Smarks * make a dummy fuid table entry for logging 618569e6c63Smarks * purposes. 619569e6c63Smarks */ 620569e6c63Smarks rid = UID_NOBODY; 621003c2582SMark Shellenbaum domain = nulldomain; 622569e6c63Smarks } 623da6c28aaSamw } 624da6c28aaSamw 62514843421SMatthew Ahrens idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, B_TRUE); 626da6c28aaSamw 6271209a471SNeil Perrin if (!zfsvfs->z_replay) 62889459e17SMark Shellenbaum zfs_fuid_node_add(fuidpp, kdomain, 62989459e17SMark Shellenbaum rid, idx, id, type); 630da6c28aaSamw else if (zfuid != NULL) { 631da6c28aaSamw list_remove(&fuidp->z_fuids, zfuid); 632da6c28aaSamw kmem_free(zfuid, sizeof (zfs_fuid_t)); 633da6c28aaSamw } 634da6c28aaSamw return (FUID_ENCODE(idx, rid)); 635da6c28aaSamw } 636da6c28aaSamw 637da6c28aaSamw void 638da6c28aaSamw zfs_fuid_destroy(zfsvfs_t *zfsvfs) 639da6c28aaSamw { 640da6c28aaSamw rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); 641e0d35c44Smarks if (!zfsvfs->z_fuid_loaded) { 642da6c28aaSamw rw_exit(&zfsvfs->z_fuid_lock); 643da6c28aaSamw return; 644da6c28aaSamw } 645e0d35c44Smarks zfs_fuid_table_destroy(&zfsvfs->z_fuid_idx, &zfsvfs->z_fuid_domain); 646da6c28aaSamw rw_exit(&zfsvfs->z_fuid_lock); 647da6c28aaSamw } 648da6c28aaSamw 649da6c28aaSamw /* 650da6c28aaSamw * Allocate zfs_fuid_info for tracking FUIDs created during 651da6c28aaSamw * zfs_mknode, VOP_SETATTR() or VOP_SETSECATTR() 652da6c28aaSamw */ 653da6c28aaSamw zfs_fuid_info_t * 654da6c28aaSamw zfs_fuid_info_alloc(void) 655da6c28aaSamw { 656da6c28aaSamw zfs_fuid_info_t *fuidp; 657da6c28aaSamw 658da6c28aaSamw fuidp = kmem_zalloc(sizeof (zfs_fuid_info_t), KM_SLEEP); 659da6c28aaSamw list_create(&fuidp->z_domains, sizeof (zfs_fuid_domain_t), 660da6c28aaSamw offsetof(zfs_fuid_domain_t, z_next)); 661da6c28aaSamw list_create(&fuidp->z_fuids, sizeof (zfs_fuid_t), 662da6c28aaSamw offsetof(zfs_fuid_t, z_next)); 663da6c28aaSamw return (fuidp); 664da6c28aaSamw } 665da6c28aaSamw 666da6c28aaSamw /* 667da6c28aaSamw * Release all memory associated with zfs_fuid_info_t 668da6c28aaSamw */ 669da6c28aaSamw void 670da6c28aaSamw zfs_fuid_info_free(zfs_fuid_info_t *fuidp) 671da6c28aaSamw { 672da6c28aaSamw zfs_fuid_t *zfuid; 673da6c28aaSamw zfs_fuid_domain_t *zdomain; 674da6c28aaSamw 675da6c28aaSamw while ((zfuid = list_head(&fuidp->z_fuids)) != NULL) { 676da6c28aaSamw list_remove(&fuidp->z_fuids, zfuid); 677da6c28aaSamw kmem_free(zfuid, sizeof (zfs_fuid_t)); 678da6c28aaSamw } 679da6c28aaSamw 680da6c28aaSamw if (fuidp->z_domain_table != NULL) 681da6c28aaSamw kmem_free(fuidp->z_domain_table, 682da6c28aaSamw (sizeof (char **)) * fuidp->z_domain_cnt); 683da6c28aaSamw 684da6c28aaSamw while ((zdomain = list_head(&fuidp->z_domains)) != NULL) { 685da6c28aaSamw list_remove(&fuidp->z_domains, zdomain); 686da6c28aaSamw kmem_free(zdomain, sizeof (zfs_fuid_domain_t)); 687da6c28aaSamw } 688da6c28aaSamw 689da6c28aaSamw kmem_free(fuidp, sizeof (zfs_fuid_info_t)); 690da6c28aaSamw } 691da6c28aaSamw 692da6c28aaSamw /* 693da6c28aaSamw * Check to see if id is a groupmember. If cred 694da6c28aaSamw * has ksid info then sidlist is checked first 695da6c28aaSamw * and if still not found then POSIX groups are checked 696da6c28aaSamw * 697da6c28aaSamw * Will use a straight FUID compare when possible. 698da6c28aaSamw */ 699da6c28aaSamw boolean_t 700da6c28aaSamw zfs_groupmember(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr) 701da6c28aaSamw { 702da6c28aaSamw ksid_t *ksid = crgetsid(cr, KSID_GROUP); 703bc584822SMark Shellenbaum ksidlist_t *ksidlist = crgetsidlist(cr); 704da6c28aaSamw uid_t gid; 705da6c28aaSamw 706bc584822SMark Shellenbaum if (ksid && ksidlist) { 707da6c28aaSamw int i; 708da6c28aaSamw ksid_t *ksid_groups; 709da6c28aaSamw uint32_t idx = FUID_INDEX(id); 710da6c28aaSamw uint32_t rid = FUID_RID(id); 711da6c28aaSamw 712da6c28aaSamw ksid_groups = ksidlist->ksl_sids; 713da6c28aaSamw 714da6c28aaSamw for (i = 0; i != ksidlist->ksl_nsid; i++) { 715da6c28aaSamw if (idx == 0) { 716da6c28aaSamw if (id != IDMAP_WK_CREATOR_GROUP_GID && 717da6c28aaSamw id == ksid_groups[i].ks_id) { 718da6c28aaSamw return (B_TRUE); 719da6c28aaSamw } 720da6c28aaSamw } else { 72114843421SMatthew Ahrens const char *domain; 722da6c28aaSamw 723da6c28aaSamw domain = zfs_fuid_find_by_idx(zfsvfs, idx); 724da6c28aaSamw ASSERT(domain != NULL); 725da6c28aaSamw 726da6c28aaSamw if (strcmp(domain, 727e0d35c44Smarks IDMAP_WK_CREATOR_SID_AUTHORITY) == 0) 728da6c28aaSamw return (B_FALSE); 729da6c28aaSamw 730da6c28aaSamw if ((strcmp(domain, 731da6c28aaSamw ksid_groups[i].ks_domain->kd_name) == 0) && 732e0d35c44Smarks rid == ksid_groups[i].ks_rid) 733da6c28aaSamw return (B_TRUE); 734da6c28aaSamw } 735da6c28aaSamw } 736da6c28aaSamw } 737da6c28aaSamw 738da6c28aaSamw /* 739da6c28aaSamw * Not found in ksidlist, check posix groups 740da6c28aaSamw */ 741e0d35c44Smarks gid = zfs_fuid_map_id(zfsvfs, id, cr, ZFS_GROUP); 742da6c28aaSamw return (groupmember(gid, cr)); 743da6c28aaSamw } 74414843421SMatthew Ahrens 74514843421SMatthew Ahrens void 74614843421SMatthew Ahrens zfs_fuid_txhold(zfsvfs_t *zfsvfs, dmu_tx_t *tx) 74714843421SMatthew Ahrens { 74814843421SMatthew Ahrens if (zfsvfs->z_fuid_obj == 0) { 74914843421SMatthew Ahrens dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); 75014843421SMatthew Ahrens dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, 75114843421SMatthew Ahrens FUID_SIZE_ESTIMATE(zfsvfs)); 75214843421SMatthew Ahrens dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); 75314843421SMatthew Ahrens } else { 75414843421SMatthew Ahrens dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); 75514843421SMatthew Ahrens dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, 75614843421SMatthew Ahrens FUID_SIZE_ESTIMATE(zfsvfs)); 75714843421SMatthew Ahrens } 75814843421SMatthew Ahrens } 759e0d35c44Smarks #endif 760