The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/openzfs/module/zfs/zfs_fuid.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 9e78dea9e3292649d841adaecb4c79b3


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.