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/lib/libzfs/libzfs_changelist.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 /*
   23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
   24  * Use is subject to license terms.
   25  *
   26  * Portions Copyright 2007 Ramprakash Jelari
   27  * Copyright (c) 2014, 2020 by Delphix. All rights reserved.
   28  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
   29  * Copyright (c) 2018 Datto Inc.
   30  */
   31 
   32 #include <libintl.h>
   33 #include <libuutil.h>
   34 #include <stddef.h>
   35 #include <stdlib.h>
   36 #include <string.h>
   37 #include <unistd.h>
   38 #include <zone.h>
   39 
   40 #include <libzfs.h>
   41 
   42 #include "libzfs_impl.h"
   43 
   44 /*
   45  * Structure to keep track of dataset state.  Before changing the 'sharenfs' or
   46  * 'mountpoint' property, we record whether the filesystem was previously
   47  * mounted/shared.  This prior state dictates whether we remount/reshare the
   48  * dataset after the property has been changed.
   49  *
   50  * The interface consists of the following sequence of functions:
   51  *
   52  *      changelist_gather()
   53  *      changelist_prefix()
   54  *      < change property >
   55  *      changelist_postfix()
   56  *      changelist_free()
   57  *
   58  * Other interfaces:
   59  *
   60  * changelist_remove() - remove a node from a gathered list
   61  * changelist_rename() - renames all datasets appropriately when doing a rename
   62  * changelist_unshare() - unshares all the nodes in a given changelist
   63  * changelist_haszonedchild() - check if there is any child exported to
   64  *                              a local zone
   65  */
   66 typedef struct prop_changenode {
   67         zfs_handle_t            *cn_handle;
   68         int                     cn_shared;
   69         int                     cn_mounted;
   70         int                     cn_zoned;
   71         boolean_t               cn_needpost;    /* is postfix() needed? */
   72         uu_avl_node_t           cn_treenode;
   73 } prop_changenode_t;
   74 
   75 struct prop_changelist {
   76         zfs_prop_t              cl_prop;
   77         zfs_prop_t              cl_realprop;
   78         zfs_prop_t              cl_shareprop;  /* used with sharenfs/sharesmb */
   79         uu_avl_pool_t           *cl_pool;
   80         uu_avl_t                *cl_tree;
   81         boolean_t               cl_waslegacy;
   82         boolean_t               cl_allchildren;
   83         boolean_t               cl_alldependents;
   84         int                     cl_mflags;      /* Mount flags */
   85         int                     cl_gflags;      /* Gather request flags */
   86         boolean_t               cl_haszonedchild;
   87 };
   88 
   89 /*
   90  * If the property is 'mountpoint', go through and unmount filesystems as
   91  * necessary.  We don't do the same for 'sharenfs', because we can just re-share
   92  * with different options without interrupting service. We do handle 'sharesmb'
   93  * since there may be old resource names that need to be removed.
   94  */
   95 int
   96 changelist_prefix(prop_changelist_t *clp)
   97 {
   98         prop_changenode_t *cn;
   99         uu_avl_walk_t *walk;
  100         int ret = 0;
  101         const enum sa_protocol smb[] = {SA_PROTOCOL_SMB, SA_NO_PROTOCOL};
  102         boolean_t commit_smb_shares = B_FALSE;
  103 
  104         if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
  105             clp->cl_prop != ZFS_PROP_SHARESMB)
  106                 return (0);
  107 
  108         if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
  109                 return (-1);
  110 
  111         while ((cn = uu_avl_walk_next(walk)) != NULL) {
  112 
  113                 /* if a previous loop failed, set the remaining to false */
  114                 if (ret == -1) {
  115                         cn->cn_needpost = B_FALSE;
  116                         continue;
  117                 }
  118 
  119                 /*
  120                  * If we are in the global zone, but this dataset is exported
  121                  * to a local zone, do nothing.
  122                  */
  123                 if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
  124                         continue;
  125 
  126                 if (!ZFS_IS_VOLUME(cn->cn_handle)) {
  127                         /*
  128                          * Do the property specific processing.
  129                          */
  130                         switch (clp->cl_prop) {
  131                         case ZFS_PROP_MOUNTPOINT:
  132                                 if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
  133                                         break;
  134                                 if (zfs_unmount(cn->cn_handle, NULL,
  135                                     clp->cl_mflags) != 0) {
  136                                         ret = -1;
  137                                         cn->cn_needpost = B_FALSE;
  138                                 }
  139                                 break;
  140                         case ZFS_PROP_SHARESMB:
  141                                 (void) zfs_unshare(cn->cn_handle, NULL,
  142                                     smb);
  143                                 commit_smb_shares = B_TRUE;
  144                                 break;
  145 
  146                         default:
  147                                 break;
  148                         }
  149                 }
  150         }
  151 
  152         if (commit_smb_shares)
  153                 zfs_commit_shares(smb);
  154         uu_avl_walk_end(walk);
  155 
  156         if (ret == -1)
  157                 (void) changelist_postfix(clp);
  158 
  159         return (ret);
  160 }
  161 
  162 /*
  163  * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or
  164  * reshare the filesystems as necessary.  In changelist_gather() we recorded
  165  * whether the filesystem was previously shared or mounted.  The action we take
  166  * depends on the previous state, and whether the value was previously 'legacy'.
  167  * For non-legacy properties, we only remount/reshare the filesystem if it was
  168  * previously mounted/shared.  Otherwise, we always remount/reshare the
  169  * filesystem.
  170  */
  171 int
  172 changelist_postfix(prop_changelist_t *clp)
  173 {
  174         prop_changenode_t *cn;
  175         uu_avl_walk_t *walk;
  176         char shareopts[ZFS_MAXPROPLEN];
  177         int errors = 0;
  178         boolean_t commit_smb_shares = B_FALSE;
  179         boolean_t commit_nfs_shares = B_FALSE;
  180 
  181         /*
  182          * If we're changing the mountpoint, attempt to destroy the underlying
  183          * mountpoint.  All other datasets will have inherited from this dataset
  184          * (in which case their mountpoints exist in the filesystem in the new
  185          * location), or have explicit mountpoints set (in which case they won't
  186          * be in the changelist).
  187          */
  188         if ((cn = uu_avl_last(clp->cl_tree)) == NULL)
  189                 return (0);
  190 
  191         if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
  192             !(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT))
  193                 remove_mountpoint(cn->cn_handle);
  194 
  195         /*
  196          * We walk the datasets in reverse, because we want to mount any parent
  197          * datasets before mounting the children.  We walk all datasets even if
  198          * there are errors.
  199          */
  200         if ((walk = uu_avl_walk_start(clp->cl_tree,
  201             UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)
  202                 return (-1);
  203 
  204         while ((cn = uu_avl_walk_next(walk)) != NULL) {
  205 
  206                 boolean_t sharenfs;
  207                 boolean_t sharesmb;
  208                 boolean_t mounted;
  209                 boolean_t needs_key;
  210 
  211                 /*
  212                  * If we are in the global zone, but this dataset is exported
  213                  * to a local zone, do nothing.
  214                  */
  215                 if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
  216                         continue;
  217 
  218                 /* Only do post-processing if it's required */
  219                 if (!cn->cn_needpost)
  220                         continue;
  221                 cn->cn_needpost = B_FALSE;
  222 
  223                 zfs_refresh_properties(cn->cn_handle);
  224 
  225                 if (ZFS_IS_VOLUME(cn->cn_handle))
  226                         continue;
  227 
  228                 /*
  229                  * Remount if previously mounted or mountpoint was legacy,
  230                  * or sharenfs or sharesmb  property is set.
  231                  */
  232                 sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
  233                     shareopts, sizeof (shareopts), NULL, NULL, 0,
  234                     B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
  235 
  236                 sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
  237                     shareopts, sizeof (shareopts), NULL, NULL, 0,
  238                     B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
  239 
  240                 needs_key = (zfs_prop_get_int(cn->cn_handle,
  241                     ZFS_PROP_KEYSTATUS) == ZFS_KEYSTATUS_UNAVAILABLE);
  242 
  243                 mounted = (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) ||
  244                     zfs_is_mounted(cn->cn_handle, NULL);
  245 
  246                 if (!mounted && !needs_key && (cn->cn_mounted ||
  247                     ((sharenfs || sharesmb || clp->cl_waslegacy) &&
  248                     (zfs_prop_get_int(cn->cn_handle,
  249                     ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) {
  250 
  251                         if (zfs_mount(cn->cn_handle, NULL, 0) != 0)
  252                                 errors++;
  253                         else
  254                                 mounted = TRUE;
  255                 }
  256 
  257                 /*
  258                  * If the file system is mounted we always re-share even
  259                  * if the filesystem is currently shared, so that we can
  260                  * adopt any new options.
  261                  */
  262                 const enum sa_protocol nfs[] =
  263                     {SA_PROTOCOL_NFS, SA_NO_PROTOCOL};
  264                 if (sharenfs && mounted) {
  265                         errors += zfs_share(cn->cn_handle, nfs);
  266                         commit_nfs_shares = B_TRUE;
  267                 } else if (cn->cn_shared || clp->cl_waslegacy) {
  268                         errors += zfs_unshare(cn->cn_handle, NULL, nfs);
  269                         commit_nfs_shares = B_TRUE;
  270                 }
  271                 const enum sa_protocol smb[] =
  272                     {SA_PROTOCOL_SMB, SA_NO_PROTOCOL};
  273                 if (sharesmb && mounted) {
  274                         errors += zfs_share(cn->cn_handle, smb);
  275                         commit_smb_shares = B_TRUE;
  276                 } else if (cn->cn_shared || clp->cl_waslegacy) {
  277                         errors += zfs_unshare(cn->cn_handle, NULL, smb);
  278                         commit_smb_shares = B_TRUE;
  279                 }
  280         }
  281 
  282         enum sa_protocol proto[SA_PROTOCOL_COUNT + 1], *p = proto;
  283         if (commit_nfs_shares)
  284                 *p++ = SA_PROTOCOL_NFS;
  285         if (commit_smb_shares)
  286                 *p++ = SA_PROTOCOL_SMB;
  287         *p++ = SA_NO_PROTOCOL;
  288         zfs_commit_shares(proto);
  289         uu_avl_walk_end(walk);
  290 
  291         return (errors ? -1 : 0);
  292 }
  293 
  294 /*
  295  * Is this "dataset" a child of "parent"?
  296  */
  297 static boolean_t
  298 isa_child_of(const char *dataset, const char *parent)
  299 {
  300         int len;
  301 
  302         len = strlen(parent);
  303 
  304         if (strncmp(dataset, parent, len) == 0 &&
  305             (dataset[len] == '@' || dataset[len] == '/' ||
  306             dataset[len] == '\0'))
  307                 return (B_TRUE);
  308         else
  309                 return (B_FALSE);
  310 
  311 }
  312 
  313 /*
  314  * If we rename a filesystem, child filesystem handles are no longer valid
  315  * since we identify each dataset by its name in the ZFS namespace.  As a
  316  * result, we have to go through and fix up all the names appropriately.  We
  317  * could do this automatically if libzfs kept track of all open handles, but
  318  * this is a lot less work.
  319  */
  320 void
  321 changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
  322 {
  323         prop_changenode_t *cn;
  324         uu_avl_walk_t *walk;
  325         char newname[ZFS_MAX_DATASET_NAME_LEN];
  326 
  327         if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
  328                 return;
  329 
  330         while ((cn = uu_avl_walk_next(walk)) != NULL) {
  331                 /*
  332                  * Do not rename a clone that's not in the source hierarchy.
  333                  */
  334                 if (!isa_child_of(cn->cn_handle->zfs_name, src))
  335                         continue;
  336 
  337                 /*
  338                  * Destroy the previous mountpoint if needed.
  339                  */
  340                 remove_mountpoint(cn->cn_handle);
  341 
  342                 (void) strlcpy(newname, dst, sizeof (newname));
  343                 (void) strlcat(newname, cn->cn_handle->zfs_name + strlen(src),
  344                     sizeof (newname));
  345 
  346                 (void) strlcpy(cn->cn_handle->zfs_name, newname,
  347                     sizeof (cn->cn_handle->zfs_name));
  348         }
  349 
  350         uu_avl_walk_end(walk);
  351 }
  352 
  353 /*
  354  * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
  355  * unshare all the datasets in the list.
  356  */
  357 int
  358 changelist_unshare(prop_changelist_t *clp, const enum sa_protocol *proto)
  359 {
  360         prop_changenode_t *cn;
  361         uu_avl_walk_t *walk;
  362         int ret = 0;
  363 
  364         if (clp->cl_prop != ZFS_PROP_SHARENFS &&
  365             clp->cl_prop != ZFS_PROP_SHARESMB)
  366                 return (0);
  367 
  368         if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
  369                 return (-1);
  370 
  371         while ((cn = uu_avl_walk_next(walk)) != NULL) {
  372                 if (zfs_unshare(cn->cn_handle, NULL, proto) != 0)
  373                         ret = -1;
  374         }
  375 
  376         for (const enum sa_protocol *p = proto; *p != SA_NO_PROTOCOL; ++p)
  377                 sa_commit_shares(*p);
  378         uu_avl_walk_end(walk);
  379 
  380         return (ret);
  381 }
  382 
  383 /*
  384  * Check if there is any child exported to a local zone in a given changelist.
  385  * This information has already been recorded while gathering the changelist
  386  * via changelist_gather().
  387  */
  388 int
  389 changelist_haszonedchild(prop_changelist_t *clp)
  390 {
  391         return (clp->cl_haszonedchild);
  392 }
  393 
  394 /*
  395  * Remove a node from a gathered list.
  396  */
  397 void
  398 changelist_remove(prop_changelist_t *clp, const char *name)
  399 {
  400         prop_changenode_t *cn;
  401         uu_avl_walk_t *walk;
  402 
  403         if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
  404                 return;
  405 
  406         while ((cn = uu_avl_walk_next(walk)) != NULL) {
  407                 if (strcmp(cn->cn_handle->zfs_name, name) == 0) {
  408                         uu_avl_remove(clp->cl_tree, cn);
  409                         zfs_close(cn->cn_handle);
  410                         free(cn);
  411                         uu_avl_walk_end(walk);
  412                         return;
  413                 }
  414         }
  415 
  416         uu_avl_walk_end(walk);
  417 }
  418 
  419 /*
  420  * Release any memory associated with a changelist.
  421  */
  422 void
  423 changelist_free(prop_changelist_t *clp)
  424 {
  425         prop_changenode_t *cn;
  426 
  427         if (clp->cl_tree) {
  428                 uu_avl_walk_t *walk;
  429 
  430                 if ((walk = uu_avl_walk_start(clp->cl_tree,
  431                     UU_WALK_ROBUST)) == NULL)
  432                         return;
  433 
  434                 while ((cn = uu_avl_walk_next(walk)) != NULL) {
  435                         uu_avl_remove(clp->cl_tree, cn);
  436                         zfs_close(cn->cn_handle);
  437                         free(cn);
  438                 }
  439 
  440                 uu_avl_walk_end(walk);
  441                 uu_avl_destroy(clp->cl_tree);
  442         }
  443         if (clp->cl_pool)
  444                 uu_avl_pool_destroy(clp->cl_pool);
  445 
  446         free(clp);
  447 }
  448 
  449 /*
  450  * Add one dataset to changelist
  451  */
  452 static int
  453 changelist_add_mounted(zfs_handle_t *zhp, void *data)
  454 {
  455         prop_changelist_t *clp = data;
  456         prop_changenode_t *cn;
  457         uu_avl_index_t idx;
  458 
  459         ASSERT3U(clp->cl_prop, ==, ZFS_PROP_MOUNTPOINT);
  460 
  461         cn = zfs_alloc(zfs_get_handle(zhp), sizeof (prop_changenode_t));
  462         cn->cn_handle = zhp;
  463         cn->cn_mounted = zfs_is_mounted(zhp, NULL);
  464         ASSERT3U(cn->cn_mounted, ==, B_TRUE);
  465         cn->cn_shared = zfs_is_shared(zhp, NULL, NULL);
  466         cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
  467         cn->cn_needpost = B_TRUE;
  468 
  469         /* Indicate if any child is exported to a local zone. */
  470         if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
  471                 clp->cl_haszonedchild = B_TRUE;
  472 
  473         uu_avl_node_init(cn, &cn->cn_treenode, clp->cl_pool);
  474 
  475         if (uu_avl_find(clp->cl_tree, cn, NULL, &idx) == NULL) {
  476                 uu_avl_insert(clp->cl_tree, cn, idx);
  477         } else {
  478                 free(cn);
  479                 zfs_close(zhp);
  480         }
  481 
  482         return (0);
  483 }
  484 
  485 static int
  486 change_one(zfs_handle_t *zhp, void *data)
  487 {
  488         prop_changelist_t *clp = data;
  489         char property[ZFS_MAXPROPLEN];
  490         char where[64];
  491         prop_changenode_t *cn = NULL;
  492         zprop_source_t sourcetype = ZPROP_SRC_NONE;
  493         zprop_source_t share_sourcetype = ZPROP_SRC_NONE;
  494         int ret = 0;
  495 
  496         /*
  497          * We only want to unmount/unshare those filesystems that may inherit
  498          * from the target filesystem.  If we find any filesystem with a
  499          * locally set mountpoint, we ignore any children since changing the
  500          * property will not affect them.  If this is a rename, we iterate
  501          * over all children regardless, since we need them unmounted in
  502          * order to do the rename.  Also, if this is a volume and we're doing
  503          * a rename, then always add it to the changelist.
  504          */
  505 
  506         if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) &&
  507             zfs_prop_get(zhp, clp->cl_prop, property,
  508             sizeof (property), &sourcetype, where, sizeof (where),
  509             B_FALSE) != 0) {
  510                 goto out;
  511         }
  512 
  513         /*
  514          * If we are "watching" sharenfs or sharesmb
  515          * then check out the companion property which is tracked
  516          * in cl_shareprop
  517          */
  518         if (clp->cl_shareprop != ZPROP_INVAL &&
  519             zfs_prop_get(zhp, clp->cl_shareprop, property,
  520             sizeof (property), &share_sourcetype, where, sizeof (where),
  521             B_FALSE) != 0) {
  522                 goto out;
  523         }
  524 
  525         if (clp->cl_alldependents || clp->cl_allchildren ||
  526             sourcetype == ZPROP_SRC_DEFAULT ||
  527             sourcetype == ZPROP_SRC_INHERITED ||
  528             (clp->cl_shareprop != ZPROP_INVAL &&
  529             (share_sourcetype == ZPROP_SRC_DEFAULT ||
  530             share_sourcetype == ZPROP_SRC_INHERITED))) {
  531                 cn = zfs_alloc(zfs_get_handle(zhp), sizeof (prop_changenode_t));
  532                 cn->cn_handle = zhp;
  533                 cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
  534                     zfs_is_mounted(zhp, NULL);
  535                 cn->cn_shared = zfs_is_shared(zhp, NULL, NULL);
  536                 cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
  537                 cn->cn_needpost = B_TRUE;
  538 
  539                 /* Indicate if any child is exported to a local zone. */
  540                 if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
  541                         clp->cl_haszonedchild = B_TRUE;
  542 
  543                 uu_avl_node_init(cn, &cn->cn_treenode, clp->cl_pool);
  544 
  545                 uu_avl_index_t idx;
  546 
  547                 if (uu_avl_find(clp->cl_tree, cn, NULL, &idx) == NULL) {
  548                         uu_avl_insert(clp->cl_tree, cn, idx);
  549                 } else {
  550                         free(cn);
  551                         cn = NULL;
  552                 }
  553 
  554                 if (!clp->cl_alldependents)
  555                         ret = zfs_iter_children(zhp, 0, change_one, data);
  556 
  557                 /*
  558                  * If we added the handle to the changelist, we will re-use it
  559                  * later so return without closing it.
  560                  */
  561                 if (cn != NULL)
  562                         return (ret);
  563         }
  564 
  565 out:
  566         zfs_close(zhp);
  567         return (ret);
  568 }
  569 
  570 static int
  571 compare_props(const void *a, const void *b, zfs_prop_t prop)
  572 {
  573         const prop_changenode_t *ca = a;
  574         const prop_changenode_t *cb = b;
  575 
  576         char propa[MAXPATHLEN];
  577         char propb[MAXPATHLEN];
  578 
  579         boolean_t haspropa, haspropb;
  580 
  581         haspropa = (zfs_prop_get(ca->cn_handle, prop, propa, sizeof (propa),
  582             NULL, NULL, 0, B_FALSE) == 0);
  583         haspropb = (zfs_prop_get(cb->cn_handle, prop, propb, sizeof (propb),
  584             NULL, NULL, 0, B_FALSE) == 0);
  585 
  586         if (!haspropa && haspropb)
  587                 return (-1);
  588         else if (haspropa && !haspropb)
  589                 return (1);
  590         else if (!haspropa && !haspropb)
  591                 return (0);
  592         else
  593                 return (strcmp(propb, propa));
  594 }
  595 
  596 static int
  597 compare_mountpoints(const void *a, const void *b, void *unused)
  598 {
  599         /*
  600          * When unsharing or unmounting filesystems, we need to do it in
  601          * mountpoint order.  This allows the user to have a mountpoint
  602          * hierarchy that is different from the dataset hierarchy, and still
  603          * allow it to be changed.
  604          */
  605         (void) unused;
  606         return (compare_props(a, b, ZFS_PROP_MOUNTPOINT));
  607 }
  608 
  609 static int
  610 compare_dataset_names(const void *a, const void *b, void *unused)
  611 {
  612         (void) unused;
  613         return (compare_props(a, b, ZFS_PROP_NAME));
  614 }
  615 
  616 /*
  617  * Given a ZFS handle and a property, construct a complete list of datasets
  618  * that need to be modified as part of this process.  For anything but the
  619  * 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
  620  * Otherwise, we iterate over all children and look for any datasets that
  621  * inherit the property.  For each such dataset, we add it to the list and
  622  * mark whether it was shared beforehand.
  623  */
  624 prop_changelist_t *
  625 changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
  626     int mnt_flags)
  627 {
  628         prop_changelist_t *clp;
  629         prop_changenode_t *cn;
  630         zfs_handle_t *temp;
  631         char property[ZFS_MAXPROPLEN];
  632         boolean_t legacy = B_FALSE;
  633 
  634         clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t));
  635 
  636         /*
  637          * For mountpoint-related tasks, we want to sort everything by
  638          * mountpoint, so that we mount and unmount them in the appropriate
  639          * order, regardless of their position in the hierarchy.
  640          */
  641         if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
  642             prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
  643             prop == ZFS_PROP_SHARESMB) {
  644 
  645                 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
  646                     property, sizeof (property),
  647                     NULL, NULL, 0, B_FALSE) == 0 &&
  648                     (strcmp(property, "legacy") == 0 ||
  649                     strcmp(property, "none") == 0)) {
  650                         legacy = B_TRUE;
  651                 }
  652         }
  653 
  654         clp->cl_pool = uu_avl_pool_create("changelist_pool",
  655             sizeof (prop_changenode_t),
  656             offsetof(prop_changenode_t, cn_treenode),
  657             legacy ? compare_dataset_names : compare_mountpoints, 0);
  658         if (clp->cl_pool == NULL) {
  659                 assert(uu_error() == UU_ERROR_NO_MEMORY);
  660                 (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
  661                 changelist_free(clp);
  662                 return (NULL);
  663         }
  664 
  665         clp->cl_tree = uu_avl_create(clp->cl_pool, NULL, UU_DEFAULT);
  666         clp->cl_gflags = gather_flags;
  667         clp->cl_mflags = mnt_flags;
  668 
  669         if (clp->cl_tree == NULL) {
  670                 assert(uu_error() == UU_ERROR_NO_MEMORY);
  671                 (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
  672                 changelist_free(clp);
  673                 return (NULL);
  674         }
  675 
  676         /*
  677          * If this is a rename or the 'zoned' property, we pretend we're
  678          * changing the mountpoint and flag it so we can catch all children in
  679          * change_one().
  680          *
  681          * Flag cl_alldependents to catch all children plus the dependents
  682          * (clones) that are not in the hierarchy.
  683          */
  684         if (prop == ZFS_PROP_NAME) {
  685                 clp->cl_prop = ZFS_PROP_MOUNTPOINT;
  686                 clp->cl_alldependents = B_TRUE;
  687         } else if (prop == ZFS_PROP_ZONED) {
  688                 clp->cl_prop = ZFS_PROP_MOUNTPOINT;
  689                 clp->cl_allchildren = B_TRUE;
  690         } else if (prop == ZFS_PROP_CANMOUNT) {
  691                 clp->cl_prop = ZFS_PROP_MOUNTPOINT;
  692         } else if (prop == ZFS_PROP_VOLSIZE) {
  693                 clp->cl_prop = ZFS_PROP_MOUNTPOINT;
  694         } else {
  695                 clp->cl_prop = prop;
  696         }
  697         clp->cl_realprop = prop;
  698 
  699         if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
  700             clp->cl_prop != ZFS_PROP_SHARENFS &&
  701             clp->cl_prop != ZFS_PROP_SHARESMB)
  702                 return (clp);
  703 
  704         /*
  705          * If watching SHARENFS or SHARESMB then
  706          * also watch its companion property.
  707          */
  708         if (clp->cl_prop == ZFS_PROP_SHARENFS)
  709                 clp->cl_shareprop = ZFS_PROP_SHARESMB;
  710         else if (clp->cl_prop == ZFS_PROP_SHARESMB)
  711                 clp->cl_shareprop = ZFS_PROP_SHARENFS;
  712 
  713         if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
  714             (clp->cl_gflags & CL_GATHER_ITER_MOUNTED)) {
  715                 /*
  716                  * Instead of iterating through all of the dataset children we
  717                  * gather mounted dataset children from MNTTAB
  718                  */
  719                 if (zfs_iter_mounted(zhp, changelist_add_mounted, clp) != 0) {
  720                         changelist_free(clp);
  721                         return (NULL);
  722                 }
  723         } else if (clp->cl_alldependents) {
  724                 if (zfs_iter_dependents(zhp, 0, B_TRUE, change_one, clp) != 0) {
  725                         changelist_free(clp);
  726                         return (NULL);
  727                 }
  728         } else if (zfs_iter_children(zhp, 0, change_one, clp) != 0) {
  729                 changelist_free(clp);
  730                 return (NULL);
  731         }
  732 
  733         /*
  734          * We have to re-open ourselves because we auto-close all the handles
  735          * and can't tell the difference.
  736          */
  737         if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
  738             ZFS_TYPE_DATASET)) == NULL) {
  739                 changelist_free(clp);
  740                 return (NULL);
  741         }
  742 
  743         /*
  744          * Always add ourself to the list.  We add ourselves to the end so that
  745          * we're the last to be unmounted.
  746          */
  747         cn = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changenode_t));
  748         cn->cn_handle = temp;
  749         cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
  750             zfs_is_mounted(temp, NULL);
  751         cn->cn_shared = zfs_is_shared(temp, NULL, NULL);
  752         cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
  753         cn->cn_needpost = B_TRUE;
  754 
  755         uu_avl_node_init(cn, &cn->cn_treenode, clp->cl_pool);
  756         uu_avl_index_t idx;
  757         if (uu_avl_find(clp->cl_tree, cn, NULL, &idx) == NULL) {
  758                 uu_avl_insert(clp->cl_tree, cn, idx);
  759         } else {
  760                 free(cn);
  761                 zfs_close(temp);
  762         }
  763 
  764         /*
  765          * If the mountpoint property was previously 'legacy', or 'none',
  766          * record it as the behavior of changelist_postfix() will be different.
  767          */
  768         if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) {
  769                 /*
  770                  * do not automatically mount ex-legacy datasets if
  771                  * we specifically set canmount to noauto
  772                  */
  773                 if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
  774                     ZFS_CANMOUNT_NOAUTO)
  775                         clp->cl_waslegacy = B_TRUE;
  776         }
  777 
  778         return (clp);
  779 }

Cache object: b86fd280ab44165e424894fcef34e21c


[ 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.