2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* CDDL HEADER START
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the terms of the
|
|
|
|
* Common Development and Distribution License (the "License").
|
|
|
|
* You may not use this file except in compliance with the License.
|
|
|
|
*
|
|
|
|
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
|
|
* or http://www.opensolaris.org/os/licensing.
|
|
|
|
* See the License for the specific language governing permissions
|
|
|
|
* and limitations under the License.
|
|
|
|
*
|
|
|
|
* When distributing Covered Code, include this CDDL HEADER in each
|
|
|
|
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
|
|
* If applicable, add the following below this CDDL HEADER, with the
|
|
|
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
|
|
|
* information: Portions Copyright [yyyy] [name of copyright owner]
|
|
|
|
*
|
|
|
|
* CDDL HEADER END
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2010-05-28 20:45:14 +00:00
|
|
|
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
|
2008-11-20 20:01:55 +00:00
|
|
|
* Use is subject to license terms.
|
|
|
|
*
|
|
|
|
* Portions Copyright 2007 Ramprakash Jelari
|
2020-07-13 16:19:18 +00:00
|
|
|
* Copyright (c) 2014, 2020 by Delphix. All rights reserved.
|
2017-02-07 22:02:27 +00:00
|
|
|
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
|
2018-10-02 19:30:58 +00:00
|
|
|
* Copyright (c) 2018 Datto Inc.
|
2008-11-20 20:01:55 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <libintl.h>
|
|
|
|
#include <libuutil.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <zone.h>
|
|
|
|
|
|
|
|
#include <libzfs.h>
|
|
|
|
|
|
|
|
#include "libzfs_impl.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Structure to keep track of dataset state. Before changing the 'sharenfs' or
|
|
|
|
* 'mountpoint' property, we record whether the filesystem was previously
|
|
|
|
* mounted/shared. This prior state dictates whether we remount/reshare the
|
|
|
|
* dataset after the property has been changed.
|
|
|
|
*
|
|
|
|
* The interface consists of the following sequence of functions:
|
|
|
|
*
|
|
|
|
* changelist_gather()
|
|
|
|
* changelist_prefix()
|
|
|
|
* < change property >
|
|
|
|
* changelist_postfix()
|
|
|
|
* changelist_free()
|
|
|
|
*
|
|
|
|
* Other interfaces:
|
|
|
|
*
|
|
|
|
* changelist_remove() - remove a node from a gathered list
|
|
|
|
* changelist_rename() - renames all datasets appropriately when doing a rename
|
|
|
|
* changelist_unshare() - unshares all the nodes in a given changelist
|
|
|
|
* changelist_haszonedchild() - check if there is any child exported to
|
|
|
|
* a local zone
|
|
|
|
*/
|
|
|
|
typedef struct prop_changenode {
|
|
|
|
zfs_handle_t *cn_handle;
|
|
|
|
int cn_shared;
|
|
|
|
int cn_mounted;
|
|
|
|
int cn_zoned;
|
|
|
|
boolean_t cn_needpost; /* is postfix() needed? */
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_node_t cn_treenode;
|
2008-11-20 20:01:55 +00:00
|
|
|
} prop_changenode_t;
|
|
|
|
|
|
|
|
struct prop_changelist {
|
|
|
|
zfs_prop_t cl_prop;
|
|
|
|
zfs_prop_t cl_realprop;
|
|
|
|
zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_pool_t *cl_pool;
|
|
|
|
uu_avl_t *cl_tree;
|
2008-11-20 20:01:55 +00:00
|
|
|
boolean_t cl_waslegacy;
|
|
|
|
boolean_t cl_allchildren;
|
|
|
|
boolean_t cl_alldependents;
|
2008-12-03 20:09:06 +00:00
|
|
|
int cl_mflags; /* Mount flags */
|
|
|
|
int cl_gflags; /* Gather request flags */
|
2008-11-20 20:01:55 +00:00
|
|
|
boolean_t cl_haszonedchild;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the property is 'mountpoint', go through and unmount filesystems as
|
|
|
|
* necessary. We don't do the same for 'sharenfs', because we can just re-share
|
|
|
|
* with different options without interrupting service. We do handle 'sharesmb'
|
|
|
|
* since there may be old resource names that need to be removed.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
changelist_prefix(prop_changelist_t *clp)
|
|
|
|
{
|
|
|
|
prop_changenode_t *cn;
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_walk_t *walk;
|
2008-11-20 20:01:55 +00:00
|
|
|
int ret = 0;
|
2020-07-13 16:19:18 +00:00
|
|
|
boolean_t commit_smb_shares = B_FALSE;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
|
|
|
|
clp->cl_prop != ZFS_PROP_SHARESMB)
|
|
|
|
return (0);
|
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
while ((cn = uu_avl_walk_next(walk)) != NULL) {
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/* if a previous loop failed, set the remaining to false */
|
|
|
|
if (ret == -1) {
|
|
|
|
cn->cn_needpost = B_FALSE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are in the global zone, but this dataset is exported
|
|
|
|
* to a local zone, do nothing.
|
|
|
|
*/
|
|
|
|
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
|
|
|
|
continue;
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
if (!ZFS_IS_VOLUME(cn->cn_handle)) {
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* Do the property specific processing.
|
|
|
|
*/
|
|
|
|
switch (clp->cl_prop) {
|
|
|
|
case ZFS_PROP_MOUNTPOINT:
|
2020-09-01 23:14:16 +00:00
|
|
|
if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
|
|
|
|
break;
|
2008-11-20 20:01:55 +00:00
|
|
|
if (zfs_unmount(cn->cn_handle, NULL,
|
2008-12-03 20:09:06 +00:00
|
|
|
clp->cl_mflags) != 0) {
|
2008-11-20 20:01:55 +00:00
|
|
|
ret = -1;
|
|
|
|
cn->cn_needpost = B_FALSE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ZFS_PROP_SHARESMB:
|
|
|
|
(void) zfs_unshare_smb(cn->cn_handle, NULL);
|
2020-07-13 16:19:18 +00:00
|
|
|
commit_smb_shares = B_TRUE;
|
2008-11-20 20:01:55 +00:00
|
|
|
break;
|
2017-02-07 22:02:27 +00:00
|
|
|
|
2010-08-26 16:52:41 +00:00
|
|
|
default:
|
|
|
|
break;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-13 16:19:18 +00:00
|
|
|
if (commit_smb_shares)
|
|
|
|
zfs_commit_smb_shares();
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_walk_end(walk);
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
if (ret == -1)
|
|
|
|
(void) changelist_postfix(clp);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the property is 'mountpoint' or 'sharenfs', go through and remount and/or
|
|
|
|
* reshare the filesystems as necessary. In changelist_gather() we recorded
|
|
|
|
* whether the filesystem was previously shared or mounted. The action we take
|
|
|
|
* depends on the previous state, and whether the value was previously 'legacy'.
|
|
|
|
* For non-legacy properties, we only remount/reshare the filesystem if it was
|
|
|
|
* previously mounted/shared. Otherwise, we always remount/reshare the
|
|
|
|
* filesystem.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
changelist_postfix(prop_changelist_t *clp)
|
|
|
|
{
|
|
|
|
prop_changenode_t *cn;
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_walk_t *walk;
|
2008-11-20 20:01:55 +00:00
|
|
|
char shareopts[ZFS_MAXPROPLEN];
|
|
|
|
int errors = 0;
|
2020-07-13 16:19:18 +00:00
|
|
|
boolean_t commit_smb_shares = B_FALSE;
|
|
|
|
boolean_t commit_nfs_shares = B_FALSE;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we're changing the mountpoint, attempt to destroy the underlying
|
|
|
|
* mountpoint. All other datasets will have inherited from this dataset
|
|
|
|
* (in which case their mountpoints exist in the filesystem in the new
|
|
|
|
* location), or have explicit mountpoints set (in which case they won't
|
|
|
|
* be in the changelist).
|
|
|
|
*/
|
2018-10-02 19:30:58 +00:00
|
|
|
if ((cn = uu_avl_last(clp->cl_tree)) == NULL)
|
2008-11-20 20:01:55 +00:00
|
|
|
return (0);
|
|
|
|
|
2020-09-01 23:14:16 +00:00
|
|
|
if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
|
|
|
|
!(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT))
|
2008-11-20 20:01:55 +00:00
|
|
|
remove_mountpoint(cn->cn_handle);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We walk the datasets in reverse, because we want to mount any parent
|
|
|
|
* datasets before mounting the children. We walk all datasets even if
|
|
|
|
* there are errors.
|
|
|
|
*/
|
2018-10-02 19:30:58 +00:00
|
|
|
if ((walk = uu_avl_walk_start(clp->cl_tree,
|
|
|
|
UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
while ((cn = uu_avl_walk_next(walk)) != NULL) {
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
boolean_t sharenfs;
|
|
|
|
boolean_t sharesmb;
|
2009-07-02 22:44:48 +00:00
|
|
|
boolean_t mounted;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
boolean_t needs_key;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are in the global zone, but this dataset is exported
|
|
|
|
* to a local zone, do nothing.
|
|
|
|
*/
|
|
|
|
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Only do post-processing if it's required */
|
|
|
|
if (!cn->cn_needpost)
|
|
|
|
continue;
|
|
|
|
cn->cn_needpost = B_FALSE;
|
|
|
|
|
|
|
|
zfs_refresh_properties(cn->cn_handle);
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
if (ZFS_IS_VOLUME(cn->cn_handle))
|
2008-11-20 20:01:55 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remount if previously mounted or mountpoint was legacy,
|
|
|
|
* or sharenfs or sharesmb property is set.
|
|
|
|
*/
|
|
|
|
sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
|
|
|
|
shareopts, sizeof (shareopts), NULL, NULL, 0,
|
|
|
|
B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
|
|
|
|
|
|
|
|
sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
|
|
|
|
shareopts, sizeof (shareopts), NULL, NULL, 0,
|
|
|
|
B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
needs_key = (zfs_prop_get_int(cn->cn_handle,
|
|
|
|
ZFS_PROP_KEYSTATUS) == ZFS_KEYSTATUS_UNAVAILABLE);
|
|
|
|
|
2020-09-01 23:14:16 +00:00
|
|
|
mounted = (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) ||
|
|
|
|
zfs_is_mounted(cn->cn_handle, NULL);
|
2009-07-02 22:44:48 +00:00
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
if (!mounted && !needs_key && (cn->cn_mounted ||
|
2009-07-02 22:44:48 +00:00
|
|
|
((sharenfs || sharesmb || clp->cl_waslegacy) &&
|
|
|
|
(zfs_prop_get_int(cn->cn_handle,
|
|
|
|
ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) {
|
|
|
|
|
|
|
|
if (zfs_mount(cn->cn_handle, NULL, 0) != 0)
|
|
|
|
errors++;
|
|
|
|
else
|
|
|
|
mounted = TRUE;
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/*
|
2009-07-02 22:44:48 +00:00
|
|
|
* If the file system is mounted we always re-share even
|
|
|
|
* if the filesystem is currently shared, so that we can
|
|
|
|
* adopt any new options.
|
2008-11-20 20:01:55 +00:00
|
|
|
*/
|
2020-07-13 16:19:18 +00:00
|
|
|
if (sharenfs && mounted) {
|
2008-11-20 20:01:55 +00:00
|
|
|
errors += zfs_share_nfs(cn->cn_handle);
|
2020-07-13 16:19:18 +00:00
|
|
|
commit_nfs_shares = B_TRUE;
|
|
|
|
} else if (cn->cn_shared || clp->cl_waslegacy) {
|
2008-11-20 20:01:55 +00:00
|
|
|
errors += zfs_unshare_nfs(cn->cn_handle, NULL);
|
2020-07-13 16:19:18 +00:00
|
|
|
commit_nfs_shares = B_TRUE;
|
|
|
|
}
|
|
|
|
if (sharesmb && mounted) {
|
2008-11-20 20:01:55 +00:00
|
|
|
errors += zfs_share_smb(cn->cn_handle);
|
2020-07-13 16:19:18 +00:00
|
|
|
commit_smb_shares = B_TRUE;
|
|
|
|
} else if (cn->cn_shared || clp->cl_waslegacy) {
|
2008-11-20 20:01:55 +00:00
|
|
|
errors += zfs_unshare_smb(cn->cn_handle, NULL);
|
2020-07-13 16:19:18 +00:00
|
|
|
commit_smb_shares = B_TRUE;
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2020-07-13 16:19:18 +00:00
|
|
|
if (commit_nfs_shares)
|
|
|
|
zfs_commit_nfs_shares();
|
|
|
|
if (commit_smb_shares)
|
|
|
|
zfs_commit_smb_shares();
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_walk_end(walk);
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
return (errors ? -1 : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is this "dataset" a child of "parent"?
|
|
|
|
*/
|
2021-05-15 09:53:14 +00:00
|
|
|
static boolean_t
|
2008-11-20 20:01:55 +00:00
|
|
|
isa_child_of(const char *dataset, const char *parent)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = strlen(parent);
|
|
|
|
|
|
|
|
if (strncmp(dataset, parent, len) == 0 &&
|
|
|
|
(dataset[len] == '@' || dataset[len] == '/' ||
|
|
|
|
dataset[len] == '\0'))
|
|
|
|
return (B_TRUE);
|
|
|
|
else
|
|
|
|
return (B_FALSE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we rename a filesystem, child filesystem handles are no longer valid
|
|
|
|
* since we identify each dataset by its name in the ZFS namespace. As a
|
|
|
|
* result, we have to go through and fix up all the names appropriately. We
|
|
|
|
* could do this automatically if libzfs kept track of all open handles, but
|
|
|
|
* this is a lot less work.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
|
|
|
|
{
|
|
|
|
prop_changenode_t *cn;
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_walk_t *walk;
|
2016-06-15 21:28:36 +00:00
|
|
|
char newname[ZFS_MAX_DATASET_NAME_LEN];
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while ((cn = uu_avl_walk_next(walk)) != NULL) {
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* Do not rename a clone that's not in the source hierarchy.
|
|
|
|
*/
|
2013-12-06 22:20:22 +00:00
|
|
|
if (!isa_child_of(cn->cn_handle->zfs_name, src))
|
2008-11-20 20:01:55 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Destroy the previous mountpoint if needed.
|
|
|
|
*/
|
2013-12-06 22:20:22 +00:00
|
|
|
remove_mountpoint(cn->cn_handle);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
(void) strlcpy(newname, dst, sizeof (newname));
|
2016-09-29 20:33:09 +00:00
|
|
|
(void) strlcat(newname, cn->cn_handle->zfs_name + strlen(src),
|
|
|
|
sizeof (newname));
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2013-12-06 22:20:22 +00:00
|
|
|
(void) strlcpy(cn->cn_handle->zfs_name, newname,
|
|
|
|
sizeof (cn->cn_handle->zfs_name));
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2018-10-02 19:30:58 +00:00
|
|
|
|
|
|
|
uu_avl_walk_end(walk);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
|
|
|
|
* unshare all the datasets in the list.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
|
|
|
|
{
|
|
|
|
prop_changenode_t *cn;
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_walk_t *walk;
|
2008-11-20 20:01:55 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (clp->cl_prop != ZFS_PROP_SHARENFS &&
|
|
|
|
clp->cl_prop != ZFS_PROP_SHARESMB)
|
|
|
|
return (0);
|
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
while ((cn = uu_avl_walk_next(walk)) != NULL) {
|
2008-11-20 20:01:55 +00:00
|
|
|
if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0)
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
2020-07-13 16:19:18 +00:00
|
|
|
zfs_commit_proto(proto);
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_walk_end(walk);
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if there is any child exported to a local zone in a given changelist.
|
|
|
|
* This information has already been recorded while gathering the changelist
|
|
|
|
* via changelist_gather().
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
changelist_haszonedchild(prop_changelist_t *clp)
|
|
|
|
{
|
|
|
|
return (clp->cl_haszonedchild);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove a node from a gathered list.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
changelist_remove(prop_changelist_t *clp, const char *name)
|
|
|
|
{
|
|
|
|
prop_changenode_t *cn;
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_walk_t *walk;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
|
|
|
|
return;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
while ((cn = uu_avl_walk_next(walk)) != NULL) {
|
2008-11-20 20:01:55 +00:00
|
|
|
if (strcmp(cn->cn_handle->zfs_name, name) == 0) {
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_remove(clp->cl_tree, cn);
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_close(cn->cn_handle);
|
|
|
|
free(cn);
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_walk_end(walk);
|
2008-11-20 20:01:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-10-02 19:30:58 +00:00
|
|
|
|
|
|
|
uu_avl_walk_end(walk);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Release any memory associated with a changelist.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
changelist_free(prop_changelist_t *clp)
|
|
|
|
{
|
|
|
|
prop_changenode_t *cn;
|
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
if (clp->cl_tree) {
|
|
|
|
uu_avl_walk_t *walk;
|
|
|
|
|
|
|
|
if ((walk = uu_avl_walk_start(clp->cl_tree,
|
|
|
|
UU_WALK_ROBUST)) == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while ((cn = uu_avl_walk_next(walk)) != NULL) {
|
|
|
|
uu_avl_remove(clp->cl_tree, cn);
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_close(cn->cn_handle);
|
|
|
|
free(cn);
|
|
|
|
}
|
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_walk_end(walk);
|
|
|
|
uu_avl_destroy(clp->cl_tree);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
if (clp->cl_pool)
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_pool_destroy(clp->cl_pool);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
free(clp);
|
|
|
|
}
|
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
/*
|
|
|
|
* Add one dataset to changelist
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
changelist_add_mounted(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
|
|
|
prop_changelist_t *clp = data;
|
|
|
|
prop_changenode_t *cn;
|
|
|
|
uu_avl_index_t idx;
|
|
|
|
|
|
|
|
ASSERT3U(clp->cl_prop, ==, ZFS_PROP_MOUNTPOINT);
|
|
|
|
|
|
|
|
if ((cn = zfs_alloc(zfs_get_handle(zhp),
|
|
|
|
sizeof (prop_changenode_t))) == NULL) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
|
|
|
|
cn->cn_handle = zhp;
|
|
|
|
cn->cn_mounted = zfs_is_mounted(zhp, NULL);
|
|
|
|
ASSERT3U(cn->cn_mounted, ==, B_TRUE);
|
|
|
|
cn->cn_shared = zfs_is_shared(zhp);
|
|
|
|
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
|
|
|
|
cn->cn_needpost = B_TRUE;
|
|
|
|
|
|
|
|
/* Indicate if any child is exported to a local zone. */
|
|
|
|
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
|
|
|
|
clp->cl_haszonedchild = B_TRUE;
|
|
|
|
|
|
|
|
uu_avl_node_init(cn, &cn->cn_treenode, clp->cl_pool);
|
|
|
|
|
|
|
|
if (uu_avl_find(clp->cl_tree, cn, NULL, &idx) == NULL) {
|
|
|
|
uu_avl_insert(clp->cl_tree, cn, idx);
|
|
|
|
} else {
|
|
|
|
free(cn);
|
|
|
|
zfs_close(zhp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
static int
|
|
|
|
change_one(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
|
|
|
prop_changelist_t *clp = data;
|
|
|
|
char property[ZFS_MAXPROPLEN];
|
|
|
|
char where[64];
|
2019-08-28 22:02:58 +00:00
|
|
|
prop_changenode_t *cn = NULL;
|
2016-10-13 21:02:07 +00:00
|
|
|
zprop_source_t sourcetype = ZPROP_SRC_NONE;
|
|
|
|
zprop_source_t share_sourcetype = ZPROP_SRC_NONE;
|
2019-08-28 22:02:58 +00:00
|
|
|
int ret = 0;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We only want to unmount/unshare those filesystems that may inherit
|
|
|
|
* from the target filesystem. If we find any filesystem with a
|
|
|
|
* locally set mountpoint, we ignore any children since changing the
|
|
|
|
* property will not affect them. If this is a rename, we iterate
|
|
|
|
* over all children regardless, since we need them unmounted in
|
|
|
|
* order to do the rename. Also, if this is a volume and we're doing
|
|
|
|
* a rename, then always add it to the changelist.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) &&
|
|
|
|
zfs_prop_get(zhp, clp->cl_prop, property,
|
|
|
|
sizeof (property), &sourcetype, where, sizeof (where),
|
|
|
|
B_FALSE) != 0) {
|
2019-08-28 22:02:58 +00:00
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are "watching" sharenfs or sharesmb
|
|
|
|
* then check out the companion property which is tracked
|
|
|
|
* in cl_shareprop
|
|
|
|
*/
|
|
|
|
if (clp->cl_shareprop != ZPROP_INVAL &&
|
|
|
|
zfs_prop_get(zhp, clp->cl_shareprop, property,
|
|
|
|
sizeof (property), &share_sourcetype, where, sizeof (where),
|
|
|
|
B_FALSE) != 0) {
|
2019-08-28 22:02:58 +00:00
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (clp->cl_alldependents || clp->cl_allchildren ||
|
|
|
|
sourcetype == ZPROP_SRC_DEFAULT ||
|
|
|
|
sourcetype == ZPROP_SRC_INHERITED ||
|
|
|
|
(clp->cl_shareprop != ZPROP_INVAL &&
|
|
|
|
(share_sourcetype == ZPROP_SRC_DEFAULT ||
|
|
|
|
share_sourcetype == ZPROP_SRC_INHERITED))) {
|
|
|
|
if ((cn = zfs_alloc(zfs_get_handle(zhp),
|
|
|
|
sizeof (prop_changenode_t))) == NULL) {
|
2019-08-28 22:02:58 +00:00
|
|
|
ret = -1;
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cn->cn_handle = zhp;
|
2008-12-03 20:09:06 +00:00
|
|
|
cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
|
|
|
|
zfs_is_mounted(zhp, NULL);
|
2008-11-20 20:01:55 +00:00
|
|
|
cn->cn_shared = zfs_is_shared(zhp);
|
|
|
|
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
|
|
|
|
cn->cn_needpost = B_TRUE;
|
|
|
|
|
|
|
|
/* Indicate if any child is exported to a local zone. */
|
|
|
|
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
|
|
|
|
clp->cl_haszonedchild = B_TRUE;
|
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_node_init(cn, &cn->cn_treenode, clp->cl_pool);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_index_t idx;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
if (uu_avl_find(clp->cl_tree, cn, NULL, &idx) == NULL) {
|
|
|
|
uu_avl_insert(clp->cl_tree, cn, idx);
|
2008-11-20 20:01:55 +00:00
|
|
|
} else {
|
2018-10-02 19:30:58 +00:00
|
|
|
free(cn);
|
2019-08-28 22:02:58 +00:00
|
|
|
cn = NULL;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!clp->cl_alldependents)
|
2019-08-28 22:02:58 +00:00
|
|
|
ret = zfs_iter_children(zhp, change_one, data);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we added the handle to the changelist, we will re-use it
|
|
|
|
* later so return without closing it.
|
|
|
|
*/
|
|
|
|
if (cn != NULL)
|
|
|
|
return (ret);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
2019-08-28 22:02:58 +00:00
|
|
|
out:
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (ret);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2018-10-11 04:13:13 +00:00
|
|
|
compare_props(const void *a, const void *b, zfs_prop_t prop)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
|
|
|
const prop_changenode_t *ca = a;
|
|
|
|
const prop_changenode_t *cb = b;
|
|
|
|
|
2018-10-11 04:13:13 +00:00
|
|
|
char propa[MAXPATHLEN];
|
|
|
|
char propb[MAXPATHLEN];
|
|
|
|
|
|
|
|
boolean_t haspropa, haspropb;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2018-10-11 04:13:13 +00:00
|
|
|
haspropa = (zfs_prop_get(ca->cn_handle, prop, propa, sizeof (propa),
|
|
|
|
NULL, NULL, 0, B_FALSE) == 0);
|
|
|
|
haspropb = (zfs_prop_get(cb->cn_handle, prop, propb, sizeof (propb),
|
|
|
|
NULL, NULL, 0, B_FALSE) == 0);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2018-10-11 04:13:13 +00:00
|
|
|
if (!haspropa && haspropb)
|
|
|
|
return (-1);
|
|
|
|
else if (haspropa && !haspropb)
|
|
|
|
return (1);
|
|
|
|
else if (!haspropa && !haspropb)
|
|
|
|
return (0);
|
|
|
|
else
|
|
|
|
return (strcmp(propb, propa));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*ARGSUSED*/
|
|
|
|
static int
|
|
|
|
compare_mountpoints(const void *a, const void *b, void *unused)
|
|
|
|
{
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* When unsharing or unmounting filesystems, we need to do it in
|
|
|
|
* mountpoint order. This allows the user to have a mountpoint
|
|
|
|
* hierarchy that is different from the dataset hierarchy, and still
|
2018-10-11 04:13:13 +00:00
|
|
|
* allow it to be changed.
|
2008-11-20 20:01:55 +00:00
|
|
|
*/
|
2018-10-11 04:13:13 +00:00
|
|
|
return (compare_props(a, b, ZFS_PROP_MOUNTPOINT));
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2018-10-11 04:13:13 +00:00
|
|
|
/*ARGSUSED*/
|
|
|
|
static int
|
|
|
|
compare_dataset_names(const void *a, const void *b, void *unused)
|
|
|
|
{
|
|
|
|
return (compare_props(a, b, ZFS_PROP_NAME));
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given a ZFS handle and a property, construct a complete list of datasets
|
|
|
|
* that need to be modified as part of this process. For anything but the
|
|
|
|
* 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
|
|
|
|
* Otherwise, we iterate over all children and look for any datasets that
|
|
|
|
* inherit the property. For each such dataset, we add it to the list and
|
|
|
|
* mark whether it was shared beforehand.
|
|
|
|
*/
|
|
|
|
prop_changelist_t *
|
2008-12-03 20:09:06 +00:00
|
|
|
changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
|
|
|
|
int mnt_flags)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
|
|
|
prop_changelist_t *clp;
|
|
|
|
prop_changenode_t *cn;
|
|
|
|
zfs_handle_t *temp;
|
|
|
|
char property[ZFS_MAXPROPLEN];
|
2009-08-18 18:43:27 +00:00
|
|
|
boolean_t legacy = B_FALSE;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL)
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For mountpoint-related tasks, we want to sort everything by
|
|
|
|
* mountpoint, so that we mount and unmount them in the appropriate
|
|
|
|
* order, regardless of their position in the hierarchy.
|
|
|
|
*/
|
|
|
|
if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
|
|
|
|
prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
|
|
|
|
prop == ZFS_PROP_SHARESMB) {
|
2009-08-18 18:43:27 +00:00
|
|
|
|
|
|
|
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
|
|
|
|
property, sizeof (property),
|
|
|
|
NULL, NULL, 0, B_FALSE) == 0 &&
|
|
|
|
(strcmp(property, "legacy") == 0 ||
|
|
|
|
strcmp(property, "none") == 0)) {
|
|
|
|
legacy = B_TRUE;
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
clp->cl_pool = uu_avl_pool_create("changelist_pool",
|
2008-11-20 20:01:55 +00:00
|
|
|
sizeof (prop_changenode_t),
|
2018-10-02 19:30:58 +00:00
|
|
|
offsetof(prop_changenode_t, cn_treenode),
|
2018-10-11 04:13:13 +00:00
|
|
|
legacy ? compare_dataset_names : compare_mountpoints, 0);
|
2008-11-20 20:01:55 +00:00
|
|
|
if (clp->cl_pool == NULL) {
|
|
|
|
assert(uu_error() == UU_ERROR_NO_MEMORY);
|
|
|
|
(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
|
|
|
|
changelist_free(clp);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
clp->cl_tree = uu_avl_create(clp->cl_pool, NULL, UU_DEFAULT);
|
2008-12-03 20:09:06 +00:00
|
|
|
clp->cl_gflags = gather_flags;
|
|
|
|
clp->cl_mflags = mnt_flags;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
if (clp->cl_tree == NULL) {
|
2008-11-20 20:01:55 +00:00
|
|
|
assert(uu_error() == UU_ERROR_NO_MEMORY);
|
|
|
|
(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
|
|
|
|
changelist_free(clp);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is a rename or the 'zoned' property, we pretend we're
|
|
|
|
* changing the mountpoint and flag it so we can catch all children in
|
|
|
|
* change_one().
|
|
|
|
*
|
|
|
|
* Flag cl_alldependents to catch all children plus the dependents
|
|
|
|
* (clones) that are not in the hierarchy.
|
|
|
|
*/
|
|
|
|
if (prop == ZFS_PROP_NAME) {
|
|
|
|
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
|
|
|
|
clp->cl_alldependents = B_TRUE;
|
|
|
|
} else if (prop == ZFS_PROP_ZONED) {
|
|
|
|
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
|
|
|
|
clp->cl_allchildren = B_TRUE;
|
|
|
|
} else if (prop == ZFS_PROP_CANMOUNT) {
|
|
|
|
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
|
|
|
|
} else if (prop == ZFS_PROP_VOLSIZE) {
|
|
|
|
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
|
|
|
|
} else {
|
|
|
|
clp->cl_prop = prop;
|
|
|
|
}
|
|
|
|
clp->cl_realprop = prop;
|
|
|
|
|
|
|
|
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
|
|
|
|
clp->cl_prop != ZFS_PROP_SHARENFS &&
|
2010-05-28 20:45:14 +00:00
|
|
|
clp->cl_prop != ZFS_PROP_SHARESMB)
|
2008-11-20 20:01:55 +00:00
|
|
|
return (clp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If watching SHARENFS or SHARESMB then
|
|
|
|
* also watch its companion property.
|
|
|
|
*/
|
|
|
|
if (clp->cl_prop == ZFS_PROP_SHARENFS)
|
|
|
|
clp->cl_shareprop = ZFS_PROP_SHARESMB;
|
|
|
|
else if (clp->cl_prop == ZFS_PROP_SHARESMB)
|
|
|
|
clp->cl_shareprop = ZFS_PROP_SHARENFS;
|
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
|
2018-10-11 04:13:13 +00:00
|
|
|
(clp->cl_gflags & CL_GATHER_ITER_MOUNTED)) {
|
2018-10-02 19:30:58 +00:00
|
|
|
/*
|
|
|
|
* Instead of iterating through all of the dataset children we
|
|
|
|
* gather mounted dataset children from MNTTAB
|
|
|
|
*/
|
|
|
|
if (zfs_iter_mounted(zhp, changelist_add_mounted, clp) != 0) {
|
|
|
|
changelist_free(clp);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
} else if (clp->cl_alldependents) {
|
2008-11-20 20:01:55 +00:00
|
|
|
if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) {
|
|
|
|
changelist_free(clp);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
} else if (zfs_iter_children(zhp, change_one, clp) != 0) {
|
|
|
|
changelist_free(clp);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have to re-open ourselves because we auto-close all the handles
|
|
|
|
* and can't tell the difference.
|
|
|
|
*/
|
|
|
|
if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
|
|
|
|
ZFS_TYPE_DATASET)) == NULL) {
|
|
|
|
changelist_free(clp);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Always add ourself to the list. We add ourselves to the end so that
|
|
|
|
* we're the last to be unmounted.
|
|
|
|
*/
|
|
|
|
if ((cn = zfs_alloc(zhp->zfs_hdl,
|
|
|
|
sizeof (prop_changenode_t))) == NULL) {
|
|
|
|
zfs_close(temp);
|
|
|
|
changelist_free(clp);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
cn->cn_handle = temp;
|
2008-12-03 20:09:06 +00:00
|
|
|
cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
|
|
|
|
zfs_is_mounted(temp, NULL);
|
2008-11-20 20:01:55 +00:00
|
|
|
cn->cn_shared = zfs_is_shared(temp);
|
|
|
|
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
|
|
|
|
cn->cn_needpost = B_TRUE;
|
|
|
|
|
2018-10-02 19:30:58 +00:00
|
|
|
uu_avl_node_init(cn, &cn->cn_treenode, clp->cl_pool);
|
|
|
|
uu_avl_index_t idx;
|
|
|
|
if (uu_avl_find(clp->cl_tree, cn, NULL, &idx) == NULL) {
|
|
|
|
uu_avl_insert(clp->cl_tree, cn, idx);
|
2008-11-20 20:01:55 +00:00
|
|
|
} else {
|
2018-10-02 19:30:58 +00:00
|
|
|
free(cn);
|
|
|
|
zfs_close(temp);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the mountpoint property was previously 'legacy', or 'none',
|
|
|
|
* record it as the behavior of changelist_postfix() will be different.
|
|
|
|
*/
|
2009-08-18 18:43:27 +00:00
|
|
|
if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) {
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* do not automatically mount ex-legacy datasets if
|
|
|
|
* we specifically set canmount to noauto
|
|
|
|
*/
|
|
|
|
if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
|
|
|
|
ZFS_CANMOUNT_NOAUTO)
|
|
|
|
clp->cl_waslegacy = B_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (clp);
|
|
|
|
}
|